起因#
最近在过 MIT 6.S191 和分布式训练相关的材料,各种 Norm 反复出现。LayerNorm 在写 Transformer Block 那篇时已经接触过,BatchNorm 在做 CV 的那几年天天调,RMSNorm 在看 LLM 代码时到处都是。单独看每个都不算陌生。
但有件事一直没想清楚:这些 Norm 的本质区别到底是什么?
我最开始的理解是——BatchNorm 沿 Batch 维度算统计量,LayerNorm 沿特征维度算统计量,所以"归一化的方向不一样"。这个说法大体没错,但太粗糙了。后来碰到 GroupNorm、InstanceNorm,又冒出两个新的"方向",我意识到自己并没有一个统一的框架去理解它们的异同。
这篇想把这件事理清楚。核心问题就一个:各种 Norm 到底在哪些维度上计算统计量?为什么选这些维度?
标准化和仿射变换是两步#
先把通用公式摆出来。给定一组输入元素,先统计均值和方差,然后标准化:
$$ \hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}} $$再加上可学习的仿射变换:
$$ y_i = \gamma_i \hat{x}_i + \beta_i $$这里有四个量需要分清楚:
- \(\mu\) 和 \(\sigma^2\) 是从当前输入中统计出来的。不同的 Norm 区别就在于"哪些元素参与这个统计"。
- \(\gamma\) 和 \(\beta\) 是通过反向传播学出来的参数。\(\gamma\) 控制缩放,\(\beta\) 控制偏移。
一个我以前理解得不太准的地方:标准化之后,这组输入的均值确实变成 0、方差变成 1。但 \(\gamma\) 和 \(\beta\) 的存在意味着最终输出不一定保持这个状态。Normalization 的目标不是强制每层输出都是零均值、单位方差,而是先把数值尺度拉到一个稳定的起点,再让模型自己学合适的尺度和偏移。
标准化是确定性的统计操作,仿射变换是可学习的参数——这两步应该分开理解。后面讨论 RMSNorm 的时候会看到,把它们混在一起容易导致概念混淆。
另外,标准化后的分布是零均值、单位方差,但不意味着输出服从正态分布。标准化只改变了前两阶矩,不改变分布的形状。
关于 BatchNorm 最初的 motivation——Internal Covariate Shift(Ioffe & Szegedy, 2015),这里不展开。后续研究(Santurkar et al., 2018)发现 BN 的收益不能完全归因于减少 ICS,更可能与 loss landscape 的平滑化有关。只需要知道:Normalization 确实能改善训练,但原始的解释并不完全准确。
BatchNorm——“沿 Batch 维度"远不够精确#
先从最简单的全连接层输入开始:
$$ X \in \mathbb{R}^{B \times F} $$\(B\) 是 Batch Size,\(F\) 是特征数。BatchNorm 对每个特征独立计算均值和方差,统计范围是当前 mini-batch 中所有样本在该特征上的值。一共 \(F\) 个均值、\(F\) 个方差。
到这里很直观。然后到 CNN:
$$ X \in \mathbb{R}^{B \times C \times H \times W} $$我之前有个疑问:BatchNorm 不是沿 Batch 维度计算吗?为什么 PyTorch 的 BatchNorm2d 还要把 \(H \times W\) 也包括进来?
想了一会儿才搞清楚。关键在于理解 Channel 的含义。
CNN 里,不同的输出 Channel 是由不同的卷积核参数产生的。Channel 0 对应一组卷积核权重,Channel 1 对应另一组——它们各自的数值尺度可能完全不同。所以 BN 对每个 Channel 独立统计,不混合不同 Channel。
而同一个 Channel 内的不同空间位置(不同的 \(H, W\)),是由同一组卷积核在不同位置滑动产生的——参数共享。之前那篇 CNN 文章讨论过权重共享的设计假设。既然参数一样,不同空间位置的输出在统计意义上应该具有相似的性质,放在一起统计是合理的。
所以 BatchNorm2d 的实际统计范围是:固定一个 Channel,跨所有样本和所有空间位置计算均值和方差。 每个 Channel 的统计量由 \(B \times H \times W\) 个元素计算得到,\(C\) 个 Channel 各自独立。
几个工程上需要注意的点:
- 训练时 BN 使用当前 mini-batch 的统计量,同时用指数移动平均维护 running mean 和 running variance。
- 推理时默认使用训练期间累积的 running statistics,不再依赖当前输入。
- 小 Batch 时统计量的估计可能不够稳定,但"小 Batch"不等于"BN 一定失效”——实际效果取决于具体场景。
- 在 DDP 多卡训练中,普通
BatchNorm默认只使用本卡 mini-batch 的统计,不跨 GPU 同步。SyncBatchNorm通过额外的 AllReduce 通信合并所有 GPU 的统计量,代价是每次 forward 都多一次跨进程同步。
LayerNorm——每个 Token 自己算自己的#
Transformer 的输入通常是:
$$ X \in \mathbb{R}^{B \times S \times H} $$\(B\) 是 Batch Size,\(S\) 是序列长度,\(H\) 是隐层维度。
之前写过,标准 Transformer 的 LayerNorm 对每个 Token 独立计算统计量——固定 \(B\) 和 \(S\),沿最后的 \(H\) 维计算均值和方差。每个 Token 有自己的 \(\mu, \sigma^2\),不依赖其他样本,也不依赖序列中的其他 Token。
Transformer 倾向用 LayerNorm 而不是 BatchNorm,有几个互相关联的原因:
- 序列长度 \(S\) 在不同样本间可能不同。BN 需要对齐同一位置的统计,长度不一致时根本对不上。
- 自回归推理时 Batch Size 经常是 1,逐 Token 生成。BN 的跨样本统计在这种场景下完全不适用。
- LN 的统计量只依赖当前 Token 自身的 \(H\) 个特征值,完全自包含。
有一点需要澄清。PyTorch 的 nn.LayerNorm 接受一个 normalized_shape 参数,它决定了实际归一化的末尾维度。写 LayerNorm(H) 表示只对最后一维归一化;写 LayerNorm((S, H)) 则会对最后两维一起归一化。LayerNorm 并不天然只能沿某个固定维度计算,normalized_shape 才是决定归一化范围的关键。 标准 Transformer 中使用 LayerNorm(d_model) 只是最常见的配置。
RMSNorm——如果不减均值会怎样#
RMSNorm(Zhang & Sennrich, 2019)的公式:
$$ \text{RMS}(x) = \sqrt{\frac{1}{H} \sum_{i=1}^{H} x_i^2} $$$$ y_i = \gamma_i \frac{x_i}{\sqrt{\frac{1}{H} \sum_j x_j^2 + \epsilon}} $$和 LayerNorm 对比,差异很明确:LayerNorm 先减去均值再除以标准差(中心化 + 尺度归一化),RMSNorm 只用 RMS 做尺度归一化,跳过中心化。
RMSNorm 论文的核心观点涉及两种不变性:
- Re-centering Invariance:如果输入整体平移 \(x \to x + c\),输出不变。LayerNorm 有这个性质(减均值消掉了偏移),RMSNorm 没有。
- Re-scaling Invariance:如果输入整体缩放 \(x \to \alpha x\),输出不变(忽略 \(\epsilon\))。LayerNorm 和 RMSNorm 都有这个性质。
论文的论点是:LayerNorm 的收益主要来自 re-scaling invariance,re-centering invariance 可能不是必要条件。实验结果支持了这一点——RMSNorm 在多个任务上取得了与 LayerNorm 相当的效果。后来 LLaMA、Gemma 等主流 LLM 广泛采用 RMSNorm,成了事实标准之一。
但有两个问题我之前一直混着理解,后来专门想了一下。
不计算均值,和不使用 β,是同一件事吗?#
不是。这是两个独立的设计选择。
- 减不减均值,决定的是归一化统计方式——是 \((x - \mu) / \sigma\) 还是 \(x / \text{RMS}\)。
- 有没有 \(\beta\),决定的是仿射变换——输出是 \(\gamma \hat{x} + \beta\) 还是 \(\gamma \hat{x}\)。
标准 RMSNorm 通常只有 \(\gamma\) 没有 \(\beta\),但这是一个实现选择,不是"不减均值"的数学必然。完全可以构造"不减均值但带 \(\beta\)“的变体。反过来,LayerNorm 也可以去掉 \(\beta\)——PyTorch 就支持:
1# LayerNorm 保留 γ,去掉 β
2ln_no_bias = nn.LayerNorm(768, bias=False)省去均值计算,真的明显更快吗?#
这一点容易被夸大。
纯看 FLOPs,RMSNorm 确实少了一次 sum(x) 和逐元素减法。但在 GPU 上:
- 高效实现通常使用
rsqrt和乘法替代逐元素除法,LN 和 RMSNorm 的除法步骤开销差别不大。 - LayerNorm 的
sum(x)和sum(x²)可以在一次遍历中融合计算,不一定需要额外的 HBM 读写。 - GPU kernel 的性能瓶颈往往在访存和 Reduction 延迟,而不是 FLOPs 本身。
RMSNorm 原论文报告了相对于 LayerNorm 7-64% 的 kernel 加速,但实验条件是 RNN + 特定硬件 + 特定实现。直接把这个数字外推到现代 LLM 训练不太合理。
用 Amdahl’s Law 估一下。假设 Normalization 占整个 training step 的 5%,RMSNorm 在 kernel 层面加速 30%:
$$ \text{Speedup} = \frac{1}{0.95 + 0.05 / 1.3} \approx 1.012 $$端到端收益大约 1.2%。不是零,但远不是选择 RMSNorm 的决定性理由。
RMSNorm 更重要的价值在于验证了"中心化可能不是必要条件"这个认知,进而简化了模型设计。性能收益是附带的。
GroupNorm——不跨 Batch,在 Channel 上做文章#
GroupNorm(Wu & He, 2018)的动机很直接:CNN 训练中 Batch Size 太小时,BN 的统计量可能不够稳定。小 Batch 场景在目标检测、视频处理等任务中很常见——显存限制导致单卡放不下太多样本。
GroupNorm 完全不跨 Batch,在单个样本内部解决问题:
- 把 \(C\) 个 Channel 分成 \(G\) 组,每组 \(C/G\) 个 Channel。
- 每组内,联合统计组内的 Channel 和空间维度 \(H \times W\)。
具体例子。\(X\) 的 shape 是 \((2, 64, 16, 16)\),分 8 个 Group:
- 每个 Group 有 \(64 / 8 = 8\) 个 Channel。
- 每个 Group 的均值和方差由 \(8 \times 16 \times 16 = 2048\) 个元素计算。
- 不同样本之间互不影响,每个样本的 8 个 Group 各自独立。
需要注意:GroupNorm 不是"仅沿 Channel 维度统计”——它通过 Channel 分组来划定统计范围,这个范围同时包含了组内的 Channel 维度和空间维度。
工程上的一个权衡:小 Batch 时选 GroupNorm 还是 SyncBatchNorm?GroupNorm 不需要跨 GPU 通信,实现简单;SyncBatchNorm 通过通信获得更大的有效 Batch,统计更稳定,但引入了同步开销。具体选择取决于场景——没有一个在所有情况下都更好的答案。
InstanceNorm——其实是 GroupNorm 的特殊情况#
我在学习时产生过一个想法:InstanceNorm 不就是把单个 Channel 的 \(H \times W\) 展平,然后在上面做 LayerNorm 吗?
从统计计算的角度来说,这个理解是对的。
对于 \(X \in \mathbb{R}^{B \times C \times H \times W}\),InstanceNorm 固定 \(B\) 和 \(C\),对 \(H \times W\) 计算均值和方差。因此:
- InstanceNorm 等价于 GroupNorm(\(G = C\))——每个 Channel 自成一组。
- 从统计方式看,也等价于对 \((H, W)\) 执行 LayerNorm。
但在 PyTorch 实现中有一些差异。InstanceNorm2d 默认 affine=False(不带可学习参数),而 GroupNorm 和 LayerNorm 默认 affine=True。此外 InstanceNorm2d 有 track_running_stats 选项(默认关闭)。在禁用仿射变换和 running statistics 的条件下,三者的归一化输出数值上一致。
下面的代码验证了这一点:
1import torch
2import torch.nn as nn
3
4B, C, H, W = 2, 8, 4, 4
5x = torch.randn(B, C, H, W)
6
7gn = nn.GroupNorm(num_groups=C, num_channels=C, affine=False)
8inst = nn.InstanceNorm2d(C, affine=False, track_running_stats=False)
9ln = nn.LayerNorm([H, W], elementwise_affine=False)
10
11out_gn = gn(x)
12out_in = inst(x)
13out_ln = ln(x)
14
15torch.testing.assert_close(out_gn, out_in)
16torch.testing.assert_close(out_gn, out_ln)
17print("GroupNorm(G=C), InstanceNorm2d, LayerNorm((H,W)) 输出一致")InstanceNorm 最初由 Ulyanov et al.(2016)为风格迁移提出,动机是去掉图像的全局风格信息(由每个 Channel 的 mean 和 variance 编码),保留内容结构。
把各种 Norm 放在一起看#
到这里可以做一个全局对比了。对于四维输入 \((B, C, H, W)\):
| Norm | 固定(独立)维度 | 统计范围 |
|---|---|---|
| BatchNorm | C | B, H, W |
| LayerNorm(对整个样本) | B | C, H, W |
| GroupNorm | B, Group | 组内 C/G, H, W |
| InstanceNorm | B, C | H, W |
| 依赖 Batch | 训练/推理统计不同 | 典型应用 | |
|---|---|---|---|
| BatchNorm | 是 | 是(running stats) | CNN |
| LayerNorm | 否 | 否 | Transformer |
| GroupNorm | 否 | 否 | 小 Batch CNN |
| InstanceNorm | 否 | 通常否 | 风格迁移 |
但仅看统计范围还不够。要完整理解一个 Normalization 方法,至少需要区分三个独立的设计维度:
- 统计范围:哪些维度的元素参与均值和方差的计算。
- 归一化公式:是减均值再除标准差,还是只除 RMS。
- 可学习仿射参数:有没有 \(\gamma\),有没有 \(\beta\),参数的形状是什么。
RMSNorm 主要改变的是第 2 点——它和 LayerNorm 的统计范围完全一样,都在 \(H\) 维上做。把这三个维度拆开看,碰到一个新的 Norm 变体就不容易搞混。
工程总结#
最后简短回答几个实际问题。
CNN 为什么经常用 BatchNorm? BN 对 CNN 效果好,这在大量实验中被反复验证。一个可能的原因和 CNN 的权重共享有关——同一个 Channel 内不同空间位置由相同卷积核产生,统计同质性为 BN 的跨位置统计提供了合理性。
小 Batch 为什么考虑 GroupNorm? 不是因为 BN 一定失效,而是统计量的估计方差随 Batch Size 减小而增大。GN 不依赖 Batch,回避了这个问题,代价是放弃了跨样本的统计信息。
Transformer 为什么倾向 LayerNorm / RMSNorm? 变长序列、自回归推理、Batch Size 不固定,这些场景下跨样本统计要么不可行要么不稳定。
RMSNorm 一定比 LayerNorm 快吗? 不一定。kernel 层面有加速,但端到端收益取决于 Norm 在总计算中的占比。选 RMSNorm 更多是因为简化了设计且不损失效果。
Norm 的统计维度与 Tensor Parallelism 通信有什么关系? 之前写过,TP 会把隐层维度 \(H\) 拆分到多张 GPU 上。LayerNorm / RMSNorm 需要在完整的 \(H\) 上计算统计量——如果 \(H\) 被拆了,就需要跨 GPU 通信来获取全局的 sum 和 sum of squares。这是 TP 与 Norm 交互时需要注意的额外通信开销。
面对一个陌生的 Normalization 变体,与其先背名字和公式,不如先问三个问题:它在哪些维度上计算统计量?为什么选这些维度?它改变的是统计范围、归一化公式,还是仿射变换?
参考资料#
- Ioffe & Szegedy. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift. ICML, 2015.
- Ba et al. Layer Normalization. arXiv, 2016.
- Zhang & Sennrich. Root Mean Square Layer Normalization. NeurIPS, 2019.
- Wu & He. Group Normalization. ECCV, 2018.
- Ulyanov et al. Instance Normalization: The Missing Ingredient for Fast Stylization. arXiv, 2016.
- Santurkar et al. How Does Batch Normalization Help Optimization? NeurIPS, 2018.
- Attention 之后还缺什么:从 Residual Connection 到 Transformer Stack
- 9 年后重温 CNN:剥掉算子细节后,真正留下了什么
- FSDP 已经能分片参数,为什么还需要 Tensor Parallelism?
- PyTorch nn.LayerNorm
- MIT 6.S191: Introduction to Deep Learning — https://introtodeeplearning.com/