跳到主内容
@wquguru
精选88Avi Chawla技巧与观点

LLM推理非确定性根源:Batch大小影响Logits与输出

This is the strangest property of LLM inference:

原文
发到 X
推荐理由

做RL训练和评测的同学必看,解释了为什么温度设为0输出仍不稳定,直接给出vLLM和SGLang的配置开关,解决复现难题。

This is the strangest property of LLM inference:

这是 LLM 推理最奇特的属性:

Your LLM's output depends on requests that aren't yours.

你的 LLM 的输出取决于不属于你的请求。

The same prompt can produce different logits depending on how many other requests share its batch on the GPU.

相同的提示词在不同的 GPU 批次共享情况下,可能产生不同的 logits。

Here's what happens inside the GPU.

以下是 GPU 内部发生的情况。

Most of an LLM's forward pass is spent accumulating sums.

LLM 的前向传播大部分时间都花在累加求和上。

Every matmul, RMSNorm, and attention step reduces thousands of floating-point values into a single output, so the order of those additions directly affects the result.

每一次矩阵乘法(matmul)、RMSNorm 和注意力步骤都将数千个浮点值缩减为单个输出,因此这些加法的顺序直接影响结果。

The catch is that floating-point addition isn't associative.

问题在于浮点加法不满足结合律。

Every addition rounds the result to the nearest representable value, so (a + b) + c and a + (b + c) can produce different low-order bits.

每次加法都会将结果舍入到最接近的可表示值,因此 (a + b) + c 和 a + (b + c) 可能会产生不同的低位比特。

fp16 has an 11-bit significand, so between 1024 and 2048 the spacing between representable values is 1.0.

fp16 拥有 11 位尾数,因此在 1024 到 2048 之间,可表示值之间的间隔是 1.0。

If you compute 1024 + 0.4 + 0.4 left to right, each addition rounds back to 1024. If the two small values are added first, 0.4 + 0.4 gives 0.8, and 1024 + 0.8 rounds to 1025.

如果你从左到右计算 1024 + 0.4 + 0.4,每次加法都会舍入回 1024。如果先加两个小值,0.4 + 0.4 得到 0.8,而 1024 + 0.8 会舍入为 1025。

The order of accumulation depends on batch size, because kernels choose their parallelization strategy to maximize GPU utilization.

累加的顺序取决于批次大小,因为内核会选择其并行化策略以最大化 GPU 利用率。

A GPU like the H100 has 132 compute units, and a small batch doesn't give each of them enough work.

像 H100 这样的 GPU 拥有 132 个计算单元,而一个小批次无法给每个单元分配足够的工作量。

So the kernel splits each long sum into chunks, adds the chunks on different units at the same time, and then adds those partial results together.

因此,内核将每个长求和拆分为多个块,在不同单元上同时对这些块进行加法运算,然后将这些部分结果相加。

So a different batch size gives a different partitioning, a different accumulation order, and slightly different logits.

因此,不同的批次大小会导致不同的分块方式、不同的累加顺序以及略微不同的 logits。

Usually the difference is negligible, except when two tokens are nearly tied.

通常这种差异可以忽略不计,除非两个标记的得分几乎持平。

In a batch of 8: - "Queens" might score 12.3417 - "New" might score 12.3415

在批次大小为 8 时: - "Queens" 的得分可能是 12.3417 - "New" 的得分可能是 12.3415

In a batch of 32, the rounding might shift them to 12.3414 and 12.3416.

在批次大小为 32 时,舍入误差可能会使它们变为 12.3414 和 12.3416。

If temperature is 0, argmax will pick "Queens" in the first case and "New" in the second. Every later token is conditioned on a different prefix, so the whole completion diverges.

如果温度为 0,argmax 会在第一种情况下选择 "Queens",在第二种情况下选择 "New"。每一个后续标记都基于不同的前缀进行条件生成,因此整个补全结果会产生分歧。

So the nondeterminism isn't coming from sampling, but rather from the fact that your request's numerics depend on concurrent traffic you don't control.

因此,这种非确定性并非来自采样,而是源于你的请求的数值计算依赖于你无法控制的并发流量。

This is pretty evident at Temperature 0, but the nondeterminism comes from the forward pass, not the sampler.

这在 Temperature 0 时非常明显,但这种非确定性来自前向传播过程,而非采样器。

You can fix this by making every reduction batch invariant, meaning each row is reduced in the same fixed order regardless of how many rows share the batch.

你可以通过使每次归约操作对批次大小不变来解决这个问题,这意味着无论有多少行共享同一个批次,每一行的归约顺序都是固定的。

This costs throughput, because the kernel can no longer select the optimal split for each batch size. It uses one fixed strategy even when that leaves compute units idle.

这会降低吞吐量,因为内核不再能为每个批次大小选择最优的分片策略。即使这会导致计算单元空闲,它也会使用一种固定的策略。

The major inference engines now expose this as a flag:

主要的推理引擎现在将此功能作为标志暴露出来:

→ vLLM has VLLM_BATCH_INVARIANT=1, still in beta. → SGLang has --enable-deterministic-inference, at a 25-45% slowdown. → DeepSeek's DeepInfer has DEEPINFER_BATCH_INVARIANT=1.

→ vLLM 设置了 VLLM_BATCH_INVARIANT=1,目前仍处于测试阶段。 → SGLang 提供了 --enable-deterministic-inference 选项,但会导致 25-45% 的性能下降。 → DeepSeek 的 DeepInfer 支持 DEEPINFER_BATCH_INVARIANT=1。

If you run evals, RL rollouts, or anything that needs reproducible outputs, enable it and treat batch composition as part of your experimental setup.

如果你运行评估(evals)、强化学习采样(RL rollouts)或任何需要可复现输出的任务,请启用该功能,并将批次组成视为实验设置的一部分。

And in production, the batch around your request never stays fixed. With continuous batching, the server adds and removes requests at every decoding step.

而在生产环境中,围绕你请求的批次永远不会保持固定。在连续批处理(continuous batching)机制下,服务器会在每个解码步骤中添加和移除请求。

I wrote an article that explains how it works and why every major inference engine uses it.

我写了一篇文章解释其工作原理以及为何所有主流推理引擎都采用它。

Read it below.

请在下方阅读。

更进一步:量化金融体系

看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力

进入量化体系 →

相似阅读

关联信息,但可能不是同一事件