Happy-LLM 学习笔记 08:模型每次吐一个 token,解码策略到底在控制什么

前面几篇都在讲模型怎么训练出来,这一篇换一个角度:模型推理时每一步只输出一个 token,那"选哪个 token"这件事,其实才是用户直接感受到的产品行为。Happy-LLM 的额外章节把生成方式单独拎出来讲,读完后我最大的感受是:解码策略不是锦上添花的小参数,而是模型能力和使用体验之间的最后一层接口。

同一个模型,用贪婪解码会显得死板但可靠,用高温采样会显得有灵气但不稳定。很多时候我们抱怨"模型不行",其实是解码策略和任务不匹配。

贪婪解码:确定性是把双刃剑

贪婪解码的逻辑最朴素:每一步都取概率最大的 token。实现上就是对 logits 做一次 topk(k=1),把选中的索引拼回序列继续下一轮。

它的好处非常工程化:快、省内存、结果确定。同样的输入永远得到同样的输出,这对 API 服务、自动化脚本和回归测试来说几乎是刚需。做基准对比时也需要这种确定性,否则没法判断模型改动到底有没有效果。

但确定性恰好也是它的问题来源。每一步的局部最优不保证全局最优,模型容易掉进重复循环,比如不断生成相似的句式。我现在的理解是:贪婪解码适合"答案空间窄"的任务,比如数学计算、代码补全、格式化输出;一旦任务需要一点发散,它就开始暴露短板。

采样解码:把随机性变成可调参数

采样解码不再取 argmax,而是按概率分布随机抽一个 token。关键不在"随机"本身,而在于教程展示的两个旋钮把随机性变得可控。

第一个是 temperature。做法很直接:logits 除以 temperature 再做 softmax。温度大于 1,分布被抹平,长尾 token 也有机会被选中;温度小于 1,分布被削尖,头部 token 优势更明显;趋近 0 时就退化成贪婪解码。这个设计很优雅,一个标量就连续地连起了"完全确定"和"完全随机"两个极端。

第二个是 top-k。先取概率最高的 k 个 token,把其余的 logits 置为负无穷再归一化。它解决的问题很实际:词表尾部那些概率极低但偶尔会被抽中的"怪 token",往往是生成质量翻车的来源。砍掉长尾,采样的多样性就限制在合理范围内。

我的体会是,temperature 和 top-k 其实是两种不同方向的约束:前者调"分布形状",后者调"候选集合"。实际调参时我倾向于先固定一个中等 temperature,再用 top-k 控制质量下限,比反过来更容易收敛。

束搜索:用空间换质量,但不是免费的

束搜索在每一步保留多条候选序列,按累积对数概率排序,最后返回得分最高的那条。束宽为 1 时退化成贪婪解码,束宽越大搜索越充分,但每一步要对每条候选做前向传播,显存里要同时存多份序列和分数。

教程里那段逐 beam 扩展的实现值得细看:它本质上是一个受限的图搜索,每步把 num_beams 条候选各扩展出 num_beams 个后继,再全局取 top num_beams。这个结构清楚说明了束搜索的成本——计算量大致和束宽成正比。

有意思的是它的适用边界。束搜索在机器翻译、摘要这类"存在相对标准答案"的任务上表现好,因为高概率路径往往就是正确路径;但在开放式对话里,它容易生成最"平均"、最无聊的回复,概率最高的序列恰恰是最平庸的那个。这提醒我:优化"序列概率最大"和优化"用户觉得好"是两回事。

投机解码:工程思路最漂亮的一节

前面三种策略改的是"选 token 的规则",投机解码改的是"验证 token 的流程"。让小模型先快速草拟几个 token,大模型一次性批量验证,接受前缀、拒绝分歧点之后由大模型补一个正确 token。因为大模型的验证是并行的,原本逐 token 的串行生成被压缩成少量几次前向传播。

这个思路最打动我的地方在于它完全不损失质量:最终输出的分布严格来自大模型,小模型只影响速度。草稿长度、接受率之间的权衡也都是纯工程问题,可以离线调优。

我的整体理解

把这几种方法放在一起看,解码策略的选择其实回答三个问题:要不要确定性(贪婪)、要不要多样性(采样及其参数)、要不要全局质量(束搜索),以及在确定性前提下怎么省算力(投机解码)。没有一种策略天然最优,只有和任务、成本、体验目标的匹配度。

对正在做小模型实验的我来说,最直接的启发是:评测一个模型时应该固定解码配置,否则比的可能不是模型;而上线一个功能时,解码参数应该和功能定位一起设计,而不是用默认值跑到底。

下一篇进入第七章,看 RAG 怎么把外部知识接到模型上。

参考资料

posted @ 2026-07-26 16:00  Hazy_star  阅读(3)  评论(0)    收藏  举报