AI推理指南
推理为何是非确定性的
“同一个”问题的迷思
即使两个请求看起来完全相同,LLM 的输出也可能不同。采样会刻意引入变化;服务端的调整、隐藏的上下文、工具返回的结果,以及某些并行内核还会带来更多变化。要做到逐字节可复现,就必须掌控完整输入、解码配置、模型与运行时版本,以及执行环境。
关键结论:这些变化主要来自四个方面:生成与采样、上下文与输入、系统与架构、硬件与实现。理解这些因素,是构建可靠 AI 系统的关键。
第 1 类:刻意设置的生成与采样参数
“掷骰子”:采样会提高输出的多样性
1. Temperature
在采样前重新缩放 token 的 logits,从而改变概率分布的集中程度。
2. 随机种子
计算机使用“种子”(起始数值)来生成伪随机序列。
3. Top-P(核采样)
模型从累积概率达到阈值(例如 0.9)的最小 token 集合中采样。候选集合会在每个生成步骤重新计算。
4. Top-K
模型从概率最高的 K 个 token 中采样(例如前 50 个)。
5. 其他采样策略
Mirostat、typical sampling 等采样器采用不同规则来挑选候选 token。
第 2 类:塑造输出的请求配置
改动这些请求设置会改变输出契约;这并不能证明存在非确定性
6. repetition/frequency/presence 惩罚
这些设置会降低已出现 token 的分值,从而抑制重复。即使固定了种子,改变惩罚项也会改变候选分布。
7. 停止条件(max tokens)
对响应长度的硬性上限。max_tokens=50 的运行结果会不同于 max_tokens=500。
8. 停止条件(stop 序列)
当模型生成配置好的字符串(例如 "\nHuman:")时就会停止。在随机采样下,不同路径可能在不同位置遇到停止序列。
第 3 类:上下文与输入阶段的变量
“记忆”:模型的输入往往不止最新那条用户消息
9. 对话历史(上下文窗口)
这是让人觉得非确定的最常见原因。模型看到的是此前的整段对话,而不只是最后一个问题。
仅仅改变一个 token 就构成了不同的输入,输出也可能随之改变
10. 上下文窗口上限(“遗忘”点)
模型的上下文窗口是有限的,大小因模型而异。较长的对话可能被外层应用截断、摘要或直接拒绝;提交的上下文之外的内容,模型无法获取。
11. 系统提示词
由应用或 API 提供的更高优先级指令。它对开发者可见,但对最终用户可能是隐藏的,改动它会改变响应的风格和行为。
12. 检索与外部工具
这是请求之间输入差异的主要来源。在以检索为依据的流程中,应用可能会:
- 暂停生成
- 调用外部工具(例如 Google Search API)
- 获得新的动态数据(搜索结果)
- 把这些数据注入上下文
工具返回的结果会在不同请求之间变化,从而改变模型的输入,也可能改变答案
第 4 类:系统与架构层面的变量
“交响乐团”:所谓“模型”往往是由多个模型组成的复杂系统
13. 模型更新与版本
你今天使用的模型,可能是昨天那个模型的新版、重训版或更新版。v1.2 与 v1.3 的模型响应并不相同。
14. A/B 测试与灰度发布
服务商或你自己的网关可能在发布期间把流量分配到不同版本。如果无法锁定版本,两个请求可能落到不同的部署上,给出不同的答案。
15. 投机解码
草稿模型提出 token,由目标模型校验。严格的实现力求保持目标分布,但运行时、批处理、近似方式或配置上的差异,仍可能影响可复现性。
16. 混合专家(MoE)架构
路由器为每个 token 选择一部分专家。对固定输入而言,路由函数可以是确定的,但容量上限、批处理和实现细节可能带来部署层面的差异。
第 5 类:硬件与底层实现
“物理层”:最深层、也最难掌控的变化来源
注意:即使温度为 0 且固定了种子,某些运行时层面的选择仍可能带来差异。
17. 并行内核与浮点归约顺序
固定的运算按固定顺序执行是可以复现的。当并行内核或归约以不同顺序执行、且舍入误差不断累积时,就可能出现差异:
- 并行性:GPU 会并行执行数十亿次计算
- 浮点运算:计算机的小数运算并不严格满足结合律
- 后果:当候选项非常接近时,微小的分值变化就可能改变最终选中的 token
例如:由于舍入,(a+b)+c ≠ a+(b+c) → 0.81234567 与 0.81234568 → 选出的词不同
18. 量化配置
量化模型使用的精度低于其他构建版本,因此可能给出不同的分值或输出。固定的量化模型与运行时组合本身并非非确定;请把确切的量化方式和运行时作为模型版本的一部分记录下来。
19. 批处理
你的请求会与其他用户的请求放在同一个“批次”中处理。这些请求的分组和填充方式会改变 GPU 计算的确切顺序,从而触发浮点问题。
20. 推理阶段的 dropout
推理时通常会关闭 dropout。蒙特卡洛 dropout 等研究方法会刻意让它保持开启;这样配置的生产运行时,只要不控制其随机状态,就会表现出随机性。
给开发者的要点
尽量接近确定性
- 把温度设为 0(贪心解码)
- 使用固定的种子值
- 精确控制全部输入上下文
- 使用同一个模型版本
- 接受硬件层面的差异仍可能出现
接纳非确定性
- 构建能从容应对变化的系统
- 使用考虑语义等价的评估指标
- 仅对安全或幂等的操作使用有次数上限的重试
- 关注质量的稳定性,而不是输出完全一致