softmax 和交叉熵里藏着两个数值 bug,有人给出修法
一篇 10 月论文指出教科书级的 softmax 与交叉熵实现里有两个数值 bug。读完能理解这两个 bug 怎么扭曲梯度、修掉之后能解决哪些训练怪象,为什么老代码也该查一查。
softmax 和交叉熵大概是深度学习里被写得最多的两段代码,教程里到处都是。一篇 10 月 22 日的 arXiv 论文说:标准实现里有两个数值 bug,一直没被当回事。
一、发生了什么
第一个 bug 出现在一家独大的时候。当某个 logit 远大于其他所有值, softmax 输出接近一,目标 logit 的梯度在浮点运算里被舍入成零。梯度本该总和为零,这时候不再成立,训练信号悄悄走了样。
第二个 bug 出现在师生对齐的时候。做蒸馏时,如果学生的 logit 已经很接近老师,即便两边完全相等,梯度也不归零,白白推着参数动。两个 bug 的共同点是:公式在纸面上完全正确,错在浮点数有限的精度——该归零的量舍入不到零,不该动的量被推了一下。
论文给出了两段很简单的替代实现,把两个 bug 一起修掉,然后报告了一串连锁效果:此前观察到的「弹弓」现象消失了;带权重衰减时,grokking(先死记后突然泛化)的速度加快;不带权重衰减时,准确率崩塌被避免;蒸馏里学生不再漂离老师;论文还据此论证,模型背下训练集之后,logit 的增长会被权重衰减与 Adam 的 epsilon 之间的平衡框住,不是无限膨胀。
一句话:这些名字古怪的训练怪象,可能不是深度学习有多神秘,而是几行浮点代码的舍入误差。
(背景补充,非论文结论:数值稳定性补丁在深度学习框架里很常见,比如 log-sum-exp 技巧,这篇的贡献是指出常见实现里的具体错位并给出可直接替换的写法。)
二、跟你有什么关系
自己训模型的人。 遇到「莫名其妙就不稳定」的损失曲线,原因可能不在超参,在这两段基础代码的实现上。换实现做对照,比盲调学习率便宜得多。
做蒸馏的人。 学生模型的漂移问题有了新解释,检查一下你的损失函数写法,可能白烧了很多算力。
复现论文的人。 复现不上别人结果时,除了版本和种子,基础算子的实现差异也该进排查清单。两个框架之间差的那零点几个点,未必是谁训得好。
三、怎么用
读到这儿,可以顺路看 怎么盯住比你强的模型?给它装一个倒带键 读完能理解弱审核者如何核验强 AI 的计算结果:新提出的可倒带交互证明允许验证器把模型回退到任意状态重来,效率高、不依赖密码学,关心 AI 安全机制的人值得一看。- 查一遍自己代码库里 softmax 和交叉熵的写法,特别是手写实现,对照数值上是否有梯度归零失效的路径。
- 训练出现诡异曲线时,先换一份修正实现跑对照,成本极低,排除起来最省事。
- 教新人时把这两个 bug 当案例讲:基础算子的浮点细节,真的会伪装成「模型行为」。
四、三个别信的说法
「这么基础的代码不可能错。」 错的不是公式,是浮点实现,公式正确和数值正确是两码事。这行代码被复制了几十年,错误也跟着复制了几十年。
「训练怪象都要靠新理论解释。」 一部分怪象可能只是舍入误差的副作用,先排除工程因素,再谈理论。
「改了就行,一劳永逸。」 论文修的是这两个具体 bug,数值陷阱还有很多,写高精度敏感的代码永远要测。
五、一句话结论
训练不稳定的锅,有时不在模型,在几行最基础的代码里。
信息来源
- arXiv 论文页:softmax 与交叉熵的修法,2026-10-22
功能与价格以官方公告为准。本文写于 2026 年 10 月 25 日。
每周一封,只发新教程和可复制模板。不发广告,随时退订。