0. 前言
语言模型在生成文本时, 每一步都会输出一个概率分布——一个长度为词表大小 的向量, 每个元素表示下一个词是第 个 token 的概率:
但问题是: 有了这个分布之后, 该怎么选一个词出来?
这个看似简单的问题, 答案并不唯一. 不同的”选法”会产生完全不同的效果——有的输出死板重复, 有的天马行空, 有的稳定可靠. 整个 Decoding Strategy 领域, 本质就是在问一个问题:
给定一个概率分布, 怎么选词才能让整体输出既准确又多样?
这两个目标其实是矛盾的: 越准确(选概率最高的), 就越缺乏多样性; 越多样(从分布里随机抽), 就越容易跑偏. 不同的策略就是在权衡这两个目标.
1. 最自然的想法: 选概率最大的
让我们从一个最朴素的想法开始: 每次选概率最大的那个词.
这叫做 Greedy Search — 贪心搜索.
这是最理性的选择吗? 单步来看, 是的—— 最大意味着在已知上下文下, 这个词是最”合理”的. 但问题出在长远.
来看一个简单的例子. 假设模型要补全 “我喜欢的食物是____”, 各方案的概率:
- “吃” → 0.35 (这个词在这里确实很合理)
- “披萨” → 0.28
- “尝” → 0.12
- …
Greedy 会选 “吃”. 但接下来呢? “我喜欢的食物是吃…” — 这句话感觉不对了. 而如果第一步选了 “披萨”, 后面可以接 “很好吃”, “我最常点的” 等等, 整句话的质量更高.
所以 Greedy 的问题本质上是短视: 它只考虑了局部最优, 没有考虑今天的决策对未来的影响.
更严重的是, Greedy 很容易陷入重复循环: “很好 → 很好 → 很好…” 或者 “这是一本有趣的书 → 这是一本有趣的书 → …”. 因为一旦进入某个”安全”的局部模式, 每一步概率最高的词就是继续重复自己.
2. 另一个极端的想法: 按概率随机抽
既然”每次都选最合理的”会导致死板重复, 那反过来: 按概率来随机抽.
概率 0.28 的词有 28% 的机会被选到, 概率 0.01 的词有 1% 的机会. 这就是纯采样 (Pure Sampling).
采样的好处是显而易见的: 每次生成的文本都不一样, 充满了多样性. 但它的问题是: 低概率的词有时候真的太离谱了.
语言模型的概率分布往往有很长的尾巴——成千上万个概率接近于 0 的词, 累加起来可能占 10-20% 的总概率. 纯采样有一定的概率选中这些词, 生成”我喜欢的食物是斑马”这种荒谬的结果.
所以我们需要在”确定性”和”多样性”之间找一个折中——能够逐步调节的折中.
3. Temperature: 一个可以”拧”的旋钮
在 softmax 之前加一个缩放参数 , 让概率分布可以动态调整:
这里 是模型输出的 logits (未归一化的分数), 不是最终概率. 这个 就是 Temperature.
当 变化时, 分布会发生什么?
我们来推导一下. 对于任意两个词 和 , 它们概率的比值是:
当 :
这意味着: 概率最高的词会被无限放大到 1, 其他词的概率都被压缩到 0——退化为 Greedy. 数学上, 这就是 argmax 的 soft 近似.
当 : 保持原始分布不变.
当 :
所有词的概率趋于相等, 变成均匀分布——完全随机选择.
所以 就是一个从 完全确定 () 到 完全随机 () 的连续旋钮.
实际操作中:
| 效果 | 解释 | |
|---|---|---|
| 退化为 Greedy | 概率最高的词被无限放大 | |
| 非常保守 | 让高概率词更突出, 减少多样性 | |
| 略保守 | 对话常用, 保留一定多样性 | |
| 原始分布 | 不做任何调整 | |
| 有创意 | 低概率词有更多机会 | |
| 接近均匀分布 | 几乎完全随机 |
但注意, Temperature 改变的是分布的形状, 它没有解决”低概率尾巴”的问题——即使 , 那些概率极低的词仍然有可能被采样到. 这就是接下来 Top-K 和 Top-P 要解决的问题.
4. Top-K: 裁剪”尾巴”
Top-K 的思路直截了当: 只保留概率最高的 个词, 其他的概率置零, 然后重新归一化.
其中 是按概率降序排列后的第 个词.
重新归一化:
这相当于把候选集之外的所有词的概率重新分配给候选集内的词. 过程等价于: 先截断, 再缩放.
K 怎么选?
- : 退化为 Greedy
- : GPT-2 默认值
- : 接近完整采样
Top-K 的问题: 固定 不够灵活.
考虑两种极端的概率分布:
情况 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 的改进: 不限定候选数量, 而限累积概率.
- 把词按概率从高到低排序
- 累加概率, 直到累加和
- 只保留这部分词
其中 是满足条件的最小值.
Top-P 的好处是自适应:
- 分布集中时: 候选数量少 (前几个词就占了 的概率), 只保留高质量的候选
- 分布分散时: 候选数量多, 保留更多多样性
典型的 或 . 等价于完整采样.
Top-K 和 Top-P 的关系:
可以用一个类比来理解: Top-K 是”一刀切”, 身高低于 1.8m 的人不准上场; Top-P 是”按比例挑”, 先选最高的, 直到总人数达到要求. 后者更灵活.
实际应用中, 两者经常组合使用: 先用 Top-K 砍掉极低概率的尾巴 (比如 ), 再用 Top-P 做动态调整 (比如 ). 这样做的好处是: 即使分布极端, Top-K 也能确保不会选到太离谱的词.
6. Temperature + Top-K + Top-P: 统一框架
把三个方法组合起来, 形成一个完整的解码流程:
输入: logits z (长度为 V 的向量)
1. Temperature 缩放: z' = z / T2. Softmax: p = softmax(z')3. Top-K 截断: 只保留概率最高的 K 个词, 其余置 04. Top-P 截断: 从最高概率开始累加, 直到累积概率 ≥ P5. 重新归一化: 对保留下来的词重算概率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 :
- 第一步: 模型输出所有词的概率, 保留概率最高的 个词作为两条路径的起点
- 第二步: 对每条路径分别计算下一步的概率, 得到 个候选项
- 从 个候选中保留全局概率最高的 条完整路径
- 重复直到满足结束条件
路径的”分数”是对数概率之和:
为什么要用对数概率, 而不是直接乘概率?
假设一个路径长 , 每个词的概率平均 0.1, 路径的总概率是:
这是一个小到浮点数都表示不了的数字——数值下溢 (underflow).
用对数就解决了:
或者说 和 是单调关系 (), 所以最大化对数概率等价于最大化原始概率, 同时数值稳定.
7.2 Beam width 的选取
- : 退化为 Greedy
- : 常用值, 质量明显提升
- : 边际收益递减, 计算量线性增长 ( 倍)
为什么边际收益递减? 因为前几个候选项之间的分数差距通常很大 (好的路径分数远高于差的路径), 多保留几个基本不会被选到. 只有当前几条路径分数接近时, 增大 才有意义.
7.3 什么时候用 Beam Search?
适合: 翻译、摘要、代码生成等追求确定性最优解的任务. 注意: Beam Search 是确定性算法, 同样的输入总是得到同样的输出.
不适合: 对话、故事创作等需要多样性的场景. Beam Search 倾向于选”最安全的”路径, 生成的文本往往比较平淡、模板化.
原因: Beam Search 每一步都在最大化全局概率, 而”最安全”的路径往往是”概率最高的常见词”的排列组合, 缺乏新意. 在这种任务中, 带采样的 Top-K + Top-P 效果更好.
8. 从 Softmax 再看一遍 Temperature
前面我们直接给了 Temperature 的公式. 现在从 softmax 的梯度视角再看一次, 可能会对 的作用有更深的理解.
Softmax 函数:
加入 Temperature 后:
求 对 的梯度:
当 很小时, 梯度 很大 —— 概率分布对 logits 的微小变化极其敏感, 分布趋于”one-hot” (一个 1, 其余 0). 当 很大时, 梯度很小 —— 分布对 logits 不敏感, 趋于均匀.
所以 Temperature 本质上是在控制概率分布对模型输出的敏感度. 低 → 模型高度自信 → 输出确定; 高 → 模型犹豫不决 → 输出随机.
9. 实践指南
| 场景 | 策略 | 参数 |
|---|---|---|
| 机器翻译 | Beam Search | |
| 代码生成 | Low T + Top-P | |
| 对话 | Medium T + Top-K + Top-P | |
| 故事创作 | High T + Top-P | |
| 事实验证/科学 | Low T + Top-P |
理解这些策略的关键, 不是记住参数值, 而是理解每一条: 我在确定性和多样性之间, 选择了哪一边?
10. 总结
| 策略 | 核心思想 | 确定 vs 多样 | 主要问题 |
|---|---|---|---|
| Greedy | 选概率最高的 | 确定 | 短视、重复 |
| Temperature | 缩放 logits 调节锐度 | 可调节 | 不解决尾部风险 |
| Top-K | 保留前 个 | 偏向确定 | 固定值不够灵活 |
| Top-P | 保留累积概率 | 自适应 | 候选数量波动大 |
| Beam Search | 维护 条路径 | 确定 | 计算量大, 平淡 |
终极建议: 大部分场景下, 先用 Temperature 调到一个你想要的”创意程度”, 然后用 Top-K 砍掉最离谱的尾巴, 再用 Top-P 做自适应精调. 这三个组合起来, 可以覆盖大部分需求.
参考资料
- Holtzman et al., The Curious Case of Neural Text Degeneration. ICLR 2020. arXiv:1904.09751 — 提出 Top-P (Nucleus) Sampling
- Fan et al., Hierarchical Neural Story Generation. ACL 2018. arXiv:1805.04833 — 提出 Top-K Sampling
- Graves, Sequence Transduction with Recurrent Neural Networks. 2012. arXiv:1211.3711 — Beam Search 的经典应用
- Hinton, G. (2015). Distilling the Knowledge in a Neural Network — Temperature 在蒸馏中的应用, 和本文用到的是同一个概念
- Vaswani et al., Attention Is All You Need. NeurIPS 2017. arXiv:1706.03762
- HuggingFace Docs: Generation Strategies