Skip to content

归一化技术 (Normalization)

观察不同归一化方法如何将数据标准化到不同范围。

📊 归一化可视化

y = (x - μ) / σ
特征维度归一化,Transformer 标配
原始数据
3.1
2.5
1.8
2.3
1.9
0.0
2.2
2.5
1.3
1.7
1.8
1.6
0.2
0.1
1.1
1.5
-1.8
-1.4
-1.0
-2.4
-1.2
-0.7
-1.5
-0.8
-1.6
-2.7
-0.1
-2.7
-2.1
-0.5
-2.3
-3.3
μ=-0.01, σ=1.84
LayerNorm
1.3
0.5
-0.3
0.3
-0.2
-2.4
0.2
0.6
0.2
0.9
1.0
0.7
-1.6
-1.7
-0.1
0.5
-0.8
-0.0
0.8
-2.0
0.2
1.2
-0.3
1.0
0.3
-0.8
1.8
-0.7
-0.2
1.3
-0.4
-1.3
μ=-0.00, σ=1.00
归一化维度对比
LayerNorm
每行独立归一化
BatchNorm
每列独立归一化
RMSNorm
每行RMS缩放
💡 观察要点:
  • LayerNorm: 每行均值→0,方差→1 (特征维度)
  • RMSNorm: 只缩放,均值不一定为0 (更快)
  • BatchNorm: 每列均值→0 (batch维度,推理时需要running_mean)

💡 颜色: 蓝色=负值, 白色=0, 红色=正值


一句话定义:归一化是将数据调整到标准范围的技术,使神经网络训练更稳定、更快收敛。不同的归一化方法在不同维度上进行标准化。

  • Batch Normalization (BN): 在 batch 维度上归一化。CNN 的标配。
  • Layer Normalization (LN): 在特征维度上归一化。Transformer 的标配。
  • RMS Normalization (RMSNorm): 简化版 LayerNorm,只用均方根。LLaMA 的选择。
  • Group Normalization (GN): 介于 BN 和 LN 之间。小 batch 场景的替代。

想象全国高考:

  • 原始分数:各省试卷难度不同,分数没有可比性(数据分布不一致)。
  • 归一化:转换为”全省排名百分比”或”标准分”,让所有人在同一尺度上比较。

神经网络中:

  • 没有归一化:每层的输出分布不稳定,像每次考试用不同标准。模型需要不断适应变化的输入分布(Internal Covariate Shift)。
  • 有归一化:每层输出都被”标准化”,模型只需学习标准化后的数据,训练更稳定。


在深度网络中,每一层的输入分布会随着前面层参数的更新而改变。这就像:

  • 你刚学会在”平静水面”上游泳
  • 突然水变成了”波涛汹涌”
  • 你需要重新适应

归一化的作用

  1. 稳定训练:让每层看到的数据分布相对稳定
  2. 加速收敛:可以使用更大的学习率
  3. 正则化效果:BN 有轻微的正则化作用(因为 batch 统计量的随机性)
  4. 缓解梯度问题:帮助梯度更稳定地流动

batch 维度上计算均值和方差:

Batch Normalization
x^=xμBσB2+ϵγ+β\hat{x} = \frac{x - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} \cdot \gamma + \beta
  • μB,σB2\mu_B, \sigma_B^2: 当前 batch 的均值和方差
  • γ,β\gamma, \beta: 可学习参数(scale 和 shift)
  • ϵ\epsilon: 数值稳定项(如 10510^{-5}
  • 归一化维度: (N,C,H,W)(N, C, H, W) → 在 N,H,WN, H, W 上统计

训练 vs 推理

  • 训练时:使用当前 batch 的统计量
  • 推理时:使用训练时累积的移动平均(running mean/var)

特征维度上计算均值和方差:

Layer Normalization
x^=xμLσL2+ϵγ+β\hat{x} = \frac{x - \mu_L}{\sqrt{\sigma_L^2 + \epsilon}} \cdot \gamma + \beta
  • μL,σL2\mu_L, \sigma_L^2: 单个样本在特征维度的统计量
  • 归一化维度: (N,L,D)(N, L, D) → 在 DD (或 L,DL, D) 上统计
  • 优势: 不依赖 batch size,适合 RNN/Transformer
  • 位置: Transformer 中在 Attention 和 FFN 之后

去掉均值中心化,只用 RMS(均方根):

RMS Normalization
x^=xRMS(x)γ,RMS(x)=1ni=1nxi2\hat{x} = \frac{x}{\text{RMS}(x)} \cdot \gamma, \quad \text{RMS}(x) = \sqrt{\frac{1}{n}\sum_{i=1}^n x_i^2}
  • 比 LayerNorm 少一次均值计算
  • 实验表明效果相当,但计算更快
  • LLaMA, Mistral 等现代 LLM 的选择
方法归一化维度适用场景依赖 Batch
BatchNormN, H, WCNN✅ 是
LayerNormD (或 L, D)Transformer, RNN❌ 否
RMSNormD现代 LLM❌ 否
GroupNorm组内 C小 batch CNN❌ 否
InstanceNormH, W风格迁移❌ 否

import torch
import torch.nn as nn
# ===== Batch Normalization =====
# 适用于 CNN: (N, C, H, W)
bn = nn.BatchNorm2d(num_features=64) # 64 个通道
x_cnn = torch.randn(32, 64, 28, 28) # batch=32, 64通道, 28x28
out_bn = bn(x_cnn)
print(f"BN 输出形状: {out_bn.shape}") # [32, 64, 28, 28]
# ===== Layer Normalization =====
# 适用于 Transformer: (N, L, D)
ln = nn.LayerNorm(normalized_shape=512) # 在最后一个维度归一化
x_transformer = torch.randn(32, 100, 512) # batch=32, 100个token, 512维
out_ln = ln(x_transformer)
print(f"LN 输出形状: {out_ln.shape}") # [32, 100, 512]
# ===== RMS Normalization (手动实现) =====
class RMSNorm(nn.Module):
def __init__(self, dim: int, eps: float = 1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim)) # 可学习 scale
def forward(self, x: torch.Tensor) -> torch.Tensor:
# x: (batch, seq_len, dim)
rms = torch.sqrt(torch.mean(x ** 2, dim=-1, keepdim=True) + self.eps)
return x / rms * self.weight
rms_norm = RMSNorm(dim=512)
out_rms = rms_norm(x_transformer)
print(f"RMSNorm 输出形状: {out_rms.shape}") # [32, 100, 512]
# ===== 验证归一化效果 =====
print(f"\n归一化前 - 均值: {x_transformer.mean():.4f}, 标准差: {x_transformer.std():.4f}")
print(f"LN 后 - 均值: {out_ln.mean():.4f}, 标准差: {out_ln.std():.4f}")



Batch Norm 原论文

Ioffe & Szegedy, 2015

阅读

Layer Norm 原论文

Ba et al., 2016

阅读

RMSNorm 论文

Zhang & Sennrich, 2019

阅读