6. 残差连接与 pre-norm
6.1 残差为什么能救深层网络
一个残差块写成
\[ y=x+F(x). \]假设损失为 \(\mathcal{L}\),由链式法则
\[ \frac{\partial\mathcal{L}}{\partial x} =\frac{\partial\mathcal{L}}{\partial y}\frac{\partial y}{\partial x} =\frac{\partial\mathcal{L}}{\partial y}\left(I+\frac{\partial F}{\partial x}\right) =\frac{\partial\mathcal{L}}{\partial y} +\frac{\partial\mathcal{L}}{\partial y}\frac{\partial F}{\partial x}. \]关键在第一个等号后的 \(I\):梯度有一条“直通”路径,不经过 F 的雅可比。即使 \(F\) 的导数很小(深层网络常见的梯度消失),梯度也不会变成 0。
把 \(L\) 个块串起来,展开乘积:
\[ \frac{\partial\mathcal{L}}{\partial x_0} =\frac{\partial\mathcal{L}}{\partial x_L}\prod_{\ell=1}^{L}\left(I+J_\ell\right), \]其中 \(J_\ell=\partial F_\ell/\partial x_{\ell-1}\)。把乘积展开成 \(2^L\) 项,每一项对应“跳过某些块”的路径;其中“全跳过”的那项就是恒等映射。所以深层网络至少有 \(2^L\) 条梯度路径,而不是只有一条连乘。
6.2 pre-norm:先归一化,再进子层
Qwen3 每个 decoder 层的实际数据流(model/block.go):
最后整个模型再算一次 final_norm,然后接 lm_head。注意两个细节:
- 残差加的是归一化之前的 \(h\),不是 Attention/MLP 的输入;
- 归一化只发生在子层入口,残差主干(residual stream)本身不被归一化,所以它的尺度可以逐层增长,但每个子层看到的输入始终是“标准化”的。
这和 post-norm(先加残差再归一化)相对。post-norm 在很深的网络里需要小心 warmup,否则早期梯度不稳定;pre-norm 把归一化放在子层前,主干保持恒等路径,训练更稳,是现代 LLM 的默认选择。
注意:pre-norm 并不意味着“数值永远不炸”。残差主干的范数会随层数增长,所以最后还要一个 final norm 再进 lm_head;这也是为什么 RMSNorm 的尺度不变性很重要。