跳转至

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 开始;先小批量过拟合;再扩大数据;每次只改一个因素;保存最优验证集权重;最后只用测试集做一次最终报告。

小率的笔记本

深度学习实践不是“堆更大模型”。稳定流程包括:数据检查、任务匹配、可复现实验、合理验证、观察曲线、控制过拟合,以及知道什么时候该停。