Transformer|反向传播(Backpropagation)(5):残差连接与归一化的选择

阅读提醒:前面我们推导了 Transformer 反向传播的完整主干。但有一个关键细节被一笔带过了——RMSNorm 到底放在哪里?这个看似微小的设计选择,直接影响了梯度能不能顺畅地流过几十层网络。这篇我们来彻底搞清楚。接下来我们要拿起放大镜来看细节了。祝食用愉快~🥱

导言:归一化的种类和演化

为什么需要归一化?

图 1 归一化示意图

“ 神经网络是一层叠一层的结构。每一层的输出是下一层的输入。如果某一层的输出数值分布发生了剧烈变化(比如突然变得很大或很小),下一层就会对此措手不及: ”

第1层输出:[0.5, -0.3, 0.8]       ← 正常范围
第2层处理后:[15.2, -9.7, 22.1] ← 突然变大了
第3层处理后:[502, -318, 891] ← 继续膨胀
...
第10层:[NaN, NaN, NaN] ← 爆炸了

归一化就是在每一层之间“踩一脚刹车”,把数值拉回稳定的范围。

主要的归一化方法

图 2 多种归一化图示

BatchNorm(2015):
对一个 batch 里同一个特征维度做归一化
在 CV 里很成功,但在 NLP 里有问题:
- 不同句子长度不一,batch 统计量不稳定
- 推理时 batch size 为 1,统计量不可靠

LayerNorm(2016):
对同一个样本的所有特征维度做归一化
不依赖 batch,适合 NLP
原始 Transformer(2017)用的就是这个

RMSNorm(2019):
LayerNorm 的简化版,去掉了均值中心化,只做缩放
计算更快,效果相当甚至更好
现在的主流选择(LLaMA、Qwen、Gemma 等)

LayerNorm 和 RMSNorm 的区别:

LayerNorm:
μ = mean(x)
σ = std(x)
x̂ = (x - μ) / σ ← 先减均值,再除标准差
output = γ ⊙ x̂ + β ← 两个可学习参数 γ 和 β

RMSNorm:
rms = sqrt(mean(x²))
x̂ = x / rms ← 只除均方根,不减均值
output = γ ⊙ x̂ ← 只有一个可学习参数 γ

为什么 RMSNorm 更受欢迎?

1. 计算量更小:少了 mean 和 β 的计算
2. 实践中效果相当:减均值这一步似乎没那么必要
3. 大模型里每一点计算节省都很重要

“ 但比归一化方法更重要的问题是:归一化放在哪里? ”

两种位置:Post-Norm vs Pre-Norm

Post-Norm vs Pre-Norm

Post-Norm(原始 Transformer,2017)

归一化放在残差相加 之后:

x_in
│
├───────────────────────┐
│ │(残差直通)
▼ │
子模块(Attention/FFN) │
│ │
▼ │
x_sub │
│ │
▼ │
x_mid = x_in + x_sub ←─┘(残差相加)
│
▼
LayerNorm(x_mid) ← Norm 在加法之后
│
▼
x_out

Pre-Norm(GPT-2 开始,现在主流)

归一化放在残差相加 之前(子模块之前):

x_in
│
├───────────────────────┐
│ │(残差直通)
▼ │
RMSNorm(x_in) │
│ │
▼ │
x_norm │
│ │
▼ │
子模块(Attention/FFN) │
│ │
▼ │
x_sub │
│ │
▼ │
x_out = x_in + x_sub ←─┘(残差相加,之后没有 Norm)

关键区别:在 Pre-Norm 里,残差相加之后不再有任何变换。

为什么选择 Pre-Norm?答案在反向传播里

“ 要理解这个问题,我们得先彻底搞清楚残差连接的反向传播。 ”

残差连接

前向传播的本质

残差连接做的事情极其简单:

其中 是子模块(Attention 或 FFN)。

这个加法意味着:

输出 = 原始输入 + 子模块的增量

打个比方:
x_in = 你原来的理解
f(x_in) = Attention/FFN 提供的新信息
x_out = 原来的理解 + 新信息

子模块不是在“替换”你的理解,而是在“补充”你的理解

反向传播

由:

对 求导:

所以:

展开:

拆成两部分:

残差直路为什么如此重要?

第一项:

完全不经过任何变换,直接从输出传到输入。

假设我们有 层 Transformer Block,每层都有残差连接。

没有残差连接时:

Δ_output ──→ f_N'(·) ──→ f_{N-1}'(·) ──→ ... ──→ f_1'(·) ──→ Δ_input

梯度要经过 N 个函数的导数连乘
如果每个导数 < 1:0.9^32 = 0.035
如果每个导数 > 1:1.1^32 = 21.1


有残差连接时:

Δ_output ──────────────────────────────────────→ Δ_input(直路)
│ │ │ │
└→ f_N' ──→ └→ f_{N-1}'→ └→ ... ──→ └→ f_1'(支路)

这就是残差连接的核心价值:

它提供了一条不受任何变换影响的梯度直通路径。

Norm 的位置问题

Post-Norm 的梯度流动

x_in
│
├─────────── 直路 ──────────┐
│ │
▼ │
Attention/FFN │
│ │
▼ │
x_sub │
│ │
▼ │
+ ←─────────────────────────┘
│
▼
LayerNorm
│
▼
x_out

反向传播:

Δ_x_out
│
▼
LayerNorm 反向传播
│
Δ_after_norm
│
├─── 直路:Δ_x_in_residual = Δ_after_norm
│
└─── 子模块路:经过 Attention/FFN 反向传播

问题在于:

直路梯度也必须经过 LayerNorm。

回忆 LayerNorm 的反向传播:

这里有:

所以:

每经过一层 Post-Norm:
梯度被缩放
梯度被修正

经过很多层后:
梯度可能已经严重失真

Pre-Norm 的梯度流动

x_in
│
├─────────── 直路 ──────────────────────────────┐
│ │
▼ │
RMSNorm │
│ │
▼ │
Attention/FFN │
│ │
▼ │
x_sub │
│ │
▼ │
x_out = x_in + x_sub ←──────────────────────────┘

反向传播:

Δ_x_out
│
├─── 直路:Δ_x_in_residual = Δ_x_out
│
└─── 支路:
Attention/FFN 反向传播
↓
RMSNorm 反向传播
↓
Δ_x_in_via_sub

最终:
Δ_x_in = Δ_x_out + Δ_x_in_via_sub

关键区别:

直路梯度:

完全不经过 Norm。

经过 32 层 Pre-Norm:

直路梯度:
完全不变

支路梯度:
可能放大或缩小

最终梯度:
直路保底 + 支路修正

用具体例子来看差异

假设:

每层 Norm 把梯度缩放成原来的 。

Post-Norm
第4层 → Δ = 1.0 × 0.8 = 0.80
第3层 → Δ = 0.80 × 0.8 = 0.64
第2层 → Δ = 0.64 × 0.8 = 0.51
第1层 → Δ = 0.51 × 0.8 = 0.41

32 层:

梯度几乎消失。

Pre-Norm
第4层 → Δ = 1.0
第3层 → Δ = 1.0
第2层 → Δ = 1.0
第1层 → Δ = 1.0

不管多少层:

始终完整。

Pre-Norm 有没有缺点?

“ 既然我都这么问了,那答案当然是有。 ”

问题 1:最终输出没有经过 Norm

Pre-Norm 每层输出:

残差不断累加,数值可能逐渐漂移。

解决方案: 最后再加一个 RMSNorm:

Transformer Block 1
│
Transformer Block 2
│
...
│
Transformer Block N
│
▼
Final RMSNorm
│
▼
W_lm · x → logits

现在主流模型都这么做。

问题 2:理论上 Post-Norm 表达能力可能更强

一些研究认为:

下,Post-Norm 有时效果更好。

但大模型训练更重视:

训练稳定性

所以现代模型几乎都选择:

Pre-RMSNorm + 残差连接 + Final RMSNorm

一个 Block 里的两层残差

Block 输入:x_in

├── Attention 残差
│ x_mid = x_in + Attention(Norm(x_in))

├── FFN 残差
│ x_out = x_mid + FFN(Norm(x_mid))

Block 输出:x_out

反向传播会产生多条路径:

路径1:两个残差都走直路
路径2:Attention 走支路
路径3:FFN 走支路
路径4:Attention + FFN 都走支路

最终梯度:

其中:

路径1提供永不衰减的保底梯度。

例:32 层 Transformer 的梯度路径

如果:

则共有:

条可能路径。

虽然不会真的枚举,但这说明:

残差连接创造了指数级多的梯度路径

即使绝大多数路径衰减
只要有一条直路畅通
整体梯度就不会消失

残差连接对初始化的影响

残差层:

训练开始时希望:

于是:

网络初始状态接近恒等映射。

例:GPT-2 的初始化

普通初始化:

残差路径最后一层:

其中:

额外的:

会让初始输出更小。

拓展:其他归一化策略

Sandwich Norm

图 3 Sandwich Norm

x_in
│
├──────────────────────────┐
│ │
▼ │
RMSNorm_before │
│ │
▼ │
Attention/FFN │
│ │
▼ │
RMSNorm_after │
│ │
▼ │
+ ←────────────────────────┘
│
▼
x_out

目的:

代价:

DeepNorm

修改残差连接:

其中:

例如:

效果:

普通残差:
x + f(x)

DeepNorm:
αx + f(x)

直路信号更强
深层训练更稳定

反向传播:

直路梯度被放大。

小结

“ 回答我们最开始的问题:为什么选择Pre Norm? ”

1. 深层网络会梯度消失
↓
2. 残差连接提供梯度高速公路
↓
3. Post-Norm 把 Norm 放在直路上
↓
4. 梯度每层都会被 Norm 干扰
↓
5. 深层训练不稳定
↓
6. Pre-Norm 把 Norm 放在支路
↓
7. 梯度直路完全畅通
↓
8. 深层 Transformer 可以稳定训练

最终方案:

Pre-RMSNorm + 残差连接 + Final RMSNorm

放回到完整反向传播图景中

Loss
│
▼ Softmax + CrossEntropy
Δ_logits
│
▼ 输出投影层
│
▼ Final RMSNorm
│
▼ Transformer Block N
│
│ ┌─── 残差直路 ──────────────────────┐
│ │ │
│ │ 梯度完整保留 │
│ │ │
│ ▼ │
│ RMSNorm → FFN → 支路梯度 │
│ │ │
│ └────────── 汇合 ───────────────────┘
│
│ ┌─── 残差直路 ──────────────────────┐
│ │ │
│ │ 梯度完整保留 │
│ │ │
│ ▼ │
│ RMSNorm → Attention → 支路梯度 │
│ │ │
│ └────────── 汇合 ───────────────────┘
│
▼ Transformer Block N-1
│
▼ ...
│
▼ Transformer Block 1
│
▼ Embedding

残差连接让梯度可以从最后一层一路畅通地流到第一层。 Pre-Norm 则保证: 这条路上没有任何障碍物。 这就是现代 Transformer 能稳定训练几十层、上百层的根本原因。

笔者的话

“ 现在进入了我们对细节内容的探索,希望对Norm的深入讨论能让你对整体的理解更加深入与透彻。很多时候一些看起来“确实是这样”的选择,背后其实存在更多深处的原因,不管是在数学中还是人生中,我们只是缺少了一些参悟的决心。 ”

参考资料