3237 字
16 分钟
次浏览
大模型 Decoding 策略 — 从"我该怎么选"出发,一步步推出一套选词方案

0. 前言#

语言模型在生成文本时, 每一步都会输出一个概率分布——一个长度为词表大小 VV 的向量, 每个元素表示下一个词是第 ii 个 token 的概率:

P(wtw<t)=softmax(zt)=ezt,ij=1Vezt,jP(w_t | w_{<t}) = \text{softmax}(\mathbf{z}_t) = \frac{e^{z_{t,i}}}{\sum_{j=1}^{V} e^{z_{t,j}}}

但问题是: 有了这个分布之后, 该怎么选一个词出来?

这个看似简单的问题, 答案并不唯一. 不同的”选法”会产生完全不同的效果——有的输出死板重复, 有的天马行空, 有的稳定可靠. 整个 Decoding Strategy 领域, 本质就是在问一个问题:

给定一个概率分布, 怎么选词才能让整体输出既准确多样?

这两个目标其实是矛盾的: 越准确(选概率最高的), 就越缺乏多样性; 越多样(从分布里随机抽), 就越容易跑偏. 不同的策略就是在权衡这两个目标.


1. 最自然的想法: 选概率最大的#

让我们从一个最朴素的想法开始: 每次选概率最大的那个词.

w^t=argmaxwP(ww<t)\hat{w}_t = \arg\max_w P(w | w_{<t})

这叫做 Greedy Search — 贪心搜索.

这是最理性的选择吗? 单步来看, 是的——P(wtw<t)P(w_t|w_{<t}) 最大意味着在已知上下文下, 这个词是最”合理”的. 但问题出在长远.

来看一个简单的例子. 假设模型要补全 “我喜欢的食物是____”, 各方案的概率:

  • “吃” → 0.35 (这个词在这里确实很合理)
  • “披萨” → 0.28
  • “尝” → 0.12

Greedy 会选 “吃”. 但接下来呢? “我喜欢的食物是吃…” — 这句话感觉不对了. 而如果第一步选了 “披萨”, 后面可以接 “很好吃”, “我最常点的” 等等, 整句话的质量更高.

所以 Greedy 的问题本质上是短视: 它只考虑了局部最优, 没有考虑今天的决策对未来的影响.

更严重的是, Greedy 很容易陷入重复循环: “很好 → 很好 → 很好…” 或者 “这是一本有趣的书 → 这是一本有趣的书 → …”. 因为一旦进入某个”安全”的局部模式, 每一步概率最高的词就是继续重复自己.


2. 另一个极端的想法: 按概率随机抽#

既然”每次都选最合理的”会导致死板重复, 那反过来: 按概率来随机抽.

概率 0.28 的词有 28% 的机会被选到, 概率 0.01 的词有 1% 的机会. 这就是纯采样 (Pure Sampling).

w^tP(ww<t)\hat{w}_t \sim P(w | w_{<t})

采样的好处是显而易见的: 每次生成的文本都不一样, 充满了多样性. 但它的问题是: 低概率的词有时候真的太离谱了.

语言模型的概率分布往往有很长的尾巴——成千上万个概率接近于 0 的词, 累加起来可能占 10-20% 的总概率. 纯采样有一定的概率选中这些词, 生成”我喜欢的食物是斑马”这种荒谬的结果.

所以我们需要在”确定性”和”多样性”之间找一个折中——能够逐步调节的折中.


3. Temperature: 一个可以”拧”的旋钮#

在 softmax 之前加一个缩放参数 TT, 让概率分布可以动态调整:

PT(wi)=ezi/Tjezj/TP_T(w_i) = \frac{e^{z_i / T}}{\sum_{j} e^{z_j / T}}

这里 z=(z1,...,zV)\mathbf{z} = (z_1, ..., z_V) 是模型输出的 logits (未归一化的分数), 不是最终概率. 这个 TT 就是 Temperature.

TT 变化时, 分布会发生什么?

我们来推导一下. 对于任意两个词 iijj, 它们概率的比值是:

PT(wi)PT(wj)=ezi/Tezj/T=e(zizj)/T\frac{P_T(w_i)}{P_T(w_j)} = \frac{e^{z_i / T}}{e^{z_j / T}} = e^{(z_i - z_j) / T}

T0+T \to 0^+:

limT0+e(zizj)/T={if zi>zj0if zi<zj1if zi=zj\lim_{T \to 0^+} e^{(z_i - z_j) / T} = \begin{cases} \infty & \text{if } z_i > z_j \\ 0 & \text{if } z_i < z_j \\ 1 & \text{if } z_i = z_j \end{cases}

这意味着: 概率最高的词会被无限放大到 1, 其他词的概率都被压缩到 0——退化为 Greedy. 数学上, 这就是 argmax 的 soft 近似.

T=1T = 1: 保持原始分布不变.

TT \to \infty:

limTe(zizj)/T=1\lim_{T \to \infty} e^{(z_i - z_j) / T} = 1

所有词的概率趋于相等, 变成均匀分布——完全随机选择.

所以 TT 就是一个从 完全确定 (T0T \to 0) 到 完全随机 (TT \to \infty) 的连续旋钮.

实际操作中:

TT效果解释
T0T \to 0退化为 Greedy概率最高的词被无限放大
0.50.5非常保守让高概率词更突出, 减少多样性
0.70.7略保守对话常用, 保留一定多样性
1.01.0原始分布不做任何调整
1.51.5有创意低概率词有更多机会
T1T \gg 1接近均匀分布几乎完全随机

但注意, Temperature 改变的是分布的形状, 它没有解决”低概率尾巴”的问题——即使 T=0.7T=0.7, 那些概率极低的词仍然有可能被采样到. 这就是接下来 Top-K 和 Top-P 要解决的问题.


4. Top-K: 裁剪”尾巴”#

Top-K 的思路直截了当: 只保留概率最高的 KK 个词, 其他的概率置零, 然后重新归一化.

candidates={w(1),w(2),...,w(K)}\text{candidates} = \{w_{(1)}, w_{(2)}, ..., w_{(K)}\}

其中 w(i)w_{(i)} 是按概率降序排列后的第 ii 个词.

重新归一化:

P(ww<t)=P(ww<t)wcandidatesP(ww<t)P'(w | w_{<t}) = \frac{P(w | w_{<t})}{\sum_{w' \in \text{candidates}} P(w' | w_{<t})}

这相当于把候选集之外的所有词的概率重新分配给候选集内的词. 过程等价于: 先截断, 再缩放.

K 怎么选?

  • K=1K=1: 退化为 Greedy
  • K=50K=50: GPT-2 默认值
  • K=200+K=200+: 接近完整采样

Top-K 的问题: 固定 KK 不够灵活.

考虑两种极端的概率分布:

情况 A (尖峰分布): 一个词概率 0.85, 另一个 0.10, 其余 5% 概率均匀分布在 1000 个词上. Top-K=50 会把一堆概率 0.00005 的词捞进来——基本上等于在随机词里选, 容易出问题.

情况 B (平坦分布): 前 10 个词的概率分别是 0.15, 0.14, 0.13, …, 到第 50 个词概率还有 0.02. Top-K=50 的候选集很合理. 但如果 Top-K=5, 就切掉了一大半合理的选项.

这就是 Top-K 的”硬伤”: 用一个固定值去应对动态变化的分布, 难免有时过松、有时过紧.


5. Top-P (Nucleus): 动态裁剪#

Top-P 的改进: 不限定候选数量, 而限累积概率.

  1. 把词按概率从高到低排序
  2. 累加概率, 直到累加和 P\ge P
  3. 只保留这部分词
candidates={w(1),...,w(k)} s.t. i=1kP(w(i)w<t)P\text{candidates} = \{w_{(1)}, ..., w_{(k)}\} \ \text{s.t.} \ \sum_{i=1}^{k} P(w_{(i)} | w_{<t}) \ge P

其中 kk 是满足条件的最小值.

Top-P 的好处是自适应:

  • 分布集中时: 候选数量少 (前几个词就占了 PP 的概率), 只保留高质量的候选
  • 分布分散时: 候选数量多, 保留更多多样性

典型的 P=0.9P=0.90.950.95. P=1.0P=1.0 等价于完整采样.

Top-K 和 Top-P 的关系:

可以用一个类比来理解: Top-K 是”一刀切”, 身高低于 1.8m 的人不准上场; Top-P 是”按比例挑”, 先选最高的, 直到总人数达到要求. 后者更灵活.

实际应用中, 两者经常组合使用: 先用 Top-K 砍掉极低概率的尾巴 (比如 K=50K=50), 再用 Top-P 做动态调整 (比如 P=0.9P=0.9). 这样做的好处是: 即使分布极端, Top-K 也能确保不会选到太离谱的词.


6. Temperature + Top-K + Top-P: 统一框架#

把三个方法组合起来, 形成一个完整的解码流程:

输入: logits z (长度为 V 的向量)
1. Temperature 缩放: z' = z / T
2. Softmax: p = softmax(z')
3. Top-K 截断: 只保留概率最高的 K 个词, 其余置 0
4. Top-P 截断: 从最高概率开始累加, 直到累积概率 ≥ P
5. 重新归一化: 对保留下来的词重算概率
6. 从最终分布中采样一个词
输出: 下一个 token

每一步都有明确的数学意义:

  • Step 1: 控制”确定性 vs 多样性”的程度
  • Step 3: 防止低概率词的”尾部风险”
  • Step 4: 自适应调整候选集大小

HuggingFace 中对应的参数:

from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("model-name")
output = model.generate(
input_ids,
do_sample=True, # 启用采样模式
temperature=0.7, # Temperature 缩放
top_k=50, # Top-K 截断
top_p=0.9, # Top-P 截断
max_new_tokens=512
)

注意: do_sample=False (默认) 时直接走 Greedy, 上面的 Temperature/Top-K/Top-P 参数不会生效.


7. Beam Search: 另一种思路#

上面讲的所有方法, 都是单步决策: 每步选一个词, 选了就定了. Beam Search 走的是另一条路: 同时维护多条候选路径.

7.1 算法流程#

假设 Beam width B=2B = 2:

  1. 第一步: 模型输出所有词的概率, 保留概率最高的 B=2B=2 个词作为两条路径的起点
  2. 第二步: 对每条路径分别计算下一步的概率, 得到 B×V=2VB \times V = 2V 个候选项
  3. 2V2V 个候选中保留全局概率最高的 B=2B=2 条完整路径
  4. 重复直到满足结束条件

路径的”分数”是对数概率之和:

score(path)=t=1TlogP(wtw<t)\text{score}(\text{path}) = \sum_{t=1}^{T} \log P(w_t | w_{<t})

为什么要用对数概率, 而不是直接乘概率?

假设一个路径长 T=100T=100, 每个词的概率平均 0.1, 路径的总概率是:

t=11000.1=10100\prod_{t=1}^{100} 0.1 = 10^{-100}

这是一个小到浮点数都表示不了的数字——数值下溢 (underflow).

用对数就解决了:

t=1100log(0.1)=230\sum_{t=1}^{100} \log(0.1) = -230

或者说 logP\log PPP 是单调关系 (P1<P2    logP1<logP2P_1 < P_2 \iff \log P_1 < \log P_2), 所以最大化对数概率等价于最大化原始概率, 同时数值稳定.

7.2 Beam width 的选取#

  • B=1B=1: 退化为 Greedy
  • B=4B=4: 常用值, 质量明显提升
  • B=10+B=10+: 边际收益递减, 计算量线性增长 (BB 倍)

为什么边际收益递减? 因为前几个候选项之间的分数差距通常很大 (好的路径分数远高于差的路径), 多保留几个基本不会被选到. 只有当前几条路径分数接近时, 增大 BB 才有意义.

适合: 翻译、摘要、代码生成等追求确定性最优解的任务. 注意: Beam Search 是确定性算法, 同样的输入总是得到同样的输出.

不适合: 对话、故事创作等需要多样性的场景. Beam Search 倾向于选”最安全的”路径, 生成的文本往往比较平淡、模板化.

原因: Beam Search 每一步都在最大化全局概率, 而”最安全”的路径往往是”概率最高的常见词”的排列组合, 缺乏新意. 在这种任务中, 带采样的 Top-K + Top-P 效果更好.


8. 从 Softmax 再看一遍 Temperature#

前面我们直接给了 Temperature 的公式. 现在从 softmax 的梯度视角再看一次, 可能会对 TT 的作用有更深的理解.

Softmax 函数:

pi=ezijezjp_i = \frac{e^{z_i}}{\sum_j e^{z_j}}

加入 Temperature 后:

pi(T)=ezi/Tjezj/Tp_i(T) = \frac{e^{z_i / T}}{\sum_j e^{z_j / T}}

pip_iziz_i 的梯度:

pizi=1Tpi(1pi)\frac{\partial p_i}{\partial z_i} = \frac{1}{T} \cdot p_i(1 - p_i)

TT 很小时, 梯度 1T\frac{1}{T} 很大 —— 概率分布对 logits 的微小变化极其敏感, 分布趋于”one-hot” (一个 1, 其余 0). 当 TT 很大时, 梯度很小 —— 分布对 logits 不敏感, 趋于均匀.

所以 Temperature 本质上是在控制概率分布对模型输出的敏感度. 低 TT → 模型高度自信 → 输出确定; 高 TT → 模型犹豫不决 → 输出随机.


9. 实践指南#

场景策略参数
机器翻译Beam SearchB=4B=4
代码生成Low T + Top-PT=0.1,P=0.9T=0.1, P=0.9
对话Medium T + Top-K + Top-PT=0.7,K=50,P=0.9T=0.7, K=50, P=0.9
故事创作High T + Top-PT=1.2,P=0.95T=1.2, P=0.95
事实验证/科学Low T + Top-PT=0.3,P=0.85T=0.3, P=0.85

理解这些策略的关键, 不是记住参数值, 而是理解每一条: 我在确定性和多样性之间, 选择了哪一边?


10. 总结#

策略核心思想确定 vs 多样主要问题
Greedy选概率最高的确定短视、重复
Temperature缩放 logits 调节锐度可调节不解决尾部风险
Top-K保留前 KK偏向确定固定值不够灵活
Top-P保留累积概率 PP自适应候选数量波动大
Beam Search维护 BB 条路径确定计算量大, 平淡

终极建议: 大部分场景下, 先用 Temperature 调到一个你想要的”创意程度”, 然后用 Top-K 砍掉最离谱的尾巴, 再用 Top-P 做自适应精调. 这三个组合起来, 可以覆盖大部分需求.


参考资料#

  1. Holtzman et al., The Curious Case of Neural Text Degeneration. ICLR 2020. arXiv:1904.09751提出 Top-P (Nucleus) Sampling
  2. Fan et al., Hierarchical Neural Story Generation. ACL 2018. arXiv:1805.04833提出 Top-K Sampling
  3. Graves, Sequence Transduction with Recurrent Neural Networks. 2012. arXiv:1211.3711Beam Search 的经典应用
  4. Hinton, G. (2015). Distilling the Knowledge in a Neural Network — Temperature 在蒸馏中的应用, 和本文用到的是同一个概念
  5. Vaswani et al., Attention Is All You Need. NeurIPS 2017. arXiv:1706.03762
  6. HuggingFace Docs: Generation Strategies
大模型 Decoding 策略 — 从"我该怎么选"出发,一步步推出一套选词方案
https://xuchenhui.cc/posts/2026-05-16-llm-decoding-strategies/
作者
CHENHUI
发布于
2026-05-16
许可协议
CC BY-NC-SA 4.0
📖 目录