Softmax:为什么要减最大值
Softmax 直接计算 exp(x) 可能溢出。先减去这一行的最大 logit,可以保留数学上的概率,同时把指数输入压到不大于零。要计算对数概率时,再进一步使用 log-softmax,避免先得到零再取对数。
公式没错,计算仍可能失败
分类器与注意力都常用 pᵢ=exp(xᵢ)/Σⱼexp(xⱼ)。其中 x 是一行有限实数,求和范围必须是同一组候选。这个公式说明相对权重,却没有保证浮点指数函数能表示中间结果。
拿人为构造的 [1000,1001,1002] 来看,三个数相差不大,概率应当平滑变化;直接取指数却可能超出浮点表示范围。问题发生在归一化之前,所以最后“再除一个很大的数”来不及挽救中间值。
减最大值为什么不改变概率
设 m=max(x),分子分母同时乘 exp(-m),得到 pᵢ=exp(xᵢ-m)/Σⱼexp(xⱼ-m)。共同因子消掉了。对有限输入,xᵢ-m≤0,最大项的指数为1,至少有一个正的可表示权重。
因此上面的三个 logit 可以先改成 [-2,-1,0]。概率约为 [0.090031,0.244728,0.665241]。这是教学计算,不是某个模型的真实预测。共同平移不改结果;把数值整体乘一个系数则会改变相对差距,不能混为一谈。
Blanchard 与两位 Higham 的数值分析论文讨论了这些等价公式的浮点表现。这里采用的是已长期使用的平移形式,不是2026年的新方法;本文也不复现论文的全部误差分析。
低概率更适合留在对数域
平移解决了大正指数的溢出风险,但很小的权重仍可能下溢为零。例如 [0,-1000] 的第二个概率,在本例 Python 浮点计算中会变成0。数学上它仍大于0。
如果随后计算 log(0),就丢失了本来有限的对数概率。直接计算 log pᵢ=(xᵢ-m)-log Σⱼexp(xⱼ-m),能绕开这次“先转概率”的信息损失。注意仍有浮点舍入,它不是任意范围、任意精度都完全准确的承诺。
它也解释了困惑度计算为什么更适合从模型对数概率出发,而不是反复在概率和对数间转换。
可运行的最小实现
import math
def softmax(xs):
if not xs or not all(math.isfinite(x) for x in xs):
raise ValueError("Expected nonempty finite logits")
m = max(xs)
weights = [math.exp(x - m) for x in xs]
total = math.fsum(weights)
return [w / total for w in weights]
def log_softmax(xs):
if not xs or not all(math.isfinite(x) for x in xs):
raise ValueError("Expected nonempty finite logits")
m = max(xs)
shifted = [x - m for x in xs]
log_total = math.log(math.fsum(math.exp(x) for x in shifted))
return [x - log_total for x in shifted]
p = softmax([1000.0, 1001.0, 1002.0])
assert math.isclose(math.fsum(p), 1.0)
assert all(math.isclose(a, b) for a, b in zip(p, softmax([0., 1., 2.])))
assert softmax([7.0]) == [1.0]
assert math.isfinite(log_softmax([0., -1000.])[1])
assert softmax([0., -1000.])[1] == 0.0
for bad in ([], [math.nan], [math.inf]):
try:
softmax(bad)
except ValueError:
pass
else:
raise AssertionError("Invalid input accepted")
print([round(x, 6) for x in p])
print(log_softmax([0., -1000.]))
此原创 Python 示例已执行:检查概率和、平移不变性、单元素输入、低概率下溢与对数域结果,以及空输入和非有限值。输出概率为 [0.090031,0.244728,0.665241],第二组对数概率为 [0,-1000]。
这里没有训练模型,也没有运行 GPU 内核。时间复杂度 O(n),列表占额外 O(n) 空间,n 为候选数;假定每次基础浮点运算按常数成本计。
注意力掩码还多一道边界
生产实现常用负无穷表示被掩码的位置。本例明确只接受有限 logit,不支持这种输入。如果一整行都被掩码,直接套用减最大值会出现负无穷减负无穷,不能靠普通 Softmax 自动定义合理答案。
应在调用链中明确掩码和空行策略,并使用所选框架经过验证的实现。排查 NaN 时先检查输入、归一化维度和全掩码行,再考虑精度;减最大值不会修复上游已经产生的 NaN。与注意力缩放 √d相比,这里的平移保留概率,缩放则改变分布。
参考资料
本文于2026年10月13日补发,文章日期保留原计划北京时间。参考资料早于计划时段,示例为原创教学计算。
支持
如果这篇文章对你有帮助,欢迎支持本站。
二维码可点击放大。更多支持方式见支持页面。


