softmax 是什么?大模型里出镜率最高的函数,一次讲明白(2026入门)

昨天讲完注意力,漏了主角

上一篇讲 Q、K、V 的文章里,注意力机制的工作流程是这样的:每个词拿自己的 Query 去和所有词的 Key 做匹配,算出相关度,再按相关度加权平均 Value。写到「算出权重」这步时,我一句带过了。

但这个权重到底怎么算出来的?原始的匹配分数是一堆没有统一含义的数字,可能是 7.3,可能是 -0.5,凭什么最后能变成「加起来等于 1」的权重?这中间缺的那块拼图,叫 softmax。

它出现的位置极其固定:几乎每个大模型,每一层,每次推理,都要跑一遍这个函数(上次写的多头注意力,每个头里都有一份)。2017 年那篇提出 Transformer 的论文《Attention Is All You Need》里,核心公式 Attention(Q,K,V) 的第一步就是「apply a softmax function to obtain the weights on the values」,即先过 softmax,再拿结果去加权 Value。你可以把它理解成大模型里的「比例分配器」:把任意一堆分数,变成一套总和为 1 的比例。

这篇文章就把这个被论文一笔带过、却每次推理都要跑一遍的函数讲清楚:它做什么,为什么非它不可,藏了哪些坑。

softmax 是干什么的

它只做一件事:把任意一组实数,变成一个概率分布。

流程两步。第一步,对每个数取指数,也就是算 e 的 x 次方;第二步,把所有指数结果加起来,用总和去除每一个,得到一组加起来必然等于 1 的比例。

拿三个分数举例:5.2、3.1、1.8。取指数之后大约是 181.3、22.2、6.0,总和约 209.5;各自除以总和,得到 86.5%、10.6%、2.9%。三个数加起来正好 100%,这就成了一个合法的概率分布。

做个对照:如果跳过指数、直接用原始分数除以总和,会得到 51.5%、30.7%、17.8%。同样归一化了,但比例关系完全不同。softmax 的指数运算会刻意放大分数之间的差距。第一名从「过半」变成「近九成」,说明指数不是可有可无的数学装饰,它承担着「拉开差距」的任务。这个特性后面还会用到。

至于为什么偏偏是 e 的 x 次方,主要是求导方便:以 e 为底的指数,导数形式最简洁,反向传播好算。别的底数也能做,没有额外好处,大家就用 e。

为什么必须是 softmax

大模型要的权重,本质上是三个硬约束的交集。

一是非负。权重代表「关注程度」,不能是负数,总不能「负 20% 地关注」某个词。

二是总和为 1。加权平均才有稳定的量纲,输出才不会随着文章长短漂移。

三是可导。训练靠梯度下降,整个模型必须处处可导,权重这一层不能断。

普通归一化只能满足前两条,min-max 那类操作压得住数值,处理不好梯度。softmax 恰好三条全占:指数运算保证结果永远为正,除以总和保证和为 1,指数函数处处光滑可导,导数性质还特别干净:输出对输入的梯度可以写成「对角线元素减去外积」的形式,数值上非常稳定。三条同时满足的简单函数,softmax 几乎是唯一的主流选择。这不是谁拍脑袋定的,是数学筛选的结果。

为什么注意力公式里有个根号

看《Attention Is All You Need》里的原公式,softmax 里面不是干净的 Q 乘以 K 的转置,而是多除了一个根号 d_k,也就是向量维度的平方根。Qwen3-8B 的配置里每个注意力头的维度是 128,这个除数就是 11.31。

为什么?论文自己解释过。原文的说法是:当 d_k 很大时,点积的数值会变得很大,把 softmax 推进梯度极小的区域。为了说明点积为什么变大,论文还专门加了条脚注:假设 q 和 k 的分量是均值为 0、方差为 1 的独立随机变量,它们的点积的均值为 0、方差为 d_k。

这话翻译一下:两个 128 维的向量做点积,结果的波动幅度和 128 成正比。维度越高,分数越容易膨胀。而 softmax 对输入的绝对大小极其敏感。分数一大,指数运算会把差距指数级放大,最大的那个分数吃掉几乎全部概率,其他位置全变成接近 0 的小数。

这就是前面说的「拉开差距」走向极端的样子。此时注意力退化成只看分数最高的那一个词,梯度也大面积变成 0,模型训练不动。顺带一提,KV 缓存的体积也是按「一个 query 对全部 key」的规模增长的,写过的那篇 KV 缓存讲的正是这件事。除以根号 d_k,等于把分数的方差拉回 1 附近,让 softmax 始终工作在舒服的区域。所以这个根号不是风格,是稳定器。

数值稳定:代码里真正的 softmax 长得不一样

翻开源码看,你可能认不出它。Hugging Face transformers 库里 Qwen3 的注意力实现,那一行调用的是 nn.functional.softmax,并指定了 dtype 为 float32。而 PyTorch 内部真正干活的版本,先减去了输入里的最大值再取指数,也就是「最大值平移」的写法。

这个改动是被迫的。softmax 的输入是一堆任意大小的实数,具体数值取决于权重矩阵,没人保证它一定小。万一来一个 200、300 的分数,e 的 300 次方是一个一百多万位的数字,float32 的上限只有大约 3.4×10 的 38 次方,e 的 89 次方就已经越过它,直接溢出成 inf。数学上 softmax 有个很好的性质:给所有输入同时减去同一个常数,输出一点不变。工程上就利用这一点,先把最大值减掉,让所有输入变成负数或 0,指数结果全部落在 0 到 1 之间,绝不会溢出。

所以教科书里的公式和代码里的实现长得不一样,但输出完全等价。这是深度学习里很典型的一幕:公式负责定义正确,工程负责活下来。

温度参数:同一个旋钮,两副面孔

你可能见过 API 里的 temperature 参数(之前专门写过一篇 temperature 和 top_p)。它和 softmax 是同一个环节的两副面孔。

如果你在 API 侧调低 temperature,到模型内部,效果就发生在 softmax 这一步:每个分数先除以温度,再取指数。除以小于 1 的温度等于放大分数差距,输出分布变尖,AI 回答更确定、更保守;调高温度则相反,差距被抹平,分布变平,回答更多样。

还是拿那三个分数验算:5.2、3.1、1.8,温度 0.5 时,分布变成 98.4%、1.5%、0.1%,第一名几乎通吃。温度 2.0 时,变成 65.2%、22.8%、11.9%,三个选项都有机会。

顺带一提,这套「分数除以温度再进 softmax」的做法不是聊天场景的专属发明,强化学习里的 softmax actor-critic 策略早就在用它平衡探索和利用。今天各家 API 暴露的 temperature 参数,调的就是 softmax 之前的那次除法。

一鱼多吃:softmax 在模型里不止一处

注意力里的 softmax 只是它最出名的岗位。同一个函数,在大模型的不同位置上反复出现。

生成下一个词时,模型输出整个词表的分数(词表大小通常十几万),收尾的一步也是 softmax,把分数变成每个词的概率,再从分布里采样,相当于整张词表在「抢答」。

这步 softmax 还有副产品:把最大的那个概率取负对数,就是 perplexity(困惑度)的组成部分,衡量语言模型质量的核心指标之一。

在混合专家模型(MoE)里,路由器用它决定每个 token 分给哪些专家,这一步也叫「门控」。

一句话总结岗位:凡是需要「把分数变成概率分布」的场合,出场的基本都是它。这也是为什么我说它是出镜率最高的函数:它不是某个功能的实现细节,而是「打分到选择」之间的标准转换器。

它也有代价:softmax 本身就是瓶颈

softmax 还有个容易被忽略的属性:它不是逐个元素独立的运算,必须等所有分数集齐才能归一化,因为除数是总和,这天然带有「全局归一化」的性质。

这个属性带来了效率难题。注意力里的 softmax 要在「一个 query 和全部 key」之间做,序列越长,分数越多,而且归一化之前谁也不能先落地,它同时吃显存带宽和算力,是推理时最耗时的环节之一。FlashAttention 那篇论文处理的正是这个难题:分块计算 softmax,配合在线归一化,绕开「必须先算完全部分数」的限制。

下次看到它别划走

总结一下这篇文章的几个关键点:

softmax 的本质是「比例分配器」,两步操作(取指数、除总和)把任意分数变成概率分布;指数的放大效应让它区别于普通归一化;非负、和为 1、可导三个约束,决定了它几乎唯一的主流地位;注意力公式里除以根号 d_k,是防止分数膨胀把 softmax 推进梯度极小的区域;生产代码里用的都是「减最大值」的数值稳定版本;temperature 参数,调的就是 softmax 之前那次除法。

下次在论文或文档里再看到 softmax,希望你看到的不再是一个神秘的数学名词,而是一个具体的函数:负责把模型的打分变成选择,也负责把梯度送到该去的地方。缺点和优点一样清楚,像极了工程世界里所有的核心组件。

想继续深入,可以看看「归一化」家族的另一个成员 RMSNorm,在 Qwen3 这类模型里,它出现在注意力前后,负责稳定每层数值。它不搞概率分配,只做数值整形,和 softmax 经常被混淆,干的完全是两码事。

发表评论