Defeating Nondeterminism in LLM Inference

Page 1: 第1页 各位同学,大家好。今天我们探讨一个在人工智能领域至关重要,却又常常被误解的问题:大型语言模型推理的非确定性。我们知道,科学进步的基石是可复现性,然而在LLM身上实现这一点却异常困难。你们可能观察到,即使将模型的“温度”参数设为0,理论上使其采样过程变为确定性的贪心采样,多次询问同一个问题,我们依然会得到不同的答案。一个广为流传的假说将其归咎于GPU的高度并行计算与浮点数运算的非结合性。这个假说认为,由于并行线程完成的顺序不确定,导致了计算顺序的随机变化,最终产生不同的输出。然而,这个解释并不完整。它无法说明为何一个简单的矩阵乘法在GPU上反复运行却总能得到完全相同的结果。要解开这个谜题,我们必须深入探究其背后的真正机制。 Page 2: 第2页 在揭示非确定性的直接原因之前,我们必须先理解一个更为根本的数学原理,我称之为“原罪”:浮点数的非结合性。在理想的数学世界里,加法满足结合律,即(a + b) + c 等于 a + (b + c)。但在计算机中,由于浮点数表示法的精度限制,这个定律失效了。文档中的例子非常经典:`(0.1 + 1e20) - 1e20` 的结果是0,而 `0.1 + (1e20 - 1e20)` 的结果是0.1。这好比我们有一个只能装三个苹果的篮子。如果先进来一个巨大的西瓜(1e20),再放一个樱桃(0.1),樱桃就被挤掉了,篮子里只有西瓜;之后拿走西瓜,篮子就空了。但如果篮子里先进来一个西瓜,又马上拿走,篮子是空的,这时再放进樱桃,篮子里就有一个樱桃。这种因运算顺序不同而导致精度损失,是产生数值差异的根源。但这仅仅解释了为何会有“差异”,尚未解释为何会“随机”出现。 Page 3: 第3页 那么,究竟是什么导致了运算顺序的随机变化呢?流行的“并发+浮点数”假说指向了“原子加法”(atomic adds),这是一种硬件层面的非确定性操作。然而,深入分析会发现,在大型语言模型的前向推理过程中,几乎不使用原子加法。模型内部的计算核心(kernels),在给定完全相同的输入(包括批次大小)时,其输出是“运行时确定”的。真正的罪魁祸首,比这更为微妙。请看这张图,它揭示了非确定性的传导链条。问题的核心在于,这些计算核心虽然自身是确定性的,但它们却不具备“批次不变性”(batch invariance)。这意味着,对于同一个用户请求,当它与不同数量的其他请求被打包在一起(即批次大小不同)进行处理时,其计算结果会发生数值上的变化。而从用户的角度看,服务器的负载是随机的,导致其请求被分配到的批次大小也是随机的。因此,一个随机变化的系统属性(批次大小)与一个对该属性敏感的计算过程(非批次不变的核心)相结合,最终导致了从用户端看来完全不确定的输出结果。 Page 4: 第4页 既然我们已经锁定了病根——缺乏批次不变性,那么治疗方案也就明确了:我们必须确保模型中的每一个关键计算步骤都具备批次不变性。这意味着,无论批次大小如何变化,每个元素的计算过程,尤其是归约(reduction)操作的顺序,都必须保持固定。我们来看两个相对简单的例子。对于RMSNorm,最直接的策略是“数据并行”,即为批次中的每个元素分配一个独立的计算核心。只要批次足够大,这种策略天然就具有不变性。挑战在于小批次,为了追求性能,工程师可能会切换到并行度更高的“分裂归约”策略,但这会破坏不变性。因此,要实现确定性,我们必须坚持使用同一种策略,哪怕在小批次上牺牲一些性能。对于矩阵乘法,情况类似,但更复杂,因为它涉及到Tensor Core的优化。不同的批次大小可能导致系统选择不同的底层计算指令(PTX instructions)或采用“Split-K”并行策略,这些都会改变归约顺序。解决方案是:为所有可能的矩阵形状,强制使用一种固定的、不依赖批次大小的计算核心配置。 Page 5: 第5页 最艰巨的挑战在于Attention机制。它不仅包含两次矩阵乘法,还涉及到对序列维度的归约,并且其处理方式在推理的不同阶段(如prefill和decode)会发生变化。这就带来了新的不变性难题。一个典型的反面例子是,当处理一个长序列时,如果将已存入KV缓存的部分与当前新生成的部分分开计算,那么一个词元的计算顺序就会因为它是在prefill阶段(缓存为空)还是decode阶段(缓存非空)而截然不同,从而破坏了不变性。要驯服这头猛兽,我们需要一种更精巧的策略。当必须沿KV维度进行分裂以保证并行度时(即Split-KV),我们不能简单地将序列长度均分为固定数量的块,因为这会导致每个块的大小随序列长度变化。正确的做法是采用“固定分块大小”策略。无论序列多长,我们都用同样大小的“尺子”去度量它,切分成若干个固定大小的块和一个可能的“零头”块。这样,每个块内部的计算顺序就恒定了,从而保证了整个Attention操作的批次不变性。 Page 6: 第6页 理论的正确性需要实验来验证。文档中的实验结果令人信服:在使用标准vLLM对一个简单问题进行1000次零温度采样时,竟产生了80种不同的答案。而一旦启用我们讨论的批次不变性核心,1000次采样的结果则完全相同,达到了比特级别的可复现性。当然,这种确定性并非没有代价,实验显示存在一定的性能损失,但通过优化可以大幅缓解。那么,追求这种极致的确定性有何重大意义呢?一个关键应用在于强化学习。在RL中,训练策略和采样策略的细微数值差异,会使得本应是“同策略”(On-Policy)的算法,在事实上变成了需要复杂校正的“异策略”(Off-Policy)算法,甚至可能导致训练崩溃。而通过实现确定性推理,我们可以确保采样器和训练器之间的计算过程完全一致,从而实现真正的“同策略”强化学习,这对于算法的稳定性和可信度是巨大的飞跃。总而言之,通过系统性的分析和工程努力,我们完全可以战胜非确定性,构建更加可靠和可理解的AI系统。

Defeating Nondeterminism in LLM Inference