归一化全家桶 讲清了 BN/LN/GN/IN 各沿哪个维度归一、如何选型;本篇只回答工程面的问题:这些层在 torch.nn 里怎么构造、running statistics 如何运转、train/eval 切换时行为差异与坑在哪。

构造参数:以 BatchNorm2d 为标本

五个构造参数里真正影响数值行为的是 num_features 与 momentum,其余三个控制「有没有附加件」。

参数默认语义
num_features必填通道数 C,即输入 [N, C, H, W] 的 C;weight、bias、缓冲区形状全由它定
eps1e-5分母稳定项,加在标准差上防除零
momentum0.1EMA 更新权重,语义见下节
affineTrue是否学习 γ/β,对应模块的 weight/bias
track_running_statsTrue是否维护 running_mean/var 缓冲区

构造完立刻能看到 num_features 决定的一切形状:

import torch
import torch.nn as nn
 
bn = nn.BatchNorm2d(16)                 # 输入应为 [N, 16, H, W]
x = torch.randn(4, 16, 8, 8)
out = bn(x)                             # train 模式:batch 统计归一 + 更新缓冲区
print(bn.running_mean.shape)            # torch.Size([16]),是 buffer 不是 parameter
print([n for n, _ in bn.named_buffers()])  # ['running_mean', 'running_var', 'num_batches_tracked']

running statistics:momentum 不是动量

BN 训练时用当前 batch 的 μ/σ 归一化,同时把这份统计量按 EMA 写进缓冲区,留给推理用:

其中 是缓冲区里的 running 统计量, 是当前 batch 算出的 μ 或 σ, 就是 momentum。注意语义反转: 是新观测的权重,调大则缓冲区更快贴近当前 batch,与优化器里「惯性」的动量正相反;设为 None 改用累计平均(以 num_batches_tracked 为分母)。running_mean/var 是 buffer:进 state_dict、不进 gradient,随模型保存加载而迁移——这份跨 batch 积累的状态,是 BN 与无状态层的本质区别。

train/eval 两副面孔与三个坑

train 和 eval 的切换之所以要紧,九成原因在 BN:train 用 batch 统计并更新缓冲区,eval 冻结缓冲区、直接引用 running stats。验证循环忘调 model.eval(),每个 val batch 都在改写 running_mean/var——训练集统计被验证数据污染,全程无报错,只有部署后指标异常才暴露。

  • 单样本推理:模型仍在 train 模式喂 batch_size=1,BN 拿这一个样本的统计归一化——BatchNorm1d 直接抛 ValueError,BatchNorm2d 不报错但输出失真;eval 后走 running stats 即无此问题
  • track_running_stats=False:显式选择「永远用 batch 统计」,推理也带 batch 依赖,适合 batch 统计本身就是要的效果的场景(如某些生成模型)
  • affine=False:模块没有 weight/bias,假定这两个参数存在的下游代码(剪枝、量化工具)会直接 KeyError
  • 惯例位置:BN 放 nn.Conv2d 或 Linear 之后、激活之前,即 Conv → BN → ReLU

LayerNorm 与无缓冲区家族

BN 的全部复杂度都来自「跨样本借统计量」。LayerNorm 反其道,统计量在每个样本内部算,构造参数里根本没有 momentum 和 track_running_stats——没有 running stats,train/eval 同一条公式:

ln = nn.LayerNorm(512)        # normalized_shape=512:输入最后一维必须是 512
gn = nn.GroupNorm(32, 16)     # 16 通道分 32 组,与 batch 无关
inn = nn.InstanceNorm2d(16)   # GN 的 G=C 特例,注意默认 affine=False

normalized_shape 声明对末尾哪些维度归一:[N, L, D] 输入配 LayerNorm(D),即对每个位置上的 D 维特征归一——统计量与序列长度 L、batch 大小全无关,变长序列和 Transformer 因此天然选它。InstanceNorm2d 是分组数等于通道数的 GN 特例,且默认 affine=False、不跟踪统计,与 BN 系默认相反;多卡 DDP 把 batch 切碎导致单卡统计失真时,用 nn.SyncBatchNorm.convert_sync_batchnorm 把 BN 换成跨卡同步版本。