第 18 章 解码与生成策略
第 18 章 解码与生成策略
训练好的模型给出的是下一个 token 的概率分布,而"把分布变成一个具体的 token"这一步由解码策略决定。本章回答:温度、Top-K、Top-P 到底对分布做了什么、为什么 Top-P 的候选集大小会自适应、以及采样参数如何与业务指标(质量、多样性、吞吐、延迟)挂钩。
学习目标
读完本章,你应当能够:
- 写出自回归解码的形式化定义,辨析贪心与束搜索的差别及各自失败模式;
- 推导温度对 softmax 分布的尖锐化/平坦化效应,并给出
T → 0、T → ∞的极限行为; - 说明 Top-K 的固有问题,以及 Top-P 如何用累积概率解决它;
- 完成 Top-P 的完整算例:确定核集合、核内重新归一化、计算单个 token 的最终概率;
- 辨析关于 Top-P 的三个常见误解;
- 解释 repetition/frequency/presence penalty 的差异,并简述 min-p 与典型采样;
- 说明为什么同一组采样参数在 batch 推理与流式推理下可能给出不同结果;
- 根据业务指标(质量/多样性/吞吐/延迟)选择解码配置。
前置知识检查
| 你需要知道 | 在哪一节补齐 |
|---|---|
| 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,模型输出的是词表上的一个概率分布:
| |
解码(decoding)就是从这个分布中选出一个 token 的规则,然后把它接回输入、重复上述过程,直到生成结束符或达到长度上限:
| |
注意一个关键事实:模型给出的永远是分布,不是 token。“取 argmax"只是众多选择规则中最简单的一个,它并不比采样"更正确”——它只是把随机性完全去掉了。
18.1.2 贪心解码
贪心解码(greedy decoding) 每步取概率最大的 token:
| |
优点:确定、快(无需额外的概率运算)、易复现。缺点有三类:
- 局部最优不等于全局最优。逐步选最大概率,不保证整条序列的联合概率
Π_t P(x_t|x_<t)最大。举个直观例子:第一步的次优选择可能打开一条"总体更好"的路径,而贪心永远不会走。 - 容易重复与退化。当模型对某个短语形成自我强化(生成的词提高下一个相同词的概率)时,会产生"循环复读"。这是贪心最典型的失败形态。
- 缺乏多样性。同一个提示词永远得到同一条输出,无法用于需要多个候选的场景(如生成 5 个方案让用户挑)。
18.1.3 束搜索
束搜索(beam search) 保留 B 条(beam width)候选序列,每步对所有候选扩展再剪枝,保留累计对数概率最高的 B 条:
| |
用对数概率而不是概率,是因为 Π P 在长序列下会数值下溢,而 Σ log P 稳定。
长度偏置与长度归一化:Σ log P 是负值累加,序列越长得分越低,因此原始束搜索系统性地偏好短序列。修正:
| |
束搜索的失败模式(在开放式生成中比贪心更明显):
| 失败模式 | 表现 | 成因 |
|---|---|---|
| 重复 | 同义句反复循环 | 高分路径被"安全但空洞"的模板占据 |
| 缺乏多样性 | B 条候选高度相似 | 束内候选共享大量前缀,互相之间的差异被剪枝抹平 |
| 空洞化 | 输出"正确但没信息量" | 高概率 ≠ 高质量;模型对高频套话赋予高概率 |
| 长度畸变 | 明显偏短或偏长 | 长度归一化参数选择不当 |
| 成本 | 计算量约为贪心的 B 倍 | 每步要评估 `B × |
结论:束搜索适合有明确正确答案的任务(机器翻译、语音识别、抽取式摘要),因为这些任务的输出空间受限、P(y|x) 与任务质量相关性高。它不适合开放式创作与对话,因为此时"高概率"与"好"之间有系统性偏差,束搜索会把偏差放大——它专门在优化那个有偏的目标。
重要
束搜索优化的目标是"序列的模型概率最大",而我们要的是"人觉得好"。这两者在受限任务上高度一致,在开放任务上系统性背离。这是所有开放域生成任务最终都转向采样的根本原因。
18.2 温度
18.2.1 数学效果
温度 T 的机制极其简单:把 logits 除以 T 再 softmax。
| |
要看清它做了什么,写出两个 token 的概率比:
| |
原始 logit 差 Δ = z_a − z_b 被放大或缩小了 1/T 倍。于是:
T < 1:差值被放大 → 大者更大、小者更小 → 分布更尖锐(sharpened),趋近 argmax;T > 1:差值被缩小 → 概率被拉向均匀 → 分布更平坦(flattened),多样性上升;T = 1:不改变任何东西,就是模型原始分布。
极限行为:
| |
18.2.2 数值算例
取三个 token 的 logits z = [2.0, 1.0, 0.0]:
| |
验证比值公式: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,必须除掉这个和才能得到一个合法的分布,否则采样器的累积分布函数对不上,采出来的 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:
| |
然后只在核内重新归一化再采样:
| |
核心价值:候选集大小自适应。 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。
| |
第三步:核内重新归一化。
| |
答案:P(A) ≈ 0.444。
干扰项归因:
| 干扰项 | 数值 | 错在哪 |
|---|---|---|
| A | 0.400 | 直接用原始概率,忘了核内重新归一化 |
| B | 0.571 | 0.4/0.7——误把核集合当成 {A, B},即累加判定提前一步停止 |
| C | 0.471 | 保留了 D 又减掉某个量,不符合 Top-P 的截断规则 |
| D | 0.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 用"相对于最大概率的比例"做阈值,而不是绝对概率或累积概率:
| |
它的自适应机制与 Top-P 相反但互补:分布尖锐时阈值 p_min·P_max 也高,只留下极少数强候选;分布平坦时阈值低,保留大量候选。与 Top-P 相比,min-p 在高温下更稳定(不会因分布平坦而让核无限膨胀),因此常被推荐作为 Top-P 的替代。
18.5.3 典型采样
典型采样(typical sampling) 依信息论选择候选:保留那些"信息量接近期望信息量"的 token。定义每个 token 的惊异度(surprise) −log P(v),并令
| |
直觉:概率过高的 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 随机种子与工程实践
| |
工程建议:
- 评测阶段:报告结果时必须写明
temperature / top_p / top_k / seed / 引擎版本 / batch 配置。温度接近 0(或do_sample=False)时可以用单测代替多样本评测,因为此时输出几乎确定。 - 需要多样性时(如
pass@k代码评测):必须真正采样并报告k与温度;用贪心跑k次相当于只跑一次。 - 对可复现性有硬要求的场景(回归测试、审计):固定 batch 组成,或直接采用贪心/低温并把种子与引擎版本写进测试基线。
- 不要用"两次调用结果相同"来验证采样实现正确——温度大于 0 时不同才是正常的。
18.7 解码策略与业务指标的权衡
18.7.1 实现顺序
所有主流推理框架的参数应用顺序是固定的,顺序错了会得到与预期不同的分布:
| |
关键点:截断必须在温度之后,归一化必须在截断之后。若先截断再调温度,被截掉的候选集是按旧分布选的,可能与新分布下的合理候选不一致;若忘了归一化,采样结果会偏向候选池中概率较大者之外的行为(严格说会采出非法概率质量)。
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 章) |
三组内在矛盾:
- 质量 vs 多样性:降低温度提升单次输出的稳定性,但降低了多次采样的覆盖度。评测必须区分"单次最好"与”
k次中至少一次最好"(pass@k)。 - 确定性 vs 可用性:贪心最可复现,但在开放任务上质量最差(重复、空洞)。工程上常用"低温度 + Top-P"作为折中:既不引入太多随机性,又保留了跳出重复循环的可能。
- 吞吐 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:
| |
关键点:
- 屏蔽(masking)而非后处理。屏蔽 logits 保证了输出在每一步都合法,因此永远不会产生需要修复的非法输出;后处理修复(正则修补、重新请求)则会浪费 token 和时间,且修补结果未必语义正确。
- 格式合法 ≠ 内容正确。约束解码只保证结构,不保证字段值正确。它消除了"解析失败"这一类错误,不能消除"解析成功但答案是错的"。
- 与采样参数兼容。屏蔽发生在截断与归一化之前,
Top-P/温度照常生效,只是在合法候选集内起作用。因此低温度仍然能让字段值更稳定。 - 代价。构建 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):
| |
叠加 Top-K = 2:
| |
要点:p 是"至少保留这么多概率质量“的下界要求,但当候选池被更严格的约束(Top-K、K < |V|)耗尽后,Top-P 无法再扩大候选,只能接受更小的累积和。两者叠加取交集,不是取并集——这是"Top-K 与 Top-P 谁更严格谁生效"的实际含义。
Q2(推导)证明 P_T(a)/P_T(b) = 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 采样,两次调用应该得到完全相同的输出。”
答案与解析
不成立。 固定种子只是必要条件,不是充分条件。影响结果的还有:
- batch 组成:不同 batch 下的 GEMM 分块与累加顺序不同,logits 末位有
1e-7量级差异;这些差异在采样阈值附近会被放大成不同 token,随后自回归放大成完全不同的序列。 - RNG 流的消耗顺序:并发请求共享或分别持有 RNG 状态、以及 token 何时被 flush,都会改变"第
t步用的是随机数流里的第几个数”。 - 引擎与 kernel 版本:不同的注意力实现(FlashAttention 版本、是否融合算子)、不同的精度配置(KV Cache 用 bf16 还是 fp16)都会改变数值。
- 序列长度与填充: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硬砍掉。可选项变窄,模型只能在少数几个高概率套话里选,于是输出变得模板化并容易进入重复循环。
两件事是同一个设计缺陷的两种表现:候选集大小应当随分布自适应。
正确方向:
- 把截断从 Top-K 改为(或叠加)Top-P,例如
p = 0.9:分布尖锐时核自动缩到 1~2 个 token(不引入噪声),分布平坦时自动扩展到几十个(保留多样性)。若担心高温下核无限膨胀,可叠加 min-p(18.5.2 节)。 - 温度单独调整,与截断解耦:客服场景建议
T = 0.2~0.5。 - 若仍有复读,优先用较弱的频率惩罚(而不是强重复惩罚),或在提示词里明确要求不重复。
- 用真实评测集验证:指标应包含"荒谬输出率"与"输出多样性"两项,否则只是把问题从一端推到另一端。
Q5(辨析)为什么束搜索在机器翻译上表现良好,却在开放式对话中常被采样策略击败?
答案与解析
因为两者优化的目标与"好"的关系不同。
束搜索优化的是序列的模型对数概率 Σ_t log P(y_t|y_<t)(带长度归一化)。这个目标在受限任务上是任务质量的好代理:机器翻译的参考译文空间狭窄,P(y|x) 高的译文大概率也准确、流畅,而且长度与内容由源句决定,长度偏置可控。
在开放式对话/创作中,“模型概率高"与"人觉得好"系统性背离:
- 高频套话概率高。“综上所述"“希望对你有帮助"这类句子在预训练语料中出现极频繁,模型给它们极高概率,但它们几乎不携带信息。束搜索专门在最大化概率,因此会主动选择这些空洞表达。
- 缺乏多样性。
B条束共享前缀、互相剪枝,最终候选彼此高度相似。对话场景下用户往往希望得到几个不同的思路。 - 退化循环。重复的短语会自我强化(生成一次后,下一个相同 token 的概率被抬高),束搜索为了高分而走进循环,甚至比贪心更严重。
- 长度畸变:
Σ log P累加负值,即使有1/|y|^α归一化,α的选择也会系统性地影响长度,而对话的"合适长度"无法预先确定。
因此:任务输出空间受限且有一个明确正确答案时用束搜索;开放生成、需要多样性与信息量时用"温度 + Top-P"采样。这不是束搜索"变差了”,而是它的目标函数在开放域不再与人的偏好对齐。
Q6(设计)某系统要求模型输出固定 schema 的 JSON,用于下游自动处理。团队目前的做法是"提示词里给 schema 示例 + 解析失败就重试”。请评估这个做法,并说明约束解码能改善什么、不能改善什么。
答案与解析
当前做法的问题:
- 浪费。解析失败意味着整次生成(输入 + 输出 token)全部作废,重试的期望成本是
1/(1−p_fail)倍,p_fail越高浪费越严重。 - 无法保证收敛。重试
n次仍失败的概率是p_fail^n,对高要求链路不构成保证;且失败时上游得不到结构化信号,只能抛异常。 - 不可控的格式漂移。用采样参数降低
p_fail会同时牺牲内容质量;而且模型可能"学会"简化 schema(少输出可选字段)来规避错误。
约束解码能改善的:把 JSON Schema 编译成 FSM,每步屏蔽掉会破坏语法合法性的 token,并只把合法后继加入候选集。于是:
- 输出每一步都合法,解析失败率降为 0(针对语法层面);
- 采样参数仍生效,只是在合法候选内起作用,温度低时字段值依然稳定;
- 消除重试带来的成本与延迟抖动。
约束解码不能改善的:
- 内容正确性。格式合法 ≠ 值正确。“故障等级"字段可以是合法枚举值
"P1"而实际应为"P2"。这类错误需要靠检索证据、字段级校验或后处理断言来兜。 - 语义完整性。字段齐全不代表理解正确,模型可能把所有值都填成默认项。
- 文法的表达能力。极复杂的约束(跨字段依赖、数值范围)难以完全由上下文无关文法表达,超出部分仍需后处理校验。
正确设计是分层防御:约束解码保语法 → 字段级规则校验保取值合法(范围、枚举、交叉字段一致性)→ 业务规则校验保语义(与检索证据比对)→ 最后才落库。任一层都无法单独成为保证。
与真题的关联
| 真题 | 考查角度 | 对应本章小节 |
|---|---|---|
【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.444 | 18.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 约束解码)。