From 8b362eae096e132cb4e43fb0ae427aeccf08e79b Mon Sep 17 00:00:00 2001 From: iomgaa Date: Sun, 19 Jul 2026 08:34:14 -0400 Subject: [PATCH] =?UTF-8?q?=E5=B1=822:=20max=5Fgrad=5Fnorm=20=E6=8F=90?= =?UTF-8?q?=E8=BF=9B=20DistillConfig=EF=BC=88=E6=98=BE=E5=BC=8F=E5=8C=96?= =?UTF-8?q?=E9=9D=99=E9=BB=98=E7=A8=B3=E5=AE=9A=E5=99=A8=EF=BC=89+=20nocli?= =?UTF-8?q?p=20=E5=AF=B9=E7=85=A7=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 首冒烟发现: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) --- ars_opd/configs.py | 11 +++++++++++ scripts/train_whitebox.py | 15 ++++++++++++++- scripts/train_whitebox.sh | 5 +++-- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/ars_opd/configs.py b/ars_opd/configs.py index f508873..3c90beb 100644 --- a/ars_opd/configs.py +++ b/ars_opd/configs.py @@ -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-op(docs/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}" diff --git a/scripts/train_whitebox.py b/scripts/train_whitebox.py index a07a6e3..6f61abc 100644 --- a/scripts/train_whitebox.py +++ b/scripts/train_whitebox.py @@ -42,6 +42,7 @@ FULL = DistillConfig( gradient_accumulation_steps=4, # 全局 batch = 4 × 4 卡 × 4 = 64(同层 1) num_train_epochs=1, max_steps=-1, + max_grad_norm=1.0, # 显式写出这个此前静默的稳定器(§4.1 爆炸靠它压平,见 config 注释) bf16=True, logging_steps=1, save_steps=100, @@ -59,7 +60,18 @@ def build_config() -> DistillConfig: return dataclasses.replace( FULL, max_steps=50, output_dir=FULL.output_dir + "-sanity" ) - raise ValueError(f"未知模式 {mode!r},只接受 full / sanity") + if mode == "noclip": + # §4.1 教学对照:关闭裁剪 + 稍抬 lr,暴露反向 KL 原始爆炸。max_grad_norm + # 设远高于实测范数(~14)故永不触发≈无裁剪;lr 5×放大让爆炸在 loss 上可见。 + # 与 sanity(裁到 1.0、lr 1e-6 的平滑曲线)并排 = 白盒脆弱性活教材,层 5 对照 + return dataclasses.replace( + FULL, + max_steps=15, + max_grad_norm=1e9, + learning_rate=5e-6, + output_dir=FULL.output_dir + "-noclip", + ) + raise ValueError(f"未知模式 {mode!r},只接受 full / sanity / noclip") def smoke_check_first_prompt(dataset, collator, tokenizer) -> None: @@ -119,6 +131,7 @@ def main() -> None: max_steps=cfg.max_steps, lr_scheduler_type=cfg.lr_scheduler_type, warmup_ratio=cfg.warmup_ratio, + max_grad_norm=cfg.max_grad_norm, gradient_checkpointing=cfg.gradient_checkpointing, bf16=cfg.bf16, seed=cfg.seed, diff --git a/scripts/train_whitebox.sh b/scripts/train_whitebox.sh index ee46507..7293970 100755 --- a/scripts/train_whitebox.sh +++ b/scripts/train_whitebox.sh @@ -2,8 +2,9 @@ # 层 2:white-box OPD 训练(远程 gpu-a800-060 专用;本地不跑训练)。 # # 用法(tmux 内执行,日志实时可查): -# bash scripts/train_whitebox.sh sanity # 50 步冒烟:看首 prompt 自检 + KL loss + -# # 生成 token 数;预期见 loss 毛刺(梯度爆炸实况) +# bash scripts/train_whitebox.sh sanity # 50 步冒烟:首 prompt 自检 + KL loss + 生成数 +# bash scripts/train_whitebox.sh noclip # §4.1 对照:15 步,关裁剪+抬 lr,暴露原始 +# # 梯度爆炸(loss 毛刺);与 sanity 平滑曲线并排 # bash scripts/train_whitebox.sh # 正式:1k 子集 1 epoch # # 前置检查清单: