第 18 章 解码与生成策略

训练好的模型给出的是下一个 token 的概率分布,而"把分布变成一个具体的 token"这一步由解码策略决定。本章回答:温度、Top-K、Top-P 到底对分布做了什么、为什么 Top-P 的候选集大小会自适应、以及采样参数如何与业务指标(质量、多样性、吞吐、延迟)挂钩。

学习目标

读完本章,你应当能够:

  1. 写出自回归解码的形式化定义,辨析贪心与束搜索的差别及各自失败模式;
  2. 推导温度对 softmax 分布的尖锐化/平坦化效应,并给出 T → 0、T → ∞ 的极限行为;
  3. 说明 Top-K 的固有问题,以及 Top-P 如何用累积概率解决它;
  4. 完成 Top-P 的完整算例:确定核集合、核内重新归一化、计算单个 token 的最终概率;
  5. 辨析关于 Top-P 的三个常见误解;
  6. 解释 repetition/frequency/presence penalty 的差异,并简述 min-p 与典型采样;
  7. 说明为什么同一组采样参数在 batch 推理与流式推理下可能给出不同结果;
  8. 根据业务指标(质量/多样性/吞吐/延迟)选择解码配置。

前置知识检查

你需要知道在哪一节补齐
Softmax 的定义与数值稳定性见第 5 章数值计算、第 9 章 Softmax 回归
交叉熵、困惑度与分布尖锐程度见第 4 章信息论、第 16 章 16.2.3
自回归分解 P(x) = Π_t P(x_t|x_<t)见第 16 章 16.1 节
因果掩码、KV Cache、prefill/decode见第 15 章;第 19 章
浮点加法不满足结合律见第 5 章 IEEE 754 部分

18.1 自回归解码的形式化

18.1.1 问题定义

给定提示词 x = (x₁, …, x_n) 和已经生成的 t − 1 个 token,模型输出的是词表上的一个概率分布:

1
2
logits:   z = f_θ(x_<t) ∈ R^{|V|}
分布:     P(x_t = v | x_<t) = softmax(z)_v = exp(z_v) / Σ_{u∈V} exp(z_u)

解码(decoding)就是从这个分布中选出一个 token 的规则,然后把它接回输入、重复上述过程,直到生成结束符或达到长度上限:

1
2
3
解码策略 π_sample(· | z)  ──►  采样 x_t ∼ π_sample
                │
                └─► x_<t+1 = (x_<t, x_t)  ──►  下一步

注意一个关键事实:模型给出的永远是分布,不是 token。“取 argmax"只是众多选择规则中最简单的一个,它并不比采样"更正确”——它只是把随机性完全去掉了。

18.1.2 贪心解码

贪心解码(greedy decoding) 每步取概率最大的 token:

1
x_t = argmax_v P(v | x_<t)

优点:确定、快(无需额外的概率运算)、易复现。缺点有三类:

  1. 局部最优不等于全局最优。逐步选最大概率,不保证整条序列的联合概率 Π_t P(x_t|x_<t) 最大。举个直观例子:第一步的次优选择可能打开一条"总体更好"的路径,而贪心永远不会走。
  2. 容易重复与退化。当模型对某个短语形成自我强化(生成的词提高下一个相同词的概率)时,会产生"循环复读"。这是贪心最典型的失败形态。
  3. 缺乏多样性。同一个提示词永远得到同一条输出,无法用于需要多个候选的场景(如生成 5 个方案让用户挑)。

18.1.3 束搜索

束搜索(beam search) 保留 B 条(beam width)候选序列,每步对所有候选扩展再剪枝,保留累计对数概率最高的 B 条:

1
2
累计得分  score(y_{1..t}) = Σ_{s=1}^{t} log P(y_s | y_<s)
每步      从 B × |V| 个扩展中取 score 最高的 B 条

用对数概率而不是概率,是因为 Π P 在长序列下会数值下溢,而 Σ log P 稳定。

长度偏置与长度归一化:Σ log P 是负值累加,序列越长得分越低,因此原始束搜索系统性地偏好短序列。修正:

1
score_norm(y) = (1/|y|^α) · Σ_{s} log P(y_s|y_<s)          α 常取 0.6~1.0

束搜索的失败模式(在开放式生成中比贪心更明显):

失败模式表现成因
重复同义句反复循环高分路径被"安全但空洞"的模板占据
缺乏多样性B 条候选高度相似束内候选共享大量前缀,互相之间的差异被剪枝抹平
空洞化输出"正确但没信息量"高概率 ≠ 高质量;模型对高频套话赋予高概率
长度畸变明显偏短或偏长长度归一化参数选择不当
成本计算量约为贪心的 B 倍每步要评估 `B ×

结论:束搜索适合有明确正确答案的任务(机器翻译、语音识别、抽取式摘要),因为这些任务的输出空间受限、P(y|x) 与任务质量相关性高。它不适合开放式创作与对话,因为此时"高概率"与"好"之间有系统性偏差,束搜索会把偏差放大——它专门在优化那个有偏的目标。

重要

束搜索优化的目标是"序列的模型概率最大",而我们要的是"人觉得好"。这两者在受限任务上高度一致,在开放任务上系统性背离。这是所有开放域生成任务最终都转向采样的根本原因。


18.2 温度

18.2.1 数学效果

温度 T 的机制极其简单:把 logits 除以 T 再 softmax。

1
P_T(v | x_<t) = exp(z_v / T) / Σ_u exp(z_u / T)

要看清它做了什么,写出两个 token 的概率比:

1
P_T(a) / P_T(b) = exp(z_a/T) / exp(z_b/T) = exp( (z_a − z_b) / T )

原始 logit 差 Δ = z_a − z_b 被放大或缩小了 1/T 倍。于是:

  • T < 1:差值被放大 → 大者更大、小者更小 → 分布更尖锐(sharpened),趋近 argmax;
  • T > 1:差值被缩小 → 概率被拉向均匀 → 分布更平坦(flattened),多样性上升;
  • T = 1:不改变任何东西,就是模型原始分布。

极限行为:

1
2
T → 0⁺:   z/T 的差异趋于无穷 ⟹ P(argmax) → 1,其余 → 0   ⟹ 退化为贪心解码
T → ∞ :   z/T → 0 对所有 token ⟹ 分布 → 均匀分布 1/|V|   ⟹ 完全随机、输出无意义

18.2.2 数值算例

取三个 token 的 logits z = [2.0, 1.0, 0.0]:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
T = 1.0:
  exp(2) = 7.389,  exp(1) = 2.718,  exp(0) = 1.000
  和 = 11.107
  P = [0.665, 0.245, 0.090]

T = 0.5:
  z/T = [4.0, 2.0, 0.0]
  exp   = [54.598, 7.389, 1.000],  和 = 62.987
  P = [0.867, 0.117, 0.016]          ← 更尖锐:第一名从 0.665 升到 0.867

T = 2.0:
  z/T = [1.0, 0.5, 0.0]
  exp   = [2.718, 1.648, 1.000],   和 = 5.367
  P = [0.506, 0.307, 0.186]          ← 更平坦:第三名从 0.090 升到 0.186

验证比值公式:T = 0.5 时 P(a)/P(b) = 54.598/7.389 = 7.389 = exp(1.0/0.5) ✓,正是原始 logit 差 1.0 被放大 2 倍。

温度分布形态适用场景风险
0 ~ 0.2接近贪心代码、数学、结构化抽取重复、无多样性
0.5 ~ 0.8略尖锐事实问答、客服偶发重复
0.9 ~ 1.1原始分布通用对话幻觉风险上升
1.2 ~ 1.5明显平坦头脑风暴、起名、创意写作语法崩坏、跑题

警告

温度不是"质量旋钮"。 调低温度不会让模型变得更聪明,只是让它更倾向于选择同一批高概率 token;调高温度也不会让它更有创意,只会让它更频繁地选到低质量 token。真正需要"更好"应该去改模型或提示词,而不是调温度。


18.3 Top-K

Top-K 采样 的做法是:按概率降序排序,只保留前 K 个 token,在核内重新归一化后采样。

1
2
S_K = {v₁, v₂, …, v_K}                       (按 P 降序的前 K 个)
P'(v) = P(v) / Σ_{u∈S_K} P(u)   若 v ∈ S_K;否则 0

为什么要重新归一化? 因为截断之后保留下来的概率之和不再等于 1,必须除掉这个和才能得到一个合法的分布,否则采样器的累积分布函数对不上,采出来的 token 频率会与设定不符。

Top-K 的固有问题:候选集大小是固定的,与分布形状无关。 这带来两个对称的麻烦:

  • 分布很尖锐时(模型非常确定):K = 50 会把排名第 10~50 的那些概率极低(如 10⁻⁶)的 token 也放进候选池,平白引入噪声,偶尔采出一个荒谬的词;
  • 分布很平坦时(模型确实犹豫,例如在"是/否"之间):K = 50 又可能把真正合理的候选(排名第 60、第 70 的同义表达)全部砍掉,候选项被人为削窄,反而降低了多样性。

换句话说,K 是一个"应该随分布自适应"的量,却被人为写死成了常数。Top-P 正是为了解决这一点。

注意

注意区分两件事:贪心是 Top-K 在 K = 1 时的特例(且无需归一化),温度与 Top-K 是正交的——温度先改变分布形状,Top-K 再在其上截断,顺序不能颠倒(见 18.7 节的实现顺序)。


18.4 Top-P(核采样)

18.4.1 定义

核采样(nucleus sampling,Top-P) 的定义是:把词表按概率从大到小排序,取最小的前缀集合 V^(p),使其累积概率不低于 p:

1
2
3
排序后:  P(v₁) ≥ P(v₂) ≥ … ≥ P(v_|V|)

V^(p) = 最小的 {v₁, …, v_m}  使得  Σ_{i=1}^{m} P(v_i) ≥ p

然后只在核内重新归一化再采样:

1
P'(v) = P(v) / Σ_{u∈V^(p)} P(u)    若 v ∈ V^(p);否则 0

核心价值:候选集大小自适应。 m 不是常数,它由分布形状决定:

  • 分布尖锐(模型确定):第一个 token 的概率可能就是 0.95,m = 1,等价于贪心;
  • 分布平坦(模型犹豫):可能要把前 40 个 token 加起来才够 0.9,m = 40,保留丰富的候选。

这正是 Top-K 想做到而做不到的事。p 的语义是"保留多少概率质量",这是一个与分布形状无关的旋钮,因此它比 K 稳定得多——换模型、换任务时通常不需要重新调。

18.4.2 完整算例(【09-04 选择 Q3】)

题目:Top-P 采样中 p = 0.85。商品概率分布 A(0.4), B(0.3), C(0.2), D(0.1)。求商品 A 被推荐的概率(保留 3 位小数)。

第一步:降序排列。 分布本身已是降序:A(0.4) > B(0.3) > C(0.2) > D(0.1)。

第二步:逐步累加,找最小前缀使累积概率 ≥ p。

1
2
3
4
5
前缀 {A}          累积 = 0.4              < 0.85   不够
前缀 {A, B}       累积 = 0.4 + 0.3 = 0.7  < 0.85   不够
前缀 {A, B, C}    累积 = 0.7 + 0.2 = 0.9  ≥ 0.85   ✓ 停止

核集合 V^(0.85) = {A, B, C}     D 被截断

第三步:核内重新归一化。

1
2
3
4
5
6
7
核内概率和 = 0.9

P'(A) = 0.4 / 0.9 = 0.4444… ≈ 0.444
P'(B) = 0.3 / 0.9 = 0.3333… ≈ 0.333
P'(C) = 0.2 / 0.9 = 0.2222… ≈ 0.222
P'(D) = 0
验算:0.444 + 0.333 + 0.222 = 0.999 ≈ 1  ✓

答案:P(A) ≈ 0.444。

干扰项归因:

干扰项数值错在哪
A0.400直接用原始概率,忘了核内重新归一化
B0.5710.4/0.7——误把核集合当成 {A, B},即累加判定提前一步停止
C0.471保留了 D 又减掉某个量,不符合 Top-P 的截断规则
D0.444正确

18.4.3 三个常见误解

误解一:“Top-P 是抛弃概率低于 p 的 token。” 错。这是把累积概率当成了单个 token 的概率阈值。若按误解一操作,p = 0.9 时几乎所有 token 的概率都低于 0.9,会被全部抛弃,留下的是空集或唯一一个 token。Top-P 的 p 约束的是前缀累积和,不是个体概率。(这正是 【09-02 选择 Q5】 的选项 A。)

误解二:“Top-P 是取前 p 个 token。”(或"取前 p × |V| 个") 错。核大小 m 不是由 p 乘以词表大小决定的,而是由分布形状决定的:同样的 p = 0.9,尖锐分布下 m 可能等于 1,平坦分布下 m 可能等于 50。把 p 当比例去数 token 个数,是与定义无关的另一种算法。(这是选项 C 描述的 Top-K,K = 90 之类写法。)

误解三:“Top-K 是 Top-P 的特例 / 两者可以互相换算。” 错。两者截断的依据不同:Top-K 按排名截断(候选数固定),Top-P 按概率质量截断(候选数可变)。给定 K 无法推出等价的 p,反之亦然——因为对应关系依赖当前分布。它们可以叠加使用(先 Top-K 再 Top-P),但那是两个独立约束的交集,不是换算。

此外 Top-P 不是"带随机性的贪心"(【09-02 选择 Q5】 选项 D):贪心必然取 argmax,而核内在归一化后仍按概率随机抽取,只是候选池被约束了,两者在分布尖锐时行为相似但机制不同。

提示

记忆锚点:Top-P 管"保留多少概率质量",Top-K 管"保留多少个候选",温度管"分布多尖锐"。 三者作用于分布的不同属性,因此可以叠加。


18.5 惩罚项、min-p 与典型采样

18.5.1 三种惩罚的差异

三种惩罚都在修改 logits,但惩罚的对象与次数不同。设 c(v) 为 token v 在本轮上下文中已出现的次数:

惩罚公式(加在 logit 上)惩罚对象是否随出现次数累加
重复惩罚(repetition penalty)z_v ← z_v / θ(z_v > 0)或 z_v × θ(z_v < 0)所有已出现过的 token否,出现 1 次与 10 次惩罚相同
频率惩罚(frequency penalty)z_v ← z_v − α·c(v)所有已出现过的 token是,线性累加
存在惩罚(presence penalty)z_v ← z_v − β·𝟙[c(v) > 0]所有已出现过的 token否,只看"是否出现过"

三者的直觉差异:

  • 重复惩罚直接缩放 logit,对高 logit 与低 logit 的影响不对称(这也是为什么实现里要按正负号分别处理),力度较"硬",容易损伤正常重复(如中文里的"的"、代码里的括号);
  • 频率惩罚按出现次数线性扣分,出现越多次越难再出现,适合压制"复读机",但可能把必要的高频词(标点、连词)也压掉;
  • 存在惩罚只要你出现过就扣一次固定分,鼓励话题/词汇的覆盖广度,但不阻止一个词重复两三次。

警告

惩罚是"钝器":它们不区分"合理的必要重复"与"病态复读"。对代码(括号、缩进、变量名)与中文(虚词)使用较强惩罚,会明显损伤输出质量。更好的替代手段通常是降低温度 + 用 Top-P 收窄候选池 + 在提示词层面禁止重复。

18.5.2 min-p

min-p 用"相对于最大概率的比例"做阈值,而不是绝对概率或累积概率:

1
S = { v : P(v) ≥ p_min · max_u P(u) }

它的自适应机制与 Top-P 相反但互补:分布尖锐时阈值 p_min·P_max 也高,只留下极少数强候选;分布平坦时阈值低,保留大量候选。与 Top-P 相比,min-p 在高温下更稳定(不会因分布平坦而让核无限膨胀),因此常被推荐作为 Top-P 的替代。

18.5.3 典型采样

典型采样(typical sampling) 依信息论选择候选:保留那些"信息量接近期望信息量"的 token。定义每个 token 的惊异度(surprise) −log P(v),并令

1
2
H = Σ_v P(v)·(−log P(v))            条件熵(期望惊异度)
S = { v : | −log P(v) − H | ≤ δ }

直觉:概率过高的 token(惊异度远低于平均)是"陈词滥调",概率过低的是"胡说八道",两者都排除,只保留"信息量适中"的候选。它追求的是"既不无聊也不离谱",在某些创意任务上比 Top-P 更自然,但实现与调参更复杂。

18.5.4 多 token 与推测解码(速览)

前面所有策略都是逐 token 的。另有一类方法一次考虑多个位置:

  • 对比搜索(contrastive search):在"模型概率高"与"与前文表示不相似"之间做联合权衡,显式抑制退化重复;
  • 推测解码(speculative decoding):用一个小的草稿模型连续猜若干 token,再用大模型一次前向并行验证,接受的 token 直接采用。它不改变输出分布(在拒绝采样框架下与原模型等价),只是把"逐 token 的访存受限 decode"变成"一次多 token 验证",是纯加速手段。

18.6 采样与确定性

18.6.1 为什么 batch 推理与流式推理可能给出不同结果

这是工程中最让人困惑的现象之一:同一提示词、同样的采样参数、同一个模型,单条流式调用与批量(batch)调用得到的输出可能不同。原因有四层:

第一层,浮点加法不满足结合律。 第 5 章讲过:(a + b) + c ≠ a + (b + c) 在有限精度下可能成立。批处理时,不同请求的矩阵乘法被打包成大矩阵(或按 batch 分块的 GEMM),累加顺序、分块切分(tiling)方式与单条请求不同,得到的 logits 在末位存在 1e-7 量级的差异。

第二层,末位差异会被采样放大。 logits 的微小差别经 softmax 后仍微小,但采样是阈值化操作:累积分布函数在某个 u ∼ U(0,1) 处的落点恰好靠近两个 token 的概率分界时,1e-7 的扰动就足以让选中结果从 token A 翻到 token B。而一旦某个 token 不同,后续整条序列都不同——这是确定性混沌(chaotic dependence on initial conditions):微小输入差异被自回归结构指数放大。

第三层,随机数流的消耗顺序不同。 流式生成与批处理生成对随机数生成器(RNG)的调用时机与次数不同:批处理可能为每个请求维护独立 RNG 状态,也可能共享一个流;流式生成中"何时抽取下一个随机数"还取决于 token 何时被 flush。若随机数流不同,即使 logits 完全一致,采样结果也不同。

第四层,数值实现的差异。 不同的算子融合、不同的 kernel 实现(如不同版本的 FlashAttention)、不同的精度策略(bf16 vs fp16 的 KV Cache)都会引入各自量级的舍入差异。

推论:“可复现"在 LLM 推理中是一个需要主动定义的目标。要严格复现,需要同时固定:模型权重版本、推理引擎与 kernel 版本、batch 组成与排序、序列长度填充策略、精度配置、随机种子与 RNG 状态。缺任何一项,复现都可能失败。

18.6.2 随机种子与工程实践

1
2
3
4
可复现性的层次:
  L1  固定种子 + 单条请求 + 同一引擎版本        →  通常可复现,但不是跨版本保证
  L2  batch 内固定位置 + 固定 batch 组成        →  同 batch 组成下可复现
  L3  任意 batch 组成                           →  一般不保证(受浮点与 RNG 流影响)

工程建议:

  1. 评测阶段:报告结果时必须写明 temperature / top_p / top_k / seed / 引擎版本 / batch 配置。温度接近 0(或 do_sample=False)时可以用单测代替多样本评测,因为此时输出几乎确定。
  2. 需要多样性时(如 pass@k 代码评测):必须真正采样并报告 k 与温度;用贪心跑 k 次相当于只跑一次。
  3. 对可复现性有硬要求的场景(回归测试、审计):固定 batch 组成,或直接采用贪心/低温并把种子与引擎版本写进测试基线。
  4. 不要用"两次调用结果相同"来验证采样实现正确——温度大于 0 时不同才是正常的。

18.7 解码策略与业务指标的权衡

18.7.1 实现顺序

所有主流推理框架的参数应用顺序是固定的,顺序错了会得到与预期不同的分布:

1
2
3
4
5
6
7
logits
  │
  ├─ 1. 惩罚项:repetition / frequency / presence penalty   (直接改 logits)
  ├─ 2. 温度: z ← z / T                                     (改分布尖锐度)
  ├─ 3. 截断: Top-K 与/或 Top-P(min-p)                    (缩小候选集)
  ├─ 4. 重新归一化: 在剩余候选集上 softmax                    (恢复合法分布)
  └─ 5. 采样: 从归一化分布中抽取(或 argmax 若 T → 0)

关键点:截断必须在温度之后,归一化必须在截断之后。若先截断再调温度,被截掉的候选集是按旧分布选的,可能与新分布下的合理候选不一致;若忘了归一化,采样结果会偏向候选池中概率较大者之外的行为(严格说会采出非法概率质量)。

18.7.2 指标权衡

业务目标主要指标推荐配置原因
事实问答 / 客服正确率、幻觉率T = 0~0.3,Top-P = 0.8~0.9需要稳定、可复现、少发散
代码生成单元测试通过率T = 0.2~0.6,Top-P = 0.95,配合 pass@k需要少量多样性来探索不同实现
结构化抽取 / JSON格式合法率T = 0(或语法约束解码,见 18.8 节)格式正确优先于措辞
创意写作 / 起名多样性(去重率、人评)T = 0.9~1.2,Top-P = 0.95~0.98需要跳出高概率套话
大批量离线生成吞吐(token/s)较大 batch + Top-P(避免 Top-K 的排序开销)吞吐由 batch 效率决定,与解码策略弱相关
交互式对话首 token 延迟(TTFT)、TPOT小 batch + 前缀缓存 + 推测解码延迟是主约束(详见第 19 章)

三组内在矛盾:

  1. 质量 vs 多样性:降低温度提升单次输出的稳定性,但降低了多次采样的覆盖度。评测必须区分"单次最好"与”k 次中至少一次最好"(pass@k)。
  2. 确定性 vs 可用性:贪心最可复现,但在开放任务上质量最差(重复、空洞)。工程上常用"低温度 + Top-P"作为折中:既不引入太多随机性,又保留了跳出重复循环的可能。
  3. 吞吐 vs 延迟:大 batch 提升吞吐但抬高单请求延迟。解码策略本身对两者的影响远小于 batching 策略与 KV Cache 管理——不要指望调 Top-P 来改善吞吐。

重要

解码参数调优的正确顺序是:先固定一个保守配置(如 T = 0.2, Top-P = 0.9)跑通基线,再用你真正关心的业务指标(而不是"看起来好不好")做小范围网格搜索。评测集必须包含"需要确定性"和"需要多样性"两类样本,否则会调出一个只在一类上好的配置。


18.8 结构化输出与约束解码

业务系统往往要求输出是合法的 JSON、SQL、函数调用参数。纯靠提示词约束的失败率不可忽略(少一个括号、多一个逗号、字段名拼错),而下游解析一失败整个链路就断。

约束解码(constrained decoding)/ 语法约束解码的做法是:把目标格式(JSON Schema、正则、上下文无关文法 CFG)编译成一个有限状态自动机(FSM),在每个解码步,只允许那些"能让当前前缀继续成为合法输出"的 token:

1
2
3
4
5
已生成前缀  ──►  FSM 当前状态  ──►  合法后继 token 集合 A(状态)
                                      │
                        logits_v ← −∞  对所有 v ∉ A(状态)
                                      │
                            在 A(状态) 内重新归一化并采样

关键点:

  1. 屏蔽(masking)而非后处理。屏蔽 logits 保证了输出在每一步都合法,因此永远不会产生需要修复的非法输出;后处理修复(正则修补、重新请求)则会浪费 token 和时间,且修补结果未必语义正确。
  2. 格式合法 ≠ 内容正确。约束解码只保证结构,不保证字段值正确。它消除了"解析失败"这一类错误,不能消除"解析成功但答案是错的"。
  3. 与采样参数兼容。屏蔽发生在截断与归一化之前,Top-P/温度照常生效,只是在合法候选集内起作用。因此低温度仍然能让字段值更稳定。
  4. 代价。构建 FSM 需要一次性成本;某些文法的状态空间很大,会让每步的候选集计算变重。工程上通常先做"预编译 + 状态缓存"。

本章小结

关键结论一句话表述常见错误
解码的本质模型给分布,解码策略从分布中选 token以为模型"输出 token"
贪心每步 argmax,确定但易重复、无多样性以为贪心等价于全局最优
束搜索保留 B 条高概率前缀,需长度归一化在开放创作中使用束搜索
温度z/T 再 softmax,等价于把 logit 差放大 1/T 倍把温度当质量旋钮
Top-K候选数固定为 K,不随分布自适应以为 Top-K 能代替 Top-P
Top-P最小前缀使累积概率 ≥ p,核内重新归一化p 当成单 token 阈值或 token 个数
惩罚项重复/频率/存在惩罚分别看"是否出现"与"出现几次"对代码与中文使用强惩罚
可复现性batch 与流式的浮点累加顺序、RNG 流不同以为同种子必然同输出
应用顺序惩罚 → 温度 → 截断 → 归一化 → 采样先截断再调温度
约束解码用 FSM 屏蔽非法 token,保证格式合法以为格式合法就等于内容正确

自测题

Q1(计算)某模型在一步输出的分布为 A(0.45), B(0.25), C(0.15), D(0.10), E(0.05)。取 Top-P 的 p = 0.8,求核集合以及归一化后 P(A) 与 P(C)。若再叠加 Top-K 的 K = 2,结果如何?

答案与解析

只做 Top-P(p = 0.8):

1
2
3
4
5
6
7
8
9
{A}        累积 = 0.45              < 0.8
{A,B}      累积 = 0.45+0.25 = 0.70  < 0.8
{A,B,C}    累积 = 0.70+0.15 = 0.85  ≥ 0.8   ✓ 停止

核集合 = {A, B, C},核内概率和 = 0.85

P'(A) = 0.45 / 0.85 ≈ 0.529
P'(C) = 0.15 / 0.85 ≈ 0.176
验算:0.529 + 0.294 + 0.176 = 0.999 ≈ 1  ✓

叠加 Top-K = 2:

1
2
3
4
5
6
Top-K 先砍到 {A, B},再在其上做 Top-P:
  累积 0.45 < 0.8 → 累积 0.70 < 0.8,但候选已耗尽
  ⟹ 核集合 = {A, B}(Top-P 在候选耗尽时不再强制要求达到 p)
  核内概率和 = 0.70
  P'(A) = 0.45/0.70 ≈ 0.643
  P'(C) = 0

要点:p 是"至少保留这么多概率质量“的下界要求,但当候选池被更严格的约束(Top-K、K < |V|)耗尽后,Top-P 无法再扩大候选,只能接受更小的累积和。两者叠加取交集,不是取并集——这是"Top-K 与 Top-P 谁更严格谁生效"的实际含义。

Q2(推导)证明 P_T(a)/P_T(b) = exp((z_a − z_b)/T),并据此说明为什么温度对已经非常尖锐的分布影响很小,却对接近均匀的分布影响很大。

答案与解析
1
2
3
4
P_T(a)/P_T(b)
  = [ exp(z_a/T) / Σ_u exp(z_u/T) ] / [ exp(z_b/T) / Σ_u exp(z_u/T) ]
  = exp(z_a/T) / exp(z_b/T)                              (分母相同,直接约掉)
  = exp( (z_a − z_b) / T )

证毕。注意分母 Σ_u exp(z_u/T) 是同一个常数,所以任何两个 token 的概率比只取决于它们的 logit 差。

为什么尖锐分布影响小:设 a 是最大值、b 是次大值,logit 差 Δ 很大(如 Δ = 8)。T = 1 → 0.5 时比值从 exp(8) ≈ 2981 变成 exp(16) ≈ 8.9 × 10⁶,看似变化巨大,但两个比值都远大于 1,P(a) 在两个温度下都已经接近 1(因为其余 token 的概率之和本来就极小)。概率是归一化的量,比值在"已经一边倒"的区域继续放大,对最终分布形状的影响被归一化吸收掉了。

为什么平坦分布影响大:设 Δ = 0.5,T = 1 时比值 exp(0.5) ≈ 1.65,两个 token 概率接近;T = 0.5 时比值变成 exp(1) ≈ 2.72,第一名明显占优;T = 2 时比值 exp(0.25) ≈ 1.28,几乎均匀。在"势均力敌"的区域,任何对差值的缩放都会显著改变谁占优,因此分布形状变化剧烈。

工程推论:模型越犹豫(分布越平),温度的影响越大。这就是为什么低温能让模型在"模棱两可"的步骤上变得果断,也解释了为什么在创意任务上调高温会迅速失控——那些步骤本来就在多个候选间势均力敌。

Q3(辨析)判断:“为了保证输出可复现,我把 seed = 42 固定在配置里,并且用温度 0.7 采样,两次调用应该得到完全相同的输出。”

答案与解析

不成立。 固定种子只是必要条件,不是充分条件。影响结果的还有:

  1. batch 组成:不同 batch 下的 GEMM 分块与累加顺序不同,logits 末位有 1e-7 量级差异;这些差异在采样阈值附近会被放大成不同 token,随后自回归放大成完全不同的序列。
  2. RNG 流的消耗顺序:并发请求共享或分别持有 RNG 状态、以及 token 何时被 flush,都会改变"第 t 步用的是随机数流里的第几个数”。
  3. 引擎与 kernel 版本:不同的注意力实现(FlashAttention 版本、是否融合算子)、不同的精度配置(KV Cache 用 bf16 还是 fp16)都会改变数值。
  4. 序列长度与填充:padding 策略、KV Cache 的分页布局会影响计算路径。

结论:“温度 > 0 时同一提示词两次调用结果相同"是错误预期;即便同种子,也必须固定 batch 组成与引擎版本才可能复现。若业务硬要求可复现(回归测试、审计),应当使用贪心或极低温度,并把模型版本、引擎版本、batch 配置一并写入基线。

Q4(辨析)某团队的客服机器人在 Top-K = 50 下偶发输出荒谬的词,于是把 K 降到 5;结果回答变得极其模板化、复读严重。请解释这两个现象的共同根因,并给出正确的调整方向。

答案与解析

共同根因是用固定候选数去适配一个形状可变的分布(18.3 节):

  • K = 50 时:当模型非常确定(分布尖锐,第一名概率 0.98),第 10~50 名的 token 概率低到 10⁻⁶ 量级,却仍被放进候选池。采到它们的概率虽小,但在长序列 + 大量请求下会偶发出现,表现为荒谬的词。
  • K = 5 时:当模型确实犹豫(例如需要在几个同义表达间选择),合理候选可能排在第 6~20 名,被 K 硬砍掉。可选项变窄,模型只能在少数几个高概率套话里选,于是输出变得模板化并容易进入重复循环。

两件事是同一个设计缺陷的两种表现:候选集大小应当随分布自适应。

正确方向:

  1. 把截断从 Top-K 改为(或叠加)Top-P,例如 p = 0.9:分布尖锐时核自动缩到 1~2 个 token(不引入噪声),分布平坦时自动扩展到几十个(保留多样性)。若担心高温下核无限膨胀,可叠加 min-p(18.5.2 节)。
  2. 温度单独调整,与截断解耦:客服场景建议 T = 0.2~0.5。
  3. 若仍有复读,优先用较弱的频率惩罚(而不是强重复惩罚),或在提示词里明确要求不重复。
  4. 用真实评测集验证:指标应包含"荒谬输出率"与"输出多样性"两项,否则只是把问题从一端推到另一端。

Q5(辨析)为什么束搜索在机器翻译上表现良好,却在开放式对话中常被采样策略击败?

答案与解析

因为两者优化的目标与"好"的关系不同。

束搜索优化的是序列的模型对数概率 Σ_t log P(y_t|y_<t)(带长度归一化)。这个目标在受限任务上是任务质量的好代理:机器翻译的参考译文空间狭窄,P(y|x) 高的译文大概率也准确、流畅,而且长度与内容由源句决定,长度偏置可控。

在开放式对话/创作中,“模型概率高"与"人觉得好"系统性背离:

  1. 高频套话概率高。“综上所述"“希望对你有帮助"这类句子在预训练语料中出现极频繁,模型给它们极高概率,但它们几乎不携带信息。束搜索专门在最大化概率,因此会主动选择这些空洞表达。
  2. 缺乏多样性。B 条束共享前缀、互相剪枝,最终候选彼此高度相似。对话场景下用户往往希望得到几个不同的思路。
  3. 退化循环。重复的短语会自我强化(生成一次后,下一个相同 token 的概率被抬高),束搜索为了高分而走进循环,甚至比贪心更严重。
  4. 长度畸变:Σ log P 累加负值,即使有 1/|y|^α 归一化,α 的选择也会系统性地影响长度,而对话的"合适长度"无法预先确定。

因此:任务输出空间受限且有一个明确正确答案时用束搜索;开放生成、需要多样性与信息量时用"温度 + Top-P"采样。这不是束搜索"变差了”,而是它的目标函数在开放域不再与人的偏好对齐。

Q6(设计)某系统要求模型输出固定 schema 的 JSON,用于下游自动处理。团队目前的做法是"提示词里给 schema 示例 + 解析失败就重试”。请评估这个做法,并说明约束解码能改善什么、不能改善什么。

答案与解析

当前做法的问题:

  1. 浪费。解析失败意味着整次生成(输入 + 输出 token)全部作废,重试的期望成本是 1/(1−p_fail) 倍,p_fail 越高浪费越严重。
  2. 无法保证收敛。重试 n 次仍失败的概率是 p_fail^n,对高要求链路不构成保证;且失败时上游得不到结构化信号,只能抛异常。
  3. 不可控的格式漂移。用采样参数降低 p_fail 会同时牺牲内容质量;而且模型可能"学会"简化 schema(少输出可选字段)来规避错误。

约束解码能改善的:把 JSON Schema 编译成 FSM,每步屏蔽掉会破坏语法合法性的 token,并只把合法后继加入候选集。于是:

  • 输出每一步都合法,解析失败率降为 0(针对语法层面);
  • 采样参数仍生效,只是在合法候选内起作用,温度低时字段值依然稳定;
  • 消除重试带来的成本与延迟抖动。

约束解码不能改善的:

  1. 内容正确性。格式合法 ≠ 值正确。“故障等级"字段可以是合法枚举值 "P1" 而实际应为 "P2"。这类错误需要靠检索证据、字段级校验或后处理断言来兜。
  2. 语义完整性。字段齐全不代表理解正确,模型可能把所有值都填成默认项。
  3. 文法的表达能力。极复杂的约束(跨字段依赖、数值范围)难以完全由上下文无关文法表达,超出部分仍需后处理校验。

正确设计是分层防御:约束解码保语法 → 字段级规则校验保取值合法(范围、枚举、交叉字段一致性)→ 业务规则校验保语义(与检索证据比对)→ 最后才落库。任一层都无法单独成为保证。

与真题的关联

真题考查角度对应本章小节
【09-02 选择 Q5】 Top-P 的定义辨析p 是累积概率下界,不是单 token 阈值;与 Top-K、随机贪心的区别18.4.1、18.4.3
【09-04 选择 Q3】 Top-P 截断后的概率计算降序累加定核 → 核内重新归一化 → P(A) = 0.4/0.9 ≈ 0.44418.4.2
【09-04 选择 Q2】(关联)Softmax 与数值稳定温度 z/T 再 softmax 的数值实现18.2
【09-09 选择 Q10】(关联)评测与指标选择pass@k 需要真正采样,标注必须含温度与种子18.6.2、18.7.2

延伸阅读

  • Ari Holtzman 等,《The Curious Case of Neural Text Degeneration》,2020——核采样(Top-P)的原始论文,同时给出贪心/束搜索退化的实验证据。
  • Angela Fan 等,《Hierarchical Neural Story Generation》,2018——Top-K 采样的早期实践。
  • Tri Dao 等,《FlashAttention》系列——影响 logits 数值路径的底层实现(见第 19 章)。
  • Nisan Stiennon、John Schulman 等相关工作,以及 min-p 采样:Minh Nguyen 等,《Min-p Sampling for Creative and Coherent LLM Outputs》,2024。
  • Clara Meister 等,《Typical Decoding for Natural Language Generation》,2022。
  • Yaniv Leviathan 等,《Fast Inference from Transformers via Speculative Decoding》,2023。
  • Brandon T. Willard、Rémi Louf,《Efficient Guided Generation for Large Language Models》,2023(outlines 的 FSM 约束解码)。