误差度量指标:绝对误差、相对误差、ULP、余弦相似度、PSNR

做算子精度对齐,说白了就是回答一个问题:我的计算结果,跟标准答案到底差多少?

这个问题看似简单,但真上手了你会发现——不同场景下,差的「多少」得用不同的尺子量。我刚开始做算子开发那会儿,就吃过这个亏。有一次跑图像预处理算子,看着绝对误差挺小,结果模型精度直接崩了。后来才发现,那个场景下应该看相对误差和ULP。

嗯,今天咱们就把这五把尺子挨个捋一遍。

1. 绝对误差(Absolute Error)

最直观的度量。公式很简单:

AE = |计算值 - 参考值|

举个例子,你算出来是 3.1415,标准值是 3.1416,那绝对误差就是 0.0001。

什么时候用?

  • 数值本身量级固定,比如角度、坐标偏移量
  • 你只关心「差了多少」,不关心「相对于原值差了多少」
注意:绝对误差有个大坑——它不反映量级。比如 AE=0.1,对于值 100 来说可以忽略,但对于值 0.2 来说就是 50% 的误差。我见过有人拿绝对误差去验收 softmax 算子,结果数值小的输出项全错了,但整体 AE 看起来还行。这其实是个陷阱。

2. 相对误差(Relative Error)

相对误差解决了上面那个问题:

RE = |计算值 - 参考值| / |参考值|

还是刚才的例子,参考值 3.1416,计算值 3.1415,相对误差 ≈ 0.003%。而参考值 0.2,计算值 0.3,相对误差就是 50%。

我个人习惯,在做激活函数、归一化这类算子的精度对齐时,优先看相对误差。因为它能告诉你「相对于真实值,你的偏差有多大比例」。

一个小技巧:当参考值接近 0 时,相对误差会爆炸。比如参考值是 1e-10,计算值是 1e-11,相对误差直接 90%。这时候我会加一个 epsilon 做分母保护,或者直接换用绝对误差 + ULP 组合。

3. ULP(Units in the Last Place)

ULP 是浮点数世界的「最后一格」。它衡量的是:两个浮点数之间,隔了多少个可表示的浮点数。

说白了,ULP 看的是「二进制精度上差了几格」,而不是「十进制数值上差了多少」。

// 伪代码示意
ulp_distance = |float_bits(计算值) - float_bits(参考值)|

为什么需要 ULP?

我举个例子你就明白了。float32 在 1.0 附近,ULP ≈ 1.19e-7;但在 1e10 附近,ULP ≈ 1024。同样的绝对误差 0.001,在 1.0 附近是 8000 多个 ULP,在 1e10 附近连 1 个 ULP 都不到。

我在项目中遇到过一个 case:两个实现算出来的结果,绝对误差 0.5,看起来很大。但一看 ULP,只有 2。说明两个结果在浮点表示上几乎一样,只是最后一位舍入不同。这种情况完全不需要对齐。

经验值:对于 float32 算子,ULP ≤ 1 通常认为是 bit-exact;ULP ≤ 3 属于正常舍入差异;ULP > 10 就要警惕了。

4. 余弦相似度(Cosine Similarity)

这个指标看的是方向一致性,不关心数值大小。

cos_sim = (A · B) / (||A|| * ||B||)

取值范围 [-1, 1],越接近 1 说明方向越一致。

什么时候用?

  • 特征向量、embedding 的精度对齐
  • 你关心的是「输出向量的相对关系是否保持」,而不是每个元素的具体值

我记得有一次做 attention 算子的精度对齐,绝对误差和相对误差都挺小,但模型推理结果就是不对。后来一查余弦相似度,只有 0.92。说明虽然每个元素偏差不大,但向量方向已经偏了,softmax 后的分布变了。

注意:余弦相似度对零向量敏感。如果输出全是 0,余弦相似度没定义。另外,它不反映数值缩放——你把所有值乘以 2,余弦相似度还是 1。

5. PSNR(Peak Signal-to-Noise Ratio)

PSNR 是图像领域的标配指标:

PSNR = 10 * log10(MAX^2 / MSE)

其中 MAX 是像素最大值(比如 255),MSE 是均方误差。

PSNR 越高,图像质量越好。一般 30dB 以上算可接受,40dB 以上算很好。

我个人的经验:

  • 做图像预处理算子(resize、crop、normalize)时,PSNR 低于 35dB 就要排查了
  • 做后处理算子(比如去噪、超分)时,PSNR 低于 28dB 说明算法本身可能有问题
  • PSNR 不反映人眼感知——有时候 PSNR 很高,但图像看起来有伪影;有时候 PSNR 不高,但视觉上能接受
避坑指南:我曾经用 PSNR 验收一个图像增强算子,结果 PSNR 有 42dB,但模型推理结果就是不对。后来发现,PSNR 对整体亮度偏移不敏感——你把所有像素加 10,PSNR 可能只掉 1dB,但下游任务就崩了。所以图像类算子,我建议 PSNR 和余弦相似度一起看。

知识体系总览

下面这张图帮你理清这五个指标的关系和适用场景:

误差度量指标选择指南 算子输出 vs 参考值 数值偏差类 方向/结构类 图像质量类 绝对误差 相对误差 余弦相似度 ULP PSNR 数值量级固定 不关心相对比例 数值跨量级 关心偏差比例 特征向量/embedding 关心方向一致性 浮点精度敏感 二进制级别对比 图像/视频处理 像素级质量评估 实际项目中,建议组合使用 2-3 个指标交叉验证

实际项目中的选择策略

说了这么多,到底怎么选?我一般按这个思路来:

算子类型 推荐指标 补充指标 验收标准(参考)
数学运算(add/mul/sqrt) ULP 绝对误差 ULP ≤ 3
激活函数(relu/sigmoid/tanh) 相对误差 ULP RE ≤ 1e-5
归一化(batch_norm/layer_norm) 相对误差 + 余弦相似度 ULP RE ≤ 1e-5, cos ≥ 0.9999
图像预处理(resize/crop) PSNR 绝对误差 PSNR ≥ 40dB
特征提取(embedding/pooling) 余弦相似度 相对误差 cos ≥ 0.999
softmax / log_softmax ULP + 余弦相似度 相对误差 ULP ≤ 5, cos ≥ 0.9999
核心原则:没有万能的指标。我建议每个算子至少用两个指标交叉验证——一个看数值精度(ULP 或相对误差),一个看结构保持(余弦相似度或 PSNR)。这样既能发现数值偏差,也能捕捉到分布漂移。

好了,这五个指标就聊到这儿。下次做精度对齐的时候,记得先问问自己:我到底关心什么?是数值准不准,还是方向对不对,还是图像清不清晰? 选对了尺子,问题就解决了一半。

一个小彩蛋:如果你在做算子精度对齐时,发现所有指标都过了但模型还是不对——别慌,先检查一下数据预处理和输入输出的 shape 对不对。我曾经花了两天调精度,最后发现是数据排布从 NCHW 变成了 NHWC。嗯,这种坑踩过一次就记住了。

想要更多资料,联系可可