从零构建AI工程能力:手写反向传播与Transformer实战
发布时间:2026/10/2 12:15:19来源:尧图网络
做了这么多年AI相关的工作我越来越发现一个现象很多人用框架用得贼溜PyTorch、TensorFlow的API信手拈来但一旦遇到网上搜不到的报错、模型效果死活提不上去、或者要在没有现成库的环境里部署就开始抓瞎。这个现象背后缺的不是调包能力而是对AI工程底层原理的理解。ai-engineering-from-scratch这个项目标题说白了就是一条从零开始构建AI工程能力的路线。它不是教你怎么调库、怎么套模板而是带着你从数学基础、手写实现、训练调试、部署评估一路走下来把AI工程这条链路完整走一遍。这篇文章我会把这套路线的核心思路、具体实操环节、以及我实际踩过的坑都拆开讲一遍。如果你是准备转行做AI工程的新手或者已经是框架熟手但想补齐底层认知这篇文章应该能帮你省下不少试错时间。1. AI工程的能力全景先看清地图再出发1.1 AI工程不是写模型是一条完整链路很多人对AI工程的理解就是训练模型这是个很普遍的误区。我在实际带项目的时候经常碰到这样的新人模型训练出来效果看着不错但一上线就崩数据分布一变化就失效或者推理速度慢到没法用。这些问题基本都不是模型本身的问题而是工程链路的问题。我习惯把AI工程拆成九个环节业务问题定义、数据采集与清洗、特征工程现在更常叫数据预处理、模型设计与实现、训练调优、评估验证、部署上线、监控运维、迭代更新。任何一个环节出问题整个系统都会出问题。更要命的是这九个环节在真实项目里的耗时比例和多数人想象的不一样。根据我自己做过的项目经验数据侧的投入通常能占到整个项目40%以上的时间模型训练和调优反而只占20%左右剩下的时间基本砸在部署、监控和迭代上。这就牵扯出一个关键认知AI工程师的能力模型应该是一个T型结构。横向要懂整条链路知道每个环节之间怎么衔接纵向要在某几个环节上有深度比如你特别擅长训练调优或者特别擅长推理性能优化。from-scratch的学习路线天然适合建立这种T型结构因为你每一步都是从原理出发链路之间的黏连感会非常强而不是像很多人那样只盯着模型训练那一小块。1.2 为什么从零实现是最稳的成长路径市面上并不缺AI课程和教程大部分走的是调包流路线——告诉你用这个函数就能实现注意力机制用那个API就能加载预训练模型。这种学习方式的短期反馈确实很好两三天就能跑通一个效果还不错的情感分析项目很有成就感。但问题在于它给你的是如何使用工具的能力而不是理解工具的能力。我这里不是否定工具的价值PyTorch、HuggingFace这些工具本身非常优秀我日常工作也离不开它们。我反对的是只会用工具的状态。举一个很实在的例子你在用PyTorch训练模型时遇到loss不降如果你理解反向传播的每一行计算逻辑、理解学习率和梯度之间的关系你排查的思路可能是在学习率策略、梯度消失、数据分布等问题上找原因。但如果你只把模型当黑盒遇到问题就只能去网上搜loss不降怎么办能搜到的往往只是别人的经验很难迁移到你的具体场景里。from-scratch的学习路径之所以稳因为它解决的问题不是怎么跑通一个模型而是模型为什么这样设计为什么这样训练这里出问题了根源在哪。这种能力是迁移性最强的。今天大模型火了你去做LLM应用明天多模态火了你去做多模态底层原理一通换框架、换模型架构都只是时间问题。2. 核心环节逐层拆解从手写反向传播到自注意力2.1 数学够用就行但要真懂说到从零开始学AI工程数学基础通常是最劝退的一关。我的观点很明确你不需要成为数学家但线性代数、概率论和微积分这三门课的核心概念必须真正理解注意是理解不是背下来。你需要的线性代数其实只有几个东西向量和矩阵的基本运算、矩阵乘法、转置、范数。你需要的微积分就是链式法则和偏导数这是反向传播的基石。概率论部分你需要的是分布、期望、方差、条件概率、极大似然估计的基本概念。就这些复杂的东西在工程里会由框架帮你算但你要能看懂公式的流向。怎么检验自己真懂了我给个土办法你用NumPy写一个两层的全连接神经网络手动实现前向传播、损失函数计算、反向传播和参数更新。这个练习我几乎推荐给每一个想进入AI工程的人因为当你不用PyTorch等框架的自动求导而是自己拿纸笔推梯度公式、再把它一行行写成代码时那种理解是任何课程都替代不了的。import numpy as np # 一个两层全连接网络的NumPy实现反向传播手动推导 class TwoLayerNet: def __init__(self, input_size, hidden_size, output_size): # 使用He初始化对ReLU激活更友好 self.W1 np.random.randn(input_size, hidden_size) * np.sqrt(2.0 / input_size) self.b1 np.zeros((1, hidden_size)) self.W2 np.random.randn(hidden_size, output_size) * np.sqrt(2.0 / hidden_size) self.b2 np.zeros((1, output_size)) def forward(self, X): self.z1 X.dot(self.W1) self.b1 self.a1 np.maximum(0, self.z1) # ReLU激活 self.z2 self.a1.dot(self.W2) self.b2 return self.z2 def backward(self, X, y, output): batch_size X.shape[0] # 输出层梯度 dz2 output - y dW2 self.a1.T.dot(dz2) / batch_size db2 np.sum(dz2, axis0, keepdimsTrue) / batch_size # 隐藏层梯度经过ReLU的导数 da1 dz2.dot(self.W2.T) dz1 da1 * (self.z1 0) dW1 X.T.dot(dz1) / batch_size db1 np.sum(dz1, axis0, keepdimsTrue) / batch_size return dW1, db1, dW2, db2这段代码虽然简单但里面藏了很多值得琢磨的细节为什么初始化要除以sqrt(input_size)为什么反向传播要除以batch_sizeReLU的梯度为什么用(z1 0)就能表示每一个细节都能延伸出一个面试题也都是实际调参时你需要具备的直觉。2.2 亲手实现Transformer当代AI工程的必修课如果说2020年之前AI工程的核心骨架还是CNN和RNN那到了现在Transformer已经成了几乎所有主流模型的基础架构。LLM、多模态模型、甚至推荐系统都在用Transformer的变体。所以from-scratch路线里亲手从零实现一个Transformer几乎是最重要的一课。很多教程会直接让你用PyTorch的nn.Transformer或HuggingFace的GPT2模型但这样写出来的代码基本封装了所有细节你什么都学不到。我的建议是分模块手写。先写多头注意力机制再写位置编码和前馈网络最后组装成一个完整的decoder-only模型。这里有一个所有新手都会困惑的点QKV到底是什么我用生活化类比解释一下。你在图书馆找书KKey相当于每本书的标签编号QQuery相当于你心里的检索需求VValue就是书架上的实体书。注意力机制做的工作就是根据你的检索需求Q去匹配每本书的标签K计算匹配度分数再按分数把实体书V加权取回来。匹配度高的书权重高匹配度低的书权重低。class SelfAttention(nn.Module): def __init__(self, embed_dim, num_heads): super().__init__() self.embed_dim embed_dim self.num_heads num_heads self.head_dim embed_dim // num_heads assert self.head_dim * num_heads embed_dim, embed_dim必须能被num_heads整除 self.W_q nn.Linear(embed_dim, embed_dim) self.W_k nn.Linear(embed_dim, embed_dim) self.W_v nn.Linear(embed_dim, embed_dim) self.out_proj nn.Linear(embed_dim, embed_dim) def forward(self, x, maskNone): batch_size, seq_len, _ x.shape Q self.W_q(x).view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2) K self.W_k(x).view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2) V self.W_v(x).view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2) # 缩放点积注意力除以sqrt(d_k)防止softmax梯度消失 scores torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.head_dim) if mask is not None: scores scores.masked_fill(mask 0, -1e9) attn_weights torch.softmax(scores, dim-1) out torch.matmul(attn_weights, V) out out.transpose(1, 2).contiguous().view(batch_size, seq_len, self.embed_dim) return self.out_proj(out)这个实现里最值得琢磨的是为什么要除以sqrt(head_dim)。解释起来并不复杂当维度增大时点积的结果数值会变大导致softmax输出趋于极端接近0或1梯度会变得非常小。除以sqrt(d)相当于把数值拉回到一个温和的范围。这种级别的理解靠调包是永远学不到的。2.3 训练稳定的关键参数与显存规划训练阶段是新手最容易心态爆炸的环节。loss不降、loss爆成NaN、显存不够、训练速度太慢每个问题都够喝一壶的。这里我先讲几个最核心的参数再讲一个我经常用来规划资源的公式。学习率恐怕是影响训练结果最重要的超参数。我过去的经验是学习率太小loss下降太慢学习率太大loss会震荡甚至在早期就发散。比较通用的做法是用warmup策略前若干步把学习率从很小的值逐步升到目标值之后再按余弦曲线衰减。这个问题在Transformer的训练里尤其明显。Batch size的选择不只是影响精度还直接影响显存和训练速度。在显存允许的范围内更大的batch size通常能带来更稳定的梯度但也不是越大越好过大时模型容易收敛到泛化能力较差的局部最优。这里我提供一个显存粗算的思路训练时的显存占用大约是参数量的12到20倍以字节计。也就是说一个7B参数的模型做全量微调按16倍估算差不多需要112GB显存。这个估算在选机器、估成本时非常管用。如果你是个人学习者没有多卡A100的预算也不用太焦虑。学习的早期阶段完全可以在小模型上完成理解原理远比训练一个大模型重要。等你需要跑7B甚至更大的模型时用LoRA这类参数高效微调方法可以大幅降低显存需求这是工程上非常实用的技能。3. 两条从零到一的最小实战项目3.1 用纯Python训练一个字符级语言模型理论知识学完光看不练约等于白学。我给新手推荐两个实战项目第一个是用纯NumPy或纯PyTorch实现一个字符级语言模型让模型学会写一段风格类似的文本。这个项目为什么好因为它用小数据量就能看到反馈而且数据不需要额外标注。你可以用莎士比亚的十四行诗、红楼梦的片段、或者你喜欢的任何中文文本作为训练语料。核心是让模型根据前面的字符预测下一个字符本质是一个字符级别的分类任务。当你看它从输出完全无意义的字符到慢慢学会输出有语法结构的文本那种成就感会给你坚持下去的动力。具体实现路径分四步。第一字符到token的映射构建字典第二用embedding把token变成向量第三搭一个小的LSTM或Transformer模型输入前面的字符序列预测下一个字符的概率分布第四训练时用交叉熵损失生成时用temperature参数控制随机性。每一步都不复杂但每一环都会用到前面学到的知识。3.2 做一个能跑的RAG问答系统第二个项目我建议做RAG检索增强生成这个方向在当前的AI工程实践里非常火热。核心思想很简单当你要让大模型回答问题时先从一个知识库里检索相关的文本片段把它们作为上下文一起喂给模型让模型的回答基于检索到的真实信息而不是凭空编造。原理不复杂但做起来细节极其多。第一个坑就是文档切分。我在刚做RAG时习惯把整篇文章丢给切分器结果检索出的片段内容非常割裂回答质量也很差。后来我总结出一套经验切分粒度要结合业务场景。如果是做规章制度问答500到800字一段比较合适如果是长篇小说知识问答可能得放到1000字以上。切分时尽量按段落或语义边界来避免硬切。第二个坑是embedding模型的选择。市面上的embedding模型参差不齐好的和差的检索效果能差出一大截。选模型时不能光看排行榜分数要拿自己的业务数据实测。第三个坑是检索结果的排序和后处理。实测下来只用向量相似度检索的召回效果往往不够好比较稳的方案是向量检索BM25关键词检索的混合模式再用一个rerank模型对召回结果精排。加上rerank环节后我的RAG系统答案准确率大概提升了20%左右这个提升非常可观。评估一个RAG系统不能只看最后答案好不好要分环节拆解检索阶段看召回率生成阶段看答案的忠实度和完整性。这样当最终效果不佳时你能定位到底是没检出来还是模型没用对而不是无头苍蝇一样乱调。3.3 工程设施要花多少钱算力选择的实操建议很多自学AI工程的人都会卡在算力这个问题上总觉得没有A100怎么搞AI。我的看法是学习阶段完全不需要大卡。你在本地用一张消费级显卡比如几年前的RTX 3060级别就足够跑通绝大多数教学项目7B以下的开源模型用LoRA也能在24GB显存的卡上玩得转。如果你的学习已经进入需要跑较大模型的阶段我再给一个性价比建议优先考虑云GPU按需租用而不是直接买硬件。因为训练任务往往是短时高消耗的你租一台配置拉满的机器跑48小时然后释放成本远低于买一张同级别显卡。不少云厂商都提供按小时计费的GPU实例实测下来非常灵活。真正需要长期跑推理服务的场景才值得考虑包月或买卡。训练时的工程技巧同样重要。混合精度训练FP16/BF16能显著降低显存占用和计算开销这在7B以上模型的训练里几乎是标配。梯度累积可以让你在batch size很小的情况下模拟出大batch的效果。我经常把这套组合用在本地小卡上跑微调效果立竿见影。4. 从实验到上线的工程化部署与迭代的实战细节4.1 模型导出与推理优化不同场景怎么选模型训练完了只是万里长征走完了一半另一半是让它稳定地在线上服务。我在不同的项目里用过三种部署方案各有各的适用场景。第一种是直接用PyTorch的TorchServe或HuggingFace的TGI框架部署优点是集成度高、上手快适合快速原型验证。第二种是用ONNX导出模型后用ONNX Runtime或TensorRT加速推理。这在延迟敏感的场景特别有用。我做过一个文本分类服务原生的PyTorch推理延迟在30毫秒左右导出到ONNX并开启FP16量化后延迟降到了8毫秒服务吞吐翻了快两倍。第三种是vLLM这类专门为LLM推理优化的框架。如果你要部署一个大模型在线服务vLLM的PagedAttention技术能大幅提升显存利用率。实测在同样显存下单卡吞吐差不多能提升5到10倍这个差距已经能直接影响成本了。选型的核心逻辑很简单看你的瓶颈是什么。如果是延迟优化推理引擎如果是吞吐做并发和批处理优化如果是成本考虑量化和模型蒸馏。没有一套方案通吃所有场景。4.2 评估不是测一下准确性那么简单模型上线之前的评估环节很多团队做得非常草率。跑一遍测试集出一个准确率或者F1分数看一眼差不多就部署了。这在真实工程里是很危险的。我在做AI工程时有一套自己的评估体系分三个层次。第一层是离线指标评估包括准确率、召回率、F1等。这一层告诉你模型在静态测试集上的表现。第二层是分场景评估不只看总体准确率还要按数据子集拆开看。比如一个客服意图识别系统你把退款和投诉这两个意图单独拎出来看准确率往往会发现总体指标被简单意图拉高了难样本的表现其实很差。第三层是badcase驱动迭代。我会专门收集线上预测失败的case逐个分析错误原因判断是数据标注问题、特征缺失还是模型理解能力不足。这种做法看起来费时间但实际上是模型效果提升的最快路径。这里多说一句数据回流。很多AI项目上线后效果下滑根源在于线上数据分布一直在变化而模型没有及时用新数据做更新。稳定的AI系统必须有一个数据回流机制线上积累的新样本经过清洗和人工标注后定期加入训练集进行增量训练。这个机制谁先做谁受益越早做效果越稳。5. 避坑指南我踩过的环境、数据与训练大坑5.1 环境配置的经典连环坑AI工程的环境配置问题是我这些年见过劝退新人最多的环节。很多人好不容易把代码写完了结果在装依赖这一步就卡了两天心态直接崩了。几个高频坑值得提前预防。Python版本和PyTorch版本不匹配这是最常见的。PyTorch新版本对Python版本有明确要求PyTorch 2.x系列通常要求Python 3.8及以上某些新特性甚至需要3.10以上。所以装PyTorch之前一定要先查官方文档的版本对应表。第二个常见坑是CUDA版本给环境配错了导致装了GPU版推理性能反而更差那多半是CUDA Toolkit和PyTorch对应的CUDA版本不一致。GPU驱动用的CUDA版本和PyTorch编译时链接的CUDA版本可以不一样PyTorch自带运行库。这个认知能帮你排查掉一半的CUDA报错问题。虚拟环境一定是必需品。我在团队里要求所有项目必须建独立的conda环境或venv环境哪怕是一个小demo。因为AI工程涉及的依赖太复杂不同的项目对同一依赖的版本要求经常冲突没有虚拟环境隔离你迟早会被解决了一个依赖又破坏了另一个项目的问题逼疯。5.2 数据侧的隐蔽陷阱数据是整个AI工程里最不能马虎的部分。我在这里分享几个我实际踩过或见过的数据坑希望你能绕开。标签错位是隐蔽性最强的数据问题。我见过一个项目训练集的标签因为Excel排序操作整体错位了一行模型训练出来效果奇差无比排查了整整两天才发现是数据对齐问题。这个问题的教训是代码里写数据加载逻辑时一定不要手动操作排序、筛选一切以代码为准。数据泄漏也是个很有意思的问题。如果你在数据预处理阶段用了全量数据的统计量比如用全量数据的均值和标准差做标准化然后在划分训练测试集之后评估看起来测试集指标很好但其实已经泄漏了上新数据时效果会大幅下滑。正确做法是只用训练集的统计量来做标准化再应用到测试集。还有一个几乎所有新手都会犯的错排序导致的时间泄漏。你在做时序预测时如果数据的切分方式没有保证训练集在时间上早于测试集模型就等于偷看了未来。训练时指标漂亮上线后一团糟。这个教训我深有体会做时间序列的AI工程一定不能随机划分数据。5.3 常见问题速查表我把实操中最常遇到问题的排查思路整理成了一张速查表建议收藏备用。问题现象优先排查方向处理建议loss不降学习率设置尝试降低学习率或加入warmuploss变成NaN学习率过大或梯度爆炸降低学习率开启梯度裁剪训练速度极慢数据加载瓶颈检查dataloader的num_workers设置显存不足batch size过大减小batch size使用梯度累积训练集指标好、测试集差过拟合增加数据量、加入正则化、做数据增强上线后效果大跌训练测试分布不一致检查数据泄漏引入新数据回流推理延迟过高模型过大或推理引擎不匹配考虑ONNX导出或量化加速这张表不会解决所有问题但能在你遇到这类问题时快速给出起点避免无头苍蝇式排查。回到开头那个话题ai-engineering-from-scratch这条路走下来确实比跟着教程调包慢很多但慢得有价值。我个人在实际带人和自学的过程中最大的感受是知识体系这东西拼图是自己一块一块拼出来的才牢固别人递给你一整幅拼图你看着好看却说不清每一块为什么在那里。所以我的建议很朴素从今天开始把第一个全连接网络用NumPy手写出来然后一路写下去。别急着跑大模型先把手底下的每一步都走稳后面自然就平了。
网站建设高端定制企业官网