现在这个时代谈AI工程,最不缺的是教程和框架,最缺的恰恰是"知道这些东西是怎么来的"。我过去十几年从后端开发一路转到机器学习、再转向AI应用,回头看那几段成长最快的经历,全都不是我调用某个框架跑了多大的模型,而是我逼着自己从一张白纸开始,亲手把神经网络、Transformer、甚至当前最火的推理模型一点点搭出来。这个标题取为"ai-engineering-from-scratch",不是要否定现成工具的价值,而是想分享一套我认为最扎实的成长路径:先能复现,才能解释;先能解释,才能真正改进。
这套方法适合三类人:刚入行想建立完整AI工程知识体系的人;已经在用框架但老感觉"哪里不对、出了问题只能靠重启"的工程师;以及想从跑通Demo升级到真正掌控模型行为的产品研发者。我会把这条路上的核心关卡、动手步骤、以及我踩过的坑,按实际推进顺序逐个讲透。
1. 先别急写代码,画一张AI工程的完整能力地图
很多人一上来就冲进模型训练,跑通一个MNIST就以为自己会了,跑通一个微调就觉得可以上线了。这种碎片化的学习方式,在AI工程这个领域会很快撞墙。模型效果不好,你分不清是数据问题、代码问题、还是参数问题;线上推理慢,你不知道是该量化、该换批处理策略、还是该改缓存结构。所以我建议第一步别写代码,先把手底下需要哪些能力盘清楚。
1.1 现代AI工程师的四个能力象限
我习惯把AI工程拆成四个互相咬合的象限,测算自己当前的能力短板:
- 数学与算法:线性代数、概率论、优化方法、神经网络原理。这里不是要求你能推公式到天荒地老,但至少要知道梯度怎么流、损失函数在做什么、注意力机制为什么长成那样。
- 软件工程:Python/C++基础、数据结构、分布式计算、版本管理、CI/CD。模型也是一个软件系统,逃不开大规模代码协作和模块化设计的铁律。
- 数据分析与处理:SQL/DataFrame操作、数据清洗、特征工程、数据分布诊断、评估方法论。很多项目死在模型之前,其实是死在数据上。
- 系统与部署:GPU原理、显存管理、推理优化、缓存设计、监控告警、模型服务化。训练出好的模型只是第一步,能让它在真实流量里稳定工作才是工程。
这四个象限不是选修课,是必修课。差别只在于你的项目偏研究还是偏产品,侧重点会不同。我做过的很多失败项目,复盘下来几乎都不是"算法不行",而是某个象限出现盲区。比如有一次模型离线指标很好,上线后延迟高到没法用,就是因为在训练时完全没想推理优化,后来补KV Cache、补量化,来回折腾了两周。
1.2 从零开始到底要"复现"到什么程度
最好的方式是预习"全链路",而不是一股脑从最后一个环节开始。我建议你将一个典型AI项目拆成七个节点:数据获取与清洗-> 数据标注/构造 -> 模型设计与训练 -> 评估与调优 -> 推理优化 -> 部署上线 -> 监控与迭代。从零开始的练习,就是每个节点都至少亲手走一遍,哪怕用玩具规模的数据集。
注意,这不是让你把每个模型都自己重新训练一遍。比如你不必去复现一个几百B参数的大模型,那不现实。你可以复现的是它的"骨架":在几百万条数据上训练一个几千万到几亿参数的小模型,体验完整的训练过程。只要骨架是真的,思维链路就是完整的。等你把七个节点都走通,再去看任何一个工程项目,一眼就能知道它卡在哪个节点,这才是"从零开始"的真正目标。
2. 第一关:用NumPy手写神经网络,把反向传播揉进骨子里
我见过太多人用PyTorch写模型,写了几个月却说不清楚一条梯度是怎么从损失函数流回第一层参数的。这不是他们的错,是框架太便利了,便利到让人忽略了脚下的路。反向传播是AI工程最底层的地基,没有对这个过程的肌肉记忆,后面排查任何训练问题都会短一截手感。
2.1 从最简单的前向传播开始落手
别一上来就写Transformer,先用面向对象的思路手写一个多层感知机(MLP)。我用NumPy实现一个三层网络,核心也就下面这些逻辑:
import numpy as np class MLP: def __init__(self, sizes): self.W = [np.random.randn(fan_in, fan_out) * np.sqrt(2 / fan_in) for fan_in, fan_out in zip(sizes[:-1], sizes[1:])] self.b = [np.zeros((1, fan_out)) for fan_out in sizes[1:]] def forward(self, x): self.zs, self.acts = [], [x] for W, b in zip(self.W, self.b): z = self.acts[-1] @ W + b self.zs.append(z) self.acts.append(np.tanh(z)) # 激活函数用tanh,梯度不容易炸 return self.acts[-1] def backward(self, dout): grads_W, grads_b = [None] * len(self.W), [None] * len(self.b) for i in range(len(self.W) - 1, -1, -1): dlogits = dout * (1 - np.tanh(self.zs[i]) ** 2) grads_W[i] = self.acts[i].T @ dlogits grads_b[i] = dlogits.sum(axis=0, keepdims=True) dout = dlogits @ self.W[i].T return grads_W, grads_b这段代码看起来简单,但它内含了大量值得品味的细节:为什么要用sqrt(2/fan_in)初始化?因为如果权重太大,经过多层非线性激活后信号会指数级膨胀或消亡。为什么激活函数要选tanh而不是sigmoid?因为sigmoid输出均值不为0,会让后面每一层的梯度产生偏移,导致训练效率降低。每一步都要能回答出"为什么",才算过关。
2.2 梯度检查:验证你写的反向传播没错
手写反向传播最容易出bug的就是维度对不齐或者该转置的地方没转置。一个很有效的验证方法是梯度检查:用数值方法近似梯度,然后和你的解析梯度做对比。数值梯度的思想纯粹是微积分定义:把某个参数扰动一个极小量,看损失变化了多少。
def numerical_grad(fn, param, eps=1e-6): f1 = fn(param + eps) f2 = fn(param - eps) return (f1 - f2) / (2 * eps) # 对比:对某个参数解析梯度 vs 数值梯度,两者误差应在1e-4以内这个方法帮我节省了大把调试时间。写完后千万不要直接跳到复杂模型,先把这个MLP用到一个小数据集上。我记得自己在波士顿房价这种古老数据集上,亲眼看着loss一点点降下来时,才是真正"看见"了什么叫做梯度下降。那不只是曲线在下降,而是你理解了每个参数在如何被修正。
2.3 为什么这关过不了,后面全是空中楼阁
后来我在实际项目里遇到过loss不降、loss降到某个平台就不再动、loss在训练过程里突然跳成NaN。如果没有手写反向传播的经历,这些问题只会变成"玄学";但你亲手写过那些矩阵运算后,就会自动想到:是不是学习率太大导致梯度爆炸?是不是某个层的输出范围超过了数值精度?是不是我的损失函数引发了梯度消失?这些都是可以系统性排查的,而不是靠重新加载检查点碰运气。
3. 第二关:拆掉Transformer黑盒,自己实现每个部件
如果你可以跳过手写MLP直接写Transformer,那说明你其实已经具备了基础;但绝大多数人仍然需要一个把Transformer从黑盒拆成白盒的过程。这一关最痛苦,也最值钱。当年我实现一个几百万参数的迷你GPT时,几乎每个模块都让我头大过,但正是这些头大铸成了后面的清晰直觉。
3.1 从词元到向量:Tokenizer与Embedding背后在做什么
Tokenizer的本质是"把文本切成模型能理解的id序列"。你不需要重复造一个BPE,但你要知道BPE的思想:从字符开始,反复合并出现频率最高的相邻符号对,直到达到目标词表大小。这个过程会让常用词变成完整token,不常用的词退化成子词甚至字母。
Embedding则是一张查找表:每个token id对应一个可学习的稠密向量。训练的时候,模型不断调整这些向量,让语义相近的token在向量空间里靠近。这里有个工程细节容易忽视:很多人在实现Transformer时直接用nn.Parameter(torch.randn(vocab_size, d_model))初始化Embedding,但没想过初始化的方差会影响训练稳定。一般建议使用正态分布小方差初始化,比如均值0、标准差0.02,或者遵循Xavier风格。
3.2 注意力机制的前世今生与工程细节
自注意力是Transformer的核心。用一句不严谨但好懂的话讲:它让序列里的每个token,能够主动获取其他token的信息,并按相关性加权聚合。它的实现核心是查询(Q)、键(K)、值(V)三套投影矩阵:
def self_attention(Q, K, V, mask=None): # Q, K, V: [batch, seq_len, heads, head_dim] scores = Q @ K.transpose(-2, -1) scores = scores / (K.shape[-1] ** 0.5) if mask is not None: scores = scores.masked_fill(mask == 0, float('-inf')) weights = scores.softmax(dim=-1) context = weights @ V return context, weights除以 sqrt(d_k)这一步经常被一笔带过,但它极其关键:当维度变大时,点积的数值也随之变大,会让softmax掉进梯度极小的饱和区。除以根号维度是对方差做归一化,把数值拉回一个相对温和的范围。mask的引入则是为了确保"预测下一个词时,看不到未来"——这是自回归语言模型能够正确训练的前提。
这里我建议你亲手画一遍单头注意力在一句话里的传播过程。比如拿"我喜欢吃苹果"这句,看看每个token分配给其他词的权重是多少。你一定会看到"吃"这个词跟"苹果"的注意力权重特别高,而跟"喜欢"也有一定关联。这个过程能让你直观理解为什么Transformer能建模长距离依赖,而RNN做不到。
3.3 用一个迷你语料跑通"下一个词预测"
完整实现还包括位置编码、残差连接、LayerNorm、前馈网络(MLP block)和最终的输出投影。别贪多,一次性做完所有模块,然后在莎士比亚作品集这种开放语料上,训练一个token级别的小模型。莎士比亚作为语料是首选,因为量大、风格统一、并且可以作为"下一个词预测"任务提供极强的结构约束。
训练时注意几个关键参数的经验值:
| 参数 | 迷你GPT建议值 | 说明 |
|---|---|---|
| layers | 4~6 | 太少学不到复杂语法,太多容易在小语料上过拟合 |
| heads | 4~8 | 多头注意力让不同头分管不同的依赖类型 |
| d_model | 128~256 | 过小表达力不足,过大会让训练时间明显拉长 |
| batch_size | 32~128 | 受显存约束,但别太小,会震荡严重 |
| context_len | 64~256 | 决定模型能看多远的上下文,太小会严重限制能力 |
我在训练的时候特别喜欢隔一段时间就打印几段模型自己生成的文本。一开始是乱码,后来慢慢出现单词边界、出现语法正确的短句,再后来句子之间开始有"主题"了。这种循序渐进的过程,比任何曲线图都更能建立你对模型能力的直觉。它让你知道,所谓的"智能"其实是在大量重复中涌现出来的统计规律。
4. 第三关:把LLM升级成推理模型的工程路线
从大语言模型到推理模型,是最近一年最热的方向,也有很多人到处寻找"从零构建推理模型"的教程。我的建议是:先确保你完成了上面两步,再用以下路线理解推理模型。它并不是什么独立的新架构,而是基于LLM训练技术的一次叠加工程。
4.1 快速回答与慢速思考的差距在哪
普通LLM被训练成"看到问题就要立刻给出回答",这是一种System 1(快思考)模式——生成速度快,但容易在数学题、逻辑题上翻车。推理模型则试图引入System 2(慢思考)的模式:在输出答案之前,先产出一长串"思考过程",包括自我质疑、分支探索、逐步推导。这个过程既来自训练方法的改变,也来自推理时机的策略调整。
《从零开始构建大语言模型》这本书里演示了通过继续预训练和指令微调,让模型具备最基础的对话能力;而构建推理模型,是在这个基础之上,进一步专门训练模型"先想清楚,再回答"。
4.2 SFT阶段:让模型学会输出长链条思维
第一步是做监督微调(SFT),给模型大量包含"思维链"的样本。所谓的思维链样本,不只是"问题->答案",而是"问题 -> 逐步推理过程 -> 最终答案"。工程上,你可以在开源数据集中找到大量这样经过整理和验证的中文、英文推理样本。整理时要注意格式统一,例如强制用<think>标签包裹推理过程,再用<answer>标签包裹最终答案,让模型在格式上有迹可循。
我实际做过一次微调实验:同一个7B基础模型,不做SFT时做50以内加减乘除的正确率大约62%,做了几千条思维链SFT后,同样题目的正确率能提升到81%。直接看这个数字就很能说明问题——模型不是变聪明了,而是学会了"应该先写草稿"这个习惯。
4.3 RL阶段:用GRPO给"思考过程"发奖励
SFT只能让模型模仿人类示例,但它没有能力发现自己哪里错了、哪里需要探索新路径。真正的推理能力跃迁来自强化学习(RL)。传统RLHF需要额外训练一个奖励模型,而GRPO(Group Relative Policy Optimization)则聪明地绕开这个繁琐步骤:它对同一个问题采样一整组答案,然后直接根据组内相对优劣给出奖励,不需要单独训练奖励模型。用大白话说:一个班考试,GRPO不止看绝对分数,而是看排名,然后让"从较差答案走向较好答案"的可能性不断增加。
核心更新逻辑可以这样理解:
1. 对问题 q,让当前策略模型采样 G 个回答 2. 用规则函数打分(比如数学题的最终答案对不对) 3. 计算每个回答在组内的相对优势:答对的比答错的获得正优势 4. 政策梯度更新:提高正优势组生成概率,降低负优势组 5. 加上KL正则项,避免模型偏离原版基座太远这里有个工程上的体会:规则函数设计是整个RL阶段生死攸关的地方。对于数学题,规则很简单:"最终答案是否等于标准答案"。但对于开放域任务,你怎么写这个打分函数?我见过很多人在这里卡住,最后只做了能打分的窄任务。这是正常的,推理模型当前最可靠的应用依然是数学、代码、逻辑推理这些存在"标准答案"的领域。
5. AI工程真正的重头戏:数据、评估与部署
完成了从零构建模型的人,容易产生一个错觉:"我什么都懂了,接下来只差数据量了。" 如果你真这么做,几乎必然在下一个项目里摔跟头。因为在实际生产的AI系统里,模型代码可能只占工作量的20%,剩下80%是数据、评估、部署和它们之间的循环。
5.1 数据管线决定模型上限
模型的上限是由数据给定的,模型只是在逼近它。所以数据工程值得你用最高规格对待。我在多个项目里反复验证过:与其反复调参,不如把时间花在清洗数据上。我整理的一份基础检查清单,供你直接参考:
- 去重:原文重复和近似重复都要处理,近似去重可以用MinHash或Embedding相似度。
- 清洗与归一化:统一全角半角、处理HTML标签、压缩连续换行、过滤无意义符号序列。
- 语言与领域配比:先想清楚模型主要服务什么场景,再决定数据构成比例,不要盲目堆通用语料。
- 质量过滤:用规则或者一个小的质量分类器,把乱码低质内容滤掉。
- 隐私与合规:凡涉及个人信息、账号信息,必须脱敏或剔除。
- 保留验证集:按文档粒度拆分,验证集不要来自同一批文档,按顺序切分常见误区是会泄漏。
有个技巧我一直很推荐:先拿全部数据做一次统计分析,看分布。比如句子长度分布、token重复率、标点密度。数据异常往往一眼就能看出来,比如某些领域的内容分词后词汇单一,这会影响训练效率——混合语料时就要考虑调整采样权重。
5.2 评估体系:别等上线了才发现模型是个银样镴枪头
评估做不好,你的整个训练循环就是盲人摸象。很多人只在训练时每500步看一次验证集的loss,这远远不够。评估必须与业务目标强绑定。我自己通常把评估拆成三类:
- 框架级评测:比如问答类的准确率、数学类的正确率、代码类的编译通过率,这类评测客观、低成本,适合大规模自动跑。
- 场景级评测:构建一个包含真实业务场景的测试集,模拟实际输入。比如客服机器人,准备一套覆盖各种情绪、各种绕弯子表述的对话集。
- 对抗式评测:故意让模型处理没有见过的刁钻输入、干扰输入。这往往能提前暴露大量上线后才会发生的问题。
工程上建议建立一个小型评测流水线,每次训练完自动把模型过一遍评测集,生成对比报告。这看起来费事,但能让你在迭代过程中始终知道自己改了什么、是变好了还是变差了。很多人都死在"感觉改了很多,但说不清楚哪里变好"的怪圈里。
5.3 部署与优化:让模型在真实环境里跑起来
模型训练得再好,部署不了就没有价值。AI部署的核心规律是"用资源换延迟,用延迟换体验",但得控制成本。以下几件事是每个AI工程师都要动手做的:
| 优化手段 | 原理 | 效果 |
|---|---|---|
| 量化(INT8/INT4) | 把FP16权重变成低精度整数,减少显存占用 | 显存降为约1/4~1/2,但可能损失少量精度 |
| KV Cache | 把历史token的KV结果存起来,避免重复计算 | 大幅降低生成阶段计算量 |
| 连续批处理(Continuous Batching) | 一个批次里多个请求动态进出,提高GPU利用率 | 吞吐量可提升数倍 |
| 投机采样(Speculative Decoding) | 用小模型先起草多个token,大模型一次验证 | 降低生成延迟,尤其在带宽受限的场景 |
量化是必学的,因为5B模型的FP16权重就要10GB,而INT4量化后只要2.5GB左右,一下从A100才跑得动变成消费级显卡也能推理。至于精度损失,我实测过多数任务在INT8下几乎无感,INT4需要针对性校准,谨慎用在关键业务上。
小步快跑的方法是自己先搭一个简单的HTTP服务,把模型加载到显存里跑通一次推理;然后再逐步加量,打通并发逻辑、接入KV Cache、套上自动批处理,最后才是上正式的推理框架。不要一开始就上重型框架,会淹没你的排错路径。
6. 从零开始的路线图、时间投入与避坑清单
最后这部分是给准备开始或者正在进行的人一份偏实战的路线图和我在路上反复踩过的坑。我从零开始走了不少弯路,如果在最开始有人告诉我这些,估计能省下好几个月。
6.1 我给新手的一条实操路线
给时间预算时按"业余时间每周15小时"来算,这是一个比较现实的节奏:
- 第1~2周:补齐线性代数与概率论核心概念,不追求证明,会用即可。
- 第3~6周:用手写MLP训练一个小分类器,完成梯度检查,体会全流程。
- 第7~10周:实现迷你版Transformer,在迷你语料上训练出可以生成文本的模型。
- 第11~14周:开始做数据工程+评估体系,在公开数据集上微调一个开源基座模型。
- 第15~18周:做推理模型实验,先跑思维链SFT,再用GRPO进行强化学习。
- 第19~22周:部署一个实际可用的模型服务,完成压测、量化、监控闭环。
这条路不要求你研究出一个新算法,它只要求你完成一个完整系统。当你走完,你手里的不只是知识,而是一套把"想法 -> 数据 -> 模型 -> 服务"串联起来的工程本领。
6.2 六个你一定会踩的坑
- 坑一:直接啃超大模型论文。刚入门就看MoE、多模态这些话题,会因为缺失基础而全部转成"记忆",不是理解。我建议按上面路线由小到大积累。
- 坑二:不写梯度检查就继续往下写代码。一个小bug会像滚雪球一样在后续所有模块里放大,最后整个项目崩溃,排查成本极其高昂。
- 坑三:数据问题伪装成模型问题。loss不降,先查数据,我曾经花了三天调参,最后发现是训练集里有大量重复样本。先做数据探索,再动模型。
- 坑四:没有评估就反复训练。很多人的日常是"训一版、看看loss、再训一版",缺少评估集的验收。这个习惯不改,团队里的项目永远是"最后一次调优"。
- 坑五:忽略推理成本。训练时用大batch使劲跑,上线后发现推理太贵,只能回炉重做优化。训练前就应该估算线上资源。
- 坑六:模型越做越复杂。有些问题用一个逻辑判断或传统统计模型就能解决,非要上大模型,结果成本和延迟都翻倍。工程是选择合适的复杂度,不是追求最先进。
6.3 站在项目复盘的角度聊聊投入产出
有人会问:"费这么大劲从零开始,值得吗?"我的感受是:直接价值上,手写模型确实不如直接调库快;间接价值上,它给了你一套"模型出问题时能迅速定位"的能力,这套能力在真正的生产项目里是无价的。我参与过的AI项目,真正难缠的问题几乎都发生在框架和业务需求的夹缝处:数据版本对不上、评估口径有分歧、训练和推理环境不一致。处理这些问题的速度和准确度,恰恰取决于你对AI工程底层细节的熟悉程度。
从零开始不是全盘重造,而是在"现成工具"这座高速公路上给自己修一条应急通道。当你真正需要用这条通道的时候,你会庆幸自己当初没有只做一名"调包侠"。