它每说一个字,都要重新算一遍"下一个字该是什么"。 而这个"该是什么"永远是一个概率分布,不是唯一答案。 怎么从这个分布里挑字,直接决定了模型看起来是聪明、是死板、还是胡说八道。
上一章 《预训练语言模型》 把整个语料压成了一份「下一个词的概率」——它给的从来不是一个答案,而是一份分布。
生成一句话的全过程就是一个 loop,每一轮都要把已经说过的话重新读一遍。
(这个"一个字一个字往下算"的过程,就叫
核心图 · 走一轮,看这个循环转到哪里去了
抽到一个不太好的词,它就会永久留在上下文里——后面的每个字都要接着它写。 模型无法"回退重写":它说「让我重新想一下」,也只是在那句话后面接着写,前面写下的字并没有被删。 这就是为什么一句错话会带来一长串崩坏——以及为什么"重复"会自我强化。
模型先给每个候选打一个原始分数(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),其他按需再加。
上面的沙盘只挑了一个字。真正的生成是把这件事重复几十遍: 每抽出一个字,就把它接到输入末尾,再拿新的前文重新算一遍分布。 下面这个"模型"不是真网络,是我们手写的一张表:39 个字,每个字后面接什么字、分数多少,都写在表里; 表里没写的组合一律给一个很低的分(这些很少用到的字,就叫长尾)。 喂给它的开头(提示词)是「开始」,每个字都按概率抽签选出来——这就是多项式采样。 真模型和它的差别只有一个:表里的分数是网络自己算出来的,抽签的机制完全一样。
图 · 那张手写表长什么样
互动 · 逐字采样生成器
每一步是怎么抽的(每一步都是一次真实的抽样)
抽签用的随机数从一个起点生成,这个起点叫种子:种子一样,抽出来的就一样。
拖滑块时用的就是同一颗种子,所以看到的变化全部来自参数本身;点「同一颗种子再跑一次」可以验证这一点。
面板上的熵衡量这个分布有多平:0 = 只有一个选项,1 = 所有选项一样多;
log P 是这整句被抽出来的概率取对数,越接近 0 越像模型最想说的话。
这是很多人对 LLM 最困惑的一点:
互动 · 用当前参数跑 300 次,看结果散成什么样
微图 · 同一份分布,抽四百次之后长什么样
互动 · 温度 → 多样性(每一档跑 100 次)
横轴是温度,每一档都把整套生成跑 100 遍,数出出现了多少种不同的句子; 橙点是当前温度。纵轴 0 ~ 1:1.0 = 每次都不一样 / 分布完全不犹豫。 分布越平,抽出来的越多样:虚线(分布的不确定性)涨上去,实线(结果的多样性)才跟着涨—— 多样性是分布形状的结果,不是可以直接设的一个参数。这条曲线固定 top-k 关闭、top-p = 1,只看温度。
前四行是四种采样策略(贪心、温度、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 好用的地方。
因为语言的确定性在不同位置差别极大。
"今天天气真___"——下一个字几乎确定是"好",分布极度尖锐(像上图那样的 T=0.6),此时只需要 1~2 个候选。
"我喜欢___"——下一个字可能是几百个词,分布非常平,需要几十个候选。
top-k 用固定的 k 硬套这两种情况;top-p 让候选集大小跟着分布形状走。
前面五节都在拖旋钮。这一节只问一件事:模型吐出来的那一串原始分数,到底是怎么变成一根根柱子的高度的? 为了看清每根柱子,这一节换成 8 个候选的小表。 整章的数学只有一个公式,它只做两步:先把分数拉开或压平,再让所有柱子加起来正好等于 1。 拖一下下面这个温度滑块,这一节的数学你就全看完了。
互动 · 同一组分数,拖温度看它被拉成什么形状
把温度拖到 0.1:最高那根柱子几乎顶满,分布变尖 = 更保守。
再拖到 2.0:高的柱子矮下去、矮的柱子长起来,一起向那条橙色虚线(完全均匀的高度)靠拢,分布变平 = 更随机。
注意上下两排的对照——上面的灰柱一根都没动,动的只有下面那排。
微型图解 · 核采样是怎么找到那个「刚刚够」的候选集
这一步就是第 5 节说的「top-p 用固定的阈值、砍出变动的候选个数」。 把上面的温度拖大,柱子变平——累积线爬得就慢,能留下来的字就变多。 同样一个阈值 p_top = 0.9,分布尖的时候只留两三个,分布平的时候要留五六个。
| 现象 | 成因 | 怎么缓解 |
|---|---|---|
| 复读机 "这个问题很好,这个问题很好,这个问题很好……" |
贪心解码 + 没有重复惩罚时,模型进入了自我强化的循环: 一旦重复了一次,前文里的重复模式会让"继续重复"成为概率最高的选择 | repetition penalty;改用 top-p 采样;不要用 beam search |
| 胡说八道 编造出不存在的论文、法条、API |
语言模型的目标是"说出像真的话",不是"说出真话"。 它没有任何机制去核对事实。这是训练目标层面的事情,不是 bug | RAG 检索增强(阶段 7);要求引用来源;降低温度没用(见《评测、基准与幻觉》那章的幻觉一节) |
| 过早收尾 回答刚开个头就输出 EOS(EOS 是表示"说完了"的结束记号) |
概率质量集中在 [EOS] 上。常见于训练数据里短回答偏多;提供模型的一方给「最多生成多少个词」(max_tokens)设的默认值太小,会让回答被截断(那是截断,不是 EOS) | 调整训练数据配比;调低 EOS 的 logit;明确要求"详细展开" |
互动 · 同一张手写表,温度两头各跑四遍
真正的解法只有三条:给它资料(RAG)、给它工具(搜索/计算器)、或者训练它说"我不知道"。
这些参数改变的是输出的风格,不是模型的能力。调温度不会让模型变聪明,只会让它更能说或更保守。 下面是每个旋钮最典型的翻车方式和对应的调法。
| 你怎么设 | 什么时候真的会痛 | 换成什么 |
|---|---|---|
| 温度太低(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 只在有唯一正确答案的任务上用。
这一章没有一条公式值得背,只有一个结论值得记住: 模型交给你的从来不是"答案",而是一整份概率分布。
互动 · 同一组权重,两个使用者
回到第 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,重复惩罚按需加;
而模型不变、参数不变时,结果的不一样只来自种子——这就是"同一份概率,不同的句子"。
幻觉是训练目标的结构性结果:解法是给它资料和工具,不是调温度。
上面讲的都是「够用」的版本。想往下挖,这里有三个入口—— 它们不是必修内容,是给想再往前走一步的读者准备的。