TT Lab
开始
学习 学习路径 课程

AI瘦身失败事件

这个差异能复现吗 — 如何测量准确率损失

在 TT Lab 中继续学习

目标

在同一批测试样本上运行 FP32 原始模型、上一版部署模型、整数候选三个版本,留下预测和输出值,并编写工具 acccmp.py,用来判断准确率的差异是不是可复现的差异。分别测出区间、配对比较、翻转样本和输出差异,把基线设为两个,给出判定。

为什么重要

“准确率下降了 0.3%”本身不是判断依据。样本 400 个时,一个准确率的 95% 区间宽度接近 8 个百分点。把两个模型分别测量再相减,得到的值说不出比这更小的差异。 能让我们说话的是同一批样本。如果两个模型看到的是同样的输入,那么两者都答对的样本和两者都答错的样本不能用于判定,只有只答对一边的样本才是信息。这些分歧样本的个数,才是实际的分母。 基线也不止一个。FP32 原始模型回答量化损失了多少,上一版部署模型回答会不会比现在在线运行的更差。两个答案的方向可能不同,所以两者都要写下来。 而且输出差异和任务指标不同。最大绝对误差很大,准确率也可能不变,反过来也一样。因为离决策边界远的样本,即使输出波动,答案也不会变。 评分器不会相信你写下的数字。它每次都会用不同的随机种子和样本数摆出自己的预测表,真正运行你的工具,并把同样的计算重做一遍来核对。第 1 步会用你留下的随机种子重新生成测试输入,并亲自运行你的模型。

步骤

  1. 创建并运行 /root/acc/gen_eval.py,生成三个模型以及 /root/acc/preds.json、/root/acc/eval_seed.json。
  2. 在 /root/acc/acccmp.py 中实现 summary,输出样本数和各次运行的准确率。
  3. 加入 interval,以 Wilson 得分区间给出一个准确率的 95% 区间。
  4. 加入 paired,在同一批样本上给出 2x2 分歧个数和配对比较统计量。
  5. 加入 flips,只按方向统计被翻转的预测。
  6. 加入 outputs,给出输出值差异(最大绝对误差、平均绝对误差、余弦)。
  7. 加入 baseline,分别与两个基线比较,并把你这份数据的判定写入 /root/acc/verdict.json。
  8. 把 /root/acc/accuracy_report.md 写成四节。

参考

在同一批样本上运行三个版本并留下预测

创建并运行 /root/acc/gen_eval.py,在 /root/acc 下生成 fp32.onnx、prev.onnx、int8.onnx 以及 /root/acc/preds.json、/root/acc/eval_seed.json。测试输入和正确答案表完全遵循参考部分的随机种子规则。

模型用 onnx.helper 把 MatMul + Add + Relu 连起来搭成。prev.onnx 用 quantize_dynamic,int8.onnx 给 quantize_static 传入 CalibrationDataReader 来生成。正确答案表由 FP32 输出生成,但要加上噪声,是因为准确率如果是 1.0,就看不出任何差异。logits 用 float() 原样写入,不要四舍五入。

把样本数和准确率汇总到一处

在 /root/acc/acccmp.py 中实现 summary <preds.json>,以 JSON 输出样本数以及各次运行的答对个数和准确率。

不要一上来就算总数,而是先为每个样本留下是否答对的真假列表。后面步骤中的配对比较和翻转统计,全都建立在这个列表之上。运行名称要排序后放入。

给一个准确率加上区间

加入 interval <preds.json> <run>(占位符依次为预测文件与运行名称),给该次运行的准确率加上 95% Wilson 得分区间并输出。包含 run、n、correct、accuracy、center、low、high、half_width。

z 固定为 1.96。Wilson 区间的中心不是样本比例 p,而是向 0.5 略微拉近的值。所以 low 和 high 不是以 p 为中点。即使准确率是 0 或 1,宽度也不会变成 0,这正是使用这个区间的原因。

在同一批样本上做配对比较

加入 paired <preds.json> <a> <b>(占位符依次为预测文件与两个运行名称),输出 2x2 表(both_correct、only_a、only_b、both_wrong)以及 delta、discordant、statistic、significant。

两者都答对的样本和两者都答错的样本,区分不了两个模型。判定的分母不是 n,而是分歧样本数。统计量直接使用参考部分的公式,分歧样本数为 0 时取 0.0。

只按方向统计被翻转的预测

加入 flips <preds.json> <a> <b>(占位符依次为预测文件与两个运行名称),输出 changed、to_wrong、to_right、changed_both_wrong、top_flip。

changed 是其余三项之和。如果这个恒等式不成立,就是某处数了两次或漏掉了。错误答案变成另一个错误答案的个数对准确率没有影响,但在用户眼里就是答案变了。

把输出差异和任务指标分开测量

加入 outputs <preds.json> <a> <b>(占位符依次为预测文件与两个运行名称),输出 max_abs_error、mean_abs_error、cos_min、cos_mean、argmax_changed。

输出动得多,并不意味着决策会变。离决策边界远的样本,即使输出波动,答案也不变。把这两个值放进同一个响应里并排看,这种关系就一目了然了。

设两个基线来判定

加入 baseline <preds.json> <run>(占位符依次为预测文件与运行名称),让其余各次运行分别作为基线来比较,并把你这份数据的判定按参考部分的键写入 /root/acc/verdict.json。

原始模型和上一版部署模型的答案方向可能不同。worst 是 delta 最小的基线,并列时取名称靠前的一项。判定规则在参考部分已经固定,原样照搬即可,reason 中用一行写下为什么会这样。

写到读的人能做出判断

把 /root/acc/accuracy_report.md 写成 ## 무엇을 견주었나、## 같은 표본에서 본 차이、## 이 차이가 재현되는가、## 판정과 한계 四节(四个标题为韩文,依次意为“比较了什么”“在同一批样本上看到的差异”“这个差异能否复现”“判定与局限”)。样本数、分歧样本数和判定,要以数字和字词写进正文。

写数字时,要同时写下这个数字是从哪里来的。局限一节要写这个判定没有说明的事——这个实验没有测时间,也没有测内存,而且只在一批测试样本上看过。