拿到一个 logits 向量,应用 temperature 和 top-p,并能说清每个旋钮为什么会让最终分布发生那样的变化。
每次前向传播都以 logits 结束;模型仍然需要一条规则,把这个向量变成一个具体的下一个 token。
术语表 · 7 个术语
- logit
- 词表中每个条目对应的一个无界实数分数。模型在任何归一化之前的原始输出。
- softmax
- exp(l_i / T) / sum_j exp(l_j / T)——把 logits 变成一个总和为 1 的概率分布。
- temperature (T)
- 施加在 softmax 之前的锐度旋钮。T<1 让分布更尖(更接近贪心);T>1 让分布更平(更多样)。
- greedy decoding
- 每一步都取 argmax(logits)。确定性、可复现;但容易陷入重复循环。
- top-p (nucleus)
- 保留概率累加到 >= p 的最小 token 集合,重新归一化后从中采样。把长尾砍掉。
- top-K sampling
- 一种采样规则:只保留概率最高的 K 个 token,重新归一化后从中采样。是 top-p 的固定大小近亲。
- top-16 capture (this demo)
- 这是展示层面的截断,不是采样:inspector 每一步记录 16 个最高的 logits,组件才有东西可画。模型本身仍然是在整个词表上采样的。
采样:把 logits 变成下一个 token
Qwen3.5 的每次前向传播都以同样的方式结束:一个 logits 向量——模型词表(约 248k)中每个 token 一个实数。logit 是一个无界的分数:模型有多强烈地推荐这个 token 作为下一个。采样就是把这个向量变成一个具体选择的那一步。
为什么不直接选最大的?
最简单的规则是贪心解码(greedy decoding):每一步都取 argmax(logits)——argmax 的意思就是“选数值最大的那个位置”,也就是得分最高的单个 token。它确定且可复现,但有一个著名的失败模式——重复。一旦模型找到一个它对续写很有信心的短语,就会不断重新进入同一个循环,因为那个循环永远是局部最优的选择。贪心解码还丢掉了大量信息:如果两个 token 的 logits 几乎相等,选其一而完全无视另一个是一种脆弱的平局裁决。
Softmax:logits → 概率
要采样,我们先用 softmax 函数把 logits 转换成一个真正的概率分布:
这里发生了两件事。指数让每个值都变成正数;除以总和让它们加起来等于 1。每个 logit 在取指数之前还会先除以一个 temperature(温度)T——同一个 T 出现在分子和分母的每一项里。temperature 起到锐度旋钮的作用:
T < 1让分布更尖——高 logit 的 token 更加占主导,输出看起来更像贪心。T > 1让分布更平——logits 之间的细小差异被抹平,输出更多样但连贯性更差。T = 0退化为贪心(argmax)。组件会把它钳制到一个极小的正数以避免除以零,数值上等价。
为什么偏偏是这个函数?
softmax 其实是 argmax 的一个平滑、可微的替身。argmax 会硬生生地跳到最大的那个 logit 上,softmax 则在候选之间平滑地滑动。喂给它 [5.0, 4.9, 1.0],你会得到大约 [0.52, 0.47, 0.01]:两个相近的领先者几乎均分了概率质量,而第三个仍然小到可以忽略。把差距拉大到 [8.0, 4.9, 1.0],输出就锐化到大约 [0.96, 0.04, 0.00]——几乎是一个硬 argmax,而这正是贪心解码(T = 0)所趋近的极限。
这种平滑性也正是 softmax 远不止用于采样的原因:因为它处处都有干净的梯度,它既是模型训练时所对照的那个函数,也是注意力内部用来给各个 key 称重的同一个归一化。
要看清分布为什么会长成那个样子,下面把这次计算拆成四个机械的阶段——原始 logits、除以 T、取指数、归一化——一步一步循环播放。用 T 按钮对同样的八个 logits 分别跑 0.2、0.7 和 2.0:归一化阶段的柱子在 0.2 时尖锐地集中到一个 token 上,在 2.0 时摊平到全部八个上。
z——模型的无界分数,直接来自 LM head。
示意用的八个 token 的 logits,为展示四个阶段而写定——并非模型的实时输出。柱子在数值变为非负之后(取指数后)才出现;显示的每个数字都是该阶段的真实值。切换 T 再看一遍归一化阶段:0.2 时几乎所有概率质量都压在最上面的 token 上,2.0 时则摊到全部八个上。
同一个种子,三种 temperature
上面的柱子是因,这里是果。我们把同一段种子文本交给真实的 Qwen3.5-0.8B 检查点——正是这个 playground 运行的那一个——跑了三次,只改 temperature。在 T = 0 时采样器是贪心的:每一步都取概率最高的单个 token,所以每次重跑的输出完全相同,并且倾向于最保险、最平淡的措辞——注意它多快就开始绕着同样几个词打转。在 T = 0.7 时分布被锐化但没有坍缩,采样出的故事——至少这一次运行——读起来很自然;重跑会得到不同的文本。
T = 1.5 的运行展示了高 temperature 为什么劣化得这么快:生成是一个循环,每个采样出的 token 都会被追加到上下文里,去产生下一步的分布。一次低概率的选择本来还能挽回——但在高 temperature 下,低概率的选择会接连发生,每一次都把上下文拖得离模型见过的任何东西更远——在这次运行里,错误不断累积,直到输出变成词语沙拉。
确定性的——重跑一次会得到一模一样的文本。安全但在原地打转:“very special” 出现了四次,“pocketed” 已经在循环。
一点温和的随机性就足以跳出最保险的车辙——有了名字、有了情节。仍然连贯;重跑会得到另一段不同的续写(这只是一次采样,不保证每次都好)。
每一个低概率的选择都会成为下一步的上下文,损害不断累积——短短几行就漂过三种语言,变成词语沙拉。
真实的预录输出,并非人工编写:每段续写都来自在本地运行与本站完全相同的 Qwen3.5-0.8B 检查点,通过原生 mlx-node 的 Qwen35Model.generate() 裸补全 API(不套 chat 模板),maxNewTokens = 70,纯 temperature 采样——没有 top-p 或 top-K 截断(top-p = 1.0,top-K 关闭)。T = 0 的运行是贪心的、完全可复现;T = 0.7 和 T = 1.5 各是一次采样,重跑会得到不同的文本。
Top-p(核)采样
即便 temperature 取得合理,词表的长尾仍然带着微小但非零的概率质量——偶尔采样器就会落在那里。这些尾部 token 大多在上下文里是胡话。Top-p 采样(也叫核采样,nucleus sampling)负责修剪尾巴:
- 把 token 按概率从高到低排序。
- 沿排序后的列表累加概率,直到累计和达到
p。 - 之后的全部丢弃。
- 把幸存者重新归一化,使它们的总和重新等于 1。
- 从这个“核”中采样:在 0 到 1 之间抽一个随机数
r,沿列表向下累加概率,停在累计值第一个超过r的 token 上——切片越大被命中的次数越多。
常见默认值是 T = 0.7 搭配 top_p = 0.9:temperature 给模型留出一些发挥创造力的空间,top-p 则保证我们永远不会从荒谬的尾部采样。右侧的组件可以让你在一次已捕获的运行上扫动这两个旋钮,看看“本来会发生什么”。
拖动下面的截断滑块,观察“核”如何形成:token 自上而下被保留,直到累计概率达到 p,尾部被丢弃,幸存者被重新归一化到总和为 1。
示意用的十个 token 的分布,已排序、预先写定——并非模型的实时输出。
这个组件如何工作
按下 Run 会生成 6 个 token,inspector 在每一步捕获 top-16 个原始 logits——捕获本身永远是在 temperature=0(也就是贪心)下运行的。temperature 和 top-p 滑块随后在缓存的 logits 上重新应用 softmax + 截断——不会重新运行模型。因为把每个 logit 都除以同一个 T 不会改变它们的相对顺序,top-p 也永远不会丢掉排名第一的 token,所以被高亮的柱子在任何滑块设置下都必然是最高的那根——它永远是模型在这一步的贪心选择。随着你拖动滑块,真正变化的是它与第二名柱子之间的置信度差距:差距大,说明真正的采样器几乎总会认同这个贪心选择;差距小,说明它常常会不认同,落到另一个 token 上。
关于柱子高度的一点说明:这些柱子只在捕获的 top-16 个 logits 上做 softmax/top-p 重新归一化,而不是在完整的 248,320 个 token 的词表上。被省略的尾部仍然携带真实的概率质量,所以每根柱子读起来都比它真实的全词表概率略高——在高 temperature 下最明显,因为那时模型把更多质量摊进了被丢弃的尾部。
当采样出问题时
有两种失败模式值得并排观看:一个低 temperature 的贪心运行陷入循环,一个高 temperature 的运行变成胡言乱语。中间的面板展示的是一个合理的生产环境设置。
重复陷阱。模型找到一个高置信短语,便不断绕回去重复它。
胡言乱语。分布平得离谱,采样器几乎是均匀地挑中罕见 token。
连贯又有变化。Top-p 修剪掉荒谬的尾部,temperature 防止分布坍缩。
生产环境的 LLM 服务通常落在 T = 0.7-1.0 搭配 top-p = 0.9(或一个适中的 top-K)附近。两个旋钮分工不同:temperature 重塑整个分布,top-p 截断长尾。两者合力避开你在上面看到的贪心循环和高温胡话这两种失败模式。
提示:在同一个置信的步骤上分别试试低 temperature(0.2)和高 temperature(1.5)。柱状图肉眼可见的“形状”变化,就是采样参数对 LLM 至关重要的全部原因。
- logits 只有经过 softmax 才变成概率。在同一步之内,重要的是它们的相对顺序——最大的 logit 就是贪心选择——但绝对值在不同运行或不同模型之间不可比。
- temperature 重塑分布;top-p 截断长尾。常见默认值是 T=0.7 搭配 top_p=0.9。
- 被高亮的柱子永远是模型在这一步的贪心(argmax)选择——捕获这次运行时用的是 temperature=0,之后无论怎样重新缩放或截断都不可能改变哪根柱子最高。滑块真正改变的是它和第二名柱子之间的置信度差距:差距大,说明真正的采样器几乎总会选出和贪心一样的结果;差距小,说明它常常会选到别的 token。
自动运行结束后,保持 temperature 为 1.0,慢慢把 top-p 从 1.0 降到 0.3,同时观察第 1 步的柱状图。降到哪个值时图表明显坍缩成一两根柱子?重新归一化后的柱高告诉你什么?