采样是什么
当我们问大语言模型一个问题时,模型不是一次性就生成所有答案的。
模型每次根据当前当上下文 token 序列 \mathbf{u}=[u_1,u_2,\cdots,u_t] 预测下一个 token 的概率分布 P,然后采样策略从这个分布中选出一个token u^\prime,选出的token u^\prime 会拼接在 \mathbf{u} 的后面作为新的上下文重复上述步骤,直到遇到停止条件,比如生成了结束符、达到最大token 数。
这个过程可以简化成:
上下文 -> 模型 -> logits -> softmax -> 概率分布 -> 采样策略 -> 下一个 token -> 更新上下文
这个根据概率分布选择输出 token 的过程就是采样(Sampling),采样方法主要关注如何基于概率分布 P 选择合适的下一个词 u^\prime。
所以,LLM 的输出并不只由模型能力决定,也由采样策略决定。模型负责给出“接下来哪些 token 可能出现?其出现的概率分别是多少”;采样策略负责给出“在这些可能性中,这一次选择哪一个token”。
采样方法的分类
| 分类 | 代表方法 | 核心问题 |
|---|---|---|
| 确定性解码 | Greedy Search、Beam Search | 如何选择概率最高或整体分数最高的序列 |
| 概率采样 | Random Sampling、Temperature、Top-k、Top-p | 如何在质量和多样性之间取平衡 |
| 约束或惩罚型调整 | Repetition Penalty、Frequency Penalty、Presence Penalty、No-repeat N-gram | 如何减少重复、控制话题展开或禁止某些模式 |
| 组合策略 | temperature + top_p + repetition_penalty 等 |
如何把多个旋钮组合成适合任务的生成行为 |
在实践中,这四类方法不是互斥的,而是模型生成过程中的不同控制方式。
一个经典的采样流程可能是:
模型输出 logits
-> 应用 repetition penalty 等惩罚
-> 应用 temperature 调整分布
-> 应用 top-k 或 top-p 截断候选集合
-> 从剩余分布中随机采样
也就是说,temperature、top-k、top-p 可以一起出现,但它们控制的不是同一件事:temperature 改变概率分布的形状,top-k/top-p 改变候选集合,penalty 改变某些 token 的分数。
确定性采样
贪心搜索(Greedy Search):每一步都选最可能的 token
贪心搜索是最直观的采样方法,每一步都选择概率最高的 token,其数学形式可以描述为:
Greedy Search 的稳定性较高,对于摘要、分类解释、短答案、格式化输出这类任务,通常希望模型少一点随机发挥,贪心搜索的确定性就很有价值。但 Greedy Search 的问题也很明显:有时候会因为过于关注局部最优,而生成不自然、重复的句子。更麻烦的是,在长文本生成中,Greedy Search 容易变得平淡、重复,甚至卡进某些循环句式里。
束搜索(Beam Search)
Beam Search 不只保留当前最可能的一个 token,而是保留多个候选序列。这个候选数量通常由 num_beams 控制。
例如 num_beams = 3 时,模型会维护 3 条目前看起来最有希望的生成路径。每一步扩展这些路径,再按累计分数筛选出新的前 3 条路径。
当
num_beams = 1时,Beam Search 实际变成 Greedy Search。
Beam Search 通常使用累计 log 概率给一条候选序列打分:
使用 log 概率是为了把连乘变成求和,数值上更稳定,也更方便比较不同候选路径。
有些实现还会加入长度归一化,避免模型过度偏好短句:
这里的 \alpha 用于控制长度惩罚强度。
Beam Search 解决的是 Greedy Search 存在的短视问题。Greedy Search 可能第一步选了最高概率 token,但后续越走越差;Beam Search 允许某些第一步稍弱但整体更好的路径留下来。
举个简化例子:
路径 1: A -> A1 -> A2,整体分数 0.21
路径 2: B -> B1 -> B2,整体分数 0.25
Greedy Search 可能因为第一步 A 比 B 概率高而选 A,但 Beam Search 有机会发现 B -> B1 -> B2 的整体分数更高。
Beam Search 很适合输入强约束任务,比如机器翻译、语音识别、图像描述,因为这些任务往往有较明确的目标输出。但它不一定适合开放式写作。开放式生成里,累积概率最高的句子通常不是最自然、最有信息量或最有创意的句子,Beam Search 有“安全但无聊”的倾向。
概率采样
确定性解码强调“最可能”,概率采样强调“按概率选择”。它不一定选概率最高的 token,而是让多个合理候选都有机会被选中。
随机采样(Random Samping)
Random Sampling 通过引入随机性来获取输出的多样性,最基础的Random Sampling 就是按完整概率分布抽样。
它的公式可以写成:
第 t 个 token 不是固定取最大值,而是从当前条件概率分布中随机抽一次。
对于下面这样一个 token 概率分布
| token | 概率 |
|---|---|
| A | 0.40 |
| B | 0.30 |
| C | 0.15 |
| D | 0.10 |
| E | 0.05 |
Random Sampling 的含义是
| token | 被选中的机会 |
|---|---|
| A | 40% |
| B | 30% |
| C | 15% |
| D | 10% |
| E | 5% |
Random Sampling 的优点是输出具有多样性,缺点是长尾低概率 token 也有机会被选中,而低概率 token 往往更容易带来跑题、事实错误或语义突兀。因此,纯 Random Sampling 在实际应用里不常单独使用,通常会搭配 temperature、top-k 或 top-p。
Temperature Sampling:调节概率分布的形状
详见:大模型文本生成中 temperature 参数的数学机制
Temperature 是最常见的采样参数之一。它不直接决定候选 token 集合,而是调整概率分布的形状。
| temperature | 效果 |
|---|---|
| 接近 0 | 分布更尖锐,更接近 Greedy Search |
| 1 | 保持原始分布 |
| 大于 1 | 分布更平坦,低概率 token 更有机会被选中 |
Temperature 通常作用在 softmax 之前:
其中 T 就是 temperature。
- T < 1 时,大 logit 会被进一步放大,分布更尖锐;
- T > 1 时,不同 token 的概率差距会被拉平。
如果 temperature 很低,最高概率 token 会变得更突出,输出更稳定、更保守。
如果 temperature 较高,不同 token 的概率差距会缩小,输出更多样、更有惊喜,但也更容易跑偏。
注意:temperature 提高的是随机性,不是能力。高 temperature 不会让模型懂得更多,只是让模型更加偏向输出低概率 token。
Top-k Sampling:只在前 k 个 token 中抽样
Top-k 的做法是:先按概率排序,只保留概率最高的 k 个 token,然后在这 k 个 token 之间重新归一化并采样。
设 V_k 表示概率最高的 k 个 token 集合:
截断之后,把保留下来的 token 概率重新归一化:
Top-k 解决的是长尾风险。它把概率很低的候选挡在外面,避免随机采样选到奇怪 token。
Top-k 存在的问题是候选token 数量 k 是固定的,不是动态变化的,有时候模型输出的合理候选token可能只有1-3个,这时候 k 如果设置的是50,会导致候选范围过于宽泛;而有时候模型输出的合理候选token可能有几十个,设置 k=10 又会导致候选范围过小。
Top-p / Nucleus Sampling:只保留累计概率达到 p 的候选token
Top-p 也叫 Nucleus Sampling(核采样),其核心思想是:不固定候选数量,而是保留累计概率达到 p 的最小候选集合。
它的候选集合可以写成:
其中给你 p 是累计概率阈值,V_p 是按概率从高到低累加后,累计概率达到阈值 p 的最小集合,后续会在这个集合内重新归一化并采样。
以下面这组预测 token 分布为例:
| token | 概率 | 累计概率 |
|---|---|---|
| A | 0.40 | 0.40 |
| B | 0.30 | 0.70 |
| C | 0.15 | 0.85 |
| D | 0.10 | 0.95 |
| E | 0.05 | 1.00 |
如果 top_p = 0.8,从高到低累加:
A = 0.40
A + B = 0.70
A + B + C = 0.85
超过 0.8 的最小集合是 {A, B, C}。所以本轮只在 A、B、C 中采样。
如果 top_p = 0.95,候选集合会变成 {A, B, C, D}。
Top-p 和 Top-k 的关键区别是:
| 方法 | 候选集合大小 | 特点 |
|---|---|---|
| Top-k | 固定 k 个 token | 简单,但不看模型是否确定 |
| Top-p | 动态变化 | 模型确定时集合小,模型不确定时集合大 |
Top-p 能够在排除 长尾低概率 token 和保留生成多样性之间取得自然的平衡。
约束惩罚
前面的几种方法主要控制“如何从分布中选择token”,约束惩罚则更像是在采样前先修改分布:降低某些 token 的分数,或者直接禁止某些模式。
重复惩罚(Repetition Penalty):惩罚已经出现过的 token
Repetition Penalty 用来减少重复,它会降低已出现 token 的得分,让模型不那么容易反复生成同一个token。
一个简化写法是:
其中, r 是 repetition penalty。现实实现通常还会处理负 logits 等细节,其效果都是:已经出现过的 token 会被压低分数。r 的值越大,惩罚越强。
例如模型已经生成:
这个方法很重要,因为这个方法
如果没有惩罚,模型可能继续生成“很重要”,形成重复句式。加上 repetition penalty 后,已经出现过的“这个”“方法”“很”“重要”等 token 得分会被压低,模型更可能换一种表达。
频率惩罚(Frequency Penalty):出现次数越多,惩罚越强
Frequency Penalty 关注 token 的出现次数,一个 token 出现得次数越多,它受到的惩罚强度越大。
Frequency Penalty 可以和 Presence Penalty 放在同一个公式中理解:
其中 c_i 表示 token i 已经出现的次数,\alpha_\mathrm{frequency} 表示频率惩罚强度,\alpha_\mathrm{presence} 表示存在惩罚强度。Frequency Penalty 关注的是出现了多少次,所以惩罚项和 c_i 成正比。\mathbf{1}_{c_i > 0} 是一个指示函数:只要 token 出现过,它就是 1;没出现过就是 0。
Frequency Penalty 适合处理模型不断重复同一个词、同一个口头禅、同一句式的问题。
例如模型反复输出:
非常非常非常重要
Frequency Penalty 会随着“非常”出现次数增加而加大惩罚。和 Repetition Penalty 相比,它更强调累计次数,而不是简单判断是否出现过。
存在惩罚(Presence Penalty):出现过就惩罚,鼓励话题展开
Presence Penalty(存在惩罚)关注是否出现过,只要某个 token 已经出现,它就会受到惩罚,不随着次数线性增加。
Presence Penalty 的作用更像是鼓励模型引入新内容,而不是围绕已经说过的词一直打转。例如在头脑风暴中,如果模型一直围绕“性能”展开,Presence Penalty 可以稍微鼓励它转向“成本”“用户体验”“可维护性”等新角度。
No-repeat N-gram:禁止重复 n 元片段
No-repeat N-gram 是更硬的约束,它不是降低重复片段概率,而是禁止已经出现过的 n-gram 再次出现。可以将其看作一个硬过滤规则:
其中 p'_i = 0 表示 token i 在当前步被完全禁止。也就是说,如果追加某个 token 会形成已经出现过的 n-gram,这个 token 在当前步就被直接排除,而不是只降低概率。
如果 no_repeat_ngram_size = 3,那么已经出现过的三 token 片段不能再次生成。
例如已生成:
机器学习 是 一种 方法
如果三元片段“是一种方法”已经出现过,后续就不能再次生成同样的三元片段。
这种方法对减少机械重复很有效,但约束过强时会误伤自然表达。尤其是专有名词、固定短语、诗歌、歌词、法律文本这类本来就需要重复结构的文本,要谨慎使用。
采样策略的组合
实际生成中,我们很少只用一种策略。更常见的是组合多个采样方法,让它们各司其职。
| 控制点 | 常用策略 | 主要解决什么 |
|---|---|---|
| 修改 token 分数 | Repetition/Frequency/Presence Penalty | 重复、啰嗦、话题停滞 |
| 调整概率形状 | Temperature Sampling | 输出保守还是发散 |
| 裁剪候选集合 | Top-k / Top-p Sampling | 排除低质量长尾候选 |
| 最终选择 token | Random Sampling 或 Greedy Search | 是否引入随机性 |
一个简化的组合流程可以写成:
Penalty 先改变 logits,temperature 决定概率分布尖锐程度,Top-k/Top-p 决定候选范围,最后再从剩余候选里采样。
总结
| 方法 | 是否随机 | 主要控制什么 | 常见参数 | 输出特点 | 适用场景 |
|---|---|---|---|---|---|
| Greedy Search(贪心搜索) | 否 | 当前最高概率 token | 无或默认 | 稳定、保守、可能重复 | 短答案、格式化输出 |
| Beam Search(束搜索) | 否 | 多条候选序列的整体分数 | num_beams |
更重视整体概率,可能平淡 | 翻译、语音识别、图像描述 |
| Random Sampling(随机采样) | 是 | 完整概率分布随机抽样 | do_sample |
多样但风险高 | 很少单独使用 |
| Temperature Sampling(温度采样) | 是 | 概率分布尖锐程度 | temperature |
低温稳定,高温发散 | 问答、写作、对话 |
| Top-k Sampling(Top-k 采样) | 是 | 固定数量候选 token | top_k |
排除长尾,简单直接 | 通用生成、轻量控制 |
| Top-p / Nucleus Sampling(核采样) | 是 | 动态累计概率候选集 | top_p |
更自适应,开放生成常用 | 聊天、创作、头脑风暴 |
| Repetition Penalty(重复惩罚) | 取决于主策略 | 已出现 token 的得分 | repetition_penalty |
减少重复 | 长文本、对话 |
| Frequency Penalty(频率惩罚) | 取决于主策略 | 出现次数越多惩罚越强 | frequency_penalty |
抑制高频重复 | 对话、开放生成 |
| Presence Penalty(存在惩罚) | 取决于主策略 | 出现过即惩罚 | presence_penalty |
鼓励新话题 | 头脑风暴、创意探索 |
| No-repeat N-gram(禁止重复 N-gram) | 取决于主策略 | 禁止重复 n-gram | no_repeat_ngram_size |
强力去重,但可能生硬 | 摘要、长文本去重 |