Skip to content

推理基础:自回归与采样

本页速览 推理阶段决定了模型"怎么说话"。本文拆解自回归生成循环、KV Cache 的预填充/解码两阶段,对比贪心、温度采样、top-k、top-p、min-p 与 beam search 等解码策略,并给出 temperature/top_p/惩罚项在不同场景下的实践建议。

推理基础:自回归与采样

推理(inference)是大模型被训练之后真正"说话"的阶段:给定输入,逐 token 生成输出。训练决定模型"会什么",推理配置决定它"怎么说"——同样的模型,温度设 0 是严谨的工程师,设 1.0 是放飞自我的文案,这就是本文的主题。

本文按"生成循环 → KV Cache → 解码策略 → 对话格式 → 流式输出"的路径展开。推理的性能工程(量化、批处理、显存)见部署与服务化,采样的效果怎么测见评测与基准

一、自回归:一次一个 token

大多数现代 LLM(GPT、Llama、Qwen 等)是自回归(autoregressive)解码器:一次只预测下一个 token,把新 token 拼到输入尾部,再预测下一个,循环往复直到生成结束符。这是语言建模的"预测下一个词"范式在推理阶段的直接体现。

text
自回归生成循环(伪代码):

输入 tokens = [t1, t2, ..., tn]
输出 tokens = []
while True:
    # 1. 前向计算:得到下一个 token 的概率分布
    probs = model(tokens)          # 形状 [词表大小]
    # 2. 按解码策略选一个 token(贪心/采样,见第三节)
    next_token = decode(probs)
    # 3. 拼回序列
    tokens.append(next_token)
    outputs.append(next_token)
    # 4. 遇到结束符或达到 max_tokens 则停止
    if next_token == EOS or len(outputs) >= max_tokens:
        break

关键推论:生成 n 个 token 需要 n 次前向计算,第 t 步要"看到"前 t-1 个 token。这带来两个工程后果——KV Cache(省重复计算)与流式输出(边算边给,不必等全部生成完)。

为什么自回归是事实标准

自回归把"生成文本"简化为"反复做语言模型最擅长的事(预测下一个词)",训练目标与生成目标完全一致,天然契合(对比 BERT 式掩码模型需要额外设计生成过程,见Transformer 架构详解)。代价是生成无法并行——这是 KV Cache 与批处理等优化要解决的根源问题。

二、KV Cache:预填充与解码两阶段

注意力机制中,每个 token 需要计算 Query、Key、Value(QKV)。在第 t 步,新 token 要与前面所有 token 的 Key/Value 做注意力计算。如果不缓存,第 t 步就要重新计算前 t-1 个 token 的 K/V——重复劳动。

KV Cache 把前面所有 token 的 Key 和 Value 缓存在显存中,生成时只算新 token 的 K/V,直接复用缓存:

text
预填充阶段(prefill / prompt phase):
  一次性处理完整输入 prompt,计算并缓存所有 prompt token 的 KV
  ── 并行度高、吞吐大

解码阶段(decode phase):
  逐 token 生成,每步只需:
    ① 计算新 token 的 Q/K/V
    ② 新 token 的 K/V 与已缓存的 KV 做注意力
    ③ 新 KV 追加进缓存
  ── 串行、时延主导(延迟由单步时间决定)

KV Cache 是推理阶段显存开销的主力之一(长上下文下甚至超过权重显存),也是上下文与长文本与部署优化的关键对象。显存估算、PagedAttention 等优化见部署与服务化

长上下文 ≠ 无限 KV Cache

KV Cache 大小随序列长度线性增长,长上下文(10 万 token 级)下显存压力陡增。这也是"模型支持 128K 上下文"与"真能跑满 128K"之间的差距所在——模型能力、显存预算、部署配置三者共同决定实际可用长度。

预填充与解码两个阶段对用户体验的意义完全不同:预填充决定 TTFT(首 token 延迟),一次并行处理整个 prompt,短 prompt 快、长 prompt 慢;解码决定 TPOT(后续 token 间隔),逐 token 串行,是生成流畅度的瓶颈。理解这个二分,才能看懂部署侧的优化为什么都围绕"压缩解码开销"展开——批量推理、连续批处理、KV Cache 量化,都是在填解码阶段的坑。详见部署与服务化

三、解码策略:从贪心到采样

解码策略决定"从概率分布里怎么挑 token"。先看两种基本路线:

1. 贪心解码(Greedy Decoding)

每步直接选概率最高的 token:

text
next_token = argmax( probs )
  • 优点:确定、稳定、可复现;适合数学/代码/事实问答。
  • 缺点:短视——单步最优不等于整体最优;且容易陷入重复(同一个词反复输出);没有多样性,同一问题永远同一答案。

2. 温度采样(Temperature Sampling)

先把 logits 除以温度 T,再做 softmax 转概率,然后按概率随机采样

text
p_i = exp( z_i / T ) / Σ_j exp( z_j / T )

T = 0    → 退化为 argmax(贪心)
T < 1    → 概率分布更尖锐,更保守、更确定
T = 1    → 保持原始分布
T > 1    → 分布更平滑,低概率词被"抬起来",更多样、更冒险

温度不是"创造力旋钮"那么简单

温度调的是"分布的平滑程度",不是"聪明程度"。T 太高会让模型选到错词,事实任务上直接掉分;T=0 在数学题上最稳。创造力来自"多给低概率路径一点机会"——但机会给多了,幻觉和语法错误也一起来(见幻觉:成因与缓解)。

一个实用直觉:temperature 决定"敢不敢走低概率路",top-p 决定"允许多宽的候选面"。两者作用维度不同但都在"放松/收紧"分布:T 从 0 往大调,分布变平;top-p 从 1 往下调,候选面收窄。实践上先定温度(风格),再用 top-p 兜底(防跑偏)。

3. top-k 截断

只从概率最高的 k 个 token 里采样(k 常见 10~100):

text
候选集 = 概率最高的 k 个 token → 归一化后采样

优点:砍掉长尾低概率词;缺点:k 是全局固定值——在"分布尖锐"的语境下(很多词概率都接近最高)k 太小反而丢掉合理选项。

4. top-p(核采样,nucleus sampling)

动态选取累积概率刚好超过 p 的最小 token 集合(p 常见 0.9~0.95):

text
1. 把 token 按概率从高到低排序
2. 从高到低累加概率,直到累积概率 ≥ p
3. 只在"被选中的核心集合"内归一化并采样

top-p 比 top-k 更自适应:分布尖锐时集合小,分布平坦时集合大。它是目前 API 与开源推理框架的默认配置之一。

5. min-p 截断

min-p(2024 年提出,核采样的一个变体)按相对阈值过滤:只保留概率不低于"当前最高概率 × min_p"的 token:

text
保留条件:p_i ≥ min_p × max_prob
示例:max_prob = 0.6,min_p = 0.05 → 保留概率 ≥ 0.03 的 token

与 top-p 相比,min-p 的过滤强度随分布动态变化,在长文本生成时通常能更好地平衡多样性与连贯性(实践上常与温度组合使用)。

6. beam search:保留多条候选

beam search 不再"每步选一个",而是始终维护 beam_size 条候选序列,每步为每条候选扩展所有选项、按得分保留前 beam_size 条,最后取总分最高的序列。

  • 优点:能"看到"更长远的组合,机器翻译/摘要等追求确定最优的场景常用。
  • 缺点:生成质量高的解码器模型上收益有限,且同样容易重复;计算开销随 beam_size 线性增加,与流式输出不兼容(要等全部候选算完)。

解码策略对比总表

策略机制多样性确定性适用场景
贪心每步 argmax完全确定数学、代码、事实问答
温度采样除以 T 后按概率采样T↑ 则↑T=0 确定通用生成、创意写作
top-k截取前 k 个再采样随机快速剪枝长尾
top-p累积概率 ≥ p 的集合内采样中高随机通用默认(约 0.9~0.95)
min-p相对最高概率阈值过滤中高随机长文本、追求连贯+多样
beam search保留 beam 条候选取最优近似确定摘要、翻译、受限生成

现代实践的组合拳

主流 API 与推理框架的推荐组合:temperature + top_p(或 min-p)一起用,不建议同时叠多个截断策略(会过度压缩分布)。常见起点:通用对话 temperature=0.7~0.9、top_p=0.9;代码/数学 temperature=0~0.3;创意写作 temperature=1.0 左右。具体取值因模型而异,以上是经验起点。

7. 停止条件:生成到哪为止

自回归循环需要一个显式的停止条件,常见三种:

条件机制备注
EOS token模型输出结束符即停自然结束,但可能停得早/晚
max_tokens硬性长度上限防止失控,但可能截断句子
停止序列(stop sequences)输出匹配指定字符串即停对话/代码补全常用(如分隔符、结束标记)

生产系统通常三者叠加:max_tokens 兜底,stop sequences 控制格式边界,EOS 自然结束。停止条件选得不好,会直接影响输出质量——比如代码补全里没设停止序列,模型可能把函数体后面的无关内容一起生成出来。

四、重复惩罚:控制复读机

温度能调多样,但管不住"重复"——模型可能陷入同一短语、同一句式的循环。重复惩罚通过压低"已经出现过的 token"的概率来抑制复读,常见两种:

参数机制典型取值(OpenAI 系)
frequency penalty按 token 出现的次数线性惩罚0~2,常用 0~1
presence penalty只要出现过的 token 就惩罚,与次数无关0~2,常用 0~0.5
text
对已出现的 token t:
  logit(t) -= frequency_penalty × count(t)     # 出现越多惩罚越重
  logit(t) -= presence_penalty                 # 出现过就罚一次

惩罚是双刃剑

惩罚调太高,模型会"绕着走"——用生僻表达避开重复,结果语句别扭、事实变差。重复问题优先排查"是不是该换解码策略(比如加 top-p)或提示本身缺乏引导",惩罚项是最后的手段。更彻底的复读治理要回到提示工程与训练侧。

五、对话格式:system prompt 与消息模板

API 推理通常以结构化消息输入:系统(system)、用户(user)、助手(assistant)三种角色。system prompt 是最高优先级的指令区,用于设定角色、行为边界与输出要求:

text
system:  你是资深前端工程师,回答技术问题要给出可运行的示例代码。
user:    Vue 里 props 如何传函数?
assistant: (模型生成)
  • system prompt 放"长期规则":角色、语言、安全边界、输出格式;放得越靠前、越明确,模型遵循越好。
  • user/assistant 轮流构成对话历史,模型会参考历史保持多轮一致性。
  • 对话模板是各模型家族的私有约定(ChatML、Llama 模板等),推理框架会按模型的 chat_template 自动套用。为什么不同模型模板不同、模板错误会怎样,与分词与词表的边界 token 设计有关。

采样参数与对话系统结合

对话产品里,system prompt 承担"稳定的行为约束",采样参数承担"风格控制":把温度视为产品功能(正式助手给低温度,创意助手给高温度),并在前端让用户可调。

六、流式输出:边生成边返回

自回归生成天然是"逐步产生"的,因此可以流式输出(streaming):每生成一个 token 就通过 SSE(Server-Sent Events)等协议推送给客户端,而不是等全部生成完。典型指标:

指标含义与流式的关系
TTFT(首 token 延迟)从请求到第一个 token 返回的时间由预填充阶段 + 首步解码决定
TPOT(每输出 token 时间)每个 token 生成的间隔由解码阶段单步延迟决定
吞吐每秒生成 token 数批处理与 KV Cache 优化目标

产品体验上,流式输出让用户"看到思考过程",感知延迟大幅下降(TTFT 可达几百毫秒级);工程上它与连续批处理(continuous batching)配合提升 GPU 利用率,详见部署与服务化

流式与 beam search 不兼容

beam search 需要探索多条候选序列,无法"边算边出";需要流式体验的产品(对话、代码补全)应使用采样类策略。这也是现代对话/编码产品几乎全用采样策略的原因之一。

七、采样超参实践建议表

场景temperaturetop_p惩罚项说明
数学/代码生成0~0.30.9~1.0低或 0要确定与正确,别加多样性
事实问答/客服0.2~0.50.90.3~0.5 存在惩罚抑制重复套话
通用对话/摘要0.7~0.90.90~0.3平衡自然与稳定
创意写作/头脑风暴0.9~1.20.95~1.00.5~1.0 频率惩罚多样性优先,接受幻觉风险
代码补全(IDE)0.1~0.30.9长补全建议配 min-p

主流 API 的参数映射:OpenAI 系使用 temperature(0~2)、top_p、frequency_penalty、presence_penalty;Anthropic 额外透传 top_k;本地推理框架(vLLM、llama.cpp)直接暴露完整的采样器组合。注意各家对参数名与取值域的差异——迁移调用前先读文档,别把 0~2 的温度域当成 0~1。

如何科学调参

不要凭感觉猜:固定模型与提示,只改变一个参数,用固定测试集对比输出质量(必要时配LLM-as-a-judge或人工盲评)。采样参数是产品体验的一部分,值得像 UI 一样做版本化与回归测试。提示侧的影响见提示工程

延伸阅读

参考资料