推理生成:模型如何一个 Token 一个 Token 地回答

开篇问题

同一个模型、同一个问题,为什么有时回答完全一样,有时措辞不同,甚至思路也会变化?

很多开发者第一次听到“模型预测下一个 Token”时,会误以为模型每次都选择最可能的词。但真实生成过程通常还有一个关键环节:解码策略。

模型本体负责计算“接下来哪些 Token 更可能”。解码策略负责从这些候选里选出真正输出的那个 Token。greedy、sampling、temperature、top-k、top-p 都属于这个环节。

理解这一章,你会知道模型不是一次吐出整段答案,而是在循环中不断做选择。

本章你会理解什么

读完本章,你会理解:

  • 推理生成为什么是一个 Token 接一个 Token 的循环。
  • logits 是什么,为什么它还不是概率。
  • softmax 如何把候选分数变成概率分布。
  • greedy 和 sampling 的根本区别。
  • temperature 如何改变输出的稳定性和多样性。
  • top-k 和 top-p 如何限制采样候选范围。
  • 为什么采样参数能改变回答风格,但不能把错误模型调成正确模型。

核心概念

推理

推理是模型训练完成后,用当前参数处理输入并生成输出的过程。

这里的“推理”不是专指逻辑推理,而是机器学习里的 inference:给定输入,运行模型,得到输出。

自回归生成

大多数文本大模型采用自回归生成方式。意思是模型每次生成一个 Token,然后把这个 Token 接回上下文,再继续生成下一个 Token。

流程大致是:

  1. 读取输入上下文。
  2. 计算下一个 Token 的候选分数。
  3. 按解码策略选出一个 Token。
  4. 把新 Token 加入上下文。
  5. 重复,直到遇到停止条件。

所以模型生成的每一步都会影响后面的每一步。

logits

logits 是模型在某一步对词表中所有候选 Token 给出的原始分数。

它们可以是正数、负数,也不要求相加等于 1。logits 越高,表示模型越偏向这个 Token。

但 logits 还不是概率。你可以把它理解为模型内部的“未归一化偏好分数”。

softmax

softmax 是把 logits 转成概率分布的常见方法。

它会把一组任意分数转换成一组 0 到 1 之间的概率,并且所有候选 Token 的概率加起来等于 1。

你不需要记公式。直觉上,softmax 会保留“分数高的 Token 概率更高”这个关系,同时把候选放进同一个概率空间。

greedy

greedy,也叫贪心解码,是每一步都选择概率最高的 Token。

它最稳定,也最容易复现。但它可能过于保守,容易陷入重复、模板化或局部最优。

sampling

sampling 是按概率随机抽样。

如果某个 Token 概率是 60%,另一个是 30%,sampling 不会永远选 60% 的那个,而是更常选它,同时保留选择其他候选的可能。

这就是模型输出多样性的来源之一。

temperature

temperature 会调整概率分布的“尖锐”或“平坦”程度。

低 temperature 会放大高概率候选的优势,让输出更稳定。高 temperature 会让低概率候选获得更多机会,让输出更多样,但也更容易偏离上下文。

top-k

top-k 表示每一步只保留概率最高的 k 个候选 Token,再从这些候选中采样。

如果 k = 10,模型只会在当前最可能的 10 个 Token 里选择,其他候选直接排除。

top-p

top-p 也叫 nucleus sampling。它不是固定保留几个 Token,而是从高到低累加概率,保留累计概率达到 p 的最小候选集合。

如果 p = 0.9,就保留那些加起来覆盖 90% 概率质量的候选,再从中采样。

top-p 的候选数量会随上下文变化。分布很集中时,它可能只保留少量 Token;分布很分散时,它会保留更多 Token。

机制拆解

1. 输入上下文进入模型

推理开始时,用户输入、系统消息、历史内容等会被 Tokenizer 转换成 Token ID 序列。

模型读取这些 Token,并结合训练得到的参数,计算当前位置之后最可能出现什么。

注意,模型此时还没有生成完整回答。它只是在回答第一个问题:下一个 Token 是什么?

2. 模型输出一整张候选 Token 分数表

假设词表里有几十万 Token。模型每一步都会给每个 Token 一个 logit。

如果上下文是:

TypeScript 是 JavaScript 的

候选可能包括:

Token直觉上的倾向
超集很高
一个较高
类型中等
数据库很低
香蕉极低

真实模型不会只看这几个词,而是对整个词表打分。

3. logits 经过 softmax 变成概率

logits 本身只是分数。为了选择 Token,系统通常会把它们变成概率。

softmax 做的事情可以粗略理解为:

  • 分数最高的候选获得最高概率。
  • 分数差距越大,概率差距通常也越明显。
  • 所有候选概率加起来等于 1。

这样,解码策略就可以基于概率做选择。

4. 解码策略选择下一个 Token

到这里,关键分叉出现了。

如果使用 greedy,系统直接选择概率最高的 Token。

如果使用 sampling,系统会把概率分布当作抽样权重。高概率 Token 更容易被选中,但不是唯一可能。

这一步决定了当前 Token。当前 Token 一旦选定,就会成为下一步上下文的一部分。

5. 新 Token 被接回上下文

假设模型生成了“超集”。下一步上下文变成:

TypeScript 是 JavaScript 的超集

模型再根据这个新上下文计算下一组 logits、softmax 概率,再选择下一个 Token。

这就是自回归生成的核心循环。

一个早期选择会影响后续分布。如果第一步选择了“超集”,后面可能继续解释类型系统;如果第一步选择了“一个”,后面可能生成“带类型语法的编程语言”。两条路径都合理,但展开方向不同。

6. 生成在停止条件处结束

模型不会天然知道什么时候“全文写完”。推理系统通常会设置停止条件,例如:

  • 生成了特殊结束 Token。
  • 达到最大输出 Token 数。
  • 命中停止序列。
  • 外部系统中断生成。

所以输出长度既受模型判断影响,也受推理配置影响。

greedy:稳定但可能僵硬

greedy 的规则很简单:每一步都选概率最高的 Token。

它的优点是稳定。对于摘要、分类、固定格式输出等任务,稳定性很有价值。

但 greedy 也有明显问题。

首先,它只看当前这一步的最高概率,不保证整段文本全局最好。局部最优可能导致后面表达不自然。

其次,它会降低多样性。模型总是走最常见路线,回答可能变得模板化。

再次,某些开放式任务本来就有多个好答案。greedy 会强行选择其中一条路径,未必是最适合用户的一条。

所以 greedy 适合追求稳定和可复现的场景,但不适合所有生成任务。

sampling:让合理候选都有机会

sampling 的核心是:按概率抽样,而不是永远选最大值。

假设下一个 Token 的概率大致是:

Token概率
超集0.55
一个0.25
类型0.12
脚本0.05
数据库0.03

sampling 大多数时候会选“超集”,但也可能选“一个”或“类型”。这让模型可以产生不同表达。

sampling 的代价是不确定性。相同输入多次运行,输出可能不同。temperature、top-k、top-p 就是为了控制这种不确定性。

temperature:调节分布的形状

temperature 不直接告诉模型选哪个词,而是改变选择前的概率分布。

低 temperature 会让高概率 Token 更突出。原本 0.55 和 0.25 的差距会被拉大,模型更常选第一名。

高 temperature 会让分布更平。低概率候选获得更多机会,输出更发散。

可以这样理解:

temperature 倾向分布效果输出表现
很低候选集中到少数高概率 Token稳定、保守、重复风险更高
中等保留一定差异,也允许变化平衡稳定和多样
很高候选差距被削弱多样、跳跃、错误风险更高

temperature 不是“创造力旋钮”那么简单。它只是在抽样前改变概率分布。模型不知道的知识,不会因为 temperature 变高就突然知道。模型推理链条错误,也不会因为 temperature 变低就必然正确。

top-k:只在前 k 个候选里采样

top-k 解决的问题是:概率分布尾部可能有大量低概率 Token。单个概率很小,但如果完全开放,偶尔也可能被抽到,导致输出奇怪。

top-k 的做法很直接:

  1. 按概率从高到低排序。
  2. 只保留前 k 个。
  3. 在这 k 个候选里重新归一化概率。
  4. 从中采样。

如果 k 很小,输出会更稳定,但可能缺少变化。如果 k 很大,限制变弱,输出更开放。

top-k 的特点是候选数量固定。不管当前上下文很确定还是很开放,它都保留同样数量的候选。

top-p:保留一团“足够可能”的候选

top-p 不固定候选数量,而是固定累计概率阈值。

假设某一步概率很集中:

Token概率
超集0.82
一个0.10
类型0.04
其他0.04

如果 top_p = 0.9,可能只需要保留“超集”和“一个”,累计就超过 0.9。

再看一个开放场景:

给这个产品起一个名字:

候选可能很分散,没有哪个 Token 占绝对优势。为了达到 0.9,top-p 可能会保留更多候选。

这就是 top-p 的优点:它会随分布形状动态调整候选范围。确定场景更收窄,开放场景更宽。

参数之间如何共同作用

实际推理中,temperature、top-k、top-p 可能同时出现。它们的顺序和实现细节会因系统而异,但直觉上可以分成两类:

  • temperature 改变分布形状。
  • top-k 和 top-p 裁剪候选集合。

然后系统在剩余候选中采样。

如果你把 temperature 设得很高,同时 top-p 也很开放,输出会更发散。如果 temperature 很低,即使 top-p 较高,最高概率候选也会占据主导。

这也是为什么不能孤立理解某一个参数。它们共同决定“模型有多大机会离开最常见路径”。

开发者常见误解

误解一:模型先想好整段答案,再输出给用户

模型通常不是先在内部写完整篇文章,再一次性发出。它是逐 Token 生成。

后面的内容依赖前面已经生成的内容。这解释了为什么开头一个小偏差,可能让整段回答走向不同方向。

误解二:logits 就是概率

logits 是原始分数,不是概率。

只有经过 softmax 等处理后,它们才变成可解释为概率的分布。理解这一点,才能明白 temperature 为什么作用在概率形成前后,而不是简单“给答案加随机数”。

误解三:temperature 越高,模型越聪明

temperature 只影响采样随机性,不提升模型能力。

高 temperature 可能带来新颖表达,也可能带来不稳定和错误。低 temperature 可能更稳,但也可能更死板。

误解四:greedy 一定最正确

最高概率 Token 不一定通向最好的整体答案。

语言生成有时需要选择次高概率但更适合上下文的表达。特别是在创意、解释和开放式写作中,greedy 可能过早锁定平庸路径。

误解五:top-k 和 top-p 是同一个东西

它们都限制候选集合,但方式不同。

top-k 固定数量,top-p 固定累计概率。前者简单直接,后者更能适应上下文的不确定程度。

误解六:采样参数能解决事实错误

采样参数只能改变从候选分布中选择 Token 的方式。

如果模型已经把错误答案赋予很高概率,调参未必能解决。事实可靠性需要更好的上下文、训练、检索、工具或评估,而不是只靠 temperature。

小实验/思考实验

实验一:手动做一次 greedy

上下文:

React 是一个用于构建

候选概率:

Token概率
用户0.45
界面0.35
组件0.12
服务端0.08

greedy 会选择“用户”。下一步上下文变成:

React 是一个用于构建用户

此时“界面”的概率可能变得非常高。greedy 每一步都看当前最可能的候选,而不是一开始就规划完整句子。

实验二:手动做一次 sampling

还是上面的概率分布。如果按 sampling,偶尔会选“界面”。

于是上下文变成:

React 是一个用于构建界面

这句话也合理,只是路径不同。

这说明 sampling 的变化不一定是错误。它只是允许多个合理延续竞争。

实验三:top-p 的候选数量会变化

场景 A:

中国的首都是

概率可能高度集中在“北京”。

场景 B:

给一个代码编辑器插件起名:

候选会分散很多。

同样的 top_p = 0.9,在场景 A 可能只保留很少候选,在场景 B 可能保留很多候选。这就是 top-p 比固定 top-k 更动态的地方。

本章小结

大模型生成回答时,通常不是一次生成整段文本,而是在自回归循环中一个 Token 一个 Token 地选择。每一步,模型先根据上下文计算所有候选 Token 的 logits,再通过 softmax 得到概率分布,最后由解码策略选择真正输出的 Token。

greedy 每次选最高概率,稳定但可能僵硬。sampling 按概率抽样,带来多样性也带来不确定性。temperature 调整概率分布的尖锐程度,top-k 固定保留前 k 个候选,top-p 动态保留累计概率达到阈值的一组候选。

这些机制决定了模型输出的风格和稳定性,但它们不等于知识来源,也不是事实校验工具。生成质量来自模型能力、上下文、对齐和解码策略的共同作用。

自测题

  1. logits 和 softmax 后的概率有什么区别?
  2. greedy 解码为什么稳定?它有什么潜在问题?
  3. sampling 为什么会让同一个问题出现不同回答?
  4. temperature 改变的是模型知识,还是候选分布?为什么?
  5. top-k 和 top-p 的区别是什么?
  6. 为什么采样参数不能从根本上解决模型事实错误?