深夜的机房里,你正在给一个门控架构的开源模型做全参数续训:loss 稳步下降,日志干净,没有 NaN,没有报错。11 月 3 日挂上 arXiv 的一篇论文说,这种看起来一切正常的训练,可能每五步就白跑一步——整批梯度在优化器动手之前就被静默清零,而日志里一个字都不会提。
问题不在优化器,也不在数据,而在嵌入表里一些被洗到接近零的行。
普查:38 个未绑定门控检查点,20 个带病
论文作者(华盛顿大学圣路易斯分校牵头,南开大学等参与)直接读取了 291 个 HuggingFace 开源仓库的嵌入矩阵,共 280 张嵌入表,101 个权重绑定、190 个未绑定。范数低于 10⁻⁶ 的每一行都做解码再重分词的可达性测试,排除对齐填充后:92 个仓库有原始近零行,67 个仓库(23.0%)真正带病。
按架构拆开,分布很干净:权重绑定模型 0% 中招;未绑定模型整体 67/190(35.3%);未绑定 Transformer 占 47/152(30.9%),但它们不发病;未绑定门控模型(Mamba、Gated DeltaNet、xLSTM 这一族)达到 52.6%(20/38)。
论文点名的中招例子:Nemotron-3-Nano-4B、Falcon-H1-1.5B/7B、Mamba-Codestral-7B、Kimi-Linear-48B-Base、Nemotron-H-4B-Base。出事的 token 全是普通字符串:泰卢固语、巴斯克语子词,西里尔蒙文词,CRLF 代码碎片,聊天标签 <|im_user|>,还有 。
病根:AdamW 把预训练没见过的 token 洗到近零
语言模型的输入表和输出表可以共用一张矩阵(绑定),也可以分开存(未绑定)。一个 token 若在预训练语料里从未出现,它的输入行每步梯度恒为零,输出行却每步都能从 softmax 拿到非零梯度。绑定架构靠输出梯度把范数撑在 1 附近;未绑定架构的输入行则被 AdamW 的解耦权重衰减逐步收缩——作者测算,百万步训练(η=4×10⁻⁴、λ=0.1)后范数缩到 10⁻¹²,学习率更大时到 10⁻³⁵。
他们拿 OLMoE-1B-7B 做了实证:检查点里的 251 个近零行,在 364M token 的开源训练语料抽样中全部零出现(0/251),与权重衰减的累计曲线对得上。而 979 个近零 token 里 92%(901 of 979)的输出范数仍在词表中位数的 0.3× 以上——推理不受这个坑影响。这就是它长期没被发现的原因:部署侧安好,训练侧失血。
机制:门控读出把小范数变成 10³⁰ 放大器
Transformer 为什么不怕?在非首位置,第 1 层注意力能把前文正常尺度的 value 平均进来,一层就把残差流拉回正常;论文实测 Llama-3.1-8B 的输入梯度只有 56–98。
门控模型走的是另一条路:Mamba、Gated DeltaNet 的历史状态读出要乘一个 SiLU 门,xLSTM 要乘一个线性 query,这两类当前 token 因子在零输入处都恰好为零。输入近零,读出跟着趋零,残差流连续多层低于 √ε 地板,Pre-RMSNorm 退化成除以 √ε:ε=10⁻⁶ 时反向梯度每层放大 10³,跨层叠加冲破 10³⁰。
作者用人工下扫输入范数的方式量出各架构的放大上限:Falcon-Mamba-7B 为 1.3×10³⁰,xLSTM-7B 为 1.0×10³²,Nemotron-3-Nano-4B 峰值 4.5×10³⁴(原生 token 上 2.1×10³⁵);并行混合的 Falcon-H1-1.5B 每层都有注意力兜底,尖峰压在 3.5×10⁶。
静默清零:FP32 溢出,无 NaN 也无报错
论文的总体测量是:这类 token 让门控模型的输入嵌入梯度放大超过 30 个数量级,最高到 35。这些天文数字本身是有限值,不会直接炸掉训练;动手的是优化器前一步的梯度裁剪。PyTorch 的 clip_grad_norm_、HuggingFace Trainer、FSDP2 都在 FP32 里累加梯度平方,FP32 上限约 3.4×10³⁸——任何梯度分量超过 1.84×10¹⁹(即 √FLT_MAX),平方就溢出成 +∞;IEEE-754 里有限值除以 ∞ 恰等于 0.0,于是整批梯度被清零,没有 NaN,也没有异常。Megatron-Core 的行为不同:检测后直接报错中止。
更麻烦的是连锁反应:清零的那一步,AdamW 只推进陈旧动量和权重衰减;新触发的近零行(动量、二阶矩都是 0)拿不到更新,还被权重衰减继续缩小,下次遇到再次溢出,形成自锁。
触发率方面,论文在 15 个语料、14 个模型上抽样 8,000 篇文档:英文网页、SFT、代码、数学语料的批次触发率都是 0.0%;多语言和模板数据是重灾区——Aya 为 10.6%–18.8%,泰语为 70.5%–97.4%,Nemotron-3-Nano-4B 泰卢固语 95.4%,Falcon-H1 蒙古语 100%,Kimi-Linear 的 <|im_user|> 标签在序列首位同样是 100%。1,000 步泰卢固语续训(每步 16,384 token)的完整漏斗:62 行(占词表 0.047%、占 token 32.3 ppm)触发 22.5% 的批次,其中 88.1% 溢出,最终 19.8% 的步数整批梯度归零。
修法:三行 GroupClip,或预训练加 GateFloor
对已有检查点,论文给出 GroupClip:既然放大只发生在输入嵌入里,主干梯度保持在 1–10,那就把两者分开裁剪,嵌入的范数改用 64 位计算——三行代码。泰卢固语实验:停滞步数从 198.3±9.9 降到 0,困惑度 13.44→13.04,追平需要预先扫词表的 Oracle Mask;遇到的 30.7 个近零行初次接触即自愈。蒙古语实验:11.7→0,困惑度 15.68→15.58。H100 上步时与峰值内存和基线持平。
对从头预训练,论文给出 GateFloor:给循环门加一个正偏置,变成 SiLU(z)+δ,δ∈[0.01,0.1],让门在零输入处仍大于零。370M 参数规模下,r=10⁻³⁰ 的梯度从 1.6×10²⁸ 降到 0.13,砍掉 28 个数量级;吞吐为 542.8k 对 527.2k token/s,验证困惑度与基线差距在 ±0.17 内。
谁该在意
论文自测的边界值得抄:rank-16 LoRA 冻结输入嵌入,不受影响,9 个检查点 0 次溢出;on-policy RL 的 rollout 走输出侧,16,384 个蒙古语与泰卢固语 token 采样零命中;但全参数 RL 只要 prompt 带近零模板标签照样中招。局限也很直白:审计和梯度探针覆盖到 48B,续训只测了 1.5B–4B,GateFloor 从头训只到 370M,每组 1–2 个种子。
所以呢:如果你正在对未绑定门控模型做全参数微调或续训,喂的又是多语言或模板化数据,开训前先对嵌入表跑一遍范数统计,低于 10⁻⁶ 的行就是雷。别再信没有 NaN 就没问题这种判断——训练里会咬人的故障,未必会喊疼;梯度被清零这件事,日志一行都不会给你。