Wink Pings

温度设为 0,答案仍会被同一张 GPU 上的请求改写

同一句提示词,batch 大小和并发流量会改变浮点累加顺序,logits 轻微漂移,温度 0 也可能翻转 token。Avi Chawla 拆了原因,以及 vLLM、SGLang、DeepInfer 的开关和代价。

把 temperature 设为 0,不代表输出可复现。Avi Chawla 在 X 上拆了一条很具体的链路:同一个 prompt,可能因为你所在的 GPU batch 里还有哪些请求,得到不同的 logits。

关键在于浮点加法不满足结合律。LLM 前向计算里大量步骤都在做累加,matmul、RMSNorm、attention 都会把成千上万个浮点值归约成一个输出。每一步加法都会舍入到最近的可表示值,所以 (a + b) + c 和 a + (b + c) 的低位可能不同。

他给了一个 fp16 的例子。fp16 的 significand 是 11 位,在 1024 到 2048 之间,相邻可表示值的间距是 1.0。按从左到右算 1024 + 0.4 + 0.4,每次加法都舍入回 1024。先把两个小数加起来,0.4 + 0.4 得到 0.8,1024 + 0.8 会舍入到 1025。数值路径变了,结果就变了。

batch size 会改变累加顺序,因为 kernel 要尽量吃满 GPU。H100 有 132 个计算单元,batch 小的时候,每个单元分不到足够的工作。kernel 会把长累加拆成块,放到不同单元并行算,再把部分结果合起来。batch size 不同,切分方式不同,累加顺序不同,logits 就会有微小差异。

平时这点差异可以忽略。麻烦出现在两个 token 分数几乎打平的时候。Avi Chawla 举的例子是:batch 为 8 时,“Queens”可能是 12.3417,“New”是 12.3415;batch 为 32 时,舍入可能把它们变成 12.3414 和 12.3416。温度 0 下 argmax 会先选“Queens”,后选“New”。后面的 token 都基于不同的前缀继续生成,整段 completion 就分叉了。

非确定性来自前向计算,采样器不背这个锅。温度 0 只保证当前这次前向计算里按 argmax 走,跨请求的数值路径没有保证。

修复思路是让每次归约都 batch invariant:不管 batch 里有多少行,每一行都按固定顺序归约。代价是吞吐。kernel 不能再为每个 batch size 选最优切分,只能用一套固定策略,哪怕有计算单元闲着。

现在几个主要推理引擎都给了开关。vLLM 有 VLLM_BATCH_INVARIANT=1,还在 beta。SGLang 有 --enable-deterministic-inference,会带来 25% 到 45% 的 slowdown。DeepSeek 的 DeepInfer 有 DEEPINFER_BATCH_INVARIANT=1。

做 eval、RL rollout,或者任何需要可复现输出的任务,可以打开它,并把 batch composition 写进实验设置。生产环境更复杂。continuous batching 下,服务器在每个 decoding step 都在加请求、减请求,你周围的 batch 不会固定。

有网友评论,测试阶段可以付这个减速成本,生产阶段用户要速度。也有网友问,如果昨天的 eval 今天因为别人的流量挂掉,回归阈值该怎么设。还有网友说,在共享端点上比较两个模型,测进去的不只是模型,还有当时的负载。

Avi Chawla 的原文在这里:链接。他提到的长文在这里。相关引用推文在这里。

发布时间: 2026-11-01 08:41