先修知识
阅读本文前,建议先理解:
1. 动机:深层网络的输入漂移
输入数据做了标准化 → 训练初始稳定。但随着网络前向传播,越深处的层,其输入分布越来越不稳定。
在训练过程中,前面层参数不断更新 → 后面层接收到的输入分布在持续变化 → 后面层刚学到的模式可能因为输入变化而失效。
输入归一化保证了第一层稳定,但谁保证后面各层的输入也稳定呢?
2. Batch Normalization 的核心操作
2.1 对什么做归一化
一般对激活前的 z 值做归一化(也可以用在对激活后的 a 值)。
2.2 四步操作
以某个神经元的 值为例,batch size = :
第一步:计算当前 batch 的均值
第二步:计算当前 batch 的标准差
第三步:标准化
第四步:缩放和平移(关键一步!)
2.3 和 是什么
| 参数 | 初始化 | 说明 |
|---|---|---|
| 1 | 可学习的缩放参数 | |
| 0 | 可学习的平移参数 |
有了 和 ,模型可以:
- 对固定统计量,取 、 可恢复输入;统计量随 batch 改变时,一组固定参数不一定能对每个 batch 同时恢复
- 或者学到任何更合适的分布
BN 并不是把输出强行固定到 N(0,1),而是给模型一个稳定训练的起点,然后让模型自己学最合适的变换。
3. 为什么 BN 前不加偏置 b
计算 z 时:
BN 时:
减均值后, 被抵消了——不论 是多少,减去均值后效果相同。而 BN 里已经有了可学习的 来控制平移。
当 Linear/Conv 后紧接着对相同通道做 BN 时,前层通常可以设 bias=False;若中间还有其他运算,不能直接套用这一结论。
4. 直观理解
输入层(标准化) → 稳定
↓
第 1 层(BN)→ 输出分布稳定
↓
第 2 层(BN)→ 输出分布稳定 ← 深层在稳固的基础上学习
↓
...
BN 控制当前统计范围内的尺度,通常有利于优化;这不保证整个输出分布恒定,也不能把其效果完全归因于消除分布漂移。
5. BN 的副作用:自带正则化
BN 最初不是为了正则化设计的,但实践中发现有防过拟合的效果:
- 每个 batch 的均值和标准差都不同(取决于具体抽了哪些数据)
- 相当于给数据注入了随机噪声
- 模型被迫学习更鲁棒的特征,不会过度依赖某个 batch 的特定数值
使用 BN 后,有时可以减小或去掉 Dropout。
6. 推理时的 BN
推理使用训练期间维护的运行统计量,以避免预测依赖当前 batch 的组成。对 [N,C] 的 BatchNorm1d,训练时每通道只有一个元素会报错;卷积 BN 的统计范围还包含空间维,不能简单说 batch size 为 1 就一定无法训练。
PyTorch 的 momentum=m 使用以下约定(它不是优化器中的动量):
running_mean = (1-m) * running_mean + m * batch_mean
running_var = (1-m) * running_var + m * batch_var_unbiased
训练前向归一化使用总体方差估计(除以统计元素数),运行方差更新使用无偏估计。默认 track_running_stats=True 时,eval() 使用运行统计量;设为 False 时,推理仍使用当前 batch 的统计量。
7. 训练 vs 推理总结
| 训练 | 推理 | |
|---|---|---|
| , 来源 | 当前 batch 数据 | 训练的指数加权平均 |
| , | 学习更新 | 使用训练好的固定值 |
| 行为 | 带噪声(好处:正则化) | 无噪声(好处:稳定预测) |
和 Dropout 一样,需要正确切换 model.train() / model.eval()。
8. PyTorch 实现
import torch.nn as nn
class NeuralNetwork(nn.Module):
def __init__(self):
super().__init__()
self.model = nn.Sequential(
nn.Linear(28*28, 128, bias=False), # BN 前不加 bias
nn.BatchNorm1d(128), # 128 是该层神经元数
nn.ReLU(),
nn.Linear(128, 128, bias=False),
nn.BatchNorm1d(128),
nn.ReLU(),
nn.Linear(128, 64, bias=False),
nn.BatchNorm1d(64),
nn.ReLU(),
nn.Linear(64, 10) # 输出层不加 BN
)
def forward(self, x):
return self.model(x.flatten(1))
层的顺序:Linear(bias=False) → BatchNorm → ReLU
9. 总结
BN 的作用:让每一层输出的分布稳定,训练更深更快
一步 BN 做的事:
z_norm = (z - μ) / √(σ² + ε) ← 标准化(每个 batch 的统计值)
z_BN = γ · z_norm + β ← 缩放平移(可学习参数)
关键点:
├── γ=1, β=0 初始化,模型可学回原始分布
├── BN 前的 Linear 不用 bias(会被减均值抵消)
├── 训练用 batch 统计值,推理用 running 平均值
├── 自带轻微正则化效果(batch 噪声)
└── 输出层一般不加 BN
PyTorch:Linear(bias=False) → BatchNorm1d → ReLU
参考资料
- PyTorch BatchNorm1d:统计维度、方差估计与运行统计量。