第 4 节 · 约 30 分钟
循环只有三拍。曲线告诉你第几拍坏了。
zero_grad、backward、step。少一拍,线上会给你三种常见样子。
先讲
PyTorch 默认把梯度累加。一个 mini-batch 用完,下一步若不清零,新梯度会叠在旧的上面,步子越走越怪。典型一拍:opt.zero_grad(set_to_none=True),算损失,loss.backward(),opt.step()。backward 只填 .grad,不改参数。step 才按学习率去减。
学习率 0.1 在上一节那个五参数例子里,损失从 0.211 降到 0.108。换成 10,一步就可能越过谷底。换成 1e-8,五十步看起来像一条平线。交叉熵第一步常在 log K 附近:十类约 2.3。一上来就是 1e-5,多半标签和 softmax 接反了。从第一步起就是 0 或 NaN,是代码,先别加 epoch。
先过拟合一个小集合。拿 8 到 32 个样本、关掉随机增强、把模型开得略大,故意让它把这一小撮记住。训练损失应能降到接近 0。这一步过不了:标签和 logits 对错轴、学习率炸了、忘了 model.train()。先修管道,再谈泛化。管道通了,再打开验证集。测试集仍封着。
训练降、验证降:还在学。训练降、验证升:记住训练样本了,停早、加数据、加一点权重衰减。两条都平:学习率太小像没走;太大通常先抖再变 NaN;也可能 zero_grad 没写。过拟合那条,验证最低点出现在 step 420,就把那份权重存成 checkpoint。再训到 2000,训练 0.02、验证 0.41,对外报 0.02 没有意义。
shuffle 只打乱训练;验证按固定顺序,曲线才能和上次比。少了 zero_grad,步子会越走越大,训练损失可能假下降。少了 backward,参数不动,两条平。少了 step,梯度填了,权重还是旧的,看起来也像卡住。先印几个 batch 的 loss 原始值。权重衰减 1e-4 往往够用;一下子加到 0.1,两条会一起抬高。第 1 节那张卡里,测试集还封着。这里用验证来决定何时停。
- lr=0.1,train 0.211→0.108,val 同步下降:还在学
- train 继续降到 0.02,val 从 0.18 升到 0.41:过拟合,停在 val 最低点
- 五十步两条都在 0.21,lr=1e-8 或忘了 zero_grad:卡住
你来做
实线训练,虚线验证。给三条草图起名。
留下三拍和三条线的名字。实验卡仍在本机。测试集还封着。