← 返回笔记列表
深度学习 第 27 / 49 篇

深度学习:批量归一化 — 让深层网络每一层的输入都稳定

查看“深度学习”专题目录(27/49)
本文目录 14 个小节

先修知识

阅读本文前,建议先理解:

1. 动机:深层网络的输入漂移

输入数据做了标准化 → 训练初始稳定。但随着网络前向传播,越深处的层,其输入分布越来越不稳定。

在训练过程中,前面层参数不断更新 → 后面层接收到的输入分布在持续变化 → 后面层刚学到的模式可能因为输入变化而失效。

输入归一化保证了第一层稳定,但谁保证后面各层的输入也稳定呢?


2. Batch Normalization 的核心操作

2.1 对什么做归一化

一般对激活前的 z 值做归一化(也可以用在对激活后的 a 值)。

2.2 四步操作

以某个神经元的 zz 值为例,batch size = mm:

第一步:计算当前 batch 的均值

μ=1m∑i=1mzi\mu = \frac{1}{m} \sum_{i=1}^{m} z_i

第二步:计算当前 batch 的标准差

σ=1m∑i=1m(zi−μ)2\sigma = \sqrt{\frac{1}{m} \sum_{i=1}^{m} (z_i - \mu)^2}

第三步:标准化

z^i=zi−μσ2+ϵ\hat{z}_i = \frac{z_i - \mu}{\sqrt{\sigma^2 + \epsilon}}

第四步:缩放和平移(关键一步!)

ziBN=γ⋅z^i+βz_i^{\text{BN}} = \gamma \cdot \hat{z}_i + \beta

2.3 γ\gamma 和 β\beta 是什么

参数初始化说明
γ\gamma1可学习的缩放参数
β\beta0可学习的平移参数

有了 γ\gamma 和 β\beta,模型可以:

  • 对固定统计量,取 γ=σ2+ϵ\gamma=\sqrt{\sigma^2+\epsilon}、β=μ\beta=\mu 可恢复输入;统计量随 batch 改变时,一组固定参数不一定能对每个 batch 同时恢复
  • 或者学到任何更合适的分布

BN 并不是把输出强行固定到 N(0,1),而是给模型一个稳定训练的起点,然后让模型自己学最合适的变换。


3. 为什么 BN 前不加偏置 b

计算 z 时:z=Wx+bz = Wx + b

BN 时:z^=z−μσ\hat{z} = \frac{z - \mu}{\sigma}

减均值后,bb 被抵消了——不论 bb 是多少,减去均值后效果相同。而 BN 里已经有了可学习的 β\beta 来控制平移。

当 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 推理总结

训练推理
μ\mu, σ\sigma 来源当前 batch 数据训练的指数加权平均
γ\gamma, β\beta学习更新使用训练好的固定值
行为带噪声(好处:正则化)无噪声(好处:稳定预测)

和 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

参考资料

继续学习本专题