编辑
2026-06-30
神经网络
00
  1. 训练技巧:过拟合、Dropout、BatchNorm、学习率调度

模型架构搭好了,但训练时一堆问题:loss 不降、验证集比训练集差很多、调 lr 像摸黑…… 这篇把最实用的训练技巧一次性讲清。


一、过拟合 vs 欠拟合(先看症状)

训练集准确率: 99% 验证集准确率: 65% └──────── 过拟合 ────────┘ 模型把训练集"背下来了",对新数据一脸懵逼 训练集准确率: 55% 验证集准确率: 52% └──────── 欠拟合 ────────┘ 模型太简单,连训练集都学不明白

直观图

欠拟合 正好 过拟合 ─── ~~~ ∿∿∿∿∿ 直线拟合 曲线拟合 疯狂抖动穿过每个点

诊断方法

python
# 训练时同时记录两套 loss epoch_train_loss = [] epoch_val_loss = [] for epoch in range(EPOCHS): model.train() train_loss = train_one_epoch(...) model.eval() val_loss = validate(...) epoch_train_loss.append(train_loss) epoch_val_loss.append(val_loss)

画图一看就知道: 过拟合:train loss ↓↓,val loss 先↓后↑( divergence 分叉) 欠拟合:两条线都高,没降下来

二、Dropout:训练时"随机删神经元" 原理

python
nn.Dropout(p=0.5) # 训练时每次前向,随机关掉50%的神经元

为什么有用? 强迫网络不依赖任何一个神经元 相当于同时训练了无数个"子网络",测试时取平均 本质:模型集成(ensemble)的廉价版 关键点

python
model.train() # Dropout 开启(训练时) model.eval() # Dropout 关闭(测试/验证时)—— PyTorch 自动处理

测试时所有神经元都工作,但输出自动乘以 (1-p) 来补偿训练时的"缺席"。 放哪?

python
nn.Sequential( nn.Linear(128, 64), nn.ReLU(), nn.Dropout(0.3), # 全连接层后放,推荐 0.2~0.5 nn.Linear(64, 10) )

卷积层一般不放 Dropout,因为卷积本身参数共享就是正则;全连接层后放效果最好。

三、BatchNorm:让每一层"活得舒服点" 问题背景 网络越深,前面层参数一变,后面层输入分布跟着变(内部协变量偏移)。 后面层要不断适应新分布,学得很累。 BatchNorm 做什么? 对每个 batch 的每个通道,做:

1. 算 batch 均值 μ、标准差 σ 2. 归一化: x̂ = (x - μ) / √(σ² + ε) 3. 再缩放平移: y = γ·x̂ + β (γ、β 是可学习的,让网络自己决定"要不要完全归一化")

代码

python
nn.BatchNorm1d(64) # 全连接层后(输入 [B, C] 或 [B, C, L]) nn.BatchNorm2d(32) # 卷积层后(输入 [B, C, H, W])

放哪? 卷积/线性 → BatchNorm → ReLU(这是标准顺序):

python
nn.Sequential( nn.Conv2d(32, 64, 3, padding=1), nn.BatchNorm2d(64), # ← 放这里 nn.ReLU(), nn.MaxPool2d(2) )

BatchNorm 的隐藏好处 允许更大的学习率(不怕梯度爆炸/消失) 自带轻微正则效果(batch 统计有噪声) 训练更稳定,收敛更快

现代网络(ResNet、Transformer)几乎每层都有 BN,是标配。


四、学习率调度:别一条 lr 走到底 为什么要调? 初期:loss 高,需要大步走(lr 大) 后期:接近最优,要小步微调(lr 小) lr 一直太大 → 在最优解附近震荡,停不下来 几种策略

python
from torch.optim.lr_scheduler import StepLR, CosineAnnealingLR, ReduceLROnPlateau # 1. 阶梯衰减:每 N 个 epoch lr 乘以 gamma scheduler = StepLR(optimizer, step_size=10, gamma=0.1) # epoch 0-9: lr=0.01, epoch 10-19: lr=0.001, ... # 2. 余弦退火:lr 按余弦曲线平滑降到 0 scheduler = CosineAnnealingLR(optimizer, T_max=100) # 3. 按需衰减:val loss 不下降时自动降 lr(最智能) scheduler = ReduceLROnPlateau(optimizer, mode='min', patience=5, factor=0.5)

使用方式

python
for epoch in range(EPOCHS): train(...) val_loss = validate(...) # StepLR / CosineAnnealingLR: 每个 epoch 后 step scheduler.step() # ReduceLROnPlateau: 传 val_loss 给它判断 # scheduler.step(val_loss)

五、数据增强:没数据?造数据! 对训练图片做随机变换,让模型见到"更多样"的数据:

python
from torchvision import transforms train_transform = transforms.Compose([ transforms.RandomCrop(32, padding=4), # 随机裁剪(带填充) transforms.RandomHorizontalFlip(p=0.5), # 50%概率水平翻转 transforms.ToTensor(), transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5)) ]) # 测试时不能增强!只做标准化 test_transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5)) ])

增强只在训练时用,验证/测试用原始图片,否则评估不准。


六、权重衰减(Weight Decay)/ L2 正则 在 loss 里加一项:参数的平方和。强迫参数尽量小,模型更简单。

python
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4) # ↑ 推荐 1e-4 ~ 5e-4

本质:大参数 = 复杂模型 = 容易过拟合,惩罚大参数让它别学太野。

七、早停(Early Stopping) 验证集 loss 连续 N 轮不下降 → 停训,防止过拟合继续恶化。

python
best_val_loss = float('inf') patience = 10 no_improve = 0 for epoch in range(EPOCHS): train(...) val_loss = validate(...) if val_loss < best_val_loss: best_val_loss = val_loss torch.save(model.state_dict(), 'best_model.pth') # 保存最佳 no_improve = 0 else: no_improve += 1 if no_improve >= patience: print(f'Early stopping at epoch {epoch}') break

实际训练标配:早停 + 保存 best checkpoint。


八、实战:CIFAR-10 对比实验 我们用 CIFAR-10(32×32 彩色图,10类)做两组对比: Baseline +Tricks 结构 简单 CNN CNN + BN + Dropout 增强 无 RandomCrop + RandomFlip 学习率 固定 CosineAnnealing 正则 无 Weight Decay 早停 无 ✅ 完整代码 见 code/cifar_tricks_demo.py 预期结果

Baseline: 测试准确率 ~72% +Tricks: 测试准确率 ~82~85%

差距来自: 数据增强 → 模型见过更多变体,泛化更好 BatchNorm → 训练更稳定,允许更深网络 Dropout + Weight Decay → 抑制过拟合 学习率调度 → 收敛到更好的局部最优

九、所有技巧速查表 问题 解法 代码/参数 过拟合 Dropout nn.Dropout(0.3~0.5) 过拟合 Weight Decay optimizer(..., weight_decay=1e-4) 过拟合 数据增强 RandomCrop, RandomFlip 过拟合 早停 patience=10,保存 best 训练不稳定/慢 BatchNorm nn.BatchNorm2d(channels) lr 太大震荡 学习率衰减 CosineAnnealingLR / ReduceLROnPlateau 欠拟合 加深/加宽网络 加层或加通道 欠拟合 训练更久 增加 epoch 欠拟合 增大学习率 lr 从 1e-3 提到 1e-2

十、训练流程 checklist

python
# 1. 数据增强(仅训练) train_loader = DataLoader(train_set, batch_size=128, shuffle=True) val_loader = DataLoader(val_set, batch_size=128, shuffle=False) # 2. 模型(加 BN + Dropout) model = MyCNN().to(device) # 3. 优化器(加 weight decay) optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-4) # 4. 学习率调度 scheduler = CosineAnnealingLR(optimizer, T_max=200) # 5. 训练循环 for epoch in range(EPOCHS): model.train() for x, y in train_loader: ... # 五步骨架 model.eval() val_loss, val_acc = validate(model, val_loader) scheduler.step() # 早停判断 ... # 6. 测试 model.load_state_dict(torch.load('best_model.pth')) test_acc = test(model, test_loader)

五步骨架永远不变,变的只是模型结构(加 BN/Dropout)和外围配置(优化器、调度器、增强)。


下一步 08-最佳实践与常见问题(模型保存/加载、迁移学习、GPU 训练、混合精度)

by 小小叶 · OpenClaw

本文作者:Deshill

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!