> 一个语料上总结的根因规则,在下一个语料上可能命中 9 条却解释 0 条失败
在一个项目的 .bib 上总结出两条根因规则,解释得很好。拿到第三个项目上逐条实测:
结论先行¶
在一个项目的 .bib 上总结出两条根因规则,解释得很好。拿到第三个项目上逐条实测:
| 规则 | 命中 | 是否解释失败 |
|---|---|---|
① doi 含 10.48550 而无 eprint |
0 条 | — |
② 自由文本里有 arXiv:NNNN.NNNNN 而无 eprint |
9 条 | 否——这 9 条本来就在通过,和失败集不相交 |
| ③ 完全没有机器可读标识符 | 37 条 | 是,37/37 的剩余失败 |
规则 ② 是最危险的那种:它命中了 9 条,所以看起来有解释力。 但那 9 条本来就是通过的——按它去"修",改的全是没坏的东西, 而且重跑之后失败数真的会降一两条(那是抖动,见 先测工具自己的抖动,再宣布修好了——同一份没改过的文件,两次跑差 2 条),于是你会以为规则生效了。
为什么¶
这是一个可以一句话说清的方法错误:
把规则的命中数当成解释力,而没有把命中集和失败集取交集。
正确的量不是 |hits|,是 |hits ∩ failures| / |failures|。
规则 ② 的这个值是 0。
为什么容易犯:诊断脚本的自然输出是"命中 N 条", 因为写脚本的时候你是在找这个形态,而不是在解释这批失败。 两件事只在你最初总结规则的那个语料上恰好重合。
推广出去,同一个形状出现在很多地方:
- 一个 linter 规则在新仓库上报了很多条,但没有一条对应真实缺陷;
- 一个"根因"在新数据集上仍然存在,却不再是瓶颈 (见 显存是 max-over-stages,延迟是 sum-over-stages——消掉最大的那一段之后必须重新 profile 的同类判断);
- 一个在 A 数据集上成立的失败模式分类,被直接拿去标注 B 数据集。
还有一条:同一类问题的"主形态"会换。这三个项目里 ① 的纯形态在第二个项目是 0 条、在第一个项目是 4 条; ③ 在第三个项目是绝对主形态,在前两个项目几乎不出现。 所以"这一类问题的典型长相"这种知识,不跨语料。
怎么用¶
- 报告根因时报两个数:规则命中多少条,以及其中多少条在失败集里。 只有后者是解释力。
- 每到一个新语料,把全部已知规则重跑一遍并重测,不要沿用上一轮的排序。 成本很低(都是本地扫描),收益是不把力气花在没坏的东西上。
- 剩余失败集必须被完全覆盖。 上表里 ③ 解释了 37/37,这才是可以收工的信号; 如果三条规则加起来只覆盖 60%,剩下的 40% 就是还没找到的那个主形态。
- 写下"这条规则在哪个语料上成立",而不是只写规则。 规则脱离了它被测出来的语料,就是一条猜测。
出处¶
- 自己的参考文献核验链(
bibguard)在三个项目的.bib上的实测,2026-10-01, 记录在~/Tools/submission-preflight/feedback/:上表三条规则的命中数、 "这 9 条本来就在通过(4 ✅ / 5 ⚠️),和失败集不相交"的逐条核对 - 相关:一个标签盖了三种根因,而三种的处置相反——"可能是幻觉"的三张脸、判断"我有没有弄坏东西",要比较失败的集合,不是失败的个数、 检查工具的噪声按「来源文件」折叠,不按条目截断