第 06 章
第 1 周:要跑多少次才够?
样本量估算:零失败证明上界、把成功率估到 ±E、检测两个版本的差距,以及允许少量失败的发版门禁。一段讲解视频,一个样本量计算器和一个门禁模拟器。
“这个 Agent 上线前要跑多少次评测?“答"100 次"或者"越多越好”,面试官都会接着问:凭什么是 100?样本量不是拍脑袋定的,它由你要下的结论反推出来。要证明失败率低于 10%,30 次全过就够;要把成功率估到 ±5%,最多要 385 个样本;要测出两个 prompt 版本差 5 个点,每个版本要 900 多个。这一章讲这三种问题怎么算 n,再把它落到发版门禁上。
讲解视频
互动演示
上面是样本量计算器:切换三种场景,拖动置信度、目标精度、基线成功率、最小可检测差距,n 和曲线实时更新。下面是门禁模拟器:设定"跑 n 次最多挂 f 次"的规则和真实失败率,看漏放率、误拦率和 OC 曲线,也可以模拟 200 轮发版。页面底部有自动判分的练习。
先问要证明什么
测开和 Agent 评测里常见的结论只有三种,它们需要的样本量差了一个数量级(95% 置信度):
| 想说的话 | 统计问题 | 典型答案 |
|---|---|---|
| “退款误操作率 ≤ 5%” | 零失败证明上界 | 59 次全过 |
| “任务成功率约 80%,误差 ±5%” | 估计到指定精度 | 246 个;不知道成功率时按最坏情况 385 个 |
| “新 prompt 比旧的好 5 个点” | 检测两个版本的差距 | 80% → 85%:每个版本 906 个;配对能省多少看两个版本有多一致 |
场景一:零失败,证明失败率 ≤ p
这是 rule of three 那一章的内容。如果真实失败率等于 p,n 次全过的概率是 \((1-p)^n\)。要让这件事"巧合到只有 α 的概率”:
$$ n \ge \frac{\ln \alpha}{\ln(1-p)} \approx \frac{-\ln\alpha}{p} $$α = 5% 时 \(-\ln 0.05 \approx 3\),就是 n ≈ 3/p。99% 置信度时常数变成 4.6。
| 要证明失败率 ≤ | 90% 置信 | 95% 置信 | 99% 置信 | 速算 3/p |
|---|---|---|---|---|
| 10% | 22 | 29 | 44 | 30 |
| 5% | 45 | 59 | 90 | 60 |
| 2% | 114 | 149 | 228 | 150 |
| 1% | 230 | 299 | 459 | 300 |
表里是精确法向上取整后的次数,速算 3/p 总是略多一点。这个公式只管"一次都没挂"。挂了 1 次,就要换 Wilson 区间或精确二项区间,或者事先设计一个允许少量失败的门禁(见后文)。
场景二:把成功率估到 ±E
想回答"成功率 80%,误差 ±5%"。误差(区间半宽)约为 \(z\sqrt{p(1-p)/n}\),令它等于 E,反解 n:
$$ n \approx \frac{z^2\,p(1-p)}{E^2} $$z 是置信度对应的正态分位数:90% 取 1.645,95% 取 1.96,99% 取 2.576。
| 预估成功率 p | ±10% | ±5% | ±3% | ±2.5% |
|---|---|---|---|---|
| 50%(最坏情况) | 97 | 385 | 1068 | 1537 |
| 80% | 62 | 246 | 683 | 984 |
| 90% | 35 | 139 | 385 | 554 |
两个规律:
- p = 50% 是最坏情况。\(p(1-p)\) 在 0.5 处最大,等于 0.25。不知道成功率大概多少,就按 50% 算,只会偏保守。
- 精度减半,样本量 ×4。E 在分母上是平方。±5% 要 385 个,±2.5% 要 1537 个;把误差从 5 个点压到 1 个点,要 25 倍的样本(9604 个)。
这个公式用来事前规划。跑完以后报区间,用 Wilson 区间,尤其是成功率接近 0 或 100% 的时候。成功率 98% 时问"±5%“没有意义,应该改问"失败率上界是多少”,回到场景一。
场景三:检测两个版本的差距
改了 prompt,想知道新版本是不是真的更好。有两种犯错方式:
| 错误 | 含义 | 控制它的参数 | 常用取值 |
|---|---|---|---|
| 误报(第一类错误) | 其实没变好,你说变好了 | 显著性水平 α | 5%(双侧) |
| 漏报(第二类错误) | 其实变好了,你没测出来 | β;功效 power = 1 − β | 80% 或 90% |
第三个量是最小可检测差距 δ(效应量):你关心"好 5 个点"还是"好 1 个点"?
两个版本各跑一批互不相关的样本(独立两组),基线 \(p_1\),新版本 \(p_2 = p_1 + \delta\),每个版本需要:
$$ n = \frac{\left(z_{1-\alpha/2}\sqrt{2\bar p(1-\bar p)} + z_{1-\beta}\sqrt{p_1(1-p_1)+p_2(1-p_2)}\right)^2}{\delta^2},\qquad \bar p = \frac{p_1+p_2}{2} $$| 基线 → 新版本 | power 80% | power 90% |
|---|---|---|
| 80% → 90% | 199 | 266 |
| 80% → 85% | 906 | 1212 |
| 90% → 95% | 435 | 582 |
| 50% → 55% | 1565 | 2095 |
| 80% → 82% | 6039 | 8084 |
α = 5% 双侧,正态近似、不带连续性校正,数字是每个版本的样本数。差距减半,样本量约 ×4。反过来看:每边只跑 100 条时,80% 对 85% 的功效只有约 15%,“新版本高了 5 个点"多半是噪声。
配对设计:同一批用例,两个版本都跑
离线评测里,两个 prompt 版本几乎总是跑同一批用例。这时应该逐条比较:大部分用例两个版本结果一样,对"谁更好"没有信息;有用的是结果不一致的用例。设不一致的用例占比为 ψ,配对设计需要的用例数(Connor, 1987):
$$ n = \frac{\left(z_{1-\alpha/2}\sqrt{\psi} + z_{1-\beta}\sqrt{\psi-\delta^2}\right)^2}{\delta^2} $$| 80% → 85%,power 80% | 每个版本要跑的用例数 |
|---|---|
| 独立两组 | 906 |
| 配对,ψ = 20% | 626 |
| 配对,ψ = 10% | 312 |
| 配对,ψ = 5%(只修好、没改坏) | 155 |
| 配对,ψ = 29%(两个版本的结果互不相关) | 909 |
两个版本行为越接近,配对省得越多:ψ = 10% 时省约三分之二,ψ = 20% 时只省约三成;两个版本的结果互不相关时(这里 ψ = 0.8 × 0.15 + 0.85 × 0.2 ≈ 29%)完全不省。改一版 prompt,大部分用例的结果通常不会变,ψ 往往不大,所以离线评测值得按配对来设计和检验。配对数据怎么做检验,是下一章 McNemar 的内容。
允许少量失败的发版门禁
把场景一变成流水线里的门禁:“跑 n 次,失败不超过 f 次就放行”。门禁有两个错误率:
- 漏放率:失败率为 q坏 的坏版本碰巧通过,\(P(\text{失败数} \le f \mid q_{坏})\)。
- 误拦率:失败率为 q好 的好版本碰巧被拦,\(1 - P(\text{失败数} \le f \mid q_{好})\)。
失败数服从二项分布,两个概率都能精确算出来。通过概率随失败率变化的曲线,就是抽样验收里的 OC 曲线。f = 0 时漏放率就是 \((1-q_{坏})^n\),也就是 rule of three 里的 α。
以 q好 = 2%、q坏 = 10% 为例:
| 门禁规则 | 漏放率 | 误拦率 |
|---|---|---|
| 5 次全过 | 59.0% | 9.6% |
| 30 次全过 | 4.2% | 45.5% |
| 30 次最多挂 1 次 | 18.4% | 12.1% |
| 89 次最多挂 4 次 | 5.0% | 3.3% |
“30 次全过"漏放率够低,但好版本将近一半会被拦下,团队很快就会习惯"挂了再跑一遍”。最多跑 3 轮、有一轮全过就放行,坏版本的漏放率从 4.2% 涨到 12.2%,门禁的保证就没了。
想让两个错误率同时 ≤ 5%,先定好 q好 和 q坏,再找最小的 (n, f)。两者离得越近 n 越大:2% 对 10% 要 89 次最多挂 4 次,1% 对 5% 要 181 次最多挂 4 次。
样本量数的是独立样本
上面所有公式里的 n,都是独立样本的个数(见「独立性假设」一章)。同一个输入重跑 400 次,只说明这一个输入的 flaky 率;对"Agent 在这类任务上的成功率”,换算成独立样本也只有个位数(公式上限约 1/ρ ≈ 3.3),而且只有 1 个用例,根本推不到这类任务。
每个用例跑 m 次、用例内相关系数为 ρ 时,要凑够 \(n_{\text{req}}\) 个有效样本,需要的用例数是:
$$ C = \frac{n_{\text{req}}\,\bigl(1+(m-1)\rho\bigr)}{m} $$| 目标 ±5%(385 个有效样本),ρ = 0.3 | 用例数 | 总运行次数 |
|---|---|---|
| 每个用例跑 1 次 | 385 | 385 |
| 每个用例跑 5 次 | 170 | 850 |
| 1 个用例跑 385 次 | 做不到:有效样本上限约 1/ρ ≈ 3.3 | — |
重跑能省一些用例,但要多花运行成本,而且省得有上限。先算需要多少个不同的用例,再决定每个跑几次;报告里写"170 个用例 × 每个 5 次",不要写"850 次"。
from math import ceil, log, sqrt
from scipy.stats import binom, norm
def n_zero_failure(p: float, conf: float = 0.95) -> int:
"""零失败时,证明失败率 <= p 需要的次数(精确法)。"""
return ceil(log(1 - conf) / log(1 - p))
def n_precision(p: float, e: float, conf: float = 0.95) -> int:
"""把成功率估到 ±e 需要的独立样本数;p 未知时用 0.5。"""
z = norm.ppf(1 - (1 - conf) / 2)
return ceil(z ** 2 * p * (1 - p) / e ** 2)
def n_two_groups(p1: float, p2: float, alpha: float = 0.05, power: float = 0.8) -> int:
"""独立两组比较成功率,每组需要的样本数(双侧、无连续性校正)。"""
za, zb = norm.ppf(1 - alpha / 2), norm.ppf(power)
pbar = (p1 + p2) / 2
num = za * sqrt(2 * pbar * (1 - pbar)) + zb * sqrt(p1 * (1 - p1) + p2 * (1 - p2))
return ceil(num ** 2 / (p1 - p2) ** 2)
def n_paired(psi: float, delta: float, alpha: float = 0.05, power: float = 0.8) -> int:
"""配对设计(McNemar)需要的用例数,psi 是结果不一致的用例占比(Connor, 1987)。"""
if not 0 < abs(delta) <= psi:
raise ValueError(f"需要 0 < |delta| <= psi,收到 delta={delta}, psi={psi}")
za, zb = norm.ppf(1 - alpha / 2), norm.ppf(power)
return ceil((za * sqrt(psi) + zb * sqrt(psi - delta ** 2)) ** 2 / delta ** 2)
def gate_errors(n: int, f: int, q_good: float, q_bad: float) -> tuple[float, float]:
"""门禁"n 次里最多 f 次失败"的(漏放率, 误拦率)。"""
return binom.cdf(f, n, q_bad), 1 - binom.cdf(f, n, q_good)
print(n_zero_failure(0.05)) # 59
print(n_precision(0.5, 0.05)) # 385
print(n_two_groups(0.80, 0.85)) # 906
print(n_paired(0.10, 0.05)) # 312
print(gate_errors(89, 4, 0.02, 0.10)) # (0.0497, 0.0334)
面试怎么答
- “失败率 < 10% 要跑几次?” 零失败 30 次(精确法 29 次)。要证明 ≤ 5% 就是 60 次。
- “两个 prompt 版本差 5 个点要多少条用例?” 基线 80%、α = 5%、功效 80% 时,独立两组每边约 900 条;同一批用例两个版本都跑、做配对检验,不一致率 10% 时约 310 条就够。
- 所有 n 数的都是不同的用例,不是重跑次数。
常见错误说法
- “失败率 < 10%,跑 100 次”:零失败 30 次就够,100 次是没算过的数。
- “估计误差 ±1%,跑 1000 次”:±1% 最坏要 9604 个,1000 个只能到 ±3.1%。
- “新版本 85%、旧版本 80%,跑了 100 条,新版本更好”:每边 100 条时差 5 个点的功效只有约 15%,这点差距多半是噪声。
- “30 次全过的门禁很严格”:对好版本也一样严格。失败率 2% 的好版本有 45% 会被拦,最后变成"挂了重跑"。
- “同一个用例跑 400 次,样本量 400”:有效样本看的是用例数。