blog

不同硬件和推理引擎模型输出的精度差异

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)上,对同一模型相同输入进行推理得到不同结果:

  1. 浮点数计算的非确定性 现代处理器为追求极致性能,广泛采用并行计算与融合运算(FMA),导致浮点运算顺序不固定 由于浮点数运算不满足严格的结合律 (a+b)+c ≠ a+(b+c),不同的计算顺序会导致微小的舍入误差累积,最终造成结果差异。

  2. 算子实现的差异 不同的硬件平台和推理框架拥有各自高度优化的算子库。
    • 硬件层面:NVIDIA GPU依赖cuBLAS/cuDNN,华为Ascend NPU依赖CANN。它们的底层数学实现、优化策略和精度处理存在差异。
    • 框架层面:vLLM、MindIE等框架会实现自定义的高性能算子(如PagedAttention),其算法逻辑和数值稳定性可能与PyTorch的原生算子不同,从而引入计算路径上的差异。
      • 即使数学公式相同,不同 kernel 的实现(如矩阵乘的分块大小、是否使用 shared memory、是否融合 layernorm)都会导致数值路径差异。
  3. 解码策略的敏感性 即使计算上的差异极其微小,解码过程也会将其放大。
    • 采样解码 (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,它将作为后续步骤的输入,引发“蝴蝶效应”,导致最终生成的序列产生巨大差异。

调试办法:

  1. 相同模型在不同环境的表现有极大的差别如何确认?
    1. 使用相同的模型权重和模型精度,不使用量化或使用相同的量化权重
    2. 使用贪心解码(temperature = 0 )
    3. 使用固定的随机种子
    4. 关闭 prefix-cache、NTP 等可能影响推理精度的优化选项
    5. 【极大影响性能】禁用并行计算非确定性:PyTorch 中设置 torch.use_deterministic_algorithms(True)
    6. 使用相同的 Prompt,并确保 Chat Template 渲染后的 Tokens 完全匹配
    7. 开启输出 logits 分布(https://platform.openai.com/docs/api-reference/chat/create#chat_create-logprobs)
      1. logprobs = true
      2. 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.
    8. 检查 Token 输出的 logprobs 的差异
  2. 如何进行算子级差异检查?
    1. 修改模型前向函数,逐层保存隐藏状态(hidden states);
    2. 在两个平台分别运行,保存每层输出张量;
    3. 计算每层输出的相对误差余弦相似度
      • L2 相对误差:||A - B||₂ / ||A||₂
      • 余弦相似度:cos_sim = (A·B) / (||A|| * ||B||)
    4. 定位误差突增的层(如相似度从 0.9999 骤降至 0.99),然后具体定位问题算子所在。
  3. 如何对模型进行 Benchmark 确定统计学意义的差异评估?
    1. 使用 evalscope/lm-eval/opencompass 等评测框架在常见的评测数据集上从统计学角度分析精度差异
    2. 推荐用 gsm8k、ceval、mmlu_redux、livebench
    3. 理论来说,应该进行多轮评测后,从统计学角度判断差异是否显著。
    4. 实际来说,相同模型的评测分数分布应该在相对 3% - 5% 以内就可以接受。

解决办法:

  1. 使用贪心解码(仅验证问题的时候使用,多数场景不推荐使用贪心解码)
  2. 确保使用相同的模型精度(比如 bfloat16和float16 就有较大区别)
  3. 更换硬件、推理框架后,重新进行提示词调优。

延伸问题:

  1. 量化后的模型一定就比量化前的模型效果差么?
    1. 量化也可以视为一种误差,加入误差有时反而会让模型在某些场景下的效果变好。
    2. 这是因为量化引入的噪声有时可以视为一种正则化的作用,类似于 Dropout,可以打破模型的一些过拟合特征(一个FP16的权重0.800001和0.800002在量化后可能都变成了同一个INT8值102,对精度的“扰动”打断了模型学到的一些“脆弱”或“过度拟合”的特征。)
  2. 大模型是天然这么不稳定的么?
    1. 对,大模型本身就是不稳定输出的,而且实践中很少使用贪心解码,也就是 temperature 设置为 > 0,本身有极大的输出随机性。
    2. 这也代表着所有的评测结果仅有统计学意义,就和相同的卷子人不会考相同分数一样。
  3. 效果不一致是否一定是硬件或算子差异?
    1. 不一定,很多时候是推理框架引入的 Bug,需要具体问题具体定位。