HelloAI
L3 第 3 篇 🐣 难度 🕒 10 分钟

深网络训练技巧:BatchNorm / Dropout / 初始化

让深层网络真的能训起来的三大件——为什么需要它们、怎么用、什么时候用。

阿莱
2026/9/3

L3-02 我们知道反向传播怎么算梯度。 但梯度有了不等于能训好深网络 ——

2012 年之前,“训 10 层以上”对学术界都是难题。

直到一系列训练技巧让深层网络可行 —— 这篇讲三大件。

问题:深网络训不动

L3-02 提过:链式法则是连乘。 深层 + sigmoid → 局部梯度 < 0.25 → 10 层之后梯度接近 0。

经典症状:

  • 前几层根本不学
  • loss 训练 100 epoch 不动
  • 改架构、调学习率都不行

解决方案分三个方向:

  1. 更好的初始化 —— 让一开始梯度就别太大太小
  2. 更好的激活 —— ReLU 替代 sigmoid
  3. 正则化 + 归一化 —— BatchNorm、Dropout 等

一、好的初始化

错误做法

W = torch.zeros(...)   # 全 0 —— 每个神经元学到一样的东西
W = torch.randn(...) * 1.0  # 方差太大 —— 激活爆炸

Xavier / Glorot 初始化(2010)

每层输出的方差 ≈ 每层输入的方差

WN(0,2nin+nout)W \sim \mathcal{N}\Big(0, \frac{2}{n_{\text{in}} + n_{\text{out}}}\Big)

适合 sigmoid / tanh 激活。

He 初始化(2015)

ReLU 砍掉一半激活 → 需要更大的方差补回来:

WN(0,2nin)W \sim \mathcal{N}\Big(0, \frac{2}{n_{\text{in}}}\Big)

ReLU 网络默认用这个

# PyTorch 默认用 He
nn.init.kaiming_normal_(layer.weight, mode='fan_in', nonlinearity='relu')

一句话:初始化让深网络一开始就别处于”梯度直接死掉”的初始位置

二、激活函数:ReLU 革命

Sigmoid 的问题

σ(x) = 1 / (1 + e^(-x))
σ'(x) ≤ 0.25  ← 最大值在 x=0 处

每层乘 0.25 —— 10 层后梯度 ≈ 0.0000001。梯度消失

ReLU = max(0, x)

ReLU'(x) = 1 (x > 0) 或 0 (x ≤ 0)

正值区域梯度恒为 1 —— 不衰减。 计算极快(一个 max)。

问题:负值区域梯度为 0 —— 神经元”死亡”(dying ReLU)。

改良版

激活特点
Leaky ReLU负值给一点小斜率(如 0.01x)
GELU平滑版 ReLU,Transformer 默认
Swish / SiLUxσ(x)x \cdot \sigma(x),GLU 系列

实战:CNN 用 ReLU / Leaky ReLU;Transformer 用 GELU。

三、Batch Normalization(2015)

直觉

训练时每层的输入分布一直在变(因为前一层参数在变)—— internal covariate shift

BatchNorm 的招:在每层激活后强制归一化,让分布稳定。

公式

对一个 batch {x1,...,xm}\{x_1, ..., x_m\}

  1. 算均值:μ=1mxi\mu = \frac{1}{m}\sum x_i
  2. 算方差:σ2=1m(xiμ)2\sigma^2 = \frac{1}{m}\sum (x_i - \mu)^2
  3. 归一化:x^i=xiμσ2+ϵ\hat{x}_i = \frac{x_i - \mu}{\sqrt{\sigma^2 + \epsilon}}
  4. 放缩偏移:yi=γx^i+βy_i = \gamma \hat{x}_i + \betaγ,β\gamma, \beta 可学习)

最后那一步很关键 —— 让模型还能学到不归一化时的分布(如果它需要)。

PyTorch

import torch.nn as nn

model = nn.Sequential(
    nn.Linear(100, 256),
    nn.BatchNorm1d(256),    # ← 这一行
    nn.ReLU(),
    nn.Linear(256, 10),
)

训练 vs 推理

训练时用当前 batch 的 μ,σ2\mu, \sigma^2推理时用训练时积累的移动平均 —— 否则单条样本统计无意义。

model.train()   # 用 batch 统计
model.eval()    # 用移动平均

忘了切 eval 是新手常犯 bug

替代品

方法何时用
BatchNormCNN / 图像
LayerNormTransformer / NLP(batch 维度有可变长度)
GroupNorm小 batch 时(< 16)
InstanceNorm风格迁移

Transformer 用 LayerNorm,因为序列长度可变,BN 的 batch 统计不稳。

四、Dropout(2012)

直觉

训练时随机丢弃一部分神经元(设为 0)—— 强迫网络不能依赖任何单一神经元,学到冗余表征

类似于:

  • 集成学习的”训一堆稍微不同的模型再平均”
  • 噪声注入让模型 robust

公式

# 训练时
mask = (random < 0.5)   # 50% 丢弃
output = input * mask / 0.5   # 缩放剩下的 keep 概率

/ 0.5 是为了让期望保持不变(inverted dropout)。

PyTorch

nn.Sequential(
    nn.Linear(100, 256),
    nn.ReLU(),
    nn.Dropout(0.5),    # 50% 丢弃率
    nn.Linear(256, 10),
)

推理时关闭

model.eval() 同时关闭 dropout(不丢弃 + 不缩放)。

用量经验

网络推荐 dropout
全连接 MLP0.3-0.5
CNN 全连接层0.5
CNN 卷积层0.1-0.2 或不用
Transformer0.1
极大模型 + 海量数据0(不需要)

现代 LLM 多数不用 dropout —— 数据量足够大,过拟合不是主要问题。

五、梯度裁剪(Gradient Clipping)

防止梯度爆炸的暴力方案:

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

如果某次 backward 整体梯度范数 > 1.0,就整体缩放到 1.0

RNN 和 LSTM 训练里几乎必用。Transformer 训练也常用。

六、残差连接(2015,ResNet)

不是单一技巧,但根本上解决了梯度消失

普通层:y=F(x)y = F(x) 残差层:y=F(x)+xy = F(x) + x

反向传播时:

Lx=Ly(Fx+1)\frac{\partial L}{\partial x} = \frac{\partial L}{\partial y} \cdot \big(\frac{\partial F}{\partial x} + 1\big)

那个 +1 —— 梯度永远有一条路径直接传回去,不会消失。

让训 100 层、200 层、甚至 1000 层都可能。 现代深度学习架构(CNN、Transformer)几乎全用残差

一个组合模板

现代 CNN block 的标准长相:

class ResBlock(nn.Module):
    def __init__(self, channels):
        super().__init__()
        self.conv1 = nn.Conv2d(channels, channels, 3, padding=1)
        self.bn1 = nn.BatchNorm2d(channels)
        self.conv2 = nn.Conv2d(channels, channels, 3, padding=1)
        self.bn2 = nn.BatchNorm2d(channels)

    def forward(self, x):
        residual = x                          # ← 残差路径
        out = self.bn1(self.conv1(x))
        out = torch.relu(out)
        out = self.bn2(self.conv2(out))
        out = out + residual                  # ← 加回去
        return torch.relu(out)

包含:He 初始化(默认)、ReLU、BatchNorm、残差。 深度学习训练的标准配方

训练实战 checklist

新建网络训不起来?按这个顺序检查:

  1. ✅ 用 ReLU/GELU 不要 sigmoid 做隐藏层
  2. ✅ 用 He / Xavier 初始化(PyTorch 默认就行)
  3. ✅ 加 BatchNorm / LayerNorm
  4. ✅ 大模型加残差连接
  5. ✅ RNN 加梯度裁剪
  6. ✅ 全连接层加 0.3-0.5 Dropout
  7. ✅ 学习率从 10310^{-3} 开始试,慢慢调
  8. ✅ Loss 不下降 → 检查数据 / 标签是不是错了
  9. ✅ Loss 训练集低、验证集高 → 加正则化 / 数据增强
💡 一个真理

2012-2016 这几年深度学习的爆发,不仅是算力—— 还有这些工程技巧让深网络第一次能稳定训起来。

Hinton:「Dropout 是用过的最有效的 ML 想法之一」。

何凯明:「ResNet 让训 152 层成为现实」。

技术细节看似琐碎 —— 但每一个都是某篇论文 / 多年经验换来的。

下一篇推荐:L3-04 RNN/LSTM 兴衰L3-07 注意力变体

📬

读到这里说明你认真在学 🎯

订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。

💬

讨论区

· 用 GitHub 账号登录评论
⚠️ Giscus 评论未配置 —— 在 src/components/Comments.astro 顶部填入 仓库 ID 和分类 ID(见组件注释里的配置步骤)。