层2: max_grad_norm 提进 DistillConfig(显式化静默稳定器)+ noclip 对照模式

首冒烟发现:sanity 的 loss 平滑、无预期毛刺,因 HF 默认 max_grad_norm=1.0 把
反向 KL 的梯度爆炸(§4.1,实测 grad_norm 14→2 是裁剪前范数)默默压平了——正是
本项目要堵的"静默行为"。

- configs.py: DistillConfig 加 max_grad_norm=1.0(默认=原 HF 行为),docstring 讲清
  它是 §4.1 爆炸的隐形稳定器、日志 grad_norm 是裁剪前值;__post_init__ 校验 >0
- train_whitebox.py: FULL 显式写出、TrainingArguments 传入;build_config 加 noclip
  模式(max_grad_norm=1e9≈关裁剪 + lr 5× + 15 步)暴露原始爆炸供教学对照
- .sh: 用法加 noclip 模式说明

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-19 08:34:14 -04:00
parent f1b6d1f668
commit 8b362eae09
3 changed files with 28 additions and 3 deletions
+11
View File
@@ -253,6 +253,14 @@ class DistillConfig:
lr_scheduler_type: str = "linear"
warmup_ratio: float = 0.0
max_grad_norm: float = 1.0
"""梯度裁剪阈值。此前是 HF Trainer 的静默默认(1.0),现显式化——它是式(2)
反向 KL 梯度爆炸(§4.1)的**隐形稳定器**on-policy 采到 teacher 眼中烂 token
时单步梯度范数可炸到十几(2026-07-19 首冒烟实测 grad_norm 14→2),HF 默认
裁到 1.0 才让 loss 曲线平稳。把它设得远大于实测范数(≈关闭裁剪)可暴露原始
爆炸,供教学对照(train_whitebox.py 的 noclip 模式)。非显然约束:日志里的
grad_norm 是**裁剪前**范数,故 14→2 那串本身就是爆炸证据,只是被裁剪掩盖了。"""
gradient_checkpointing: bool = False
"""默认不开(§5 显存账 B=4 富余);OOM 时作为降 batch 之后的第二道降显存手段。
注意 FSDP 下此开关是 no-opdocs/02 §2.6),但层 2 坚持 DDP 故此处有效。"""
@@ -289,6 +297,9 @@ class DistillConfig:
raise ValueError(f"max_new_tokens 必须为正,收到 {self.max_new_tokens}")
if self.learning_rate <= 0:
raise ValueError(f"learning_rate 必须为正,收到 {self.learning_rate}")
if self.max_grad_norm <= 0:
# 用远大于实测范数的值≈关闭裁剪;≤0 无意义(0 会把梯度裁没)
raise ValueError(f"max_grad_norm 必须为正,收到 {self.max_grad_norm}")
if self.subset_size is not None and self.subset_size <= 0:
raise ValueError(
f"subset_size 必须为正整数或 None(全量),收到 {self.subset_size}"