训练损失反弹解释 | AI生成和翻译
这个波动是数据和架构中固有的。具体原因如下:
1. 每一步的训练损失只是单个小批量的样本,而不是评估损失
train_loss_f = train_loss.item() 这行代码获取的是一个全局批次(65,536 个 token ≈ 32 段代码片段)的损失。虽然这个值是在大约 8 次梯度累积微批次上平均得到的,但它仍然只是数据点。评估损失追踪的是真实的收敛情况——而这是一个噪声较大的代理指标。
2. EMA 窗口非常小
ema_beta = 0.9 # line 563
smooth_train_loss = 0.9 * smooth_train_loss + 0.1 * train_loss_f
β=0.9 对应的有效窗口约为 10 步。这意味着:
- 单个劣质批次会在接下来的 10 步中贡献约 10% 的显示损失
- 在 10 步窗口内出现 0.12 的范围(1.25 ↔ 1.37),表明原始每批次方差要大得多——可能为 ±0.3 或更大
- 在余弦学习率为 0.84 的情况下,梯度更新很小,因此每批次的损失主要由恰好落在这个批次中的具体数据决定,而不是由权重更新决定
3. Codeparrot 数据具有很高的内在方差
54 个 parquet 分片,每个来自不同的 GitHub 仓库。一个 65K token 的批次可能包含:
print("hello world")→ 接近零损失- 带有类型注解的复杂异步生成器 → 高损失
- 一段文档字符串 → 中等损失
这些数据在批次间分布不均匀。模型还没有记住任何东西(2.86 亿参数,总共 247 亿 token 数据),因此每个批次本质上都是一次不同的“测试”,分数变化很大。
4. 在 65K 批次 / 2.86 亿参数的情况下,梯度噪声是真实存在的
使用 8 个微批次 × 4 个序列 × 每个序列 2048 个 token = 每步 65K token,梯度是对完整数据分布上真实梯度的带噪声估计。在训练进行到 46% 时,损失已经足够平坦,以至于梯度噪声主导了方向——模型在某个区域附近振荡,而不是沿着陡峭的斜坡下降。
5. 平滑处理并没有掩盖这一点
如果 Karpathy 的 nanoGPT 使用更宽的 SMA(比如 100 步)或在保留集上进行评估,你会看到一条平滑的曲线。但 nanochat 记录的是训练损失的约 10 步 EMA 值。这种波动只是赤裸裸的采样噪声。
你可以自己验证——当评估运行时(根据 --eval-every=2000 每 2000 步进行一次),评估损失会更平滑,并且持续低于波动的训练损失带。请检查下一个评估点来确认。
