AI基础知识-第二章-从“猜下一个词”到回答:概率分布与采样

35 阅读12分钟

上一篇我们讲了模型怎么“读”句子:文字先被拆成 Token,再通过注意力机制融合上下文。到这里,模型已经拿到了一串带上下文的信息表示。

接下来问题来了:模型怎么从这些信息里说出一句话?

很多人会把大模型的回答理解成“它想好了一个答案,然后打出来”。这个理解很自然,但并不准确。更贴近模型实际工作方式的说法是:

模型每一步都在预测下一个 Token,然后把预测出来的 Token 接到原文后面,再继续预测下一个。

也就是说,模型不是一次性吐出一整段回答,而是一步一步生成。每一步,它都会根据当前上下文,计算“下一个 Token 可能是什么”。这些可能性不是一个确定答案,而是一组概率。

这篇我们就讲清楚三个问题:

  1. 模型为什么是在“猜下一个词”?
  2. 概率分布、logits、softmax 到底是什么关系?
  3. 温度、top_p 这些采样参数为什么会影响回答风格?

一、模型每次只决定“下一个 Token”

先看一个简单例子。

如果你给模型一句话:

今天的天气真

模型接下来可能会想到很多候选:

好、不错、热、冷、糟糕、舒服……

这些候选并不是同等可能。根据大量训练数据和当前上下文,“好”“不错”可能概率更高,“芯片”“数据库”这种词概率就很低。

所以模型真正做的事情不是“理解后直接输出完整答案”,而是对词表里的每个 Token 打一个分,判断它成为下一个 Token 的可能性。

词表可能有几万到几十万个 Token。模型每一步都会给这些 Token 各自算一个分数。分数高,说明模型认为它更适合接在当前上下文后面;分数低,说明它不太合适。

这一步得到的不是一个词,而是一整张候选表。

04-next-token-distribution.png

图 1:模型不是只给出一个答案,而是给很多候选 Token 分配不同概率。

比如当前上下文是:

今天的天气真

模型内部可能形成类似这样的判断:

候选 Token可能性
不错较高
中等
中等
糟糕较低
数据库很低

注意,这里只是帮助理解的简化表。真实模型面对的是完整词表,而且每个候选都有一个数值分数。

二、logits:模型先给候选打“原始分”

你可能见过一个词:logits。

logits 可以粗略理解成模型给每个候选 Token 打的原始分。它还不是概率。

为什么不能直接把 logits 当概率?因为 logits 可能是负数,也可能大于 1,而且所有候选加起来也不等于 1。它只是模型内部计算出来的一组相对分数。

比如模型看到“今天的天气真”后,可能给候选打出这样的原始分:

候选 Token原始分 logits
8.2
不错7.5
6.1
5.8
糟糕3.0

这些分数表达的是排序和差距:谁更像下一个 Token,谁不太像。

但如果要真正“抽一个 Token 出来”,还需要把这些原始分转换成概率。这个转换过程通常叫 softmax。

你不需要记公式。只要记住 softmax 做了两件事:

第一,把原始分转换成 0 到 1 之间的概率。

第二,让所有候选 Token 的概率加起来等于 1。

转换之后,模型就得到了一张概率分布表。比如:

候选 Token概率
45%
不错25%
12%
10%
糟糕3%
其他5%

这就是“概率分布”。它不是最终回答,而是模型在当前这一步面对的选择空间。

三、生成回答,其实是重复很多次选择

当模型从概率分布里选出一个 Token 后,会把它接到原来的上下文后面。

假设它选了“好”,上下文就变成:

今天的天气真好

然后模型继续计算下一个 Token 的概率分布。

接下来它可能选择:

上下文变成:

今天的天气真好,

再继续预测:

适合

再继续:

出门

最终你看到的是一句完整回答:

今天的天气真好,适合出门走走。

但在模型内部,这不是一次性写出来的,而是多次“预测下一个 Token”的结果。

05-sampling-strategies.png

图 2:模型每生成一个 Token,就把它放回上下文,再继续预测下一个 Token。

这也是为什么大模型回答时看起来像“逐字输出”。它确实是在一步步生成。流式输出只是把这个过程提前展示给你看。

理解这一点后,很多现象就容易解释了。

比如模型为什么会越写越偏?因为每一步生成的新 Token 都会成为下一步的上下文。如果前面某一步选错了方向,后面就可能沿着这个方向继续展开。

再比如为什么提示词开头很重要?因为初始上下文会影响第一批概率分布,而第一批生成又会影响后续所有生成。

四、如果每次都选概率最高的,会发生什么?

最直接的生成方式,是每一步都选择概率最高的 Token。这通常叫 greedy decoding,也就是贪心解码。

它的优点是稳定。相同输入下,模型更容易给出相同回答。

但它的问题也很明显:回答可能变得死板。

比如你让模型写一句广告文案,如果每一步都选概率最高的 Token,它可能会给出非常常见、非常安全的表达:

这是一款高效、便捷、值得信赖的工具。

这句话没错,但也没有什么记忆点。因为高概率 Token 往往代表训练数据里最常见、最稳妥的表达。

如果你希望回答更自然、更有变化,就不能永远只选概率最高的那个候选。你需要让模型在合理范围内“抽样”。

这就进入采样。

五、采样:不是乱选,而是在概率表里抽选

采样不是让模型乱说。它仍然基于概率分布,只是不会永远拿第一名。

如果某个 Token 概率是 45%,另一个 Token 概率是 25%,那么第一个更容易被选中,但第二个也有机会出现。概率低到离谱的 Token,正常情况下很难被抽中。

你可以把它想象成一个抽奖箱。

概率高的 Token 在箱子里有更多票,概率低的 Token 票更少。抽样就是从这个箱子里抽一张票。票多的更容易中,但不是百分百中。

这就是为什么同一个问题,多问几次模型,回答可能不完全一样。模型不是只执行一条固定路线,而是在概率空间里走出一条路径。

采样带来了多样性,也带来了风险。参数控制得好,回答会更有创造力;控制不好,就可能跑偏、啰嗦,甚至胡说。

六、温度:控制概率分布“尖”还是“平”

温度是最常见的采样参数之一。

它的作用可以用一句话理解:

温度越低,模型越保守;温度越高,模型越敢选低概率候选。

当温度低时,概率分布会变得更“尖”。原本概率最高的 Token 会更加突出,其他候选的机会被压低。模型输出更稳定,也更容易重复常见答案。

当温度高时,概率分布会变得更“平”。不那么靠前的候选也有更多机会被选中。模型输出更发散,也更有可能出现意外表达。

06-generation-loop.png

图 3:温度改变概率分布的尖锐程度,top_p 则限制模型只在高概率候选集合里抽样。

不同任务适合不同温度。

如果你让模型做代码修复、信息抽取、格式转换,通常希望它稳定准确,温度可以低一点。

如果你让模型写标题、想创意、生成故事,通常希望它多一些变化,温度可以稍高一点。

但温度不是越高越好。温度太高时,低概率 Token 被选中的机会变大,回答就可能开始飘。

七、top_p:只在“够可能”的候选里抽

另一个常见参数是 top_p,也叫 nucleus sampling,中文常译为核心采样。

它解决的问题是:模型的词表太大了,长尾候选很多。虽然每个低概率 Token 单独看概率很小,但它们加起来可能也不少。如果完全不限制,采样时就可能抽到奇怪的候选。

top_p 的思路是:先按概率从高到低排序,然后只保留累计概率达到某个阈值的候选集合。

比如 top_p = 0.9,意思是只在累计概率前 90% 的候选里抽样。剩下那些长尾候选,即使理论上有概率,也先排除掉。

这比固定保留前几个候选更灵活。

如果当前上下文很明确,前几个 Token 的概率就可能已经占到 90%,候选集合会很小,回答更稳定。

如果当前上下文比较开放,概率会分散到更多候选上,候选集合会更大,回答也会更多样。

所以 top_p 可以理解为给采样加了一道护栏:不让模型从太离谱的长尾候选里选,但仍然保留一定变化空间。

八、不同采样参数,适合不同任务

应用开发里,不要把采样参数当玄学。你可以按任务类型粗略选择。

如果是事实问答、代码生成、SQL 生成、结构化抽取,通常希望模型少发挥,多遵守事实和格式。可以用较低温度,比如 0 到 0.3,top_p 也不要太放飞。

如果是文章标题、短视频脚本、营销文案、头脑风暴,可以适当提高温度,比如 0.7 到 1.0,让模型产生更多表达变化。

如果是长文写作,通常要折中。温度太低会模板化,温度太高会跑题。可以先用中等温度生成初稿,再用较低温度做整理、改写和校对。

一个实用经验是:

生成创意时放开一点,生成结论时收紧一点。

同一个产品里,不同环节也可以用不同参数。比如智能写作工具里,“生成 10 个标题”可以高温度,“整理最终大纲”可以低温度,“提取文章摘要”可以更低。

九、为什么说模型不是在“思考”,而是在“计算”

理解概率分布和采样后,我们可以更谨慎地看待“模型会思考”这句话。

从用户体验上看,模型确实像在思考。它能分析问题、组织语言、给出步骤,甚至能解释自己的理由。

但从生成机制上看,它每一步做的是:根据上下文计算下一个 Token 的概率分布,然后按解码策略选择一个 Token。

这不是说模型“没有能力”,也不是说它只是简单背答案。现代大模型的上下文表示非常复杂,训练数据和参数规模也让它具备了强大的模式归纳能力。

更准确的理解是:

模型的回答能力,来自大规模训练后形成的概率建模能力。

它能写代码,是因为它学到了大量代码模式、语法结构、问题描述和解决方案之间的对应关系。

它能总结文章,是因为它学到了文本结构、重点信息和摘要表达之间的关系。

它能进行多步推理,是因为很多推理路径也可以被表示成语言中的中间步骤,并在生成过程中逐步展开。

但它仍然可能犯错。因为高概率不等于真实,流畅不等于正确。模型选出了“看起来最可能接下去的内容”,不代表它一定核验过事实。

这就是为什么后面我们讲 RAG、工具调用、代码执行、测试验证时,会反复强调:大模型生成很强,但需要外部机制来补事实和验证。

十、一段极简代码:感受采样差异

下面这段代码不调用真实大模型,只模拟“从概率分布里抽下一个词”。

import random
​
candidates = [    ("好", 0.45),    ("不错", 0.25),    ("热", 0.12),    ("冷", 0.10),    ("糟糕", 0.05),    ("离谱", 0.03),]
​
def sample(candidates):
    words, probs = zip(*candidates)
    return random.choices(words, weights=probs, k=1)[0]
​
for _ in range(10):
    print("今天的天气真" + sample(candidates))

你多运行几次,会发现“好”出现得最多,但“不错”“热”“冷”也会出现。它们不是最高概率,却仍然有机会被抽中。

如果你把“好”的概率调得更高,输出会更稳定。如果你把几个候选的概率调得更接近,输出会更随机。

真实模型比这段代码复杂得多,但核心直觉相通:生成不是从一个固定答案里复制,而是在每一步的概率分布中选择。

小结

这篇先记住四个结论。

第一,大模型生成回答时,不是一次性写出整段内容,而是一步步预测下一个 Token。

第二,模型每一步先输出 logits,也就是候选 Token 的原始分,再通过 softmax 转成概率分布。

第三,采样决定了模型如何从概率分布里选 Token。贪心选择更稳定,随机采样更多样。

第四,温度和 top_p 是控制输出风格的重要参数。低温度更保守,高温度更发散;top_p 会把选择范围限制在相对靠谱的候选集合里。

所以,当我们说模型“回答了一个问题”,背后其实是这个循环:

读入上下文 → 计算下一个 Token 的概率分布 → 按采样策略选一个 Token → 拼回上下文 → 继续下一步

理解了这个过程,你就会更清楚为什么提示词会影响输出,为什么同一个问题可能得到不同答案,为什么参数设置会改变回答风格,也会更自然地理解下一篇要讲的内容:后训练如何让一个“会续写文本”的模型,变成一个更像合格员工的对话助手。