paired bootstrap + CI
Day 27 拿到了两模型在同一 29 任务上的 Δcompletion,但一个点估计(比如 Δ=+8pp)不能直接拿来选型——它有抽样误差,可能真值横跨 0。
阶段: B3 · agent loop + 评测统计(Day 21-30) 标签: #paired-bootstrap #confidence-interval #ab-testing #variance-reduction
今日导引(由浅入深)
Day 27 拿到了两模型在同一 29 任务上的 Δcompletion,但一个点估计(比如 Δ=+8pp)不能直接拿来选型——它有抽样误差,可能真值横跨 0。
今天 Day 28 给这个 Δ 加误差棒:用配对 bootstrap 重采样 2000 次,取 Δ 分布的 2.5/97.5 百分位得 95% CI;CI 含 0 则差异不显著。
在 B1→B18 曲线上,这是从「看上去高」到「统计上真高」的关键一跳——承接 Day 23 的 Wald CI(单率误差棒),升级为「两率之差的误差棒」。明天 Day 29 接着问「要稳定看到 Δ5pp 需要多少任务」(power/样本量)。今天的最小可判定产出是:src/agent/eval/stats.ts 的 pairedBootstrap + 单测在确定性种子下绿(已达成),Δ 真实数值待 Day 27 真模型跑数填入。
1. 机理精读
为什么用配对(paired)而非独立两样本。
两模型跑的是同一批任务——任务难度(有的题天生好做、有的天生坑)是两模型共享的方差源:
- 用独立两样本检验,任务难度方差会进到两个样本里,把信噪比压低;
- 配对设计直接对同一任务的 a、b 成对取差
diff_i = a_i − b_i,任务难度作为公共项被消去,只剩「同一题上两模型的差」。
结果是 Δ 的 CI 更紧、检验更敏感——同样的 N,配对能检出更小的真实差异。这是方差缩减(variance reduction)的经典手法,也是 A/B 评测必须配对的根本原因。
bootstrap 的直觉:用重采样模拟「重跑实验」。
我们只有一份 29 个配对差。bootstrap 的核心假设是:这 29 个差就是总体的经验分布。
- 从中有放回地抽 29 个、求均值,重复 B=2000 次;
- 得到 2000 个「Δ 均值」——近似了「如果重做 2000 次实验,Δ 会落在哪」;
- 把这 2000 个 Δ 排序,取第 2.5 和第 97.5 百分位,就是 percentile 95% CI。
若这个区间跨 0,说明「Δ 可能是正、可能是负、也可能恰好是 0」,差异不显著,不能据此选型。
为什么是 2000 次,而不是 200 或 20000。
bootstrap 迭代数控制的是 CI 端点的蒙特卡洛噪声,不是统计功效本身。Miller《Adding Error Bars to Evals》(2024-11) 把 2000 次作为稳健下限:再少端点会抖,再多边际收益递减。本仓 pairedBootstrap 默认 iters=2000,与该建议一致。
percentile bootstrap 的边界与替代。
- 对极少样本,percentile CI 会偏窄(低估不确定性);
- 更严谨可用 BCa(偏差校正 + 加速);
- 但本套件 29 任务下 percentile 已够用——这是工程取舍:BCa 实现复杂、收益边际,29 任务不值得。
真正的硬约束是:绝不用裸点估计下选型结论,无 CI 的 Δ 不予采信(评审硬要求误差棒,承接 Day 23 的 Wald CI 思路)。
这一步与 power 的边界。
今天解决的是「拿到 Δ 后,它显不显著」;它不回答「为了能稳定检出某个 Δ,我该准备多少任务」——那是 Day 29 的 power/样本量反推。stats.ts 里 requiredNForDelta 已经为明天埋好,但 Day 28 的产出聚焦在 pairedBootstrap 本身。
2. 推导 / 手算 / 代码走读
代码在 src/agent/eval/stats.ts(纯函数、RNG 可注入、无 key):
pairedBootstrap(a, b, { iters, rng })(第 36–55 行):- 先校验
a.length === b.length(否则 throw'arrays must be equal length'),n=0也 throw(需 ≥1 对); - 算
diffs = a.map((x,i)=>x - b[i]),默认iters=2000、rng=Math.random; - 重采样循环:每轮内层
acc += diffs[Math.floor(rng()*n)]累加 n 个有放回抽样、means.push(acc/n); - 最后
means.sort((x,y)=>x-y),返回{ deltaMean: mean(diffs), ci95: [percentile(means,2.5), percentile(means,97.5)], n, iters }。
- 先校验
percentile(sorted, p)(第 17–26 行):线性插值百分位,idx=(p/100)*(len-1),对lo/hi加权——保证 2.5/97.5 不是粗暴取整。mean/sd(第 5–15 行):sd用n-1(样本标准差),n<2返回 0。requiredNForDelta(delta, sdDiff)(第 59–65 行):明天 power 用,公式n = ((z_{α/2}+z_β)·sd/|Δ|)²,zAlpha=1.959963985(双侧 α=.05)、zBeta=0.841621234(power=.8),delta=0→Infinity,sdDiff<=0→2,结果Math.max(2, ceil(...))。
手算最小例(验直觉):设 5 个任务,模型 a 的逐题 0/1 得分与 b 成对:
diffs = [+1, 0, 0, +1, −1]
mean(diffs) = (+1+0+0+1−1)/5 = +0.2 // a 比 b 高 20pp 点估计
这 5 个差里有正有负,bootstrap 重采样均值会在 0 附近大幅抖动,95% CI 很可能跨 0 → 不显著。这正说明「+0.2 的点估计」在 N=5 下毫无说服力——CI 才是真相。把同样的 +0.2 放到 29 个一致为正的差上,CI 才可能不含 0。
为什么单测要注入固定 rng。 pairedBootstrap 默认用 Math.random,结果每次不同,无法断言。单测注入确定性种子 rng(如线性同余或固定序列),让 2000 次重采样完全可复现,CI 端点变成可断言的定值——这是「纯函数 + RNG 注入」设计的直接收益。
配对差为什么必须等长。 pairedBootstrap 开头就 throw 不等长输入——这不是防御性洁癖,而是配对的语义要求:a[i] 与 b[i] 必须是同一个任务上两模型的得分,错位一格整组差就失去「消去任务难度」的意义。所以两份报告必须按 taskId 对齐后再取 completion 向量,不能假设两次跑的 perTask 顺序天然一致(虽然本仓 EVAL_TASKS 顺序固定,但对齐是更稳的做法)。
CI 的三种读法(落到结论):
[+0.03, +0.18]全正不含 0 → A 显著优于 B,可据此选 A;[−0.05, +0.21]跨 0 → 未检出显著差异,转看成本/延迟/unknown 率;[−0.20, −0.04]全负不含 0 → B 显著优于 A。
第二种是最常见也最容易被误读的——它不是「两者一样好」,而是「这个实验还没本事区分它们」(power 不足,见 Day 29)。
3. 今日实战
src/agent/eval/stats.ts已写:mean/sd/pairedBootstrap(a,b)→Δ点估计 + 95% CI、requiredNForDelta。- 单测覆盖已知小样本(确定性种子,注入固定
rng使结果可复现),运行pnpm test验证stats.test绿。 - 待 Day 27 真模型跑出 DeepSeek-vs-Qwen 的逐题 0/1 completion 向量后,把两向量喂
pairedBootstrap得 Δcompletion 的真实 95% CI;Day 30 据 CI 是否含 0 写选型结论。
4. 今日实测 / 产出
- stats.ts + 单测已建,
pnpm test该文件绿(mean/sd/pairedBootstrap+95%CI/requiredNForDelta,测试通过)。已完成。 - 全套件 378 tests passing、tsc clean。已完成。
- Δ 的真实数值仍待 Day 27 真模型跑数填入——不臆造。
5. 常见误区 / 陷阱
- 用独立两样本而非配对:白白让任务难度方差进来,CI 变宽、检验变钝。
- 报点估计不报 CI:评审硬要求误差棒;裸 Δ 不予采信。
- CI 含 0 还硬下「A 比 B 强」结论:含 0 = 不显著,必须如实说「未检出显著差异」。
- bootstrap 不固定 rng 做单测:结果不可复现;本仓单测注入确定性种子正是为此。
- 以为 percentile CI 永远对:极少样本下偏窄,必要时上 BCa——但 29 任务 percentile 够用。
- 把 bootstrap 迭代数当统计功效:2000 次只是控蒙特卡洛噪声,与样本量 N(功效)是两回事。
6. 学习资源(每条带 YYYY-MM)
- Miller, Adding Error Bars to Evals(2024-11)—— eval 误差棒权威,2000 次 bootstrap 为稳健下限。
- Efron & Tibshirani, An Introduction to the Bootstrap(经典专著)—— percentile / BCa 区间原理(打底,非主线)。
- 本仓
src/agent/eval/stats.ts+src/agent/__tests__/eval/stats.test.ts—— 实现 + 确定性单测走读。 - Anthropic, Demystifying evals(2026-01)—— 选型须配 CI、勿用裸点估计的方法学背书。
SOTA检查 (2026-06 更新)
- 当前主流:paired bootstrap 是 eval 对比的稳健标准做法,无过时风险,仍是 SOTA。
- 是否仍 SOTA:是。配对设计 + 2000 次 percentile bootstrap + 报 CI 仍是 2026 评审基线做法。
- 边界:对极少样本 percentile CI 偏窄,必要时配 BCa——但 29 任务下 percentile 足够。
- 过时黑名单:勿用裸点估计下选型结论;勿用独立两样本检验代替配对设计;勿把迭代数当功效。
- 下次复查点:Day 29 把
requiredNForDelta用于 power/样本量反推;Day 30 接真实报告判 Δcompletion 95% CI 是否含 0。
衔接
- 昨天:Day 27 — Qwen3 对比与稳健性(产出两模型逐任务 Δ)。
- 今天:配对 bootstrap 2000 次给 Δ 加 95% CI,CI 含 0 即不显著;stats.ts 单测绿,全套 378 测试 + tsc clean。
- 明天:Day 29 — 统计功效 power(反推检出 Δ5pp 所需任务数,量化「29 任务够不够」)。