前向过程正确之后,训练程序还需要管好数据、状态和评估。这里沿着 nanoGPT 的源码阅读方向,把一个循环拆成可检查的动作。nanoGPT 已被作者标为旧项目,本课用它学习简洁实现,不把它视作新项目的默认依赖。
每一批数据都走同一条链
先清空旧梯度,再计算当前批次的 logits 和损失,反向传播,最后更新参数。下面是循环骨架,假设 model 返回 logits,x/y 已正确对齐,optimizer 已创建。
model.train()
optimizer.zero_grad(set_to_none=True)
logits = model(x)
loss = torch.nn.functional.cross_entropy(
logits.reshape(-1, logits.size(-1)), y.reshape(-1)
)
loss.backward()
optimizer.step()
PyTorch 的梯度默认会累加。刻意累加多个小批次是一种方法,但它需要明确的缩放和更新时机;忘记清零则是另一回事。
验证时同时控制两种状态
model.eval() 改变 dropout 等模块的行为;torch.no_grad() 关闭梯度记录。两者各自负责一件事,不能互相替代。验证完成后,要恢复训练模式。
验证数据不用于参数更新。若定期看验证集并据此选超参数,这个集合已经参与模型选择;最终报告时应清楚说明评估口径。
检查点应该保存哪些信息
仅保存模型权重,足以做部分推理场景,但不保证能接着同一次训练走。恢复训练通常还需要优化器状态、学习率调度器状态、步数、模型配置和分词器信息;希望更严格地复现时,还要记录随机状态与数据顺序。
保存时要避免把失败或半写入的文件误当成有效检查点。可以先写临时文件,再完成替换;加载来源不明的检查点前,应了解所用格式的安全边界。
日志要能回答具体问题
每次记录步数、学习率、训练损失、独立验证损失和对应计算条件。若记录速度,注明批次、序列长度与硬件。别把教程里的耗时或验证损失当作本机的结果。
本课完成后,你应该能中断一次小实验,再按保存的配置恢复。下一课正式设计预训练实验:固定语料和留出集,判断训练到底带来了什么变化。