核心要点

  • 在 batch 维度上对每个特征通道计算均值 μ 和方差 σ²。

  • 归一化 (x−μ)/sqrt(σ²+eps),再用可学习的 γ、β 做缩放和平移。

  • 训练用当前 batch 统计量,同时以动量更新滑动均值/方差供推理使用。

  • 易错点:eps 防止除零;推理阶段必须用 running 统计量而非 batch 统计量。

标准回答

Batch Normalization 在每个特征维度上、沿批次方向做标准化,缓解内部协变量偏移、让训练更稳更快。训练时用当前 mini-batch 的均值与方差归一化输入:(x−μ)/sqrt(σ²+eps),eps 防止除零;再乘以可学习参数 γ、加上 β 恢复表达能力。同时用动量维护全局 running_mean 与 running_var,供推理时使用——推理不能依赖单个 batch 的统计量。下面给出对 (N, D) 输入的 NumPy 前向实现:

python
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),不是特征维。

追问

追问 1BatchNorm 为什么对小 batch 效果差?有何替代?

小 batch 时用少量样本估计的 μ/σ² 噪声大、不稳定,导致训练退化。替代方案有 LayerNorm(沿特征维,与 batch 无关,Transformer 常用)、GroupNorm(按通道分组,检测/分割友好)、InstanceNorm 等,它们都不依赖 batch 大小。

追问 2BatchNorm 放在激活函数前还是后?

原论文将 BN 放在线性层之后、激活函数之前(Conv/FC → BN → ReLU),这样归一化的是仍近似线性的预激活值。实践中也有「BN 放激活后」的变体,差异不大;关键是保持训练与推理一致,且 BN 前的线性层通常可省略 bias(被 β 吸收)。

延伸学习

与本题相关的知识库文章、术语、工具与行业资讯。