外观
推理基础:自回归与采样
推理(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 需要探索多条候选序列,无法"边算边出";需要流式体验的产品(对话、代码补全)应使用采样类策略。这也是现代对话/编码产品几乎全用采样策略的原因之一。
七、采样超参实践建议表
| 场景 | temperature | top_p | 惩罚项 | 说明 |
|---|---|---|---|---|
| 数学/代码生成 | 0~0.3 | 0.9~1.0 | 低或 0 | 要确定与正确,别加多样性 |
| 事实问答/客服 | 0.2~0.5 | 0.9 | 0.3~0.5 存在惩罚 | 抑制重复套话 |
| 通用对话/摘要 | 0.7~0.9 | 0.9 | 0~0.3 | 平衡自然与稳定 |
| 创意写作/头脑风暴 | 0.9~1.2 | 0.95~1.0 | 0.5~1.0 频率惩罚 | 多样性优先,接受幻觉风险 |
| 代码补全(IDE) | 0.1~0.3 | 0.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 一样做版本化与回归测试。提示侧的影响见提示工程。
延伸阅读
- Transformer 架构详解——注意力机制与 QKV,KV Cache 的源头
- 上下文与长文本——长上下文对 KV Cache 与注意力的挑战
- 提示工程——system prompt 与输出格式如何影响生成
- 部署与服务化——KV Cache、量化、批处理与显存估算
- 分词与词表——token 粒度对生成速度与成本的影响
- 提示工程实践——采样参数与提示的配合调优
参考资料
- Holtzman et al. The Curious Case of Neural Text Degeneration(ICLR 2020,核采样) —— top-p 采样原始论文,含重复问题分析
- Nguyen et al. Truncate or Penalize? A New Approach to Penalizing and Truncating the Next Token Distribution(2024) —— min-p 截断方法
- Vaswani et al. Attention Is All You Need(NeurIPS 2017) —— 注意力机制与 QKV 的原始出处
- OpenAI API Documentation: Text generation(temperature/top_p/惩罚参数说明) —— 采样参数官方定义与取值
- Kwon et al. Efficient Memory Management for Large Language Model Serving with PagedAttention(vLLM, SOSP 2023) —— KV Cache 管理与推理优化的代表性系统