采样参数
自回归解码每一步输出的是整个词表上的概率分布——而词表可以有 151K 个 token。怎么从这个分布里挑出下一个词,就是采样参数干的事。
它们只影响解码阶段,不改变模型的内部表示或权重。所以同一份权重配不同参数,行为可以差很多——这正是「本地自托管和 API 输出不一样」的一个常见原因。
温度:重塑整个分布
温度在 softmax 之前缩放 logits:
| 温度 | 效果 |
|---|---|
| T = 0 | 等价于贪心——总取最高概率那个 |
| T = 1 | 从模型的自然分布采样 |
| T < 1 | 分布变尖锐——大值更突出,输出更可预测、更连贯,但可能重复、缺乏创造力 |
| T > 1 | 分布变平坦——更有创意,但会不连贯或出现事实错误 |
实践边界:超过约 1.3 输出通常就退化成不连贯了。
三种「切候选池」的方式
温度是重塑整个分布;下面这三种是把分布切到一个候选池再采样——无论骰子怎么掷,模型都不可能选到极端不合理的 token。
| 方法 | 机制 | 问题 |
|---|---|---|
| Top-k | 只保留概率最高的 k 个,丢弃其余,重新归一化再采样 | 不自适应。模型很自信时 k=40 会保留 39 个几乎无关的 token;模型不确定、有几百个合理候选时 k=40 又砍掉了真实候选 |
| Top-p(nucleus) | 保留累积概率达到 p 的最小集合,然后只从这个集合里采样 | 池子随模型置信度自适应:自信时几个 token 就够,不确定时需要很多 |
| Min-p | 设一个相对于最高 token 的概率下限:保留所有至少是最高概率那个的某个倍数的 token | 同样自适应,但更容易推理,而且不会像固定 top-p 阈值那样让一条平坦的长尾溜进候选池 |
为什么 top-p 成了多数聊天 API 的默认:它的自适应机制正好匹配「模型有时候很确定、有时候很不确定」这个现实。
三者的具体行为(一组对照)
以 logits = [0.6, 0.3, 0.05, 0.03, 0.015, 0.005] 为例:
| 参数 | 结果 | 问题 |
|---|---|---|
| Top-k = 4 | [0.6, 0.3, 0.05, 0.03, 0, 0] | Top-k 可能选到概率很低的样本 |
| Top-p = 0.95 | [0.6, 0.3, 0.05, 0, 0, 0] | —— |
| Min-p = 0.5 | [0.6, 0.3, 0, 0, 0, 0] | —— |
反过来看各自的失效场景:
- Top-k:
logits = [0.6, 0.3, 0.05, 0.03, ...],k=3 → 0.05 概率的 token 也被采进去了 - Top-p:
logits = [0.2, 0.2, 0.2, 0.2, 0.1, 0.1],p=0.95 → 全部值都有效,池子等于没切 - Min-p:
logits = [0.8, 0.1, 0.05, ...],p=0.5 → 只剩一个 token
所以生产上通常是混用的,而不是三选一。
同一组 logits 上,三种切法的边界落点不同。
logits = [0.6, 0.3, 0.05, 0.03, 0.015, 0.005],按概率降序排列
规则 0.6 0.3 0.05 0.03 0.015 0.005
│ │ │ │ │ │
Top-k = 4 █───────█───────█───────█ ▏ ▏ ← 固定保留 4 个
│ │ │ │ │ │
Top-p = 0.95 █───────█───────█ │ │ │ ← 累积 0.6+0.3+0.05 = 0.95
│ │ │ │ │ │
Min-p = 0.5 █───────█ │ │ │ │ ← 概率 ≥ 0.6 × 0.5 = 0.3
│ │ │ │ │ │
留 留 留 丢 丢 丢
三种各自的失效场景
Top-k logits = [0.6, 0.3, 0.05, 0.03, …],k = 3 → 0.05 的 token 也被采进去
Top-p logits = [0.2, 0.2, 0.2, 0.2, 0.1, 0.1],p = 0.95 → 全部值都有效,池子等于没切
Min-p logits = [0.8, 0.1, 0.05, …],倍数 0.5 → 只剩一个 token一条必须知道的交互
温度先于 top-p / top-k 应用。 这意味着低温度会先把分布变尖,进而缩小 top-p 的有效 nucleus。
具体量级:在温度 0.3 + top-p 0.9 下,nucleus 通常只含 1–3 个 token。
这条解释了为什么「调低温度」和「调低 top-p」不是两种等价的做法——它们的作用顺序决定了叠加后的效果。
生产 API 的顺序通常是:temperature → top-k → top-p(HuggingFace 的 _get_logits_warper 就是这个次序;min-p 排在最后),即它们是叠加而不是互斥选项。
一个常见的组合是 top_p=0.9 + temperature=0.7 且不显式设 top-k,让 nucleus 做自适应的工作。
反过来,也有人设 top-k=50 + top-p=0.9——top-k 在这里当安全边界用,防止分布很平时 nucleus 病态地变大。
top-p 太小的副作用:低于 0.6 会导致重复,因为 nucleus 太小,模型反复从一小组 token 里采。
五种对 logits 的调整
前面几种是「切池子」,还有几类是直接改 logits 值。
三种惩罚
| 惩罚 | 机制 | 默认值 / 范围 |
|---|---|---|
| 频率惩罚(frequency) | 按已出现次数成比例扣减——用了 5 次的词比用 1 次的被罚得重得多 | 0,[-2, 2] |
| 存在惩罚(presence) | 只要出现过就扣一个固定值,不论次数多少 —— 推向引入新话题而非只避免字面重复 | 0,[-2, 2] |
| 重复惩罚(repetition) | 乘法:对已出现 token 的 logit 除以(正值)或乘以(负值)一个因子 | 1.0,[0, 2] |
重复惩罚的具体算法:
logits[i] -= frequency_penalty × count
logits[i] -= presence_penalty
logits[i] /= repetition_penalty ** (count - 1) 当 logits[i] > 0
logits[i] *= repetition_penalty ** (count - 1) 当 logits[i] < 0注意最后两行的方向是反的——正 logit 除以因子会变小,负 logit 乘以因子会更负。两者都是「降低被选中的概率」,方向和量级要一起看才正确。
重复惩罚的副作用:超过 1.5 往往会过度校正,让模型回避本来合理、确实需要重复出现的词。1.2–1.4 是开放式生成常用的区间。
还有两种
| 操作 | 作用 |
|---|---|
| 偏置(logit bias) | 给指定 token 加一个固定偏移——用于施加硬约束(例:只允许输出 7 个星期名) |
| 掩膜(mask) | 把范围外的值置为 0——结构化输出的底层机制 |
操作顺序会改变结果,而且各实现不同
有些实现把惩罚放在温度之前,有些放在之后——同样的参数会产生略有不同的输出分布。
这是自托管模型与 API 端点输出不同的一个原因(即使权重完全一样)。所以复现别人的结果时,除了权重和参数,还要确认采样管线的顺序。
一条 logits 从模型输出到被采成一个 token,中间要过一整条算子链。
logits(整词表,151K 维)
│
├─▶ 惩罚(直接改 logits 值)
│ frequency :减去 frequency_penalty × count
│ presence :只要出现过就减去一个固定值,不论次数
│ repetition:除以 / 乘以 repetition_penalty^(count−1),正负 logit 方向相反
│
├─▶ temperature:logits / T ← 重塑整个分布
│
├─▶ top-k:只保留概率最高的 k 个 ┐
├─▶ top-p:只保留累积概率达 p 的最小集合 ├─ 切候选池(不改分布形状)
├─▶ min-p:只保留 ≥ 最高概率 × 倍数的 ┘
│
└─▶ softmax 归一 ──▶ 采样一个 token
各实现的差异都在链上的两处:惩罚放在温度之前还是之后、top-k 与 top-p 谁先。
同样的参数因此会产出略有不同的分布。确定性采样
对应「不需要多样性、只要更准」的场景(机器翻译、摘要生成)。
贪心:单步最优,不是全局最优
贪心保证每一步取概率最大的,但不保证累积概率最大。 一个具体例子:
| 路径 | 累积概率 |
|---|---|
| step1 选「吃」(0.6) → step2 选「你」(0.3) | 0.18 |
| step1 选「爱」(0.3) → step2 选「吃」(0.7) | 0.21 |
前缀一变,下一步的分布就变——所以单步贪心会错过累积概率更高的路径。
Beam Search:贪婪与暴力之间的折中
要找全局最优,本质是一个多叉树最优解问题。暴力搜索的路径数是
压缩
| 方案 | |
|---|---|
| = 1 | 贪心搜索(一条路径) |
| = 定值 k | Beam Search(k 条路径) |
Beam Search 的步骤:首轮 top-k → 每个分支各自 top-k 并算累积概率 → 全局 k×k 里取前 k → 重复到全部分支遇到结束符 → 取累积概率最大的分支。
它的缺陷与修正:因为概率都在 (0,1) 区间,同一分支计算次数越少,累积概率越高——这让 beam search 趋向于选短输出。解法是长度惩罚:
也可以用 min_new_tokens / max_new_tokens 控制长度范围(实现方式是:长度小于 min_new_tokens 时不让模型输出终止符)。
Beam Search 的扩展与剪枝可以用一棵
k = 2 的示意
步骤 0 步骤 1 步骤 2
┌─ a (0.6) ─┬─ a·x (0.42)
│ └─ a·y (0.18)
[起始] ─────────┤
│ ┌─ b·u (0.21)
└─ b (0.3) ─┤
└─ b·v (0.09)
第一轮:从整词表取 top-k → 得到 k 条候选路径
第二轮:每个分支各自取 top-k(共 k×k 个) → 全局取前 k,其余整支连同后续剪掉
重复直到全部分支遇到结束符 → 在 k 条完整路径里取累积概率最大的一条
长度惩罚:概率都在 (0, 1) 区间,同一分支算的次数越少累积概率越高,
beam search 因此天然偏向短输出
logprob = cumulative_logprob / seq_len^length_penalty按任务配参数
这是这一篇最有用的一张表——因为最常见的错误就是在所有任务上用同一套参数:
| 场景 | temperature | top-p | 说明 |
|---|---|---|---|
| 函数调用 / JSON 提取 | 0 | —— | 用贪心,不需要采样参数 |
| 代码生成 | 0–0.2 | 1.0 | 任务有正确答案,多样性是负面的 |
| 结构化输出(JSON) | 0 | 1.0 | 贪心避免格式错误 |
| 事实问答 | 0–0.3 | 0.95 | 低创造 |
| IDE 代码补全 | 0.2–0.4 | 0.95 | 比聊天低,保持接地 |
| 指令跟随聊天 | 0.7 | 0.9 | 不另设 top-k |
| 创意写作 | 0.7–1.0 | 0.95 | 需要更高多样性 |
| 头脑风暴 | 1.0–1.3 | 1.0 | 最大多样性 |
这张表对 Agent 的直接含义:工具调用必须接近贪心。
同一个智能体里,「决定调哪个工具、参数填什么」和「生成给用户看的自然语言」是两种需求相反的任务——前者要求确定性,后者要求自然。用一个参数跑完全流程,必然有一端是错的。
生产系统应该按端点区分配置,而不是全局一套。
流式的一些实现细节
这些细节会在排查「输出乱码 / 断字」这类问题时用上:
| 点 | 说明 |
|---|---|
| 去分词(detokenization) | 模型生成的是 token ID 而不是文本。token 边界不与字符边界对齐(尤其多字节 UTF-8),而且一个 token 可能是另一个 token 的前缀——所以服务器有时候要缓冲 1–2 个 token 才能保证去分词后的文本是合法 UTF-8 |
| 特殊 token | 特殊 token 要在输出里过滤掉 |
| token 分组 | 有些提供商会把多个 token 批进一个 SSE 事件——100 token/s 的服务器可能每 30–50 ms 发一个含 3–5 个 token 的事件 |
| 背压 | 客户端读得慢时服务器输出缓冲会填满。实现良好的推理服务器会对 decode 循环施加背压——不会生成得比客户端消费得更快,代价是慢客户端拿到更低吞吐 |
「缓冲 1–2 个 token」这条解释了流式输出里偶尔出现的「字卡一下」——那不是网络问题,是去分词器在等够字节。
流式输出从 token ID 到客户端拿到文本,中间有几处缓冲。
decode 循环
│ 产出的是 token ID,不是文本
▼
去分词缓冲(1–2 个 token)
│ 为什么必须缓冲:token 边界不与字符边界对齐(多字节 UTF-8),
│ 而且一个 token 可能是另一个 token 的前缀
│ —— 攒够字节才能保证拼出来的文本是合法 UTF-8
▼
过滤特殊 token
│
▼
按 token 分组打包
│ 100 token/s 的服务器常每 30–50 ms 发一个含 3–5 个 token 的 SSE 事件
▼
SSE 事件 ──▶ 客户端
背压:客户端读得慢 → 输出缓冲填满 → 服务器对 decode 循环施加背压
不会生成得比客户端消费得更快,代价是慢客户端拿到更低吞吐和相邻概念的关系
| 概念 | 关系 |
|---|---|
| 温度 vs 幻觉 | 随机采样会偶尔选到「不太准确但很可能」的 token 并引发一连串错误(见 07-模型幻觉)。但不连贯 ≠ 幻觉——前者是话说不通,后者是说得很通但内容假 |
| 温度 vs 一致性检测 | Self-Consistency 和 置信度分层都靠多次采样的一致性当信号——温度太低会削弱这个信号(每次都一样,无从比较);太高则会把「模型不确定」和「采样噪声」混在一起 |
| 惩罚 vs 循环 | 重复惩罚是压循环的手段,但它是概率性的;Agent Loop 里的「重复动作检测」是确定性的(同一动作+同一参数连续出现就中断) |
相关
- 03-Decoder-Only 与自回归 —— 「每步输出是整词表上的分布」这个前提从哪来
- 17-Prompt Engineering —— CoT 与 Self-Consistency 都受温度影响
- 07-模型幻觉 —— 随机采样与幻觉的关系
- 08-模型选型 —— 模型定了之后才轮到这一层
- 23-置信度分层与 Human-in-the-loop —— 一致性信号与温度的关系
参考
- 来源为腾讯云开发者社区的《LLM 推理采样(Sampling)基本原理》整理
- https://devonburriss.me/notes/sampling-parameters 、https://stochasticsandbox.com/posts/the-inference-stack-top-to-bottom-2026-03-27
- https://careerstack.dev/llm-decoding-strategies
- https://mljourney.com/?p=4880/
YJ