15.7 深度学习实战技巧¶
小率照着食谱做煎饼,第一锅糊了,第二锅太稀,第三锅终于像样。均哥没有急着换高级锅具,而是让他先做小批量测试、记录火候、分开验证口味、再慢慢调配方。
训练深度学习模型也一样。很多时候,失败不是因为模型不够高级,而是数据、损失、学习率、验证方式或训练记录出了问题。
15.7.1 先让小批量样本过拟合¶
新任务上手时,不要一开始就跑大训练。先拿一个很小的数据子集,例如 32 个样本,让模型把它几乎背下来。
如果连一个小批量都无法过拟合,优先检查:
- 标签是否错位。
- 损失函数是否匹配任务。
- 输出层维度是否正确。
- 数据是否含有 NaN 或无穷大。
- 学习率是否离谱。
这一步像先煎一小张饼。小锅都煎不熟,换大锅只会浪费时间。
15.7.2 用训练集和验证集识别问题¶
| 现象 | 可能原因 | 调整方向 |
|---|---|---|
| 训练损失降不下去 | 欠拟合、学习率不合适、实现错误 | 增大模型、检查数据、调学习率 |
| 训练好,验证差 | 过拟合 | 数据增强、正则化、Dropout、早停 |
| 损失震荡很大 | 学习率过大、batch 太小 | 降学习率、增大 batch、梯度裁剪 |
| 训练很慢 | 数据加载或硬件瓶颈 | profile、增大 workers、混合精度 |
验证集(Validation Set)不是最后考试卷,而是调参时的体检表。最终性能应该留给测试集(Test Set)评估。
15.7.3 一个稳妥训练循环的骨架¶
for epoch in range(num_epochs):
model.train()
for x_batch, y_batch in train_loader:
y_hat = model(x_batch)
loss = loss_fn(y_hat, y_batch)
optimizer.zero_grad()
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
model.eval()
with torch.no_grad():
val_losses = []
for x_val, y_val in val_loader:
val_losses.append(loss_fn(model(x_val), y_val).item())
print(epoch, sum(val_losses) / len(val_losses))
真实项目里还要记录随机种子、数据版本、模型配置和指标曲线。没有记录,调参会变成凭感觉猜。
均哥的训练清单
从 baseline 开始;先小批量过拟合;再扩大数据;每次只改一个因素;保存最优验证集权重;最后只用测试集做一次最终报告。
小率的笔记本
深度学习实践不是“堆更大模型”。稳定流程包括:数据检查、任务匹配、可复现实验、合理验证、观察曲线、控制过拟合,以及知道什么时候该停。
