核心要点
在 batch 维度上对每个特征通道计算均值 μ 和方差 σ²。
归一化 (x−μ)/sqrt(σ²+eps),再用可学习的 γ、β 做缩放和平移。
训练用当前 batch 统计量,同时以动量更新滑动均值/方差供推理使用。
易错点:eps 防止除零;推理阶段必须用 running 统计量而非 batch 统计量。
标准回答
Batch Normalization 在每个特征维度上、沿批次方向做标准化,缓解内部协变量偏移、让训练更稳更快。训练时用当前 mini-batch 的均值与方差归一化输入:(x−μ)/sqrt(σ²+eps),eps 防止除零;再乘以可学习参数 γ、加上 β 恢复表达能力。同时用动量维护全局 running_mean 与 running_var,供推理时使用——推理不能依赖单个 batch 的统计量。下面给出对 (N, D) 输入的 NumPy 前向实现:
import numpy as np
def batchnorm_forward(x, gamma, beta, running_mean, running_var,
training=True, momentum=0.9, eps=1e-5):
# x: (N, D),沿 batch 维 (axis=0) 归一化每个特征
if training:
mu = x.mean(axis=0) # (D,) 批均值
var = x.var(axis=0) # (D,) 批方差(有偏)
x_hat = (x - mu) / np.sqrt(var + eps)
# 用动量更新滑动统计量,供推理使用
running_mean = momentum * running_mean + (1 - momentum) * mu
running_var = momentum * running_var + (1 - momentum) * var
else:
# 推理:使用训练阶段累积的全局统计量
x_hat = (x - running_mean) / np.sqrt(running_var + eps)
out = gamma * x_hat + beta # 缩放平移
return out, running_mean, running_var
if __name__ == '__main__':
np.random.seed(0)
x = np.random.randn(8, 4) * 5 + 3
D = x.shape[1]
out, rm, rv = batchnorm_forward(
x, np.ones(D), np.zeros(D), np.zeros(D), np.ones(D), training=True)
print(out.mean(axis=0).round(5), out.std(axis=0).round(3)) # ~0, ~1常见误区
⚠️ 常见踩坑
推理阶段误用当前 batch 的 μ/σ²(尤其 batch_size=1 时方差为 0)会导致输出抖动甚至 NaN;务必切换到 running 统计量。归一化方向是 batch 维(axis=0),不是特征维。
追问
追问 1:BatchNorm 为什么对小 batch 效果差?有何替代?
小 batch 时用少量样本估计的 μ/σ² 噪声大、不稳定,导致训练退化。替代方案有 LayerNorm(沿特征维,与 batch 无关,Transformer 常用)、GroupNorm(按通道分组,检测/分割友好)、InstanceNorm 等,它们都不依赖 batch 大小。
延伸学习
与本题相关的知识库文章、术语、工具与行业资讯。