import torch
import torch.nn as nn
import torch.nn.functional as F
from torch import Tensor
print('PyTorch version:', torch.__version__)PyTorch version: 2.12.1+cpu
jshn9515
2026-06-27
2026-06-27
前面几节,我们分别介绍了 Batch Normalization、Layer Normalization、Instance Normalization、Group Normalization 和 RMS Normalization。前四种方法都会先计算均值和方差,再进行标准化;RMSNorm 则省略均值中心化,只根据均方根调整特征尺度。
真正让这些方法彼此不同的,并不是标准化公式本身,而是:
哪些元素会被放在一起计算均值和方差。
对于一个形状为 (N, C, H, W) 的图像特征张量,不同归一化方法会选择不同的统计集合:
N, H, W 统计;C, H, W 统计;H, W 统计;H, W 统计;normalized_shape 对应的尾部维度计算均方根。因此,这一节不再把它们当作四套独立公式,而是从一个统一框架出发,回答下面几个问题:
GroupNorm(1, C) 为什么接近 LayerNorm?GroupNorm(C, C) 为什么接近 InstanceNorm?PyTorch version: 2.12.1+cpu
设某个归一化集合为 \(S\),其中包含若干个输入元素。归一化层首先计算这个集合的均值:
\[ \mu_S = \frac{1}{|S|} \sum_{i\in S}x_i \]
以及方差:
\[ \sigma_S^2 = \frac{1}{|S|} \sum_{i\in S}(x_i-\mu_S)^2 \]
随后,对集合中的每个元素进行标准化:
\[ \hat{x}_i = \frac{x_i-\mu_S}{\sqrt{\sigma_S^2+\epsilon}} \]
最后,再应用可学习的仿射变换:
\[ y_i = \gamma_i\hat{x}_i+\beta_i \]
BatchNorm、LayerNorm、InstanceNorm 和 GroupNorm 都遵循这套“中心化 + 尺度归一化”的形式,它们的差异来自集合 \(S\) 的定义。
RMSNorm 稍有不同。它不计算均值,也不执行中心化,而是直接根据集合中的均方根:
\[ \operatorname{RMS}(x_S) = \sqrt{\frac{1}{|S|}\sum_{i\in S}x_i^2+\epsilon} \]
对输入进行缩放:
\[ \hat{x}_i = \frac{x_i}{\operatorname{RMS}(x_S)} \]
因此,RMSNorm 仍然需要先定义统计集合 \(S\),只是它计算的是二阶矩,而不是均值和中心化方差。
换句话说,学习归一化方法时,最重要的问题不是公式是什么,而是:
对当前输入中的某个元素,哪些其他元素会和它共享同一组均值和方差?
考虑一个四维输入:
\[ X\in\mathbb{R}^{N\times C\times H\times W} \]
其中,\(N\) 表示 batch size,\(C\) 表示通道数,\(H\)、\(W\) 表示空间尺寸。
在 PyTorch 中,mean(dim=...) 和 var(dim=...) 中给出的维度可以称为统计维度,也就是会被归约掉的维度;而没有被归约的维度可以称为保留维度。每个保留位置都会拥有一组独立的均值和方差。
例如,沿 (0, 2, 3) 统计:
Input shape: torch.Size([4, 3, 5, 5])
Mean shape: torch.Size([1, 3, 1, 1])
Variance shape: torch.Size([1, 3, 1, 1])
输出统计量的形状为 (1, C, 1, 1),说明只有通道维度被保留下来。
可以把两类维度理解为:
对于输入 (N, C, H, W),BatchNorm 的统计维度为:
因此,它固定通道 \(c\),沿 batch 和空间维度统计:
\[ \begin{aligned} \mu_c &= \frac{1}{NHW} \sum_{n,h,w}x_{n,c,h,w} \\ \sigma_c^2 &= \frac{1}{NHW} \sum_{n,h,w}(x_{n,c,h,w}-\mu_c)^2 \end{aligned} \]
x = torch.randn(4, 3, 5, 5)
dim = (0, 2, 3)
bn_mean = x.mean(dim, keepdim=True)
bn_var = x.var(dim, correction=0, keepdim=True)
x_bn = (x - bn_mean) / (bn_var + 1e-5).sqrt()
print('Statistics shape:', bn_mean.shape)
print('Output means by channel:', x_bn.mean(dim))
print('Output variances by channel:', x_bn.var(dim, correction=0))Statistics shape: torch.Size([1, 3, 1, 1])
Output means by channel: tensor([-9.5367e-09, 0.0000e+00, 0.0000e+00])
Output variances by channel: tensor([1.0000, 1.0000, 1.0000])
BatchNorm 的统计集合中包含不同样本,因此当前样本的输出会受到 batch 中其他样本影响。这也是 BatchNorm 与其他几种方法最根本的区别。
如果对图像输入使用:
那么 LayerNorm 会固定样本,沿 (C, H, W) 统计:
均值和方差可以写成:
\[ \begin{aligned} \mu_n &= \frac{1}{CHW} \sum_{c,h,w}x_{n,c,h,w} \\ \sigma_n^2 &= \frac{1}{CHW} \sum_{c,h,w}(x_{n,c,h,w}-\mu_n)^2 \end{aligned} \]
x = torch.randn(4, 3, 5, 5)
dim = (1, 2, 3)
ln_mean = x.mean(dim, keepdim=True)
ln_var = x.var(dim, correction=0, keepdim=True)
x_ln = (x - ln_mean) / (ln_var + 1e-5).sqrt()
print('Statistics shape:', ln_mean.shape)
print('Output means by sample:', x_ln.mean(dim))
print('Output variances by sample:', x_ln.var(dim, correction=0))Statistics shape: torch.Size([4, 1, 1, 1])
Output means by sample: tensor([1.9073e-08, 1.9073e-08, 0.0000e+00, 9.5367e-09])
Output variances by sample: tensor([1.0000, 1.0000, 1.0000, 1.0000])
不过,LayerNorm 并不固定归一化某几个语义维度。它的规则是:
对输入最后
len(normalized_shape)个维度进行归一化。
因此,对于 Transformer 输入 (N, L, D) 和 nn.LayerNorm(D),它只沿最后一个特征维度 \(D\) 统计,每个 token 都拥有独立的均值和方差。
Input shape: torch.Size([2, 6, 8])
Output means by token:
tensor([[ 3.3528e-08, -1.4901e-08, -1.4901e-08, 0.0000e+00, -1.8626e-08,
-4.4703e-08],
[-3.7253e-09, 2.9802e-08, 1.8626e-08, -3.3528e-08, -3.7253e-08,
-5.2154e-08]], grad_fn=<MeanBackward1>)
所以,讨论 LayerNorm 时不能只说在样本内部归一化,还需要明确 normalized_shape 到底覆盖输入的哪些尾部维度。
对于输入 (N, C, H, W),InstanceNorm 的统计维度为:
它固定样本 \(n\) 和通道 \(c\),沿空间维度统计:
\[ \begin{aligned} \mu_{n,c} &= \frac{1}{HW} \sum_{h,w}x_{n,c,h,w} \\ \sigma_{n,c}^2 &= \frac{1}{HW} \sum_{h,w}(x_{n,c,h,w}-\mu_{n,c})^2 \end{aligned} \]
Statistics shape: torch.Size([4, 3, 1, 1])
Output means by sample and channel:
tensor([[ 0.0000e+00, -9.5367e-09, -1.6689e-08],
[-4.2915e-08, -2.8610e-08, 5.7220e-08],
[-3.8147e-08, -3.5763e-09, 9.5367e-09],
[-1.9073e-08, 8.3447e-09, 4.7684e-09]])
每个 (sample, channel) 组合都有独立统计量,因此 InstanceNorm 既不跨样本,也不跨通道。它比 LayerNorm 使用更小的统计集合,也更强地消除了每个通道自己的空间均值和方差。
GroupNorm 不能直接通过原始 (N, C, H, W) 中的一组 dim 完整表达,因为通道维度需要先划分为 group。
假设通道数为 \(C\),组数为 \(G\)。首先 reshape:
\[ (N, C, H, W) \rightarrow \left(N, G, \frac{C}{G}, H, W \right) \]
随后固定样本和 group,沿组内通道与空间维度统计:
num_groups = 4
x = torch.randn(4, 8, 5, 5)
n, c, h, w = x.size()
x_grouped = x.reshape(n, num_groups, c // num_groups, h, w)
dim = (2, 3, 4)
gn_mean = x_grouped.mean(dim, keepdim=True)
gn_var = x_grouped.var(dim, correction=0, keepdim=True)
x_gn = (x_grouped - gn_mean) / (gn_var + 1e-5).sqrt()
x_gn = x_gn.reshape_as(x)
y_grouped = x_gn.reshape(n, num_groups, c // num_groups, h, w)
print('Grouped shape:', x_grouped.shape)
print('Statistics shape:', gn_mean.shape)
print('Output means by sample and group:')
print(y_grouped.mean(dim))Grouped shape: torch.Size([4, 4, 2, 5, 5])
Statistics shape: torch.Size([4, 4, 1, 1, 1])
Output means by sample and group:
tensor([[-9.5367e-09, 1.9073e-08, 4.7684e-09, 0.0000e+00],
[ 9.5367e-09, 0.0000e+00, 7.1526e-09, 2.3842e-09],
[-1.9073e-08, -2.3842e-08, -9.5367e-09, -1.4305e-08],
[-1.5497e-08, -9.5367e-09, -2.5034e-08, 1.9073e-08]])
因此,GroupNorm 可以看成:
通过 reshape 创建一个显式的 group 维度,然后对每个样本中的每个 group 独立执行标准化。
这也说明很多归一化操作在实现上都可以统一为三步:
RMSNorm 与 LayerNorm 一样,由 normalized_shape 决定统计集合。
对于 Transformer 输入 (N, L, D) 和:
它会固定 batch 位置和 token 位置,沿最后一个隐藏维度 \(D\) 计算均方根:
\[ \operatorname{RMS}_{n,l} = \sqrt{\frac{1}{D}\sum_{d=1}^{D}x_{n,l,d}^2+\epsilon} \]
与 LayerNorm 不同,RMSNorm 不减去均值,因此它只控制特征的整体尺度,不强制输出均值为 0。
Output RMS by token:
tensor([[1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000],
[1.0000, 1.0000, 1.0000, 1.0000, 1.0000, 1.0000]])
Output means by token:
tensor([[ 0.2008, -0.3279, -0.0903, -0.0773, -0.6280, -0.1636],
[-0.1966, 0.7445, -0.3278, 0.0602, -0.0068, -0.4860]])
RMSNorm 的统计维度与 LayerNorm(D) 相同,都是最后一个隐藏维度,但统计量不同:
因此,把 RMSNorm 纳入统一视角时,需要同时考虑两件事:
对于图像输入 (N, C, H, W),五种归一化方法可以总结为:
| 方法 | 保留维度 | 统计维度 | 可学习参数形状 | 是否维护运行统计量 |
|---|---|---|---|---|
| BatchNorm | \(C\) | \(N, H, W\) | \((C,)\) | 是 |
LayerNorm (C,H,W) |
\(N\) | \(C, H, W\) | \((C, H, W)\) | 否 |
| InstanceNorm | \(N, C\) | \(H, W\) | \((C,)\) | 默认否,可选 |
| GroupNorm | \(N, G\) | \(\frac{C}{G}, H, W\) | \((C,)\) | 否 |
RMSNorm (D) |
\(N, L\) | \(D\) | \((D,)\) | 否 |
这张表是理解归一化方法最重要的总结。公式中的标准化步骤几乎不变,真正变化的是哪些元素共享同一组统计量。
GroupNorm 通过 num_groups 控制统计集合的大小,因此它可以连接 LayerNorm 和 InstanceNorm。
当 \(G=1\) 时,所有通道都属于同一个 group。GroupNorm 会对每个样本的全部 C, H, W 元素一起统计。这与:
使用相同的统计集合。
Maximum difference: 2.384185791015625e-07
但是,两者默认的仿射参数形状不同:
weight 和 bias 形状为 (C, H, W);weight 和 bias 形状为 (C,)。因此,关闭仿射变换时,两者的标准化结果相同;开启默认仿射变换后,它们并不是完全相同的层。
当每个通道单独构成一个 group 时(也就是 \(G=C\) 时),GroupNorm 会固定样本和通道,只沿空间维度统计。这与默认 InstanceNorm 使用相同的统计集合。
Maximum difference: 2.384185791015625e-07
这里仍需注意运行时行为:
track_running_stats=True,推理阶段就不再与 GroupNorm 等价。是否把 batch 维度 \(N\) 放进统计集合,是判断一个归一化方法是否依赖 batch 的关键。
BatchNorm 的统计维度包括 \(N\),因此:
LayerNorm、InstanceNorm 和 GroupNorm 都不会沿 \(N\) 统计,因此:
train() 与 eval() 下的统计规则相同。下面用 GroupNorm 验证当前样本不会受到其他样本影响:
target = torch.randn(1, 8, 4, 4)
other_sample1 = torch.randn(3, 8, 4, 4)
other_sample2 = torch.randn(3, 8, 4, 4) * 100 + 50
group_norm = nn.GroupNorm(4, 8, affine=False)
other_sample1 = torch.concat([target, other_sample1], dim=0)
other_sample2 = torch.concat([target, other_sample2], dim=0)
output_sample1 = group_norm(other_sample1)[0]
output_sample2 = group_norm(other_sample2)[0]
max_diff = (output_sample1 - output_sample2).abs().max()
print('Maximum difference for target sample:', max_diff.item())Maximum difference for target sample: 0.0
如果把这里换成训练模式下的 BatchNorm,两个输出通常就会不同。
归一化方法没有绝对的优劣,选择取决于网络结构、batch size 和任务特点。
BatchNorm 通常仍然是经典选择。它在卷积网络中表现稳定,而且推理时可以和卷积层融合。
GroupNorm 更合适,因为它不依赖 batch statistics。目标检测、实例分割和高分辨率视觉任务经常属于这种情况。
实际使用时需要保证 num_channels % num_groups == 0,并根据通道数调整 group 数量。
LayerNorm 长期以来一直是 Transformer 中最常见的归一化方法。它通常对每个 token 的隐藏特征独立计算统计量,因此不依赖 batch size,也不会受到 batch 中其他样本组成的影响。
随着大语言模型的发展,RMSNorm 也已经成为非常常见的选择。由于省略了均值中心化,RMSNorm 的计算形式比 LayerNorm 更简单,同时在许多现代大语言模型中表现出良好的训练稳定性。
经典 Transformer 架构通常使用 LayerNorm,而许多现代大语言模型则更倾向于使用 RMSNorm。
InstanceNorm 可以独立移除每个样本、每个通道的空间均值和方差,因此常用于强调内容结构而弱化实例级风格统计的任务。
当然,这些只是常见经验。现代网络也可能使用完全不同的归一化设计。
归一化层能够改善优化,但也会改变模型的表示方式,并带来额外约束。
例如:
因此,不应该把归一化理解为任何层后面都必须添加的固定操作。更合理的思路是:
归一化方法的本质不是记住四个类名,而是设计统计集合。
这一节从统计维度出发,统一比较了 BatchNorm、LayerNorm、InstanceNorm、GroupNorm 和 RMSNorm。
它们都需要先选择统计集合,再根据集合中的统计量调整特征尺度:
BatchNorm、LayerNorm、InstanceNorm 和 GroupNorm 使用均值与方差,RMSNorm 则只使用均方根。
真正的区别是哪些元素共享同一组统计量:
N, H, W;normalized_shape 对应的尾部维度;normalized_shape 对应维度计算均方根。其中,只有 BatchNorm 默认依赖 batch,并在训练和推理阶段使用不同来源的统计量。LayerNorm、InstanceNorm、GroupNorm 和 RMSNorm 通常都直接使用当前输入的统计量。
GroupNorm 的两个边界情况进一步展示了这些方法之间的联系:
num_groups=1 时,标准化统计接近覆盖全部非 batch 维度的 LayerNorm;num_groups=C 时,标准化统计接近 InstanceNorm。但统计集合相同并不总意味着模块完全等价,因为仿射参数形状和 running statistics 机制也可能不同。
到这里,本章的主要归一化方法已经全部介绍完毕。面对新的输入时,可以先不考虑具体类名,而是先问:
我希望哪些元素共享统计量?需要进行均值中心化,还是只需要控制特征尺度?
只要回答了这个问题,归一化方法的选择通常就会清晰很多。