翻 LLaMA 的 forward 代码会看到两个 RMSNorm:一个在 embedding 之后,一个在 lm_head 之前。为什么输出层前面还要再归一化一次?这个问题的答案就是 pre-LN 架构的隐藏账单:归一化全部搬进分支之后,残差主干上没有任何一个 op 约束向量的范数,几十层分支输出直接累加,范数随深度一路膨胀。这篇文章推导这个增长的量级,再看模型用哪些手段接住它。
pre-LN 的结构回顾
pre-LN 残差块写成
$$ x_{l+1} = x_l + f_l\big(\mathrm{RMSNorm}(x_l)\big),\qquad l=1,\dots,L, $$
归一化只作用在分支入口,主干 $x_l$ 是纯累加通路。对比 post-LN($x_{l+1}=\mathrm{LN}(x_l+f_l(x_l))$,归一化直接压在主干上),pre-LN 的梯度沿主干无损回传,深层参数拿到和浅层参数同等量级的梯度——这是它取代 post-LN 成为事实标准的直接原因。
代价是:没有任何东西控制 $\|x_l\|$。
范数随深度怎么涨
做最朴素的随机假设:每个分支输出 $f_l(\hat x_l)$ 的范数有界且各层输出在方向上近乎随机(相互夹角接近直角),$x_0$ 是 embedding。那么
$$ \|x_L\|^2 = \Big\|\sum_{l=0}^{L} f_l\Big\|^2 = \sum_{l}\|f_l\|^2 + 2\sum_{i
交叉项因随机正交而抵消,平方范数随层数线性累加:
$$ \|x_L\| \approx \|x_0\| + \Theta(\sqrt{L})\cdot s,\qquad s = \text{每层分支输出的典型范数}, $$
即范数按 $\sqrt{L}$ 的速率增长。若各层输出方向存在系统性同向分量(训练早期常见,浅层特征被深层反复读取),交叉项不抵消,速率退化为 $\Theta(L)$。实测的 GPT 系模型,残差流范数随深度的曲线介于两者之间,对数坐标下斜率在 $1/2$ 和 $1$ 之间("Transformer Circuits" 系列的测量大致如此)。
增长的直接后果:谁来接住这个大范数
残差流最终要喂给 lm_head 算 logits:$\mathrm{logits} = W_e\,\hat x$(输出投影常与 embedding 共享权重 $W_e$)。$W_e$ 的行范数有限,$\hat x$ 范数膨胀会让 logits 随之放大,softmax 温度漂移、loss 数值不稳。解法直白:在 lm_head 之前放一个 RMSNorm,把范数重新钉回 $\sqrt{d}$ 量级。这就是 LLaMA 系列输出端那个 RMSNorm 的存在理由——它不是架构审美,是对残差流增长的对账。
第二个后果更隐蔽:分支函数 $f_l$ 的输入是 $\mathrm{RMSNorm}(x_l)$,尺度已经被归一化掉了,所以各层分支看到的输入量级一致,这本身是稳定的来源。但 $f_l$ 的输出没有任何约束。标准做法是控制初始化:GPT 系把每层的输出投影(attn 的 $W_O$、FFN 的 $W_2$)用标准差 $\sigma/\sqrt{2L}$ 初始化(LLaMA 源码里是 std=0.02 配合 fan_in),使初始时刻分支输出的典型范数是 $1/\sqrt{L}$ 量级,L 层累加起来恰好与主干范数同量级。初始账是平的,剩下的问题就交给训练自己调节。
为什么"先膨胀、再归一"不是免费午餐
一个自然的疑问:反正输出端有 final RMSNorm 接住范数,膨胀本身有害吗?部分有害。残差流范数大意味着主干承载的信息尺度大,对参数扰动的相对敏感度下降(等效于隐式学习率收缩),训练动态会变钝。更实质的问题是"有效深度":当 $\|x_l\|$ 远大于分支输出的范数时,第 $l$ 层的输出在累加向量里的占比是 $O(1/\sqrt{l})$,深层的增量被越来越大的存量淹没——pre-LN 网络实际上用不满它的深度,表达能力和梯度稳定性做了不等价交换。文献里缓解这个问题的方向包括 DeepNorm(残差加权,见本系列后续篇)和残差分支上显式乘衰减系数的做法,核心思路都是给累加一个反膨胀的权重,让深层贡献重新可见。
小结
pre-LN 用"归一化进分支"换来梯度稳定,账单是残差主干无人看管:在分支输出随机正交的假设下范数按 $\sqrt{L}$ 增长,存在同向分量时退化到 $L$。模型用三处设计对账:输出端 final RMSNorm 接住范数、分支输出投影的 $1/\sqrt{L}$ 初始化压住初始增长、部分架构再给残差主干加权重。理解这条账单的用处在于:当你看到某个新架构在残差路径上动了手脚,先问它是在优化梯度流还是在对账范数增长,两者经常是同一件事的两面。