Judge 说 53% 的报告有效,真实是多少?

Judge 是一台有误差的测量仪器。先拿人工标注量出它的 TPR 和 TNR,再用它们把"Judge 判的通过率"校正回"真实通过率",区间要同时算上标注集和评测集两边的抽样误差。

准确率和 κ 告诉你 Judge 大概靠不靠谱;校正和门禁要用的是 TPR、TNR,以及漏判、误杀各自的代价。

1问题:Judge 的数字能直接用吗?

BugHunt-Bench 里,测试 Agent 每次运行会提交一批 Bug 报告。人工逐条看太贵,于是让一个 LLM Judge 读报告和注入 Bug 清单,判 PASS(有效报告) 或 FAIL(误报)。新版 Agent 提交了 1000 条报告,Judge 判了 530 条 PASS:有效率 53%?

不一定。Judge 自己会判错:把误报判成有效,把有效报告判成误报。两种错不会互相抵消,除非刚好一样多。所以要先回答三个问题:

  1. Judge 在哪类样本上错、错多少?(混淆矩阵、TPR、TNR、κ)
  2. 两种错,哪一种更贵?(漏判和误杀的成本,阈值怎么定)
  3. 知道了 Judge 的错误率,怎么从 53% 反推真实有效率,区间多宽?(Rogan-Gladen 校正)

上一讲「Bug 报告写得好不好,打 1~5 分还是判 pass / fail?」讲的是两个人工标注者之间的一致性,这一讲把 Judge 当成"第三个标注者",拿人工标注当真值来校准它。本章所有数据都是合成数据:标注集的四格计数是设定的,新版本的 1000 条报告用固定种子模拟,所以真实有效率已知(45.0%),可以检验校正到底对不对。

2混淆矩阵:四个格子,两种错

人工标了 200 条报告(120 条有效、80 条误报),同一批也让 Judge 判一遍:

Judge 判 PASSJudge 判 FAIL合计
人工 PASS(有效)TP = 108FN = 12(误杀)120
人工 FAIL(误报)FP = 16(漏判)TN = 6480
合计12476200

约定 PASS 为正类(和 judgy 等工具一致)。本章的两个中文词:

注意:英文里的 miss rate 通常指 FNR = 1 − TPR(本章的误杀率),和本章的"漏判"方向相反;代码里因此用 false_pass_rate / false_fail_rate 命名。

这和第 2 周评估者–优化者那里的用法一致:漏判是坏稿子被放行,误杀是好稿子被打回。

指标算式值回答的问题
准确率(TP + TN) / n172 / 200 = 86.0%整体判对多少,混在一起
TPR(灵敏度)TP / (TP + FN)108 / 120 = 90.0%真有效的报告,Judge 认出多少
TNR(特异度)TN / (TN + FP)64 / 80 = 80.0%真误报,Judge 拦下多少
Youden JTPR + TNR − 10.700比瞎猜强多少;0 表示和随机一样
为什么校正要用 TPR / TNR,而不是准确率:TPR 和 TNR 都是"在人工标签给定的条件下" Judge 判对的概率,只要 Judge 在每一类样本上的表现不变,它们就不随这批数据里有效报告的占比变化。准确率是两者按占比加权:准确率 = π·TPR + (1 − π)·TNR,换一个版本、有效率 π 变了,准确率就跟着变。

3Cohen's κ:扣掉"碰巧一致"

κ(Cohen, 1960, Educational and Psychological Measurement 20(1):37–46)衡量两个评判者的一致程度,扣掉随机也会有的那部分:

$$\kappa = \frac{p_o - p_e}{1 - p_e},\qquad p_e = \pi_H\,\pi_J + (1-\pi_H)(1-\pi_J)$$

\(p_o\) 是实际一致的比例,\(p_e\) 是两边按各自的 PASS 比例(人工 \(\pi_H\)、Judge \(\pi_J\))独立乱判时的期望一致率,也就是上一讲「Bug 报告写得好不好,打 1~5 分还是判 pass / fail?」里的"随机期望一致率"。上面的例子:

量算式值
\(p_o\)(108 + 64) / 2000.8600
\(p_e\)0.60 × 0.62 + 0.40 × 0.380.5240
κ(0.86 − 0.524) / (1 − 0.524)0.7059

自己写的实现和 scikit-learn 1.1.1 的 cohen_kappa_score 结果一致(0.7059),单元测试里另外用 50 组随机数据逐个对照。

κ 会随有效率变

同一个 Judge(TPR 90%、TNR 80% 不变),只改真实有效率:

真实有效率准确率κ
30%83.0%0.634
60%86.0%0.706
90%89.0%0.534
97%89.7%0.283

有效率往 PASS 一侧偏(90%、97%)时,准确率越来越高,κ 却越来越低;往另一侧偏(30%),两者都降。准确率总是朝 TPR、TNR 里占多数那一类的值靠,κ 则在两端都会被"碰巧一致"压低。极端情况:真实有效率 95%,Judge 永远判 PASS,准确率 95%,κ = 0,TNR = 0(一条误报都拦不住)。Feinstein 和 Cicchetti(1990, J Clin Epidemiol 43(6):543–549)专门讨论过这类悖论;续篇(Cicchetti & Feinstein, 1990, 同卷 551–558)建议 κ 要和正、负两类各自的一致率一起报。

怎么用:κ 适合回答"这个 Judge 比瞎猜强多少",适合在同一批标注数据上比较两个 Judge 版本。要做校正、定阈值、算门禁的代价,用 TPR 和 TNR。跨数据集比较 κ("上个月 0.7,这个月 0.5,Judge 变差了")要先看有效率是不是变了。

4漏判和误杀,哪个更贵?

两种错的代价取决于 Judge 用在哪:

用途漏判(误报被放过)误杀(有效报告被打掉)
离线评测,算 Agent 的有效率有效率虚高有效率虚低
线上自动关闭"无效"报告开发多看一条误报一个真 Bug 被扔掉,往往贵得多
自动给开发提单开发被误报打扰,信任流失少提一个单,还有人工兜底

设真实有效率为 \(\pi\),漏判一次成本 \(c_\text{miss}\),误杀一次成本 \(c_\text{kill}\)。每条报告的期望成本:

$$\text{Cost} = (1-\pi)(1-\text{TNR})\,c_\text{miss} + \pi(1-\text{TPR})\,c_\text{kill}$$

Judge 如果输出一个分数(或者能调 prompt 的松紧),阈值往上调,TNR 升、TPR 降,反之亦然。最优阈值取决于 \(\pi\) 和成本比,不是固定的 0.5。下面用双正态模型算(有效报告分数 ~ N(d′, 1),误报 ~ N(0, 1),d′ = 2.123,此时阈值 0.84 正好对应 TPR 90%、TNR 80%;\(\pi\) = 45%):

成本比 cmiss : ckill最优阈值TPRTNR每千条成本阈值 0.84 时
1 : 11.15683.3%87.6%143.1155.0
1 : 10(误杀贵)0.07298.0%52.9%349.8560.0
5 : 1(漏判贵)1.91458.3%97.2%264.2595.0

成本以较便宜的那种错为 1 个单位。等方差双正态时最优阈值有解析解(对成本求导令其为 0):

$$t^* = \frac{d'}{2} + \frac{1}{d'}\ln\frac{(1-\pi)\,c_\text{miss}}{\pi\,c_\text{kill}}$$
同一个 Judge,换个用途,阈值就要换。另一个推论:最大化 Youden J(TPR + TNR)等价于最小化成本,只在 \(\pi\,c_\text{kill} = (1-\pi)\,c_\text{miss}\) 时成立(把成本式展开就能看出来)。"选 J 最大的阈值"默认了这个条件。

5Rogan-Gladen:从 Judge 通过率反推真实通过率

设真实有效率为 \(\theta\)。Judge 判 PASS 的报告来自两部分:有效报告被认出来,加上误报被漏判:

$$p_\text{obs} = \text{TPR}\cdot\theta + (1-\text{TNR})(1-\theta)$$

这是 \(\theta\) 的一次函数,反解:

$$\hat\theta = \frac{p_\text{obs} + \text{TNR} - 1}{\text{TPR} + \text{TNR} - 1}$$

这就是流行病学里用筛查结果估计患病率的 Rogan-Gladen 估计量(Rogan & Gladen, 1978, American Journal of Epidemiology 107(1):71–76;原文全文在付费墙后,没读到;公式与 Chen et al. 2026 arXiv 2601.05420 第 2.2 节式 (6)、judgy 库 core.py 的实现一致)。分母就是 Youden J,必须 > 0。

新版本 1000 条:Judge 判 PASS 530 条,\(p_\text{obs}\) = 53.0%。

$$\hat\theta = \frac{0.530 + 0.800 - 1}{0.700} = 47.1\%$$

合成数据里真实有效率是 45.0%。不校正的 53.0% 偏高 8 个点。其中约 6.5 个点是系统误差:真实有效率 45% 时,Judge 通过率的期望是 0.9 × 0.45 + 0.2 × 0.55 = 51.5%,因为漏判率(20%)高于误杀率(10%),误报又占了一半多;剩下约 1.5 个点是这 1000 条的抽样波动。如果直接对 53.0% 套第 1 周的 Wilson 区间(「30 次挂了 2 次,失败率在什么范围?」),得到 [49.9%, 56.1%],连 45% 都罩不住:Wilson 只管抽样误差,管不了 Judge 的系统误差。

前提:(1) Judge 在标注集上的 TPR、TNR 能迁移到新数据:标注集里每一类样本要能代表新数据里同一类样本(类别占比可以不同,类内的难度分布不能变);(2) Judge 的模型、prompt、阈值和标注时完全一样,换任何一样都要重新标;(3) 人工标签当真值。上一讲的标注一致性如果不高,这一条本身就站不住。

6区间:误差有三个来源

\(\hat\theta\) 里有三个估出来的量,各带抽样误差:\(p_\text{obs}\)(来自 n 条未标注报告),TPR(来自标注集里 \(m_1\) = 120 条有效报告),TNR(来自 \(m_0\) = 80 条误报)。三者来自互不重叠的样本,相互独立。

delta 法

对 \(\hat\theta\) 求三个偏导:\(\partial\theta/\partial p = 1/J\),\(\partial\theta/\partial\text{TPR} = -\theta/J\),\(\partial\theta/\partial\text{TNR} = (1-\theta)/J\)。一阶近似:

$$\operatorname{Var}(\hat\theta) \approx \frac{1}{J^2}\left[\frac{p(1-p)}{n} + \theta^2\frac{\text{TPR}(1-\text{TPR})}{m_1} + (1-\theta)^2\frac{\text{TNR}(1-\text{TNR})}{m_0}\right]$$

例子里三项分别占方差的 26%、17%、57%,95% 区间 [38.4%, 55.9%],宽度差不多是 Wilson 的 3 倍,这才是诚实的宽度。方差的大头来自 TNR,三个原因叠在一起:误报只标了 80 条(有效报告 120 条);TNR(1 − TNR) = 0.16 大于 TPR(1 − TPR) = 0.09;\((1-\theta)^2\) 的权重是 \(\theta^2\) 的约 1.26 倍。按每多一条样本算,多跑未标注报告的边际收益最小(但它不用人工,便宜):未标注从 1000 翻到 2000,方差约降 13%;有效报告的标注从 120 翻到 240,只降约 8.5%。本例最划算的是扩大随机标注量,让误报那一类变多,或者按错误分析找到的误报类别分层抽样。注意:标注前不知道哪些是误报,如果按 Judge 的判定去多抽"Judge 判 FAIL"的样本,就是按 Judge 输出分层,算 TPR、TNR 时要按各层的抽样率加权,否则有偏。

bootstrap

不想推公式就用 bootstrap(第 1 周「新 prompt 从 82% 涨到 86%,是真的变好了吗?」那一章讲过重抽的原则):标注集整体重抽一次,算 TPR、TNR;未标注集也重抽一次,算 \(p_\text{obs}\);代入公式、截断到 [0, 1];重复 2 万次取 2.5 和 97.5 百分位。两边都重抽得到 [37.6%, 55.4%]。

开源库 judgy(ai-evals-course/judgy,commit e6cd4fe,src/judgy/core.py 第 120–153 行)的 bootstrap 只重抽标注集,\(p_\text{obs}\) 当常数:照这个做法得到 [38.6%, 54.2%],judgy 本身跑出 [38.7%, 54.2%](差别来自随机数)。未标注 1000 条时覆盖率约 90%(名义 95%),100 条时只有 64%:未标注集越小,偏窄越严重。重复 2000 次的模拟(真实有效率 45%,标注 120 + 80 条):

未标注条数直接 Wilsondelta 法bootstrap(两边重抽)bootstrap(只重抽标注集)
10001.1%95.0%93.8%89.7%
10071.9%94.6%94.0%64.1%

表里是"95% 区间罩住真实有效率"的比例,2000 次模拟的蒙特卡洛误差在覆盖率 95% 附近约 ±1 个百分点,在 64% 附近约 ±2.1 个百分点。

7截断:算出负数怎么办

\(\hat\theta\) 落在 [0, 1] 的条件是 \(1-\text{TNR} \le p_\text{obs} \le \text{TPR}\)。\(p_\text{obs}\) 比漏判率还低(Judge 判的 PASS 比"全是误报时漏判出来的"还少),就会算出负数;比 TPR 还高,就会超过 1。真实有效率接近 0 或 1 时,光是抽样波动就能把 \(p_\text{obs}\) 推出这个范围。

例:某个坏掉的版本,200 条报告 Judge 只判了 30 条 PASS,\(p_\text{obs}\) = 15% < 漏判率 20%。\(\hat\theta = (0.15 + 0.8 - 1)/0.7 = -7.1\%\),截断为 0。delta 区间 [0.0%, 8.0%],bootstrap [0.0%, 6.3%]。

截断有代价:它把所有负的估计都抬到 0,均值就偏高了。模拟 2000 次(未标注 200 条):

真实有效率点估计 < 0 的比例未截断的均值截断后的均值
2%39.2%1.4%3.9%
10%9.0%9.9%10.3%
怎么报:对外报截断后的点估计加区间上界("有效率不超过 8%"),日志里留未截断的原值,汇总时偏差小得多(未截断的估计因为分母 J 是估出来的,还有约 Var(TNR)/J² 量级的小偏差,本例不到 1 个点)。经常截断要当成信号:要么真实有效率确实接近 0,要么 Judge 的 TNR 在新数据上已经变了(比如新版 Agent 的误报换了一种写法,Judge 认不出来),该重新标一批了。

▶演示 1:混淆矩阵、κ 和有效率

改四个格子的计数,指标实时重算。下面的滑块保持 Judge 的 TPR、TNR 不变,只改真实有效率,看准确率和 κ 怎么跟着变。

Judge 判 PASS
Judge 判 FAIL
人工 PASS
TP
FN · 误杀
人工 FAIL
FP · 漏判
TN
准确率
TPR
TNR
po / pe
Cohen's κ
Youden J

准确率 κ TPR、TNR(不随 π 变)

▶演示 2:Rogan-Gladen 校正和区间

TPR、TNR 和标注条数取演示 1 的混淆矩阵。拖动 Judge 判 PASS 的比例和未标注报告条数;bootstrap 每次 2 万个重抽样本、固定种子;和正文(Python 跑的)用的随机数不同,区间端点会差零点几个百分点。

预设
不校正(pobs + Wilson)
θ̂(未截断)
θ̂(截断到 [0, 1])
delta 法 95%
bootstrap,两边重抽
bootstrap,只重抽标注集

横轴真实有效率 θ,纵轴 Judge 通过率。直线 p = TPR·θ + (1 − TNR)(1 − θ);从 pobs 水平画过去,交点就是 θ̂。

三种区间对比;灰色竖线是不校正的 pobs。

▶演示 3:阈值和成本

双正态模型:误报的 Judge 分数 ~ N(0, 1),有效报告 ~ N(d′, 1),分数 ≥ 阈值判 PASS。成本以较便宜的那种错为 1 个单位。

Judge 判别力
TPR / TNR
每千条:漏判 / 误杀
每千条成本

误报的分数分布(右侧阴影 = 漏判) 有效报告(左侧阴影 = 误杀) 期望成本,圆点 = 最优阈值

✎练习

题 1(算 κ):100 条报告,TP = 45,FN = 5,FP = 10,TN = 40。Cohen's κ 是多少?
κ =
po = 85/100 = 0.85。人工 PASS 50%,Judge PASS 55%,pe = 0.5 × 0.55 + 0.5 × 0.45 = 0.50。κ = (0.85 − 0.50)/(1 − 0.50) = 0.70。
题 2(概念):Judge 把一条误报判成了有效。这算哪种错,影响哪个指标?
误报的人工标签是 FAIL,Judge 判 PASS,是 FP。TNR = TN/(TN + FP) 的分母是所有误报,这一条从 TN 变成 FP,TNR 下降。误杀是有效报告被判 FAIL,影响 TPR。
题 3(校正):Judge 的 TPR = 85%,TNR = 90%。新版本上 Judge 判 PASS 的比例是 40%。校正后的真实有效率是多少?
%
(0.40 + 0.90 − 1)/(0.85 + 0.90 − 1) = 0.30/0.75 = 40%。这里恰好和 pobs 相等:漏判数 (1 − θ)(1 − TNR) = 0.6 × 0.1 = 0.06,误杀数 θ(1 − TPR) = 0.4 × 0.15 = 0.06,两种错正好抵消。只是巧合,θ 一变就不抵消了。
题 4(截断):Judge 的漏判率是 10%(TNR = 90%)。一个新版本上 Judge 只判了 8% 的报告 PASS,校正公式算出负数。怎么报?
pobs 低于漏判率时公式给负数,有效率不可能为负,截断到 0;但截断会让均值偏高,所以原值要留着。B 把 Judge 的系统误差又加了回去。经常截断说明 Judge 的 TNR 可能已经不适用于新数据。
题 5(区间):标注 200 条,未标注只有 100 条。用 judgy 默认的 bootstrap(只重抽标注集)算 95% 区间。这个区间:
本章模拟里,未标注 100 条时只重抽标注集的区间只有 64.1% 的时候罩住真值;两边都重抽或用 delta 法约 94%~95%。校正修的是 Judge 的系统误差,抽样误差要靠区间。

8面试要点与代码

一句话讲清楚

Judge 是有误差的测量仪器。先人工标一批(两类都要够),算 TPR、TNR,不只看准确率和 κ,因为 κ 会随有效率变;按漏判、误杀的代价选阈值;再用 Rogan-Gladen 公式 θ̂ = (pobs + TNR − 1)/(TPR + TNR − 1) 把 Judge 通过率校正成真实通过率,区间同时算标注集和评测集的抽样误差,越界就截断并留原值。

追问准备

  1. 标多少条?看 delta 公式里哪一项最大。本例 TNR 那一项占 57%,要扩大随机标注量让误报变多(或按误报类别分层抽样;按 Judge 判定分层要加权)。两类各几十条是下限,少于这个 TPR、TNR 本身的 Wilson 区间就很宽。
  2. 换了 Judge 的 prompt 呢?TPR、TNR 作废,在同一批标注数据上重新跑。两个 Judge 版本对比是配对数据,用第 1 周的 McNemar 检验。
  3. J 很小会怎样?分母接近 0,区间爆炸;J ≤ 0 时公式无意义,judgy 直接报错。先改 Judge,不要硬校正。
  4. 能不能只用 Judge 判过的样本加少量人工标注做得更好?有,比如 prediction-powered inference(PPI)一类方法;Chen et al. 2026 比较了它和 Rogan-Gladen 的渐近方差。本章没有实测,只做指引。

核心代码(Python,只依赖 numpy)

def rogan_gladen(p_obs, tpr, tnr, clip=True):
    j = tpr + tnr - 1
    if j <= 0:
        raise ValueError("TPR + TNR - 1 <= 0:Judge 不比随机好,不能校正")
    theta = (p_obs + tnr - 1) / j
    return min(max(theta, 0.0), 1.0) if clip else theta


def rg_delta_ci(p_obs, n, m_pass, m_fail, tpr, tnr, z=1.959964):
    j = tpr + tnr - 1
    theta = (p_obs + tnr - 1) / j
    var = (p_obs * (1 - p_obs) / n
           + theta ** 2 * tpr * (1 - tpr) / m_pass
           + (1 - theta) ** 2 * tnr * (1 - tnr) / m_fail) / j ** 2
    half = z * var ** 0.5
    return theta, max(theta - half, 0.0), min(theta + half, 1.0)


print(rogan_gladen(0.53, 0.9, 0.8))                # 0.4714285714285714
print(rg_delta_ci(0.53, 1000, 120, 80, 0.9, 0.8))  # (0.4714..., 0.38401..., 0.55883...)

完整代码(混淆矩阵、κ、两种 bootstrap、成本和阈值、覆盖率模拟)和 14 个单元测试在学习目录 week06_Judge与错误分析/code/judge_calibration/,Python 3.9 + numpy 1.22 跑通;κ 和 scikit-learn 1.1.1 对照一致,点估计和 judgy 一致。

公式显示依赖 KaTeX(CDN),断网时公式会显示成原始 LaTeX 源码,交互部分不受影响。