【声明】本博客所有内容均为个人业余时间创作所述技术案例均来自公开开源项目如GithubApache基金会不涉及任何企业机密或未公开技术如有侵权请联系删除标题237、【AI】【模型部署】基座模型研究反向传播背景上篇 blog【AI】【模型部署】基座模型研究硬标签与软标签把训练目标讲清了真实标签默认是硬标签one-hot但模型输出Q QQ一直是软的为了缓解过度自信、给其他 token 一点机会可以用标签平滑或知识蒸馏把目标换成软标签推理时还能靠温度与 top-k/top-p 采样调节多样性。无论目标用硬标签还是软标签最后都要算一个损失交叉熵再面对同一个问题知道错了多少之后怎么知道每个参数该改多少这正是本篇要讲的反向传播。模型部署训练循环里loss.backward()只占一行但它承担了整个训练的归因工作把最后的误差按贡献大小分摊到成千上万个参数上。这篇把backward()拆开从链式法则讲到代码。从链式法则说起反向传播的数学内核是链式法则chain rule复合函数求导外层导数乘以内层导数。一个最简单的两层函数y w x b , L ( y − t ) 2 y wx b,\qquad L (y-t)^2ywxb,L(y−t)2想求损失对参数w ww的导数就一层层往回乘∂ L ∂ w ∂ L ∂ y ⋅ ∂ y ∂ w 2 ( y − t ) ⋅ x \frac{\partial L}{\partial w} \frac{\partial L}{\partial y}\cdot\frac{\partial y}{\partial w} 2(y-t)\cdot x∂w∂L​∂y∂L​⋅∂w∂y​2(y−t)⋅x图 2 展示了这条链先算L LL对y yy的敏感度2 ( y − t ) 2(y-t)2(y−t)再乘y yy对w ww的敏感度x xx。敏感度就是导数的物理含义——w ww动一点点损失会跟着动多少。计算图前向算数值反向算梯度把上面的x → y → L x \to y \to Lx→y→L每一步都看成一个节点就得到一张计算图computational graph前向从输入往输出边走边算数值并把每一步的中间结果记下来反向从损失节点出发往回走每个节点用上游传来的梯度 × 本节点局部导数算出对流经变量的梯度。因为反向走的是同一条图的逆序所以整体代价只比前向多一个常数倍约 2~3 倍这就是反向模式自动微分reverse-mode automatic differentiation也是各框架autograd的实现方式。梯度就是敏感度一个参数的梯度∂ L / ∂ w \partial L/\partial w∂L/∂w回答的就是把w ww调大一点损失会变大还是变小、变多少。它的符号给出方向绝对值给出幅度。整张网络里成千上万个参数反向传播会一次性把每个参数的梯度都算出来——这正是它不可替代的地方参数量太大逐个数值求导根本不可行而反向传播让求全部梯度和前向同阶。为什么不用数值求导最朴素的想法是扰动一个参数、看损失变化多少也就是数值微分。它有两个致命问题代价太高每动一个参数都要重新前向一次N NN个参数就要N NN次前向千亿参数直接不可行精度差用有限差分近似导数会引入截断误差还容易被浮点误差淹没。反向传播只做一次前向 一次反向就把所有参数的梯度都算出来代价与前向同阶——这才是它能训练大模型的根本原因。softmax 交叉熵的梯度为什么漂亮上篇讲到F.cross_entropy内部是 softmax 加负对数。把这两步合起来对logits求导会得到一个极其简洁的结果∂ L ∂ z p − y \frac{\partial L}{\partial z} p - y∂z∂L​p−y其中p pp是 softmax 后的预测概率y yy是真实 one-hot 标签。预测到哪里、真实在哪里差多少梯度就是多少。图 3 用一个 5 词表的例子展示正确项p − y p-yp−y为负梯度把它的概率往上推其余项为正把它们的概率往下压。这也解释了为什么交叉熵好训练——梯度天然带方向不需要额外的缩放。最小例子手推一次更新用最前面那个y w x b , L ( y − t ) 2 ywxb,\ L(y-t)^2ywxb,L(y−t)2的例子设x 1 , b 0 , t 1 x1,\ b0,\ t1x1,b0,t1当前w 0.5 w0.5w0.5步骤计算结果前向y w x b y wxbywxb0.5 0.50.5损失L ( y − t ) 2 L(y-t)^2L(y−t)20.25 0.250.25梯度2 ( y − t ) ⋅ x 2(y-t)\cdot x2(y−t)⋅x− 1.0 -1.0−1.0更新w ← w − η ⋅ ( − 1.0 ) w \leftarrow w-\eta\cdot(-1.0)w←w−η⋅(−1.0)0.5 η 0.5\eta0.5η若学习率η 0.1 \eta0.1η0.1w ww从0.5 0.50.5变成0.6 0.60.6y yy更接近目标1 11损失随之下降。真实网络只是把这一步在每层、每个参数上重复并用计算图自动完成。图 5 是这一步的几何直觉沿着负梯度方向把参数往损失更低的地方挪一点点。梯度怎么穿过整张 Transformer在真实模型里反向传播要依次穿过输出层 → 每一层的均方根归一化Root Mean Square NormalizationRMSNorm→ 前馈网络Feed-Forward NetworkFFN→ 注意力 → 词嵌入。以第 231 篇同款的微型模型为例结构是词嵌入 若干 Block 归一化 输出头xself.tok_emb(idx)# 词嵌入forblkinself.blocks:# 每个 Block: RMSNorm 注意力 RMSNorm FFNxblk(x,cos,sin)returnself.lm_head(self.norm(x))# 输出头 → logits前向是从上到下走一遍反向就是从lm_head的梯度开始一层层往回乘直到词嵌入。注意力里的 matmul、softmax、RMSNorm、SiLU 激活每个算子都有对应的局部导数——框架在backward()时自动调用它们。三个接口zero_grad / backward / step上篇代码里的opt.zero_grad(); loss.backward(); opt.step()三者分工明确接口做什么少了它会怎样opt.zero_grad()把上一轮梯度清零梯度累加更新错乱loss.backward()反向传播填好每个参数的.grad没有梯度无法学习opt.step()用.grad更新参数光算不改loss 不动opt是优化器。最朴素的更新是随机梯度下降Stochastic Gradient DescentSGDw ← w − η ∂ L / ∂ w w \leftarrow w - \eta\,\partial L/\partial ww←w−η∂L/∂w。实践中常用AdamW自适应学习率 权重衰减它对不同参数自动调整步长收敛更稳——微型模型用的就是torch.optim.AdamW。为什么还要梯度裁剪与 warmup反向传播把很多层的导数连乘层数一深就容易出事若每层导数约0.5 0.50.5连乘十层就是0.5 10 ≈ 0.001 0.5^{10}\approx0.0010.510≈0.001梯度消失若每层约2 22连乘十层就是2 10 1024 2^{10}10242101024梯度爆炸。真实预训练因此加两道保险上篇提过梯度裁剪gradient clipping给梯度的范数设上限防止一步跨太大把权重打飞学习率 warmup 后衰减先小步热身再逐渐放大避免一开始就震荡。这些在微型实验里被省掉了但放大到千亿参数时是必需品。把它接回预训练循环现在再看整条链路就完整了前向得到 logits交叉熵算出错了多少反向传播把误差分摊成每个参数的梯度优化器按梯度更新权重然后重复。上篇的 loss 从 7.3883 一路降到 0.7821就是这一套循环把语言统计规律一点点压进权重的过程。图 4 是这条循环的总览前向 → 损失 → 反向 → 更新重复万亿次。一句话记忆反向传播是链式法则在计算图上的反向应用前向算数值反向用上游梯度 × 本层局部导数逐层求出每个参数的梯度softmax 与交叉熵组合后对 logits 的梯度恰好是p − y p-yp−ybackward()填好.grad优化器SGD 或 AdamW按梯度把参数往低处挪一点深层网络还要靠梯度裁剪与 warmup 保持稳定。OK本篇先到这里如有疑问欢迎评论区留言讨论祝各位功力大涨技术更上一层楼更多内容见下篇 blog