阶段 3 · 处理序列

采样:模型不是"想好了
再说出来"的

它每说一个字,都要重新算一遍"下一个字该是什么"。 而这个"该是什么"永远是一个概率分布,不是唯一答案。 怎么从这个分布里挑字,直接决定了模型看起来是聪明、是死板、还是胡说八道。

1

自回归循环:一个字一个字地吐

上一章 《预训练语言模型》 把整个语料压成了一份「下一个词的概率」——它给的从来不是一个答案,而是一份分布。 生成一句话的全过程就是一个 loop,每一轮都要把已经说过的话重新读一遍。 (这个"一个字一个字往下算"的过程,就叫 推理。)

核心图 · 走一轮,看这个循环转到哪里去了

① 前向把当前的所有词喂进模型
② 输出分布得到词表里每个词的概率(几万个数)
③ 采样按概率挑一个词
④ 接回去把挑出来的词加到输入末尾,回到第 ① 步
💡 一个必须理解的后果

抽到一个不太好的词,它就会永久留在上下文里——后面的每个字都要接着它写。 模型无法"回退重写":它说「让我重新想一下」,也只是在那句话后面接着写,前面写下的字并没有被删。 这就是为什么一句错话会带来一长串崩坏——以及为什么"重复"会自我强化。

2

从概率分布到具体的字

模型先给每个候选打一个原始分数(logit,还没压成概率),再用 softmax 把分数压成加起来等于 1 的概率,几万个数。 怎么挑?这是四种主流策略:贪心(每次都挑概率最大的那一个),加上温度、top-k、top-p 三个旋钮。 下面是一个真实的分布——拖动滑块看它怎么被改造,然后真的采一个词出来。

互动 · 真实的采样沙盘

把鼠标停在下面这几个零件里的 T、k、p 上——上面(以及第 3 节那个生成器里)对应的滑块会亮起来。

互动 · 同一个分布被三个旋钮依次改造

四行是同一个分布的四个版本,用的就是上面那三个滑块。 默认状态下 top-k 没启用、top-p 是 1,所以 ②③④ 三行完全一样—— 拖动上面任意一个滑块,就能看出是哪一行开始变。
③④ 两行里会有候选一根根地"归零",不是没画,是概率真的被置零了—— 被砍掉的位置画成空框,实心柱才是保留的; 保留下来的柱子同时变高,那是重新归一化的结果(剩下的概率必须重新加起来等于 1)。 最下面那排字按最终结果标灰。

⚠️ 三个旋钮不是三件独立的事,顺序还会改变结果

温度改变分布的形状,top-k 砍固定个数,top-p 按累积概率砍——它们是依次作用在同一个分布上的三道筛子。 参考实现的顺序是 温度 → top-k → top-p(nanoGPT 就是这么写的:先缩放、再筛选、最后抽样)。
把 top-k 和 top-p 都拖起来(两个筛子都启用),再点一下上面的「🔀 顺序」按钮换成先 top-p 再 top-k,候选集个数会变: top-k 砍完之后概率会重新归一化,累积和到达阈值的位置就变了,先削掉的那批候选自然不一样。 HuggingFace 的官方示例干脆在开 top-p 时把 top_k=0 关掉,就是不想让两个筛子互相干扰。 所以实践中常见的是温度 + top-p 一起用(比如 T = 0.7、top_p = 0.9),其他按需再加。

3

真的让模型吐一句话出来

上面的沙盘只挑了一个字。真正的生成是把这件事重复几十遍: 每抽出一个字,就把它接到输入末尾,再拿新的前文重新算一遍分布。 下面这个"模型"不是真网络,是我们手写的一张表:39 个字,每个字后面接什么字、分数多少,都写在表里; 表里没写的组合一律给一个很低的分(这些很少用到的字,就叫长尾)。 喂给它的开头(提示词)是「开始」,每个字都按概率抽签选出来——这就是多项式采样。 真模型和它的差别只有一个:表里的分数是网络自己算出来的,抽签的机制完全一样。

图 · 那张手写表长什么样

互动 · 逐字采样生成器

每一步是怎么抽的(每一步都是一次真实的抽样)

抽签用的随机数从一个起点生成,这个起点叫种子:种子一样,抽出来的就一样。 拖滑块时用的就是同一颗种子,所以看到的变化全部来自参数本身;点「同一颗种子再跑一次」可以验证这一点。
面板上的熵衡量这个分布有多平:0 = 只有一个选项,1 = 所有选项一样多; log P 是这整句被抽出来的概率取对数,越接近 0 越像模型最想说的话。

4

同一份概率,不同的种子

这是很多人对 LLM 最困惑的一点:参数一模一样,问两次,答案却不一样。 原因不在模型——模型每次给出的都是同一个分布,不一样的是从分布里抽签的那只手。 下面两次统计都用上面那一套参数,每一次都真的重跑。

互动 · 用当前参数跑 300 次,看结果散成什么样

微图 · 同一份分布,抽四百次之后长什么样

互动 · 温度 → 多样性(每一档跑 100 次)

横轴是温度,每一档都把整套生成跑 100 遍,数出出现了多少种不同的句子; 橙点是当前温度。纵轴 0 ~ 1:1.0 = 每次都不一样 / 分布完全不犹豫。 分布越平,抽出来的越多样:虚线(分布的不确定性)涨上去,实线(结果的多样性)才跟着涨—— 多样性是分布形状的结果,不是可以直接设的一个参数。这条曲线固定 top-k 关闭、top-p = 1,只看温度。

5

四种策略的完整对照

前四行是四种采样策略(贪心、温度、top-k、top-p);最后两行是另外两类补丁——beam search 改的是搜索,重复惩罚改的是分数,都不是「从分布里挑字」的采样器。 既然贪心最稳,为什么不永远选最大?——因为那样容易车轱辘话来回说,回答千篇一律(第 6 节有例子)。 这些旋钮平时藏在聊天产品背后——默认值由开发方定好;要自己改,得用 API(直接用程序调用模型的接口),不填的参数就用服务方给的默认值。

策略怎么做效果调参
贪心 Greedy 永远选概率最大的那个 确定性输出;同一句话永远得到同一个回答。容易陷入重复和死循环 无
温度 Temperature 把 logits 除以 T 再做 softmax T < 1 变保守,T > 1 变随机,T → 0 等于贪心 常见取值 0.7 ~ 1.0
Top-k 只保留概率最高的 k 个,其余归零后重新归一化 砍掉长尾的胡话。但 k 固定,遇到"只有一个合理选项"时也会强行塞进 k 个 常见取值 k = 20 ~ 50
Top-p(核采样) 按概率从高到低累加,累加到阈值 p_top 就截止(越过阈值的那个也保留),剩下的全砍掉 候选集随分布形状自适应:模型确定时候选集自动变小,犹豫时候选集自动变大 top_p = 0.9 ~ 0.95
Beam Search 同时保留 b 条最优路径(b 叫束宽),最后挑总概率最高的一条 适合翻译这类有唯一正确答案的任务;
不适合开放式生成——会产生"看起来很安全但很无聊"的文本
常见取值 beam = 4 ~ 10
Repetition Penalty 已经在文中出现过的词,把它的 logit 往下压 压制复读。但压过头会破坏正常的重复(比如中文的量词、代码的括号) 1.05 ~ 1.2

把上面那张表画成图 · 同一份分布,三种策略各留哪几根

四排柱子都画在同一份分布上——所以它们砍的是同一堆候选的不同部分。 这里故意挑了一份偏尖的分布(温度 0.6)来画:
贪心永远只留 1 根;top-k 不管分布形状都硬留固定的 4 根; 而 top-p 只需要留到累积概率够 0.9,两根就够了—— 上面 top-k 硬留的第 3、4 根在这份分布里根本没必要看。根数跟着分布变,就是 top-p 比 top-k 好用的地方。

💡 为什么 top-p 比 top-k 好用

因为语言的确定性在不同位置差别极大。
"今天天气真___"——下一个字几乎确定是"好",分布极度尖锐(像上图那样的 T=0.6),此时只需要 1~2 个候选。
"我喜欢___"——下一个字可能是几百个词,分布非常平,需要几十个候选。
top-k 用固定的 k 硬套这两种情况;top-p 让候选集大小跟着分布形状走。

M

数学 · 一个字,凭什么被挑中

前面五节都在拖旋钮。这一节只问一件事:模型吐出来的那一串原始分数,到底是怎么变成一根根柱子的高度的? 为了看清每根柱子,这一节换成 8 个候选的小表。 整章的数学只有一个公式,它只做两步:先把分数拉开或压平,再让所有柱子加起来正好等于 1。 拖一下下面这个温度滑块,这一节的数学你就全看完了。

互动 · 同一组分数,拖温度看它被拉成什么形状

🎬 自己验一遍

把温度拖到 0.1:最高那根柱子几乎顶满,分布变尖 = 更保守。
再拖到 2.0:高的柱子矮下去、矮的柱子长起来,一起向那条橙色虚线(完全均匀的高度)靠拢,分布变平 = 更随机。
注意上下两排的对照——上面的灰柱一根都没动,动的只有下面那排。

微型图解 · 核采样是怎么找到那个「刚刚够」的候选集

T1.00上面那个滑块 此刻最可能的字—真算出来的 它的概率—八根柱子加起来永远是 1

这一步就是第 5 节说的「top-p 用固定的阈值、砍出变动的候选个数」。 把上面的温度拖大,柱子变平——累积线爬得就慢,能留下来的字就变多。 同样一个阈值 p_top = 0.9,分布尖的时候只留两三个,分布平的时候要留五六个。

6

三种典型的崩坏

现象成因怎么缓解
复读机
"这个问题很好,这个问题很好,这个问题很好……"
贪心解码 + 没有重复惩罚时,模型进入了自我强化的循环: 一旦重复了一次,前文里的重复模式会让"继续重复"成为概率最高的选择 repetition penalty;改用 top-p 采样;不要用 beam search
胡说八道
编造出不存在的论文、法条、API
语言模型的目标是"说出像真的话",不是"说出真话"。 它没有任何机制去核对事实。这是训练目标层面的事情,不是 bug RAG 检索增强(阶段 7);要求引用来源;降低温度没用(见《评测、基准与幻觉》那章的幻觉一节)
过早收尾
回答刚开个头就输出 EOS(EOS 是表示"说完了"的结束记号)
概率质量集中在 [EOS] 上。常见于训练数据里短回答偏多;提供模型的一方给「最多生成多少个词」(max_tokens)设的默认值太小,会让回答被截断(那是截断,不是 EOS) 调整训练数据配比;调低 EOS 的 logit;明确要求"详细展开"

互动 · 同一张手写表,温度两头各跑四遍

⚠️ 关于幻觉,一个必须说清的事实

幻觉不是可以通过调参消掉的缺陷,而是语言模型的结构性特征: 它在做的是"给定前文,什么下一个词最合理"。 当它不知道答案时,"编一个听起来合理的"和"说不知道"在统计上是同等有效的策略—— 而前者在训练数据里出现得更多。
真正的解法只有三条:给它资料(RAG)、给它工具(搜索/计算器)、或者训练它说"我不知道"。

7

这些旋钮什么时候会咬你

这些参数改变的是输出的风格,不是模型的能力。调温度不会让模型变聪明,只会让它更能说或更保守。 下面是每个旋钮最典型的翻车方式和对应的调法。

你怎么设什么时候真的会痛换成什么
温度太低(T < 0.5)需要创意的任务变得死板,同一个问题永远是同一个回答创意任务把 T 调到 0.9 ~ 1.2
温度太高(T > 1.3)长尾里的胡话开始被抽中,句子说着说着跑偏降到 0.7 ~ 1.0
top-k / top-p 收得太紧候选被砍到只剩一两个,输出重复、不通顺放宽到 top_p = 0.9 ~ 0.95
重复惩罚太重(> 1.2)正常的重复被压坏:中文量词、代码括号、人名降到 1.05 ~ 1.1
beam search 用在开放式生成输出安全但无聊,来回重复换成 top-p 采样
需要确定性答案写代码、做数学、抽取信息时,同一个问题每次答案不同T = 0(贪心)

最常用的组合是温度 + top-p(比如 T = 0.7、top_p = 0.9);重复惩罚按需加;beam search 只在有唯一正确答案的任务上用。

8

小结

这一章没有一条公式值得背,只有一个结论值得记住: 模型交给你的从来不是"答案",而是一整份概率分布。

它对应哪条线 ④ 学习即压缩——预训练把整个语料压成了一份"下一个词的概率"(第 2 节那排柱子就是它的样子); 这一章讲的是反方向的那一半:怎么从这份压缩里把字取出来—— 温度、top-k、top-p 都只是取法,取法一变,同一个压缩包就吐出不同的句子。
一句话 这一章做的所有事,本质上是在把"模型给出的那份不确定"搬到一个它管不着的地方: 前向算出分布(模型的事),从分布里挑字(采样器的事)。 随机性不在权重里,在抽签的那只手上。
它牺牲了什么 牺牲了确定性:要不胡说,就得砍掉候选(top-k / top-p); 要多样,就得把分布摊平(高温),于是长尾里的"香蕉""量子"也会被抽出来。 这笔取舍发生在推理时刻,训练时根本无法预知你要哪一种。(回扣暗线 B、D)

互动 · 同一组权重,两个使用者

🎬 自己验一遍

回到第 2 节那张「真实的采样沙盘」。先把「温度 T」滑块从 1.0 慢慢拖到 0.1—— 你会看到其它柱子一根根被压到底,只剩「好」那一根独大;这就是从分布里抽签。

再把它拖到 1.8——连「香蕉」「量子」「紫色的」这些明显不搭的词也长出了柱子。

那个瞬间你看到的就是「模型没变,变的是你从分布里挑字的方式」: 每个候选的 logit 一个都没动过,动的是你怎么读它。 如果不信,点一下「🎲 采一个」——同一份分布,每次抽出来的字可能都不一样。 你如果没想指着那排柱子说这句话,就把温度再拖一遍。

它在暗线里站在哪

上面那句「④ 学习即压缩」来自 《第一性原理》 那章列的七条线;下面这张表是拆方法时用的 六条暗线(A–F),是另一套编号。

暗线这一章的回答
A 信息流动 一次送进去好几句话、每句若干个编号;模型出来的是每句话对词表里每个词的分数(GPT-2 的词表有 50257 个词), softmax 变成概率,采样挑出 1 个词,拼回输入末尾,序列长度 +1。
形状几乎没变,变的是"每说一个字,前文都要被重读一遍"—— 这就是自回归循环的真身(回扣暗线 A)
B 什么被牺牲了 牺牲了确定性与可复现性:想要多样(高温)就得接受长尾胡话, 想要安全(低温 / top-p)就得接受"同一个问题永远是同一个回答"。
而且这笔账在推理时付、训练时无法预知——同一组权重,换一个使用者就是另一种风格
C 参数账本 采样本身不花算力:对几万个数排一次序。真正的账单在前向——GPT-3 有 1750 亿参数,每说一个字约需 2 × 1750 亿 = 3.5 × 1011 次乘加(FLOPs);连说 100 个字就是 3.5 × 1013。
风格旋钮的成本和模型本身差了几十万倍—— 所以"调参"永远比"换模型"便宜,但也永远改不了能力的天花板
D 跑在什么上 瓶颈是搬权重,不是算数。生成每吐一个字,都要把模型的全部权重重新读一遍: 1750 亿参数按半精度(fp16,每个数 2 字节)存就是 350 GB; 而计算只有 3.5 × 1011 次乘加——每读 1 字节只算约 1 次, GPU 大部分时间在等内存(硬件的算力 / 带宽拐点远在这之上)。
最贵的动作不是"算",是"把权重搬过来"。 这正是 batch 能救它的原因:一次读进来的权重,可以同时服务很多条请求。 完整的账在 《硬件与算力账本》
E 它假设了什么 三条:
① "语言在每一个位置的随机程度是一个常数"——所以温度 T 是全句一个值; 但真实文本里,"今天天气真___"几乎确定,"我喜欢___"高度不确定,两者需要完全不同的 T。
② "下一个词只取决于已经写下的前文"——而人写句子是先想好整体再落笔的。
③ 最根本的一条:"说得像"就足够了——它从不假设"说得对"
F 违背了哪个直觉 "同样的输入 + 同样的权重 = 同样的输出"在神经网络里不成立。 模型其实是确定性的——每一次给出的分布一模一样; 不一样的是那只抽签的手(第 4 节那 300 次重跑量的就是它)。
第二个:调温度不会让模型变聪明,它只改风格—— 但人们总是把它当成"智力旋钮"。
第三个:幻觉不能靠调参消掉——它是"目标是像不像、不是对不对"的直接后果
🎯 前后钩子

它接住了上一章的什么:预训练语言模型 (《预训练语言模型》)负责"把世界压成一份概率", 这一章负责"怎么从那份概率里说出一个句子"。

它给后面留了什么:下一章 《自编码器》 反过来问: 「什么重要」能不能不用人挑,让网络压缩着自己学出来(潜空间)。 而"说得对"、给出处,是更后面的 《RAG 检索增强》 的事。

一句话带走采样

模型每说一个字,都是在一个概率分布里挑一个,挑出来的就收不回去。
温度改分布的陡峭程度,top-k 砍固定数量的尾巴,top-p 按累积概率自适应地砍—— top-p 是更好的默认选择。
三个旋钮依次作用在同一个分布上;常见的组合是温度 + top-p,重复惩罚按需加; 而模型不变、参数不变时,结果的不一样只来自种子——这就是"同一份概率,不同的句子"。
幻觉是训练目标的结构性结果:解法是给它资料和工具,不是调温度。

9

拓展阅读

上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。

📄 这一章的说法从哪来

💻 工业界怎么写

∑ 更严格的形式