层1: 损失缩放终解——显式退出 HF 新式梯度累积契约(model_accepts_loss_kwargs=False)

第二幕根因:新式契约的 ×world_size 补偿在基类 compute_loss 尾部(v5
trainer.py:2028),整体重写会绕过它 → loss 与梯度 ÷4(sanity 0.244≈0.85/4)。
按 HF 文档建议(trainer.py:1977)退出新式契约回经典行为;docs/02 勘误改为
两幕全记录。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 10:25:11 -04:00
parent ee16e29846
commit 4f13365ffa
2 changed files with 13 additions and 9 deletions
+1 -1
View File
@@ -83,5 +83,5 @@ F.cross_entropy(..., ignore_index=-100)
## 5. 验证方式
1. **本地(CPU**:collator 单测对拍参考行为——构造超长解答样本断言 prompt 未被截空;断言 -100 位置分布;断言 enable_thinking 两种取值下边界正确。数据加载单测:断言 `except:pass` 已变显式报错。
2. **远程**:先 sanity(官方 SFTTrainer 或我们管线跑 50 步)确认 loss 稳定下降;再全量 1 epoch。W&B 或实时日志盯 `sft/loss` 与有效 token 数。⚠️ 两条实测勘误(2026-07-18):① 起点不是想象的 ~2-3——预训练 Qwen3-0.6B 对 M3 风格数学文本的真实 CE ≈ **0.85**(探针 scripts/diag_loss_probe.py 实测),健康曲线 ≈ 0.9→0.4;② **HF 梯度累积契约坑**:模型 forward 接受 loss_kwargs 时(Qwen3 是),自定义 compute_loss 必须返回 `sum/num_items_in_batch` 而非裸 mean,否则日志与梯度都放大"累积步数"倍(首跑 loss 7.5 ≈ 0.94×8 即此坑;修复在 trainer.py compute_loss
2. **远程**:先 sanity(官方 SFTTrainer 或我们管线跑 50 步)确认 loss 稳定下降;再全量 1 epoch。W&B 或实时日志盯 `sft/loss` 与有效 token 数。⚠️ 两条实测勘误(2026-07-18):① 起点不是想象的 ~2-3——预训练 Qwen3-0.6B 对 M3 风格数学文本的真实 CE ≈ **0.85**(探针 scripts/diag_loss_probe.py 实测),健康曲线 ≈ 0.9→0.4;② **HF 梯度累积契约坑(两幕剧)**:模型 forward 接受 loss_kwargs 时(Qwen3 是),Trainer 默认按"新式契约"对待自定义 compute_loss——第一幕:返回裸 mean 会被 ×累积步数(首跑 loss 7.5 ≈ 0.94×8);第二幕:改成 sum/num_items 后又 ÷world_size0.244 ≈ 0.85÷4),因为新式契约的 ×num_processes 补偿在**基类** compute_loss 尾部(v5 trainer.py:2028),整体重写 compute_loss 会绕过它。终解 = 按 HF 文档(trainer.py:1977)显式 `self.model_accepts_loss_kwargs = False` 退出新式契约,回到"返回 mean、Trainer ÷累积步数"的经典行为(代价:微批等权而非 token 加权,偏差百分之几,与参考实现同行为)。教训:**整体重写框架方法时,必须检查基类同名方法里除了你替换的逻辑还捎带了什么**
3. 关账判据:1k 子集 1 epoch 跑完,loss 曲线正常,checkpoint 能被 `from_pretrained` 加载并生成通顺文本。