批量归一化(Batch Normalization)与层归一化(Layer Normalization)
一句话概述
批量归一化(Batch Normalization, BN)和层归一化(Layer Normalization, LN)是深度学习中最核心的归一化技术,它们通过标准化中间层的特征分布来加速训练、稳定梯度、允许更大的学习率。BN在批次维度上计算均值和方差——对每个特征通道,在batch内所有样本上标准化;LN在特征维度上计算均值和方差——对每个样本,在其所有特征通道上标准化。BN是CNN的标配,能够显著加速图像分类模型的训练;LN是Transformer的标配,不依赖batch size,适合NLP和序列任务。近年来还出现了Instance Norm(风格迁移)、Group Norm(小batch场景)、RMSNorm(大模型简化版LN)等变体。
💡 核心要点:①归一化通过标准化中间层分布来加速训练,是深层网络训练的基石 ②BN在batch维度归一化(依赖batch size),适合CNN ③LN在feature维度归一化(不依赖batch size),适合Transformer/RNN ④BN使用训练时累积的移动平均均值和方差进行推理 ⑤归一化还起到轻微正则化作用
教学与演示
一、Batch Normalization:批次维度归一化
是什么(定义):Batch Normalization由Ioffe和Szegedy于2015年提出。对于卷积层输出x∈R^{B×C×H×W}(B=批次大小,C=通道数),BN沿B、H、W三个维度计算均值和方差,对每个通道独立标准化:x̂_c = (x_c - μ_c)/√(σ_c²+ε),然后通过可学习的缩放γ_c和偏移β_c恢复表达能力:y_c = γ_c·x̂_c + β_c。训练时使用当前batch的统计量;推理时使用训练过程中累积的移动平均(running mean和running var)。
大白话 BN就是"给每一层的输出做体检"。就像体检时量血压,如果"收缩压"偏离正常范围,医生会预警。BN在每个训练步骤检查每层输出的"均值和方差"是否正常——太偏了就把数据"拉回"均值为0、方差为1的标准状态。医生(BN)还会根据情况"微调"(γ和β),让正常范围符合当前层的需求。BN还有一个"记忆"功能——在推理时用训练时累积的"过往体检记录"(移动平均),而不是当前这一批的数据。
为什么(原理):BN解决了"内部协变量偏移"(Internal Covariate Shift)问题——训练过程中,由于前面层参数更新,后续层的输入分布不断变化,迫使后续层不断适应新分布。BN将每层输入标准化,减少了分布漂移。此外,BN还有以下效果:①允许更大的学习率(梯度缩放更稳定);②减少对权重初始化的敏感性;③轻微正则化(batch统计中的噪声类似于dropout效果);④缓解梯度消失/爆炸(激活值控制在合理范围)。
import numpy as np
# Batch Normalization的完整实现
# 展示训练和推理时的差异
class BatchNorm:
def __init__(self, num_features, momentum=0.9, eps=1e-5):
np.random.seed(42)
self.num_features = num_features
self.momentum = momentum
self.eps = eps
# 可学习参数
self.gamma = np.ones(num_features) # 缩放
self.beta = np.zeros(num_features) # 偏移
# 移动平均(用于推理)
self.running_mean = np.zeros(num_features)
self.running_var = np.ones(num_features)
def forward_train(self, x):
"""训练时的BN前向传播"""
# x shape: (B, C),B=批次大小,C=特征数
# 步骤1:计算当前batch的统计量
batch_mean = np.mean(x, axis=0) # 每个特征在batch上的均值
batch_var = np.var(x, axis=0) # 每个特征在batch上的方差
# 步骤2:标准化
x_norm = (x - batch_mean) / np.sqrt(batch_var + self.eps)
# 步骤3:缩放和偏移(恢复表达能力)
output = self.gamma * x_norm + self.beta
# 步骤4:更新移动平均(用于推理)
self.running_mean = self.momentum * self.running_mean + (1 - self.momentum) * batch_mean
self.running_var = self.momentum * self.running_var + (1 - self.momentum) * batch_var
return output, batch_mean, batch_var
def forward_test(self, x):
"""推理时的BN前向传播"""
# 使用训练时累积的移动平均
x_norm = (x - self.running_mean) / np.sqrt(self.running_var + self.eps)
output = self.gamma * x_norm + self.beta
return output
# 创建示例数据
np.random.seed(1)
X = np.random.randn(16, 8) * 2 + 3 # batch=16, features=8, 均值≈3, 方差≈4
print(f"输入统计: 均值={np.mean(X):.2f}, 方差={np.var(X):.2f}")
bn = BatchNorm(num_features=8)
print("\n=== Batch Normalization ===\n")
# 训练模式
out_train, batch_mean, batch_var = bn.forward_train(X)
print("【训练模式】")
print(f"Batch均值: {np.round(batch_mean, 3)}")
print(f"Batch方差: {np.round(batch_var, 3)}")
print(f"输出均值: {np.mean(out_train):.2f}, 方差: {np.var(out_train):.2f}")
print("→ 输出被标准化(通过γ=1, β=0),但gamma/beta可学习调整")
# 更新多次移动平均
for _ in range(5):
X2 = np.random.randn(16, 8) * 2 + 3
bn.forward_train(X2)
print(f"\n运行均值: {np.round(bn.running_mean[:4], 3)}")
print(f"运行方差: {np.round(bn.running_var[:4], 3)}")
# 推理模式
X_test = np.array([[5.0, 3.0, 7.0, 2.0, 4.0, 6.0, 1.0, 8.0]])
out_test = bn.forward_test(X_test)
print(f"\n【推理模式】")
print(f"测试输入: {X_test.flatten()}")
print(f"标准化输出: {np.round(out_test.flatten(), 3)}")
print("→ 使用运行均值/方差而非batch统计量")
大白话 BN就是把数据"规整"一下:先把当前这批数据的每个特征"掐头去尾"归到标准范围(均值0方差1),然后乘以一个"可学习的幅度"γ,加上"可学习的基线"β。γ和β让网络自己决定"我到底需要什么样的分布"——如果网络发现"不归一化更好",它会学得γ=σ, β=μ,相当于把归一化"抵消"掉。推理时用训练期间积累的"平均均值"和"平均方差",因为推理可能batch_size=1,没法算batch统计量。
什么用(应用):BN是CNN训练的标准配置。ResNet、DenseNet、EfficientNet等所有经典CNN都使用BN。BN允许使用大学习率(如0.1),减少了对权重初始化的敏感度,使得上百层的CNN训练成为可能。
哪些坑(缺点):BN依赖于batch size——batch太小时(如<8),batch统计量不稳定。RNN和Transformer中每个时间步/位置的特征分布不同,BN不适合。推理时的移动平均统计量需要保存,增加了模型大小。BN打破了batch内样本的独立性(一个样本的BN输出依赖同batch的其他样本),在某些任务(如对比学习)中有负面影响。
二、Layer Normalization:特征维度归一化
是什么(定义):Layer Normalization由Ba等人于2016年提出。与BN在batch维度上标准化不同,LN在特征维度上标准化——对每个样本,计算其所有特征通道的均值和方差,然后标准化。LN不依赖batch size,每个样本独立标准化。公式为:μ = (1/d)Σ_i x_i, σ² = (1/d)Σ_i (x_i - μ)², x̂_i=(x_i-μ)/√(σ²+ε), y_i=γx̂_i+β。
大白话 LN就是"每个人自己量血压"。BN是"一批人体检,算出这批人的平均血压,然后每个人的血压按这批人的平均值来校准"——依赖一群人。LN是"每个人自己量血压,按自己的历史血压来校准"——不依赖别人。这种独立性让LN在"单打独斗"的场景(如翻译、对话生成)中很稳定——即使只有一句话(batch=1),LN也能正常工作。
为什么(原理):LN适合Transformer和RNN的原因:①序列长度可变——BN在可变长度序列上维度不对齐;②RNN的时序依赖——不同时间步使用相同的BN统计量不合理;③推理时batch可能为1——BN在batch=1时统计量退化。LN在特征维度上标准化的方式也与NLP任务中词的表示特性更契合——每个词的嵌入向量的"平均值"和"方差"反映的是该词在当前上下文中的"激活强度",标准化后使不同词的表示更具可比性。
import numpy as np
# Layer Normalization的完整实现
# 对比BN和LN在不同场景下的适用性
class LayerNorm:
def __init__(self, num_features, eps=1e-5):
self.gamma = np.ones(num_features)
self.beta = np.zeros(num_features)
self.eps = eps
def forward(self, x):
"""LN前向传播:沿特征维度标准化"""
# x shape: (N, d),N=序列长度,d=特征数
# 关键:沿axis=-1计算(特征维度)
mean = np.mean(x, axis=-1, keepdims=True) # 每个样本的均值
var = np.var(x, axis=-1, keepdims=True) # 每个样本的方差
x_norm = (x - mean) / np.sqrt(var + self.eps)
return self.gamma * x_norm + self.beta
# 对比BN和LN的适用场景
print("=== Layer Normalization vs Batch Normalization ===\n")
# 场景1:NLP——变长序列,batch_size=1
X_nlp = np.random.randn(1, 5, 8) # 1个句子,5个词,8维特征
print("【NLP场景:1个句子,5个词】")
print(f"输入形状: {X_nlp.shape}")
# BN:batch=1时统计量退化
# LN:正常工作
ln = LayerNorm(num_features=8)
out_ln = ln.forward(X_nlp[0]) # (5, 8)
print(f"LN:每个词独立标准化 → 输出形状: {out_ln.shape}")
# 场景2:CNN——大batch,固定尺寸
X_cnn = np.random.randn(32, 16, 7, 7) # 32张图,16通道,7×7
print(f"\n【CNN场景:32张图,16通道】")
print(f"输入形状: {X_cnn.shape}")
print("BN:使用batch统计量 → 适合CNN")
print("LN:每张图独立标准化 → 计算量大,不适合CNN")
print("\n总结:")
print("┌──────────┬─────────┬─────────┬──────────┐")
print("│ 特性 │ BN │ LN │ 适用场景 │")
print("├──────────┼─────────┼─────────┼──────────┤")
print("│ 标准化轴 │ batch │ feature │ │")
print("│ 依赖batch│ 是 │ 否 │ │")
print("│ 适用架构 │ CNN │Transformer│ │")
print("│ batch=1 │ 不稳定 │ 正常 │ │")
print("│ 变长序列 │ 不适合 │ 适合 │ │")
print("└──────────┴─────────┴─────────┴──────────┘")
大白话 LN就是"自己的特征自己管"。不管你是batch里的第1个样本还是第100个样本,LN只看你自己的d个特征值——算出这d个值的均值和方差,然后标准化。这种方式使得LN在推理时完全不需要"记忆"训练时的统计量(BN需要),代码简洁,行为一致。Transformer里的每个词都独立做LN,无论batch里有多少句子。
什么用(应用):LN是Transformer(BERT、GPT、T5等)的标配。每个注意力子层和前馈网络子层后都有LN。LN也是RNN训练的标准归一化方式。近年来,RMSNorm(只除σ不做中心化)作为LN的简化版,在LLaMA等大模型中使用,计算更高效。
哪些坑(缺点):LN在CNN上效果不如BN——图像的空间维度上使用LN会破坏空间信息(每个空间位置的归一化是独立的)。LN不提供BN的"轻微正则化"效果(因为没有batch统计的噪声)。大模型中LN的计算开销虽小但可积累,RMSNorm通过去掉减均值操作来加速。
三、其他归一化变体
是什么(定义):除BN和LN外,还有Instance Normalization(IN)、Group Normalization(GN)和RMSNorm。IN沿空间维度(H,W)标准化——每个样本的每个通道独立标准化,用于风格迁移。GN将通道分组——在每组内标准化,是BN(组数=1)和IN(组数=通道数)的推广,适合小batch_size。RMSNorm只除以均方根(RMS),不做减均值操作:x̂=x/RMS(x)·γ。
大白话 不同归一化就是"在哪个维度上算均值和方差"的区别。BN算"同一通道上所有样本"的均值和方差;LN算"同一样本上所有通道"的均值和方差;IN算"同一样本同一通道上所有空间位置"的均值和方差;GN算"同一样本同一组通道上所有空间位置"的均值和方差。维度选择的不同,决定了归一化适合不同的任务和架构。RMSNorm是LN的"偷懒版"——不减均值只除标准差,但大模型上效果差不多,计算还更快。
概念关系图谱
| 概念 | 核心含义 | 与AI的关系 | 关联概念 |
|---|---|---|---|
| 归一化(Normalization) | 标准化中间层特征分布 | 深层网络训练的基石 | 标准化、白化 |
| Batch Normalization | batch维度标准化 | CNN训练的标配 | 内部协变量偏移 |
| Layer Normalization | feature维度标准化 | Transformer/RNN的标配 | 序列建模 |
| Instance Normalization | 空间维度标准化 | 风格迁移的核心技术 | 风格解耦 |
| Group Normalization | 分组标准化 | 小batch场景替代BN | BN/IN的一般化 |
| RMSNorm | 仅除RMS不去均值 | 大模型LN的高效替代 | 计算效率 |
重点答疑
Q1: BN为什么不适合RNN和Transformer?
三个原因:①序列长度可变——BN需要固定的维度来维护移动平均,RNN/Transformer处理变长序列时维度不一致;②RNN的时序共享权重——每个时间步使用相同的RNN单元,但BN在每个时间步需要不同的统计量(因为时间步的分布不同);③推理时batch_size=1——BN在batch=1时batch统计量极不稳定,而NLP推理经常batch=1。
Q2: LN中减均值(center)操作有什么用?为什么RMSNorm可以去掉?
减均值(center)让数据以0为中心,有助于梯度的对称流动。但大模型实验发现,去掉减均值(只保留除RMS)对效果影响不大,却能节省约7-8%的计算量。这是因为在大模型中,后续的网络层(特别是注意力机制)本身已经提供了足够的灵活性来处理非零均值的数据。
Q3: BN的移动平均为什么需要momentum参数?
momentum控制训练统计量向推理统计量的"平滑过渡"。如果momentum太小(如0.5),running_mean/var受近期batch影响大,波动大;如果momentum太大(如0.999),running_mean/var更新太慢,反映不出训练早期到后期的分布变化。通常momentum=0.9或0.99是一个较好的平衡。
章节单词汇总
| 英文 | 音标 | 术语/释义 |
|---|---|---|
| Batch Normalization | /bætʃ ˌnɔːrməlaɪˈzeɪʃən/ | 批量归一化,batch维度标准化 |
| Layer Normalization | /ˈleɪər ˌnɔːrməlaɪˈzeɪʃən/ | 层归一化,feature维度标准化 |
| Running Mean/Var | /ˈrʌnɪŋ miːn/ | 移动均值/方差,训练中累积的统计值 |
| Internal Covariate Shift | /ɪnˈtɜːrnəl koʊˈveriət ʃɪft/ | 内部协变量偏移,层输入分布变化 |
| RMSNorm | /ɑːr em es nɔːrm/ | 均方根归一化,仅除RMS不去均值 |
| Instance Normalization | /ˈɪnstəns/ | 实例归一化,空间维度标准化 |
| Group Normalization | /ɡruːp/ | 组归一化,分组标准化 |
面试练习
Q1 [单选] Batch Normalization在哪个维度上计算均值和方差?
- A. 特征维度
- B. 批次(batch)维度
- C. 空间维度
- D. 时间维度
解答:BN在batch维度上计算——对于每个特征通道,计算该通道在当前batch中所有样本的均值和方差。
Q2 [单选] Layer Normalization为什么适合Transformer?
- A. 不依赖batch size,每个样本独立标准化
- B. 比BN速度更快
- C. 比BN参数更少
- D. 比BN对CNN更有效
解答:LN不依赖batch size——即使batch=1(NLP推理常见场景)也能正常工作。每个词(位置)的表示独立标准化,适合变长序列。
Q3 [单选] BN推理时使用什么统计量?
- A. 当前batch的统计量
- B. 训练时累积的移动平均(running mean/var)
- C. 全零
- D. 随机统计量
解答:BN推理时使用训练过程中累积的running_mean和running_var。这是因为推理时batch_size可能很小(甚至为1),batch统计量不可靠。
Q4 [多选] 关于BN和LN,以下哪些是正确的?
- A. BN在batch维度标准化,LN在feature维度标准化
- B. BN依赖batch size,LN不依赖
- C. BN是CNN的标配,LN是Transformer的标配
- D. LN推理时也需要移动平均统计量
- E. 两者都有可学习的γ和β参数
解答:BN batch维度标准化、依赖batch size、CNN标配;LN feature维度标准化、不依赖batch、Transformer标配。LN训练推理行为一致,不需要移动平均。两者都有γ和β。
Q5 [单选] 大语言模型(如LLaMA)中常使用哪种归一化?
- A. Batch Normalization
- B. Layer Normalization
- C. RMSNorm
- D. Instance Normalization
解答:LLaMA等大模型使用RMSNorm(Root Mean Square Normalization),它是LN的简化版——只除RMS不去均值,计算更高效,在大模型上与LN效果类似。