ML 学习站
跳到正文

训练神经网络的技巧

初始化、学习率、BatchNorm、Dropout、正则化。

30 分钟4 / 41,877
加载中...

训练神经网络的技巧

训神经网络90% 的时间都在调超参。这一章把工业界常用的技巧系统讲一遍。

一、数据预处理

标准化(必须)

把所有特征缩放到零均值、单位方差:

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)  # 训练集用 fit_transform
X_test = scaler.transform(X_test)        # 测试集只用 transform

为什么必须? 如果特征在 [0, 1] 和 [0, 1000000] 之间,梯度更新会严重偏向大方差的方向,训练极慢。

归一化(可选,某些场景)

把数据压到 [0, 1]:x_new = (x - x_min) / (x_max - x_min)。图像像素处理常用。

二、权重初始化

不能全初始化为 0(那样所有神经元对称更新,等于一个神经元)。也不能太大(梯度爆炸)或太小(梯度消失)。

Xavier / Glorot 初始化

适用 tanh / sigmoid。公式:Var(w) = 2 / (n_in + n_out)

He 初始化

适用 ReLU。公式:Var(w) = 2 / n_in

nn.init.kaiming_normal_(layer.weight, nonlinearity='relu')

三、学习率调度

学习率是重要的超参。太大学不到东西,太小训练太慢。

三大策略

1. 固定学习率:简单但僵化

optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)

2. Step Decay:每隔 N 轮降一次

scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)

3. Cosine Annealing:余弦曲线下降,平滑

scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)

4. Warmup + Cosine(现代大模型标配)

# 前 5% 步数线性 warmup, 之后余弦下降
def lr_lambda(step):
    if step < warmup_steps:
        return step / warmup_steps
    progress = (step - warmup_steps) / (total_steps - warmup_steps)
    return 0.5 * (1 + math.cos(math.pi * progress))
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda)

四、Batch Normalization(标配)

在每层激活前对输入做归一化,让分布稳定。

nn.Sequential(
    nn.Linear(256, 256),
    nn.BatchNorm1d(256),  # 关键!
    nn.ReLU()
)

好处:

  • 允许更大学习率
  • 减少对初始化的敏感
  • 轻微的正则化效果
  • 加速收敛

五、Dropout(防止过拟合)

训练时随机让一部分神经元"罢工",推理时全部启用。

nn.Sequential(
    nn.Linear(256, 256),
    nn.ReLU(),
    nn.Dropout(0.5),       # 50% 概率失活
    nn.Linear(256, 10)
)

Dropout 率怎么选:

  • 输入层:0.1 ~ 0.2
  • 隐藏层:0.3 ~ 0.5
  • 输出层:不用

六、梯度裁剪(防爆炸)

训练 RNN / Transformer 时,梯度爆炸很常见。裁剪:

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

七、优化器选择

优化器特点适用
SGD + Momentum经典,稳定,慢简单任务,需要仔细调
Adam自适应学习率,快99% 情况的默认
AdamWAdam + 正确权重衰减Transformer 时代标准
RMSProp老的自适应RNN
# AdamW(现代推荐)
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2)

八、训练循环模板

model = MyModel()
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2)
scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)
criterion = nn.CrossEntropyLoss()

for epoch in range(100):
    model.train()
    for x, y in train_loader:
        optimizer.zero_grad()       # 重要!梯度清零
        pred = model(x)
        loss = criterion(pred, y)
        loss.backward()
        torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)  # 梯度裁剪
        optimizer.step()             # 更新
    scheduler.step()                 # 学习率调度
    print(f"Epoch {epoch}, loss={loss.item():.4f}, lr={scheduler.get_last_lr()[0]:.6f}")

九、超参数调优的经验法则

小结

  • 数据标准化 + 合理初始化 + 学习率调度 是三件套
  • BatchNorm + Dropout 是现代网络的标配
  • AdamW 几乎是万能优化器
  • 训练循环注意 optimizer.zero_grad() 否则梯度会累加
  • 学习率是最重要的超参,先调它

练习思考

  1. 为什么 optimizer.zero_grad() 必须在 loss.backward() 之前调用?不调会怎样?
  2. BatchNorm 在训练和推理时的行为为什么不同?model.eval() 起什么作用?
  3. 用 PyTorch 训一个 MNIST 分类器,比较不调学习率 / 用 StepLR / 用 CosineAnnealing 三种情况的训练曲线。

章末小测验

检验你对《训练神经网络的技巧》的掌握程度。

1

以下哪个超参数最重要,通常先调?

2

BatchNorm 的作用是?

讨论区(0)

加载评论中...