不同硬件和推理引擎模型输出的精度差异
Author: ninehills
Labels: blog
Created: 2025-09-09T02:39:09Z
Link and comments: https://github.com/ninehills/blog/issues/137
不同硬件(如NVIDIA GPU, 华为Ascend NPU)和不同推理框架(如PyTorch, vLLM, MindIE)上,对同一模型相同输入进行推理得到不同结果:
-
浮点数计算的非确定性
现代处理器为追求极致性能,广泛采用并行计算与融合运算(FMA),导致浮点运算顺序不固定
由于浮点数运算不满足严格的结合律 (a+b)+c ≠ a+(b+c)
,不同的计算顺序会导致微小的舍入误差累积,最终造成结果差异。
- 算子实现的差异
不同的硬件平台和推理框架拥有各自高度优化的算子库。
- 硬件层面:NVIDIA GPU依赖cuBLAS/cuDNN,华为Ascend NPU依赖CANN。它们的底层数学实现、优化策略和精度处理存在差异。
- 框架层面:vLLM、MindIE等框架会实现自定义的高性能算子(如PagedAttention),其算法逻辑和数值稳定性可能与PyTorch的原生算子不同,从而引入计算路径上的差异。
- 即使数学公式相同,不同 kernel 的实现(如矩阵乘的分块大小、是否使用 shared memory、是否融合 layernorm)都会导致数值路径差异。
- 解码策略的敏感性
即使计算上的差异极其微小,解码过程也会将其放大。
- 采样解码 (temperature > 0):微小的
logits
差异会改变整体概率分布,导致采样到完全不同的token,从而放大不确定性。
- 贪心解码 (temperature = 0):即使使用贪心解码,当两个token的
logits
值非常接近时,微小的计算误差足以改变它们的排序,使得argmax
操作选择不同的token。
- 示例:logits_A = 10.00, logits_B = 10.01 → softmax 后 P(A)≈49.9%, P(B)≈50.1% → argmax 选 B;若误差使 logits_A=10.012,则 argmax 可能选 A。
- 一旦在某个生成步骤选择了不同的token,它将作为后续步骤的输入,引发“蝴蝶效应”,导致最终生成的序列产生巨大差异。
调试办法:
- 相同模型在不同环境的表现有极大的差别如何确认?
- 使用相同的模型权重和模型精度,不使用量化或使用相同的量化权重
- 使用贪心解码(temperature = 0 )
- 使用固定的随机种子
- 关闭 prefix-cache、NTP 等可能影响推理精度的优化选项
- 【极大影响性能】禁用并行计算非确定性:PyTorch 中设置
torch.use_deterministic_algorithms(True)
。
- 使用相同的 Prompt,并确保 Chat Template 渲染后的 Tokens 完全匹配
- 开启输出 logits 分布(https://platform.openai.com/docs/api-reference/chat/create#chat_create-logprobs)
- logprobs = true
- Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the content of message.
- 检查 Token 输出的 logprobs 的差异
- 如何进行算子级差异检查?
- 修改模型前向函数,逐层保存隐藏状态(hidden states);
- 在两个平台分别运行,保存每层输出张量;
- 计算每层输出的相对误差或余弦相似度:
- L2 相对误差:
||A - B||₂ / ||A||₂
- 余弦相似度:
cos_sim = (A·B) / (||A|| * ||B||)
- 定位误差突增的层(如相似度从 0.9999 骤降至 0.99),然后具体定位问题算子所在。
- 如何对模型进行 Benchmark 确定统计学意义的差异评估?
- 使用 evalscope/lm-eval/opencompass 等评测框架在常见的评测数据集上从统计学角度分析精度差异
- 推荐用 gsm8k、ceval、mmlu_redux、livebench
- 理论来说,应该进行多轮评测后,从统计学角度判断差异是否显著。
- 实际来说,相同模型的评测分数分布应该在相对 3% - 5% 以内就可以接受。
解决办法:
- 使用贪心解码(仅验证问题的时候使用,多数场景不推荐使用贪心解码)
- 确保使用相同的模型精度(比如 bfloat16和float16 就有较大区别)
- 更换硬件、推理框架后,重新进行提示词调优。
延伸问题:
- 量化后的模型一定就比量化前的模型效果差么?
- 量化也可以视为一种误差,加入误差有时反而会让模型在某些场景下的效果变好。
- 这是因为量化引入的噪声有时可以视为一种正则化的作用,类似于 Dropout,可以打破模型的一些过拟合特征(一个FP16的权重0.800001和0.800002在量化后可能都变成了同一个INT8值102,对精度的“扰动”打断了模型学到的一些“脆弱”或“过度拟合”的特征。)
- 大模型是天然这么不稳定的么?
- 对,大模型本身就是不稳定输出的,而且实践中很少使用贪心解码,也就是 temperature 设置为 > 0,本身有极大的输出随机性。
- 这也代表着所有的评测结果仅有统计学意义,就和相同的卷子人不会考相同分数一样。
- 效果不一致是否一定是硬件或算子差异?
- 不一定,很多时候是推理框架引入的 Bug,需要具体问题具体定位。