Skip to content

解码策略与采样

本页讲解 LLM 应用生态概览中”生成输出”环节的关键环节——解码策略(Decoding Strategies):模型在每个位置给出一个概率分布,而我们用什么规则从中”挑词”,直接决定了输出的质量、多样性与速度。它和 LLM 推理紧密相关——推理优化解决”算得快”,解码策略解决”挑得好”。

生成文本 = 在一棵无限分支的树上选路,解码策略就是你的选路规则。

大语言模型(LLM)本质上是一个”下一词预测器”:给定已写好的文本,它输出整个词表上每个候选词的概率。词表(vocabulary)通常有 3 万到 25 万个 token——token 是模型处理文本的最小单位,由 tokenizer(分词器)将原始文本切分而来,一个 token 可能是一个字、一个词、甚至半个词。从这个巨大的概率分布中选出”下一个词”的方法,就是解码策略。

  • Greedy(贪心):每一步都选当前概率最大的那条路——最稳但最容易在原地打转,重复啰嗦。
  • Beam Search(束搜索):同时保留 k 条最有希望的路径,最后选总概率最高的——质量高但多样性差,容易”正确但无聊”。
  • Temperature(温度):相当于”旋钮”,调高让分布变平(更随机冒险),调低让分布变尖(更保守确定)。
  • Top-K:只允许从概率最高的 K 个路口里挑,把长尾的烂路堵死。
  • Top-P / Nucleus(核采样):只从累积概率达到 P 的最小路口集合里挑——路口多时多挑,少时少挑,比 Top-K 更灵活。
  • Min-P 采样:2024 年流行的较新方法,只保留概率不低于”最大概率 × 阈值”的词,在分布尖与平之间自适应,比 Top-P 更稳健。
  • Contrastive Search(对比搜索):挑词时还检查”这个词跟前面是不是太像了”,太像就惩罚,专门治重复。
  • Speculative Decoding(投机解码):派个小模型先快速跑一截草稿,大模型再批量验证,目标是加速而非改变分布。
  • Constrained Decoding(受限解码):在解码时强制输出符合某种格式(如 JSON、正则、语法),让”挑词”只在合法的分支上进行。

LLM 生成文本是**自回归(autoregressive)**的:每一步根据已生成的全部 token,预测下一个 token 在整个词表上的概率分布。具体地,模型最后一层输出每个词的 logit(未归一化分数),经 softmax 转成概率:

pi=exp⁡(logiti/T)∑jexp⁡(logitj/T)p_i = \frac{\exp(\text{logit}_i / T)}{\sum_j \exp(\text{logit}_j / T)}

其中 T 是温度参数。选哪个词,就是解码策略要回答的问题。选完一个词拼回输入,再预测下一个,如此循环——这就是生成时”逐词吐出”的本质。

每一步生成都需要重新做一次前向传播(forward pass),而且每增加一个 token,注意力机制(attention)的计算量也会增长——因为新 token 要”关注”(attend to)之前所有 token。这也是为什么推理速度与生成长度相关,以及 KV cache(将历史 token 的键值向量缓存下来避免重复计算)如此重要的原因。详见 推理优化技术。

每步取概率最大的词(argmax)。优点是完全确定、实现简单、速度最快;缺点是只看局部最优,容易陷入”重复循环”——比如反复输出同一句话。它适合确定性要求高、容错的任务(如结构化抽取、固定格式输出)。

贪心解码的重复问题源于:当模型对自己生成的上下文产生”自我强化”时,概率分布会在某个局部最优区域稳定下来,形成循环。这也是为什么后续的采样类方法被广泛采用。

维护 k(束宽)个候选序列,每一步对每个候选扩展所有可能词,保留总累计概率最高的 k 条,最后选总概率最高的序列。它平衡了质量与多样性,在机器翻译、摘要等”有标准答案”的任务上是经典选择。但束宽较大时计算量是 Greedy 的 k 倍,且对开放式生成容易产生”安全但平庸”的文本,多样性不足。

实践提示:在现代 instruction-tuned(指令微调)LLM 中,Beam Search 已不再是默认推荐——指令微调后的模型在 Greedy 或低温采样下已经能产生高质量输出,Beam Search 的”正确但无聊”问题反而更加明显。它仍在机器翻译、语音识别等领域有不可替代的价值。

解码前对 logit 做缩放:用 logit 除以温度 T 再做 softmax。T 大于 1 时分布变平,低概率词更易被选,输出更随机多样;T 小于 1 时分布变尖,更倾向高概率词,更确定保守;T 等于 1 即原始分布。温度常与 Top-K、Top-P 组合使用,是调节”创造性”最常用的旋钮。

直观地理解温度:

  • T → 0:分布退化为 one-hot,等价于 Greedy——永远选概率最大的词。
  • T = 1:原始概率分布。
  • T → ∞:分布趋于均匀,每个词概率几乎相等——输出变成随机噪声。
T = 0.5 (更尖锐) T = 1.0 (原始) T = 2.0 (更平坦)
████████████ 90% ██████ 60% ████ 35%
█ 5% ███ 25% ███ 25%
▏3% ██ 10% ██ 15%
▏2% █ 5% ██ 15%
▏...其余 ▏...其余更分散

每步只保留概率最高的 K 个词,把它们的概率重新归一化后采样,其余词概率清零。它过滤了长尾的”噪音词”,提升了质量。缺点是 K 是固定的——当分布很集中时 K 太大会放进噪音词,分布很平均时 K 太小会漏掉好词。

为解决 Top-K 的固定阈值问题,Top-P 选择累积概率达到 P 的最小词集合:把词按概率从高到低排序,依次累加,直到累积概率首次大于等于 P,只在这个集合里采样。于是分布集中时集合小(少挑词),分布平均时集合大(多挑词),自适应且效果好。Top-P 等于 1 等价于不截断。它是开放式对话与创意生成的主流选择。

2024 年在开源社区(尤其 llama.cpp / Ollama 生态)中广泛流行的新方法。它的思路是设定一个相对阈值:只保留概率不低于 min_p × max_prob 的词,其中 max_prob 是当前步最高概率词的概率值,min_p 是 0 到 1 之间的参数(如 0.05)。

与 Top-P 相比,Min-P 的优势在于它自适应于分布的”尖峰程度”:

  • 当模型非常确定(某个词概率 0.95)时,Min-P 只保留概率 ≥ 0.05×0.95 ≈ 0.048 的词,天然地只留下极少数候选。
  • 当模型不确定(最高词概率才 0.15)时,Min-P 保留概率 ≥ 0.05×0.15 ≈ 0.0075 的词,允许更多候选参与采样。

这使得 Min-P 在同一参数值下,对不同确定性的生成步都能给出合理的候选集,减少了需要针对不同模型反复调参的负担。

每步在 Top-K 候选中选词时,额外引入一个惩罚项:衡量当前候选词的表示(通常取模型某一层的隐藏状态向量,即 embedding 维度的特征)与”已生成上下文”中各 token 表示的相似度,相似度高(即重复)则降权。它用一个对比得分 = 概率项减去惩罚系数乘以最大相似度,来综合排序候选。效果上既能保持流畅连贯,又能显著减少重复,是兼顾质量与多样性的较新方法。

除了选择不同的解码策略框架,实践中还经常加入各种惩罚项来微调采採样行为:

  • Repetition Penalty(重复惩罚):对已经出现过的 token 的 logit 做惩罚除法(logit = logit / penalty,penalty > 1),降低已生成词被再次选中的概率。OpenAI API 中常见 frequency_penalty(频率惩罚,按出现次数惩罚)和 presence_penalty(存在惩罚,出现过即惩罚,不论次数)。
  • Min_new_tokens / Min_length:强制模型至少生成 N 个 token 才允许输出 EOS(结束符),防止模型”过早结束”。
  • No-repeat-ngram-size:禁止生成已出现过的 n-gram(连续 n 个词的组合),强制阻断重复短语。

这是一种加速技术而非改变输出分布。思路是:用一个小的 draft 模型快速自回归生成若干个候选 token(小模型单步更快),再用大模型对这些候选做一次并行的前向验证。大模型接受所有与小模型一致的 token,在第一个不一致处重新采样。由于大模型一次前向能验证多个 token,整体大幅减少大模型的串行前向次数。关键是:最终分布与大模型单独解码完全一致,是无损加速。

2024-2025 年,投机解码的变体大量涌现:

  • Medusa:训练多个”头”(head)让单个模型同时预测未来多个位置的 token,无需额外的 draft 模型。
  • EAGLE / EAGLE-2:用一个轻量级 head 预测大模型的隐藏状态,再从隐藏状态推导出候选 token,接受率(acceptance rate)显著提高。
  • Lookahead Decoding:通过 Jacobi 迭代从目标序列的末尾同时并行推测多个候选,无需任何额外训练。
  • Self-Speculative Decoding:同一个大模型用”跳层”(early-exit,提前在中间层输出)来充当 draft 模型,无需第二个模型。

详见 推理优化技术。

受限解码是 2024-2025 年随着结构化输出需求爆发(尤其 Agent 与 function calling(函数调用,即让 LLM 调用外部 API/工具)场景)而备受关注的方向。核心思想:在每一步采样前,根据预定义的格式约束(JSON Schema、正则表达式、上下文无关文法 CFG 等)计算出当前合法的 token 子集,将非法 token 的概率直接置零,再在合法集合中做采样。

这意味着模型永远不会生成不符合格式的输出——不是事后纠错,而是从源头限制。

# 概念示意:受限解码每步的"合法集合"过滤
for step in generation_steps:
logits = model.forward(context) # 原始 logit 分布
legal_mask = grammar.get_legal_tokens(context) # 根据语法/Schema 计算合法 token
logits[~legal_mask] = -inf # 非法 token 概率清零
next_token = sample(softmax(logits)) # 在合法集合中采样
context.append(next_token)

典型工具与框架:

  • Outlines:Python 库,将正则/JSON Schema/Pydantic 模型编译成 token 级别的约束,与 HuggingFace、vLLM 等兼容。
  • xGrammar(2024):高性能的 grammar-constrained 解码引擎,支持上下文无关文法,已集成进 vLLM 和 TensorRT-LLM。
  • LMQL(Language Model Query Language):声明式约束查询语言,可以在 prompt 中直接写约束表达式。
  • OpenAI Structured Outputs / JSON Mode:API 级别的受限解码,传入 JSON Schema 即保证输出 100% 符合 Schema。

2024-2025 年随着”测试时计算(test-time compute / inference-time scaling)“概念的兴起,Best-of-N 采样(也称拒绝采样,Rejection Sampling)重新受到关注。思路是:用某采样策略独立生成 N 条候选输出,再用一个**奖励模型(reward model)**或打分函数选出最优的一条。

这本质上是用更多推理时的计算换取更高质量——类似于让模型”多想几次再回答”。OpenAI 的 o1/o3 系列以及 DeepSeek-R1 等推理模型在概念上沿用了”推理时多花计算 → 更好结果”的范式(虽然它们用的是学习出来的思维链而非简单的 Best-of-N)。

策略多样性质量速度典型场景
Greedy低一般最快结构化输出、抽取、固定格式
Beam Search低高慢(k 倍)翻译、摘要
Temperature+Top-P高较高快对话、创意写作、续写
Temperature+Min-P高较高快通用生成(自适应性好)
Top-K中较高快通用生成(需调 K)
Contrastive Search中高高较快长文本、抗重复
Constrained Decoding—高中JSON/结构化输出、function calling
Speculative Decoding同基方法同基方法更快加速任意上述方法
Best-of-N高高慢(N 倍)高质量输出、推理时扩展

基础:HuggingFace Transformers 中的各策略对比

Section titled “基础:HuggingFace Transformers 中的各策略对比”
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
model_id = "meta-llama/Llama-3.2-1B"
tok = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto")
prompt = "人工智能的未来是"
ids = tok(prompt, return_tensors="pt").input_ids.to(model.device)
# --- 1. 贪心解码:do_sample=False,等价于 argmax ---
out_greedy = model.generate(ids, max_new_tokens=20, do_sample=False)
# --- 2. 核采样 Top-P + 温度:开放式创意生成 ---
out_nucleus = model.generate(
ids, max_new_tokens=20, do_sample=True, temperature=0.8,
top_p=0.9, top_k=0, # top_k=0 表示不启用 Top-K 截断
)
# --- 3. Contrastive Search:抗重复 ---
out_contrastive = model.generate(
ids, max_new_tokens=20,
penalty_alpha=0.6, # 对比惩罚强度,0.5-0.7 常用
top_k=4, # 对比搜索的候选集大小
)
# --- 4. 带重复惩罚的采样 ---
out_penalty = model.generate(
ids, max_new_tokens=20, do_sample=True,
temperature=0.7, top_p=0.95,
repetition_penalty=1.15, # > 1 惩罚已出现的词
)
print("贪心: ", tok.decode(out_greedy[0], skip_special_tokens=True))
print("核采样: ", tok.decode(out_nucleus[0], skip_special_tokens=True))
print("对比搜索:", tok.decode(out_contrastive[0], skip_special_tokens=True))
print("带惩罚: ", tok.decode(out_penalty[0], skip_special_tokens=True))

进阶:使用 Outlines 做受限解码(结构化输出)

Section titled “进阶:使用 Outlines 做受限解码(结构化输出)”
# pip install outlines
import outlines
from pydantic import BaseModel
# 加载模型(Outlines 会自动处理 tokenizer 和约束编译)
model = outlines.models.transformers("meta-llama/Llama-3.2-1B")
# 定义输出 Schema:让模型输出一个结构化的"人物信息"
class Person(BaseModel):
name: str
age: int
occupation: str
interests: list[str]
# Outlines 会将 Pydantic Schema 编译成 token 级别的约束
# 模型在每一步只能生成符合 JSON 格式的 token
@outlines.generate.json(model, Person)
def extract_person(prompt: str):
return prompt
result = extract_person(
"从以下文本中提取人物信息:"
"张三,28 岁,是一名软件工程师,喜欢编程和登山。"
)
print(result)
# Person(name='张三', age=28, occupation='软件工程师', interests=['编程', '登山'])
# ↑ 保证输出 100% 符合 Person Schema,无需重试或修复
# vLLM 0.6+ 支持 EAGLE / Medusa 等投机解码变体
# 使用命令行启动:
# vllm serve meta-llama/Llama-3.1-70B \
# --speculative-model meta-llama/Llama-3.2-1B \
# --num-speculative-tokens 5
from vllm import LLM, SamplingParams
llm = LLM(
model="meta-llama/Llama-3.1-70B",
# 投机解码在服务端配置,API 层无感知
)
sampling_params = SamplingParams(
temperature=0.8,
top_p=0.9,
max_tokens=200,
)
outputs = llm.generate(["解释什么是扩散模型"], sampling_params)
print(outputs[0].outputs[0].text)
# 输出质量与非投机解码完全一致,但延迟可能降低 2-3 倍

直觉实验:观察概率分布如何被采样参数改变

Section titled “直觉实验:观察概率分布如何被采样参数改变”
import torch
import torch.nn.functional as F
# 模拟模型输出的 6 个 token 的 logit
logits = torch.tensor([8.0, 6.5, 5.0, 3.0, 1.0, 0.5])
def show(label, probs):
bar = lambda p: "█" * int(p * 50)
print(f"\n{label}:")
for i, p in enumerate(probs):
print(f" token_{i}: {p*100:6.2f}% {bar(p)}")
# 原始分布(temperature=1.0)
show("T=1.0 原始", F.softmax(logits, dim=-1))
# 低温:分布更尖
show("T=0.5 保守", F.softmax(logits / 0.5, dim=-1))
# 高温:分布更平
show("T=2.0 冒险", F.softmax(logits / 2.0, dim=-1))
# Top-P=0.9:只保留累积概率达 0.9 的最小集
probs = F.softmax(logits, dim=-1)
sorted_probs, idx = torch.sort(probs, descending=True)
cumsum = torch.cumsum(sorted_probs, dim=-1)
nucleus_mask = cumsum <= 0.9
nucleus_mask[0] = True # 至少保留概率最高的一个
nucleus_probs = sorted_probs * nucleus_mask.float()
nucleus_probs = nucleus_probs / nucleus_probs.sum()
result = torch.zeros_like(probs)
result[idx[nucleus_mask]] = nucleus_probs[nucleus_mask]
show("Top-P=0.9 核采样", result)
  • 对话与创意首选 Top-P + Temperature:Top-P 取 0.9-0.95,温度取 0.7-1.0,是开放式生成最稳妥的默认组合。
  • Min-P 作为更省心的替代:Min-P 取 0.05-0.1 配合温度 0.7-0.9,在 2024 年开源社区中被证明对不同模型泛化性更好,减少逐模型调参的工作量。
  • 确定性输出用 Greedy 或低温度:生成 结构化输出(JSON、表格)或受限解码时,低温度甚至 Greedy 能减少格式错误;最佳实践是直接用 Constrained Decoding 从源头保证格式正确。
  • Beam Search 不一定适合聊天:它在”有标准答案”的任务(翻译、摘要)上强,但在开放聊天中容易输出”正确但无趣”的内容,多样性不足。
  • 遇到重复就换策略:若输出陷入循环,改用 Contrastive Search 或调低温度、加 repetition_penalty(1.1-1.3),往往立竿见影。如果问题顽固,尝试 no_repeat_ngram_size=3 强制阻断三连词重复。
  • 采样参数要随模型调整:小模型分布更平,通常需要更低的温度;大模型分布更尖,可承受更高温度。先小步长扫描再定参。
  • 指令微调 vs 基座模型:instruction-tuned 模型(如 Llama-3-Instruct)经过 RLHF/DPO 训练,分布本身就很”好控”,低温度(0.6-0.8)通常就够了;基座模型(base model)往往需要更高温度和更强的截断。
  • 推理模型(o1/o3/R1 类)特殊处理:这类模型内部已经有思维链推理机制,过高的温度会干扰其推理过程,通常建议低温度(0.0-0.3)甚至 Greedy。
  • Speculative Decoding 是无损加速:在引擎层开启即可(vLLM、TGI 等已原生支持),不改变生成质量,适合降低首字(TTFT,Time To First Token)与整体延迟。draft 模型越大、与目标模型越”同步”,加速比越高。详见 推理优化技术。
  • Constrained Decoding 有性能开销:维护语法状态和逐 token 合法性检查会增加计算,xGrammar 等工具通过预编译和并行化已将开销压到很低,但在极端长输出场景仍需注意。
  • Best-of-N 的成本:N 条生成意味着 N 倍的推理成本。如果搭配奖励模型,还需额外的一次前向打分。适合离线高质量场景,不适合对延迟敏感的实时场景。
  • 聊天机器人:用 Top-P 或 Min-P 加中低温度(0.7-0.9),保证回复既自然又有一定多样性,避免每次回答雷同。
  • 代码生成:低温度(0.0-0.2)或 Greedy,追求语法正确与逻辑一致,减少随机性带来的语法错误。配合 Constrained Decoding 保证输出是合法 JSON 或可执行代码。
  • 创意写作与续写:高温度(0.9-1.2)加 Top-P(0.92-0.97),鼓励模型跳出常见表达,产出更具想象力的文本。
  • 翻译与摘要:Beam Search 是经典选择,在”有参考答案”的评测上通常取得更高 BLEU/ROUGE 分数。
  • Agent 与 Function Calling:Constrained Decoding 保证工具调用的 JSON 参数格式 100% 合规,是现代 Agent 框架的底层支撑。低温度减少随机性,确保工具名和参数名的准确性。
  • 客服与知识问答:低温度保证答案稳定一致,必要时配合 RAG 与护栏约束范围。
  • 推理模型(数学/逻辑/代码竞赛):低温度或 Greedy,让模型内部的思维链(chain-of-thought)推理不被采样噪声干扰。Best-of-N 可在离线场景进一步提升准确率。
类库语言说明
transformersPythonHuggingFace 的 generate 接口,内置 Greedy / Beam / Top-K / Top-P / Min-P / Contrastive 等全部主流策略
vLLMPython高性能推理引擎,支持 Top-K / Top-P / Min-P / Temperature,原生集成 Speculative Decoding(EAGLE / Medusa)与 xGrammar 受限解码
TGIPythonHuggingFace 文本生成推理服务,支持多种采样参数与投机解码
llama.cpp / OllamaC++支持 Greedy / 温度 / Top-K / Top-P / Min-P / 典型采样 / 重复惩罚等,适合边缘部署(本地、移动端)
OutlinesPython结构化/受限解码库,支持 JSON Schema、正则、Pydantic 模型约束
xGrammarC++/Python高性能 grammar-constrained 解码引擎,集成进 vLLM / TensorRT-LLM
LMQLPython声明式约束查询语言,在 prompt 中直接嵌入约束表达式
GuidancePython微软出品的模板+约束生成库,支持 JSON / 正则 / 选择约束
术语英文解释
解码Decoding从模型概率分布中选取下一个 token 的过程
贪心解码Greedy Decoding每步取概率最大词的确定性解码
束搜索Beam Search保留 k 个最优候选序列的解码方法
温度Temperature缩放 logit 以调节分布尖锐程度的参数
Top-K 采样Top-K Sampling只从概率最高的 K 个词中采样
核采样Top-P / Nucleus Sampling从累积概率达到 P 的最小词集合中采样
Min-P 采样Min-P Sampling只保留概率不低于 max_prob × min_p 的词,自适应于分布尖锐程度
对比搜索Contrastive Search惩罚与已生成内容相似的候选,抑制重复
投机解码Speculative Decoding小模型起草、大模型验证的无损加速方法
受限解码Constrained Decoding每步只在符合指定格式/语法的 token 子集中采样
拒绝采样Best-of-N / Rejection Sampling生成 N 条候选,用奖励模型/打分函数选最优
LogitLogitsoftmax 之前的未归一化分数
TokenToken模型处理文本的最小单位,由 tokenizer 切分得到
词表Vocabulary模型所有可用 token 的集合
KV CacheKV Cache缓存历史 token 的 Key/Value 向量以避免重复计算的机制
Function CallingFunction Calling让 LLM 调用外部 API/工具的能力,依赖受限解码保证参数格式
重复惩罚Repetition Penalty对已出现的 token 降低其 logit 的技巧,抑制重复
奖励模型Reward Model为生成输出打分的模型,用于 Best-of-N 选择和 RLHF 训练
  • Holtzman et al.,「The Curious Case of Neural Text Degeneration」(ICLR 2020):提出 Nucleus Sampling(Top-P),揭示 Greedy / Beam 导致重复退化问题。[论文]
  • Su et al.,「A Contrastive Framework for Neural Text Generation」(NeurIPS 2022):提出 Contrastive Search,兼顾质量与多样性。[论文]
  • Leviathan et al.,「Fast Inference from Transformers via Speculative Decoding」(2023):系统化投机解码,大模型无损加速。[论文]
  • Cai et al.,「Medusa: Simple LLM Inference Acceleration Framework with Multiple Decoding Heads」(2024):多头投机解码,无需额外 draft 模型。[论文]
  • Li et al.,「EAGLE: Speculative Sampling Requires Rethinking Feature Uncertainty」(2024):EAGLE 投机解码,基于隐藏状态预测,大幅提升接受率。[论文]
  • Fan et al.,「Hierarchical Neural Story Generation」(2018):较早引入 Top-K 采样改善生成多样性。[论文]
  • Mehrabi et al.,「Typical Decoding for Natural Language Generation」(2022):Locally Typical Sampling,基于信息熵的自适应截断采样。[论文]
  • 社区讨论:Min-P Sampling:由 llama.cpp / Ollama 社区推动的实践方法,相关讨论见 min_p paper 及各推理引擎文档。
  • Dong et al.,「XGrammar: Flexible and Efficient Structured Generation Engine for Large Language Models」(2024):高性能 grammar-constrained 解码引擎。[论文]
  • 加速原理见推理优化技术,格式约束可结合 grammar-constrained generation 等受限解码技术,结构化输出最佳实践见结构化输出。