从0训练LLM · 现代大模型体系 岔路 阅读 + 实验 ≈ 30 分钟 页内实验 ×2
C13 推理:生成为什么慢、怎么变快 岔路课

解码循环的最后一步:概率分布怎么变成一个字?温度、top-k、top-p、min-p 各截掉了什么?

采样:温度、top-k、top-p、min-p
每一步算出 2,048 个 logits 之后,还要挑一个。一直挑最大的,我们的模型 47% 的 4-gram 都在重复;按概率抽,又有两成的概率落在一千多个几乎不可能的 token 上。温度改分布的形状,top-k、top-p、min-p 决定尾巴里还有谁有机会——同一组真实 logits 上,高温时 top-p 0.9 放进 1,108 个 token,min-p 0.1 只留 175 个。
↳

岔路课 · 挂在第 1 课旁边。第 1 课讲的是解码循环里最贵的那一格(读权重);这一课讲最后一格:算出 logits 之后,怎么挑出一个字。第 3 课的投机解码会用到这里的结论——它保证不变的,正是「调过温度、截过尾巴」之后的那个分布。

❓

本课一问:每一步模型给出整个词表的 logits。一直挑最大的那个行不行?不行的话,按概率抽又会出什么事?温度、top-k、top-p、min-p 各自截掉了什么?

这一课先不碰:beam search(只在 Dig 里说一句);重复惩罚、频率惩罚这类改 logits 的技巧;投机解码(第 3 课)。

从已有出发

从已有出发 · 一次请求在服务端走过的路 点任一格看锚点 Qwen3.8-27B 上的真实量
decode 循环:每个 token 走一圈
旁路:

decode 循环每走一圈,lm_head 都吐出一整排 logits(锚点是 248,320 个)。softmax 把它们变成概率,剩下的问题只有一个:这一步输出哪个 token。

🔁

这一课为什么换例子。采样要看的是一个真模型在真前缀上给出的完整分布。锚点 27.8B 在浏览器里跑不动,我们也不下载它的权重;贯穿例句「小明把苹果给了小王」是现代白话,而我们项目的小模型只学过《三國志演義》(C3 的实验里「苹」「给」都不在它的词表里)。所以换成我们自己训练的 ref_baseline(3,688,704 个参数,验证 loss 4.640542)在古文提示后的真实 logits——提示和项目实验 exp_c13_inference.py 用的是同一组,实验课里能逐项对上。

先自己设计:每次挑概率最大的那个

最省事的规则是贪心(温度 0):永远选 logit 最大的。结果确定、可复现。

  1. ① 朴素方案
  2. ② 它在哪失败
  3. ③ 改进方案
朴素方案贪心:永远选概率最大的 token

贪心生成(ref_baseline,提示「孔明曰:「」,128 个 token 里的开头一段):

孔明曰:「孔明有計,孔明必不用兵,卻教他去取了。」孔明曰:「孔明令孔明,先去取了城池,如何不識?吾料孔明有計,卻教他去取了城池,卻教他來降。」孔明曰:「吾已知吾計,如何不聽我?」孔……

贪心的问题不是「选错了某一个」,而是每一步都选最可能的,整段就会收敛到一个最可能的循环。按概率抽能跳出循环,却带来另一个问题——长尾。

新的失败:长尾

提示「孔明曰:「」之后(温度 1),最可能的 token「孔明」概率只有 10.1%;而概率低于 0.1% 的 token 有 1,871 个,它们加起来占 20.2%。也就是说,每生成一个 token,都有约五分之一的机会抽到其中某一个几乎不可能的 token;生成 100 个 token,几乎一定会抽到好几个。一个离谱的 token 进了上下文,后面的分布全被它带偏——上面那段「通而不顺」的文字就是这么来的。

所以要做两件事:改分布的形状(温度),以及把尾巴截掉(top-k、top-p、min-p)。

温度:先把 logits 除以 T,再 softmax
= ÷
👆 公式里带下划线的每一项都可以点——点哪项,讲哪项。
这和 C3 第 3 课的 √d 是同一个数学:「分数先除以一个数,再 softmax」。√d 是训练时写死在注意力里的温度,T 是生成时你手里的旋钮。

截断有三种思路,都在温度之后做、截完重新归一化:

真模型 · 我们项目 ref_baseline 在 4 个提示后的真实 logits(2,048 维,浏览器现算,与 python 逐项对照)
采样实验台:同一组真实 logits,温度与截断各改了什么
① logits(不随温度变)
② 温度 + 截断之后的概率
③ 这个分布有多「散」

🎯

试四件事(先保持第一个提示):① 拖温度,盯住左边的 logits:它一根都不动;中间的概率条和右边的熵一起变。② 点预设「贪心」,再点「纯采样 T=1」,读右栏「截掉的原始概率」与熵,再点「按这个分布抽 20 次」。③ 点预设「T=1.3 + top-p 0.9」:保留 664 个 token;再点「T=1.3 + min-p 0.1」:只保留 34 个。看下面那张小图:蓝线(top-p)随温度往上窜,绿线(min-p)涨得慢得多。④ 换到「玄德」这个提示:最可能的是一个半字(显示成 <E6 82>,它和下一个字节拼成一个汉字),这时 top-k = 1 会发生什么?

把第一个提示下的数字列成表(python 在同一组 logits 上现算,组件里对照打 ✓):

温度熵(不截断,nats)top-p 0.9 保留min-p 0.1 保留
0.52.15162
1.05.4035217
1.36.2066434
2.06.971108175

温度一高,分布变平,「凑够 90% 的概率」需要的 token 数暴涨,top-p 就把一整段尾巴放了进来;min-p 的门槛跟着最高概率一起降,可降得慢,留下的仍是相对像样的候选。这正是 min-p 论文想解决的「高温下既要多样又要通顺」 。

⚠ 常见误解

⚠

「温度改变了模型的判断。」logits 一个没变,T > 0 时排序也不变。温度只改分布的形状,也就是「第二名、第三名还有多少机会」。

⚠

「T = 0 的输出最好,因为每一步都最可能。」每一步最可能,不等于整段最好:我们的模型贪心生成时 47% 的 4-gram 在重复。

⚠

「top-p = 0.9 就是保留 90% 的词。」保留的是「累计概率刚好到 0.9 的最小集合」,个数完全由分布决定:同一个提示,T = 0.5 时 16 个,T = 2 时 1108 个。

真数据:我们项目的实测

项目实验在同一个 checkpoint、同一个提示上扫了温度 × top-p,还让 4 个提示各生成 8 条 × 128 个 token 统计重复率(读 c13-inference.json):

项目实测 · 下一个 token 的分布

项目实测 · 采样设置与重复率

🔍 beam search 呢?

beam search 同时保留几条得分最高的候选序列,每步扩展、再按累计对数概率留下最好的几条。它是「确定性地找高概率序列」,和贪心同一类,所以开放式生成里同样容易重复;翻译、摘要这类答案比较确定的任务里常用。vLLM 能在 beam 之间共享 KV 块,省下的显存比并行采样还多(第 2 课)。

在演进地图上的位置

推理加速的演进:每一代都被上一代的某个真数字逼出来(点名字去那一课;完整版在章首页)
  1. 每一步把整段重新喂进模型
  2. KV cache
  3. 批处理:一次解码多个请求
  4. 连续批处理(迭代级调度)
  5. PagedAttention:KV 分块按需分配
  6. 投机解码:草稿猜、目标一次验
  7. 让模型自己当草稿:MTP 与专用草稿器
  8. 量化:每个权重少读几位
  9. 按 TTFT / TPOT / goodput 调度:分块 prefill、P/D 分离
  10. 采样:温度 → top-k → top-p → min-p
  11. 少存 KV:GQA / MLA / 线性注意力
旁支 ①采样:温度 → top-k → top-p → min-pFan 2018;Holtzman 2019;min-p 2024你在这里
解决了top-p 按累计概率自适应、min-p 跟着最高概率自适应:我们的模型在 T = 1.3 时 top-p 0.9 保留 664 个 token,min-p 0.1 只留 34 个。
卡在哪种截断「更好」只能靠生成质量的实验判断,没有证明(岔路课)。
数字口径:锚点 Qwen3.8-27B 的字节与时间是 H100 SXM 上的屋顶线上界(不是实测),由 anchor-model.json 现算;调度、投机、量化、采样的数字来自我们项目的 3 个小模型(章内 precomputed.json,脚本 gen_c13_precomputed.py,2026-09-26);vLLM、DeepSeek-V3 的数字出自本地 PDF,Orca、DistServe 出自论文原文,均为提出方自报。

证据三问

问本课的回答
① 它解决什么问题?贪心会陷入重复循环;纯采样会被长尾带偏。温度与截断在「多样」和「通顺」之间调
② 数学上为什么?温度不改排序、截断后重新归一化,都是定义上的事实 ;top-p 与 min-p 的保留个数随温度怎么变,是计数(上表)
③ 实验上呢?哪种截断「生成得更好」只能靠实验:Holtzman 2019 用人评与统计指标支持 top-p,min-p 论文报告高温下更好

一句话

📌

温度改分布的形状,截断决定尾巴里多少 token 还有机会;top-p 看累计概率,高温时会放进一长串,min-p 跟着最高概率自适应。

回到主线

成百上千个请求一起解码,KV cache 在显存里怎么放、怎么排? → 第 2 课

第 3 课的投机解码会回到这里:它保证「输出分布不变」,这个分布指的就是目标模型经过同样的温度与截断之后的分布。

你可能会问

这一课还没有收到真实提问;你的提问会记到 05-问答记录/C13-B1.md。