Temperature 0 并不确定性:为什么你的 LLM 结果仍会漂移
CI 里有一个测试大约每周失败一次。同样的 prompt、同样的模型快照、temperature=0、种子固定、对输出字符串做快照断言,diff 里没有任何相关改动。重跑就绿,怪网络。直到把完全相同请求循环 500 次并 diff 结果——12 次不同,其中一次把退单工单从 fraud 分类成了 billing。这是字节级相同请求带来的真实行为变化。
Temperature 0 从来就不确定性,原因不是模型在背后"发挥创意"。
为什么 temperature=0 不保证确定性
temperature=0 让采样变成贪心(总是选最高 token),但这不保证两次运行的 logits 相同——logits 变了,最高 token 就可能变。
logits 漂移的原因:浮点加法不满足结合律,(a+b)+c 和 a+(b+c) 最后几位可能不同。CPU 单线程看不出来(顺序从不改变);GPU 上,reduction(跨隐藏维求和、softmax 分母、RMSNorm)被切到多个 block 再合并,切分方式取决于 kernel 拿到的张量形状——而形状取决于 batch。
什么是 batch 不变性,为什么推理 kernel 没有它
Batch 不变性 = 单个请求的输出不随同批的其他请求变化。大多数生产推理 kernel 没有这个性质,而且这是性能决策,不是 bug。
服务栈激进地合批:周二的请求落在 48 个请求的 batch 里,凌晨 3 点的请求落在 3 个的 batch 里。不同 batch size → 不同 tiling 策略、不同 split-K 选择、不同 reduction 顺序、不同最后一位舍入。没人随机化任何东西——每次运行在给定 batch 下完全确定,你只是不控制 batch,也看不见它。
真实端点上还有几层叠加:Mixture-of-experts 路由(容量限制下,token 路由到哪个 expert 取决于同 batch 里竞争同一 expert 的其他 token——你的 token 的邻居是陌生人);前缀缓存(是否命中缓存 KV 前缀改变计算边界位置,进而改变后续 token 的 reduction 分组);投机解码(设计上输出等价,但验证路径是不同的数值路径)。
近并列处的放大
一个翻转的 token 落在近并列处,被自回归解码放大——1e-6 的数值抖动能重写整个段落。这解释了"每周一次的 flake":batch 形状变化让某个原本稳定第一的 token 偶尔让位。
实践建议
- 修测试,别修数学:对解析后的字段与不变式断言,而不是对输出字符串做位级快照;
- 固定模型快照,保持 flake 预算——位级可复现只存在于你自己的硬件、batch size 1 的场景;
- 涉及分类/关键决策的自动化测试,用结构化断言(字段、标签、类别)而不是全文比对。
来源:Temperature 0 Isn't Deterministic: Why Your LLM Still Drifts - DEV Community