跳到主要内容

训练技巧与策略

问题

深度学习训练中有哪些关键技巧?Batch Normalization 和 Layer Normalization 有什么区别?Dropout 的原理是什么?混合精度训练如何加速?

答案

深度学习模型能否训好,很大程度取决于训练策略。本文汇总面试中常问的核心训练技巧。

一、归一化(Normalization)

归一化的目的:稳定训练、加速收敛、减少对初始化的敏感度

Batch Normalization(BN)

对同一 batch 中所有样本的同一特征维度归一化:

x^=xμBσB2+ϵγ+β\hat{x} = \frac{x - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} \cdot \gamma + \beta
  • μB,σB\mu_B, \sigma_B:当前 mini-batch 的均值和方差
  • γ,β\gamma, \beta:可学习的缩放和偏移参数

Layer Normalization(LN)

单个样本的所有特征归一化(与 batch 大小无关):

归一化归一化维度依赖 batch适用场景
Batch Norm跨样本,同一特征CNN(图像)
Layer Norm跨特征,同一样本Transformer / NLP
Group Norm跨通道分组小 batch 的 CNN
RMS NormLN 的简化版(去掉均值)LLaMA、现代 LLM
为什么 Transformer 用 LN 而不是 BN?
  1. NLP 中序列长度不固定,BN 难以在不同长度上计算统计量
  2. 推理时 batch size 可能为 1,BN 需要维护 running mean/var
  3. LN 对每个样本独立归一化,更适合变长序列

RMSNorm(Root Mean Square Normalization)

LLaMA 等现代 LLM 使用 RMSNorm——去掉了均值中心化,只做方差归一化:

RMSNorm(x)=x1ni=1nxi2+ϵγ\text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{n}\sum_{i=1}^{n}x_i^2 + \epsilon}} \cdot \gamma

比 LN 少一次均值计算,速度更快,效果相当。

二、正则化

Dropout

训练时随机丢弃一定比例的神经元(输出置零),迫使网络不依赖任何单个神经元:

# PyTorch 中的 Dropout
import torch.nn as nn

class MyModel(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(768, 256)
self.dropout = nn.Dropout(p=0.1) # 10% 的神经元被丢弃
self.fc2 = nn.Linear(256, 10)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout(x) # 训练时随机丢弃,推理时自动关闭
return self.fc2(x)
关键细节
  • 训练时:以概率 pp 将激活设为 0,并将剩余激活除以 (1p)(1-p) 保持期望不变(Inverted Dropout)
  • 推理时:不做任何 Dropout,使用完整网络

其他正则化技巧

方法原理典型场景
L2 正则化(Weight Decay)惩罚大权重所有模型
Label Smoothing软标签替代 hard 0/1分类任务
数据增强增加训练数据多样性CV(翻转、裁剪)、NLP(回译)
Early Stopping验证集指标不提升时停止防过拟合

三、混合精度训练

使用 FP16(半精度)代替 FP32(全精度)进行前向和反向传播,速度翻倍、显存减半

为什么不全用 FP16?

FP16 的表示范围小(最大 65504),容易出现:

  • 溢出:梯度/损失值太大
  • 下溢:梯度太小变成 0

Loss Scaling 技巧

将 Loss 乘以一个大系数(如 1024),使梯度放大到 FP16 可表示的范围,更新权重时再除回来:

# PyTorch AMP(Automatic Mixed Precision)
from torch.cuda.amp import autocast, GradScaler

scaler = GradScaler()

for data, target in dataloader:
optimizer.zero_grad()

# 前向传播用 FP16
with autocast():
output = model(data)
loss = criterion(output, target)

# 反向传播:自动 Loss Scaling
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()

精度类型对比

类型位数范围用途
FP3232 bit±3.4×1038\pm 3.4 \times 10^{38}权重存储
FP1616 bit±65504\pm 65504前向/反向计算
BF1616 bit±3.4×1038\pm 3.4 \times 10^{38}现代 LLM 训练首选
FP88 bit--推理、部分训练
BF16 vs FP16
  • BF16 范围和 FP32 一样大(8 bit 指数位),但精度低(7 bit 尾数)
  • FP16 精度更高(10 bit 尾数),但范围小,容易溢出
  • 现代 GPU(A100、H100)原生支持 BF16,LLM 训练首选 BF16

四、梯度累积

当 batch size 受显存限制时,可以用梯度累积模拟大 batch:

accumulation_steps = 4  # 相当于 batch size × 4

for i, (data, target) in enumerate(dataloader):
with autocast():
output = model(data)
loss = criterion(output, target) / accumulation_steps

loss.backward()

if (i + 1) % accumulation_steps == 0:
optimizer.step()
optimizer.zero_grad()

五、Warmup 与学习率调度

现代训练通常使用 Warmup + Cosine Decay

  1. Warmup(前 1-5% 步):学习率从 0 线性增长到目标值
  2. Cosine Decay:余弦曲线从目标值衰减到最小值
from torch.optim.lr_scheduler import CosineAnnealingLR, LinearLR, SequentialLR

optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4)

# 前 1000 步 warmup,之后余弦衰减
warmup = LinearLR(optimizer, start_factor=0.01, total_iters=1000)
cosine = CosineAnnealingLR(optimizer, T_max=50000, eta_min=1e-5)
scheduler = SequentialLR(optimizer, [warmup, cosine], milestones=[1000])

六、梯度裁剪

防止梯度爆炸——限制梯度的最大范数:

# 全局梯度裁剪(按范数)
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)

LLM 训练中通常将 max_norm 设为 1.0,这是标准做法。


常见面试问题

Q1: Batch Normalization 在训练和推理时的行为有什么不同?

答案

  • 训练时:使用当前 mini-batch 的均值和方差归一化,同时更新 running mean 和 running var(指数移动平均)
  • 推理时:使用训练过程中积累的 running mean 和 running var(固定值),不依赖当前 batch

Q2: 为什么 Dropout 能防止过拟合?

答案: 两个角度解释:

  1. 集成学习角度:每次随机丢弃不同神经元,相当于训练了指数级数量的子网络,推理时是所有子网络的集成
  2. 特征冗余角度:强迫每个神经元独立有用,不能依赖特定的"协作伙伴",学到更鲁棒的特征

Q3: 什么是梯度消失和梯度爆炸?如何解决?

答案

问题原因解决方案
梯度消失sigmoid/tanh 饱和区梯度趋零;网络太深ReLU 激活、残差连接、LN、合适的初始化
梯度爆炸权重/梯度连乘导致指数增长梯度裁剪、权重正则化、BN/LN

Q4: 混合精度训练的原理是什么?

答案: 核心思路:前向计算和梯度用 FP16/BF16(速度快、显存省),权重拷贝用 FP32(保持精度)。Loss Scaling 防止梯度下溢——先放大 Loss 使梯度变大,更新权重时再缩回。PyTorch 的 AMP(autocast + GradScaler)会自动处理这些细节。


相关链接