📑 本页目录(点开跳转)
06b · 解码策略:模型是怎么选出下一个词的
⏱ 24 分钟 | ⭐ 输出飘、复读、JSON 偶尔崩 —— 从这一页开始查
🎯 一句话
模型每一步吐出来的是一个概率分布,不是一个词。从这个分布里挑出下一个 token 的那套规则,就是解码策略。 这一页讲挑法本身:贪心、Beam Search、top-k、top-p 各自在做什么。 ⭐ 参数具体怎么配、出了问题怎么排查,在下一页。
🧭 先接住上一章
第 6 章推理优化的三大武器(量化 / 批处理 / 投机解码)解决的是「这一步跑得多快」。 这一章解决完全不同的另一件事:「这一步选哪个词」。
两件事是正交的,但经常被混在一起。分开的办法很简单,看现象:
| 现象 | 去哪一章 |
|---|---|
| 慢、贵、显存不够、吞吐上不去 | 第 6 章 |
| 输出飘 / 复读 / 每次结果不一样 / JSON 间歇性打不开 | ⭐ 这一章 |
它是每个调 API 的人天天在动、却最少被讲清楚的东西。 唯一的前置是知道 decode 是一个一个吐 token 的——第 6 章「两个阶段」讲过。
🎲 一步 decode 里到底发生了什么
流程图
⚠️ 主流实现(HuggingFace transformers、vLLM)的默认顺序是 温度 → top_k → top_p, 但不同框架不保证一致。拿不准就只动一个参数,别指望跨框架搬参数还是同一个行为。
🥇 Greedy:永远选最大的那个
每一步取 argmax,不抽签。等价于 T→0。
| ✅ 好处 | ❌ 代价 |
|---|---|
| 确定性、最快(省掉抽签)——但"可复现"有个坑,见本章最后 ⚠️ | 局部最优 ≠ 全局最优:第一个词选歪,整句跟着歪 |
| 抽取/分类/工具调用的默认选择 | 最容易掉进复读循环(下面会讲机制) |
🌲 Beam Search:以及为什么开放式生成不用它
机制:不再只留一条路,而是同时留 k 条部分序列(beam)。每一步把每条 beam 的所有可能扩展都算出来,按累计对数概率排序,全局取前 k 条继续。
因果链
它在哪些任务上是对的:机器翻译、语音识别、OCR、结构化摘要——这些任务有一个(接近)唯一的正确输出,"找到概率最高的那条序列"就是任务目标本身。
⭐ 但开放式生成(聊天、写作、创意)不用它,原因有四条,第一条最根本:
| 原因 | 说明 |
|---|---|
| 高概率 ≠ 好文本 ⭐ | 人写的句子并不走最高概率路径——真实人类文本的逐词概率是起伏的(时而出人意料),而 beam 挑出来的序列概率曲线平得不自然。追求"最可能的一句话",得到的是最平庸的一句话 |
| 文本退化 | beam 开得越宽,输出越短、越干瘪、越容易复读。这个观察正是核采样(top-p)被提出来的直接动机 |
| 多样性塌缩 | k 条 beam 常常只差一两个词。想要「给我 5 个不同的开头」,beam 给你的是 5 个几乎一样的开头 |
| 成本 k 倍 | k 条 beam = k 份 KV Cache,显存和带宽都乘 k。而 decode 本来就卡在带宽上(第 6 章的第一条铁律:decode 卡在显存带宽,不是算力) |
🔗 最后那条成本其实有救:vLLM 的块共享让各条 beam 共用公共前缀的 KV 块, 只有分叉之后的部分才各存一份 —— 展开见 AI基础设施 17。
⚠️ 一个常见的搬运事故:把翻译服务里调好的 num_beams=5 原样搬到"生成营销文案"上,
结果每次生成的十条文案开头几乎一模一样。参数没错,任务变了——从"有唯一正确答案"变成了"要多样性"。
✂️ top-k:固定砍到 k 个
只保留概率最高的 k 个候选,重新归一化再抽签。
它的毛病只有一条:k 是固定数量,但分布形状是变的。
关键信息
🎯 top-p 核采样(nucleus sampling):候选集自适应
按概率从大到小累加,累计一超过 p 就停手,只在这个"核"里抽签。
⭐ 关键优点:候选集大小自己会变。 分布尖 → 核可能只有 1–2 个;分布平 → 核可以有几百个。这正好补上了 top-k 的毛病,所以它是现在的默认选择(典型值 0.9 / 0.95)。
top_p=0.9 只剩 2 个候选(因为「天气」的概率被拉到了 0.89)。⚠️ top_p = 1.0 不是"安全值"——它表示完全不截断,长尾里那 0.001% 的怪词有真实机会被抽中。想要"别乱来"应该设 0.9,不是 1.0。
🔗 想再深一层,去哪一套
| 去哪 | 为什么 |
|---|---|
| 06 · 推理优化 | 同样是 decode 那一步,那一章管的是怎么让它更快,这一章管选哪个词。两件事,两套参数 |
| AI全栈 05 · 流式输出 | 流式输出里一个字一个字往外吐 —— 每一个字都走了一遍这一页的四个动作 |
| 强化学习基础 12 · RLHF全流程 | 为什么「高概率 ≠ 好文本」:对齐那一步改的正是模型对「什么算好」的分布 |
✅ 检查点
- 一步 decode 里的四个动作按什么顺序发生?为什么说
temperature和top_p不是独立的两个旋钮? - Beam Search 适合什么任务?开放式生成为什么不用它(至少说出两条)?
- top-p 比 top-k 好在哪?
top_p = 1.0意味着什么?
👀 答案
- ① 吐 logits(不是概率)→ ② 温度缩放 → ③ top_k/top_p 截断 → ④ softmax 归一化后抽签。不独立是因为温度先改分布形状、截断再在改过的分布上切:降温到 0.3 后,同样的
top_p=0.9从 6 个候选缩到 2 个。 - 适合有唯一正确输出的任务:机器翻译、语音识别、OCR、结构化摘要。开放式生成不用它的四条:①高概率 ≠ 好文本(人类文本的逐词概率是起伏的,追求"最可能的一句话"得到的是最平庸的一句话)②文本退化(beam 越宽越短越干瘪,这正是核采样被提出来的动机)③多样性塌缩(k 条 beam 只差一两个词)④成本 k 倍 KV Cache。
- top-k 是固定数量,不看分布形状——分布尖时塞进 49 个荒唐候选,分布平时又砍掉合理选项;top-p 的候选集大小自适应。
top_p = 1.0表示完全不截断,长尾里那 0.001% 的怪词有真实机会被抽中——想"别乱来"该设 0.9 不是 1.0。
🛑 可以停在这里
读到这里,你已经知道那几个参数各自在分布上做了什么手脚 —— 这足够让你看懂 API 文档里的 temperature / top_p / top_k 分别是什么。
⚠️ 什么时候看下一页:你已经知道它们是什么,但不知道该配成多少;或者线上出现了复读、JSON 偶尔打不开、同样输入两次结果不一样。
⚡ 走神救援
先记住这几件事
- 模型输出的是候选分数,解码策略决定怎样从中选出下一个 token。
- 温度改变分布的尖锐程度,top-k 与 top-p 限制候选集合。
- 贪心、束搜索与采样的目标不同,用真实输出比较稳定性、多样性和成本。
下一节 👉 06c-解码参数怎么调.md