Softmax:大模型背后的玻尔兹曼分布与温度采样原理

Softmax:大模型背后的玻尔兹曼分布与温度采样原理 全球大模型都在用同一个公式却很少有人知道它背后的名字。这话听起来很像标题党但在大模型推理链路上确实有一个每天被调用无数次、技术圈默认要学的零件叫 Softmax。它把一组任意实数变成概率是所有生成式模型输出层和注意力机制里最常见的数学操作。而 Softmax 的数学底子通常可以追溯到统计力学里的玻尔兹曼分布以及那位一百多年前提笔写下这个公式的物理学家路德维希·玻尔兹曼。今天大多数人的认知里大模型等于 Transformer、GPU、海量数据、RLHF很少有人会觉得热力学和生成式 AI 有什么关系。但如果你认真看一次大模型生成 token 的过程就会发现几乎所有概率类问题最终都会落到一个指数函数和归一化分母上。它不是一个“知道就行”的趣味知识而是直接会影响到你怎么调 temperature、怎么判断输出异常、怎么理解训练损失、怎么在本地部署时排查稳定性。我更愿意把它当成一条主线来看。模型能不能生成通顺文本取决于训练数据和模型结构但模型“在这一个时刻倾向于选哪个词”本质上是由一组 logits 经过 Softmax 后形成的概率分布决定的。理解了这条主线再看那些五花八门的部署工具和采样参数就不会觉得它们只是黑盒旋钮。1. 先别急着调 temperature先看清 logits 和 Softmax 在哪里相遇1.1 从一次真实调用说起假设你正在使用某个开源大模型做一次最简单的文本生成。输入一段 Prompt 之后模型并不会直接“蹦出”一个词而是先计算最后一层隐藏向量再映射到一个巨大的词表维度上得到一组被称为 logits 的实数。这组实数通常没有上下界可能是负数也可能是十几、几十甚至上百。然后模型会调用一次 Softmaxprobs softmax(logits)把这组实数转换成所有候选词的概率分布。每个候选词得到一个非负概率所有概率加和正好等于 1。接下来再根据这个概率分布去采样选出一个 token 作为输出。生成完成后模型把新 token 拼到原来的序列里重新计算一轮再得到下一批 logits继续做 Softmax继续采样。所以只要你调用过文本生成接口不管用的是云端 API 还是本地 Ollama、vLLM、transformers每一次生成新词背后基本都会经历一次 Softmax。很多框架还允许你直接传temperature、top_p、top_k这些参数本身并不改变模型计算 hidden states 的过程而是在 logits 到最终采样结果之间起作用。1.2 logits 不是概率甚至连排序都不完全可靠新手最容易踩的第一个坑就是直接拿 logits 的绝对值来判断模型倾向。假设词表里有三个候选词logits 分别是词A: -1.0 词B: 2.0 词C: 3.0直观上词 C 的 logits 最大看起来最有可能被选中。但这并不能直接说明“词 C 的概率是词 B 的 1.5 倍”更不能说明“词 A 的概率是负的”。因为 logits 本身只是打分不是一个合法的概率。要让它们变成概率至少要满足两个条件一是所有值都非负二是所有值加起来等于 1。最简单的思路是先把每个分数减去最小值再除以总和让负数变成正数同时归一化。但这样有一个问题它只保留了相对大小损失了“置信度”层面的区分度。比如原始 logits 是[-1000, -999, 500]如果做简单归一化500 会占据近乎全部权重。但模型面对一组很接近的候选词时例如[0.01, 0.02, 0.03]简单归一化也会把概率摊开变成 0.167、0.333、0.5。可是模型可能希望表达“这几个词都非常平庸不能因为 0.03 比 0.01 大一点就把选择差异拉得太大”。Softmax 提供的是另一种归一化方式先对每个 logits 取指数再做归一化。指数函数天然能把负数压到 0 和 1 之间还能放大正分数之间的差距。更重要的是它来自一类有明确概率解释的分布和“最大化似然”天然匹配。1.3 Softmax 做了三件事拉正、归一化、放大区分度可以把 Softmax 理解成三步给每个分数取指数exp(z)保证结果大于 0把所有指数结果相加得到分母用每个指数结果除以分母得到总和为 1 的概率。用公式表示就是softmax(z_i) exp(z_i) / sum_j exp(z_j)这一步看起来简单但它同时完成了三件重要的事把任意实数变成非负数强制所有候选概率加起来等于 1并且通过指数方式把原本差异比较大的 logits 进一步拉开让模型更容易表达“明确选项”。这里也藏着一个容易被忽略的数值问题exp在输入很大时会上溢出为inf。如果某个 logits 是 1000exp(1000)直接就是无穷大整个概率输出就废了。所以工程实现里通常会先减去这批 logits 的最大值再做指数运算。这就是在 Python 代码里经常看到logits logits - logits.max()的稳定写法def stable_softmax(logits): logits logits - np.max(logits) exp_logits np.exp(logits) return exp_logits / np.sum(exp_logits)你不需要每次手写但理解这一点对你后续排查本地部署中“概率结果出现 NaN”这类问题很有帮助。2. 这个公式的真正前身可能是你没想到的热力学分布2.1 从玻尔兹曼分布到 Softmax如果只把 Softmax 当成神经网络里的一个激活/归一化函数它看起来很像工程师拍脑袋设计的工具。可它的思想源头不是在 2017 年 Transformer 论文里才出现而是在统计力学里早就被严格研究过。物理学家在研究大量粒子组成的系统时关心一个基本问题在某个温度下粒子处于不同能量状态的概率到底有多大。玻尔兹曼给了一个关键结论系统处于能量为 E 的状态的概率正比于exp(-E / (k * T))其中 T 是温度k 是玻尔兹曼常数。把所有可能状态的概率加起来归一化就得到了玻尔兹曼分布。这个形式是不是很眼熟如果令能量 E -logits再令k * T为一个缩放常数那么分子就变成了exp(logits / 温度)下面再除以总和正好就是机器学习里的 Softmax。换句话说Softmax 可以被看成玻尔兹曼分布在“离散有限选项”下的一个特例。它之所以能把 logits 变成概率不是因为“指数函数好看”而是因为它本身描述的就是一组状态在某种温度环境下的概率分配。这也解释了为什么大模型的temperature参数不叫randomness而叫温度它本来就是从物理公式里带过来的。物理温度决定粒子分布的分散程度模型温度则决定 logits 概率分布的平滑程度。2.2 temperature 参数不是 Prompt Engineering 技巧而是数学公式的一部分当你调用大模型接口时经常能看到这样的参数response client.chat.completions.create( model..., messages[...], temperature0.7 )这个 0.7 并不是一个神秘的内部控制项。它在多数实现里的做法就是把 logits 除以温度然后再做 Softmaxprobs softmax(logits / temperature)温度越低logits 被除得越大概率分布越向最大 logits 的选项集中。当温度趋近于 0 时输出就慢慢退化成贪心解码也就是每次只选最高概率的 token。温度越高logits 被压缩得越平缓概率分布越接近均匀分布采样结果就更随机、更多样。理解了这一点你就能明白两件事低温不会让模型“变聪明”它只是让模型更倾向于选择已有概率分布中排名靠前的词高温不会让模型“更有创意”它只是让概率更均匀让低概率词有更高机会被选中。如果模型本身没有高质量的知识高温只会带来更多胡言乱语。2.3 为什么“他的名字”没有被记住回到文章开头那个判断全球大模型都在用他的公式却没人知道他的名字。这里的“他”更严谨地说是可以指代玻尔兹曼分布背后这位物理学家。当然我不会说玻尔兹曼在他的时代就发明了现代机器学习里的 Softmax 函数这不符合历史事实。但如果你是做科研史和数学谱系梳理的会看到 Softmax 这种“归一化指数函数”形式广泛出现在统计力学中而最著名的代表就是玻尔兹曼分布。机器学习在 20 世纪 90 年代前后开始把类似形式用于分类和概率建模后来才被起了 Softmax 这个简洁的名字。问题在于主流大模型教程太习惯直接给结论这里用 Softmax别再问为什么。于是它背后的热力学来源、温度参数从哪来、为什么指数归一化而不是别的归一化并没有成为默认常识。它变成了所有大模型都依赖、却很少有人向新用户讲清楚的公式。当一个公式被使用到几乎无可替代的程度使用者反而容易忘记它也有历史也有提出者和理论语境。这就是这个题目最有趣的地方。3. 注意力机制里的 Softmax不仅是最后一步更是每一层都在做3.1 再看一眼 Attention 公式重点看它出现的位置很多第一次接触 Transformer 的同学会觉得注意力机制很神秘。去掉复杂包装它的核心操作在论文里浓缩成一个公式Attention(Q, K, V) softmax(Q * K^T / sqrt(d_k)) * V这里 Q 和 K 做点积得到 Query 和 Key 之间的相似度分数为了防止数值过大除以sqrt(d_k)然后马上接一个 Softmax把这些相似度分数变成注意力权重最后用这些权重去加权 V。所以 Softmax 不只是出现在模型输出层它出现在 Transformer 的每一层、每一个注意力头里。自注意力机制的本质就是让序列里的每个 token 对序列里的其他 token 做一次“软性选择”用 QK 算相关性用 Softmax 把相关性变成一组加和为 1 的权重再对 V 做加权求和得到当前 token 融合其他 token 信息后的表示。一个 7B 模型有几十层每一层有多个头意味着一次前向推理中Softmax 会被调用极其多次。它才是真正的隐形主角。3.2 为什么要除以 sqrt(d_k)这个细节值得留意。在 QK 点积之后如果维度d_k很大点积结果的方差也会变大。极端时候一批 logits 可能分布得很开Softmax 会很快变成近似 one-hot最大的那个 token 拿到几乎全部注意力权重其他 token 直接趋近于 0。这样做的结果是模型能关注的位置数量会非常少梯度信号也会变得很弱。除以sqrt(d_k)本质上是把点积结果的尺度拉回一个更合适的范围让 Softmax 能保持相对平滑、可分且保留足够的梯度。实际经验中这一步不只是一个“数字标准化”的小技巧。它决定注意力是能灵活分配还是很快陷入“只看一两个位置”的极端状态。类似的思路也会出现在其他归一化组件里真正重要的不是某个公式能算出来而是它有没有让后续的非线性函数处在合适的操作区间。3.3 为什么 Softmax 会成为优化和部署里的关键对象如果你深入看部署优化会发现 Softmax attention 是显存和计算优化的重点。原因是标准 Softmax 在数学上要求先拿到所有分数再计算总和最后再做归一化。当你处理一段几千甚至几万 token 的长文本时注意力矩阵本身就非常大如果先把完整注意力分数存到显存里再做 Softmax消耗会很惊人。这也是 FlashAttention 这类优化出现的背景之一。它没有改变结论也就是说它仍然要算出一个和标准 Softmax 等价的结果但通过分块计算、在线更新和重新缩放避免了把完整矩阵写进显存。你看推理框架更新时经常提到“FlashAttention 让长文本推理更快”底层最重要的数学依赖之一仍然是如何稳定、高效地计算带 Softmax 的注意力。所以不要以为 Softmax 只是输出层最后一个小函数它在模型内部的调用频率和优化难度远比想象中大。4. 训练、部署、微调理解 Softmax 后的三个实际收益4.1 训练时模型到底在学什么大模型预训练常被说成“预测下一个 token”。更精确地说是让模型在给定上文时对真实的下一个 token 给一个足够高的概率。假设真实答案在词表中的编号是 target模型输出的一整组 logits 经过 Softmax 后真实 token 对应的概率是 p。训练目标通常就是最大化这个概率等价于最小化它的负对数也就是loss -log(p)如果 p 接近 1损失接近于 0如果 p 非常小损失就很大。这一项只关注真实 token而不需要把词表里所有错误 token 都显式写成标签。相比之下如果把模型输出看作一个概率分布交叉熵损失正好就在衡量“预测分布”和“真实分布”之间的距离。这里的 Softmax 提供了一件不可替代的事模型输出可以被解释为概率并且它是处处可导的。梯度可以通过 Softmax 反传到前面的所有层。这也是为什么我们不能在输出层只做“argmax”argmax 不可导也没法提供“模型对自己预测有多确定”的连续信号。理解这一点对微调也很有帮助。你做 LoRA 或全量微调时本质上是让模型调整参数使得对目标风格的数据Softmax 后的概率分布更符合期望而不是直接教模型复读某个字符串。如果一条样本本身充满矛盾Softmax 损失会让模型做出一个“折中”的分布结果可能学着学着还是很奇怪。4.2 部署时看采样器temperature、top_p、top_k 正在改什么本地部署大模型时很多人会遇到一个困扰同一个模型用 Ollama 加载和用 vLLM 加载答案会不一样有时候差别还挺大。这不一定说明部署有问题更常见的原因是采样参数不同或者框架默认值不同。这些采样参数都作用在 Softmax 概率分布上temperature改变概率分布的平滑度低温让分布更陡高温让分布更平top_k只保留概率最高的 k 个 token并对它们重新归一化强制忽略其他 tokentop_p保留从高概率到低概率累计达到 p 的那些 token再重新归一化repetition_penalty会在 Softmax 之前调整历史 token 对应的 logits减少重复。所以如果本地部署后总觉得输出太死板不要一上来就怀疑量化把模型搞坏了。先确认 sampling 参数是否被意外设置成极端值。一个常见的排查组合是先把 temperature 设为接近 0比如 0.01看输出是否偏向确定性再把 temperature 设为 1top_p 设为 1top_k 设为 0不限制观察随机性变化如果随机性变化明显说明模型本身正常只是采样参数不一致如果这里调整没有效果才需要进一步看 prompt、上下文、量化格式和推理框架。4.3 微调时不能靠调 temperature 解决的毛病还有一个很常见的认知错位把模型输出格式不对、回答不专业、频繁说废话全部归结为“temperature 太高”。实际上temperature 只能改变采样时的分布倾向并不能凭空补齐模型本身没有学到的知识或能力。如果模型给出的候选 token 概率分布里正确的答案本来就不在前面你把 temperature 调到 0.1它也只是更坚定地选择错误答案里排名最高的那个。所以在微调之前一定要先分清楚问题出在哪一层如果模型能理解问题但候选概率分散可以尝试调采样参数如果模型根本不知道某个领域的正确表达方式应该去补数据、微调或换更强基座如果输出格式经常崩大概率不是 temperature 的问题而是监督微调阶段没有足够多统一格式样本如果模型上下文里没有足够信息却要求它答出具体事实那更像检索或系统设计问题。可以把它当成一个判断框架采样参数只能改变形状不能改变内容。内容来自训练、微调、上下文和检索。两者协同工作但不可以互相替代。5. 用一个最少代码实验建立对概率分布的手感5.1 10 行 Python 观察温度变化建议你自己动手做一次实验。不需要大模型只需要一组人工构造的 logits。import numpy as np def softmax(logits, temperature1.0): logits np.asarray(logits, dtypenp.float64) / temperature logits logits - np.max(logits) exp_logits np.exp(logits) return exp_logits / np.sum(exp_logits) logits np.array([0.5, 2.0, 1.2, -1.0, 3.0]) for T in [0.2, 0.7, 1.0, 2.0]: probs softmax(logits, T) print(fT{T}: {np.round(probs, 4)})运行后你会发现温度 T最大概率 token概率分布特征0.23.0 对应的 token概率极度集中几乎变成 argmax0.73.0 对应的 token前几名差距明显低分 token 概率很小1.03.0 对应的 token相对正常仍有少量分布给其他选项2.03.0 对应的 token分布更平滑低分 token 也有机会被采样这个实验能帮你建立最基本的“概率分布手感”。你还会看到一个重要事实唯一的最大值在低温时概率接近 1不意味着模型“知道正确答案”。它只是代表模型倾向于输出这个 token。如果 logits 本身不合理低温只会让不合理的答案显得更自信。5.2 一个输出异常时的排查链路把 Softmax 放进大模型调试流程里我们可以得到一个很实用的排查顺序先看现象是输出太随机、太重复、还是答非所问不同现象指向不同层。再看采样参数temperature、top_p、top_k、repetition_penalty 都是多少和预期是否一致再看 logits如果框架允许尝试拿到 top 5 候选 token 的概率判断高概率集中在哪里。再看上下文Prompt 本身是否提供了足够信息有没有冲突指令上下文长度有没有截断关键内容。最后看模型和数据如果 logits 分布和采样参数都正常但输出仍然不能满足业务需求问题往往在模型能力或训练数据分布上。这套链路不是万能的但它能避免“一遇到输出怪就怪模型垃圾”或“一遇到结果差就盲目调低 temperature”的弯路。5.3 工程边界这套判断不覆盖所有场景需要说明白的是Softmax 只是大模型里大量数学组件中的一个。它不负责位置编码、不负责归一化、不负责矩阵乘法本身也不覆盖所有生成策略。例如有些模型在训练或解码时会使用 Gumbel-Softmax、对比解码、speculative sampling 这类更复杂技巧但它们的底层通常还是要回到概率分布的比较和归一化。另外不同框架对采样参数的处理可能存在细微差异比如某些库会自动给 logits 加一个固定的重复惩罚某些库把 top_k0 理解成“不限制”某些库则理解成“只允许 0 个候选”语义不一致。所以你在 A 框架上正常不代表在 B 框架上也一定正常。如果你要用这些知识排查线上问题第一步是确认你用的部署框架文档里对这些参数的默认值和 0 值语义是怎么规定的。在没有确认版本和文档前不要默认所有框架行为一致。6. 一次理解长时间受益把它放进你的大模型调试工具箱6.1 一个长期有效的操作习惯我在看一个生成模型时会先问自己三句话模型最后一步输出的是 logits还是已经经过 Softmax 的概率我真正能控制的采样参数是作用在 logits 上还是作用在采样结果上一条输出不符合预期是因为概率分布本身不对还是因为我在这个分布上采样采偏了前两个问题帮你定位工作层第三个问题帮你区分“模型能力”和“采样随机性”。如果你需要的是稳定、可复现的结果可以把 temperature 调得很低甚至直接用贪心解码然后记录下当时的 prompt、采样参数和模型版本。如果你想做更多样的创作再把 temperature 调高同时配合 top_p 或 top_k限制高风险候选范围。这样做比单纯“调一下温度”更可控。6.2 回到最初那个判断回到文章标题。全球大模型确实每天都在使用一个公式而它的出身很容易被遗忘。Softmax 不是一个突然出现在神经网络里的技巧也不是为了“好看”才加上的函数。它把逻辑变成概率把模型输出变成可训练的分布把物理世界的温度概念带进了现代生成式 AI 的采样过程里。对一个普通使用者来说理解这个公式最大的价值不是让你多背一个数学名词而是让你清楚知道大模型的生成结果本来就是概率采样不是“照本宣科”。你看到的每一次回答都是候选词分布上的一次落子而你手里的 temperature、top_p、top_k正是修正这颗骰子形状的工具。下次跑通一个本地模型或者调用一次接口时不妨多看一眼日志里能不能输出 logits或者手动写几行代码观察温度如何改变概率。这个动作看起来小但它会帮你把“大模型很玄”变成“大模型也是可以被理解、被调试、被预期”的工程系统。