qinghuan
← 评测、复现与研究实践 / 全部笔记

单元 44 / 评测、复现与研究实践

P0 · 必会主线L3 · 深入

pass@k 与统计比较:正确解释实验提升

计算有限候选中抽取 k 个、至少有一个成功的 pass@k,再按同一题目配对比较方法,检查抽样单位、置信区间和预算限制。

本篇目录 · 按需要跳转

这篇重点学什么

本篇重点:推导 pass@k 的组合估计,比较系统时按题目重新抽样并保留配对;事先固定采样预算和报告规则。

开始读正文 ↓
学过相近内容?查看哪些可以跳过

两条路线有些基础相同。已经学会的部分可以跳过,另一条路线只用来补你当前需要的内容。

回归与统计评估

共同基础先约定实验方法,再用独立样本得到可以比较的结果。

另一条路线补充什么?

先列出发布必须通过的检查,校准评分器,再比较新旧版本有没有退步,决定能否发布。

读完这篇,你能做什么

Java稳定计算组合概率;Python枚举小集合校验公式,并生成按题目配对的bootstrap差值区间。

学习理由、准备要求与知识点

为什么学它

多试几次通常更容易找到一个成功答案,但不能因此声称单次就可靠。比较提升时,也要选对独立抽样和统计的单位。

先读这些内容

知识点清单

  • pass@k定义与候选预算
  • 无放回组合估计
  • 独立同分布假设
  • 数值稳定log1p与expm1
  • 按题目宏平均
  • 配对bootstrap
  • 重复比较与选择偏差
  • 统计与实际效果

运行前先确认2021年代码模型评估论文公式1、ACL2018统计指南;Java17与Python3.14标准库。未进行真实模型对比或GPU实验。

01先弄懂它是什么

一道题允许生成 k 个候选,只要至少一个通过验证,就记为该题 pass@k 成功。它衡量的是多次尝试中找到成功候选的机会,前提是能检查这些候选。例如生成十次里有一次正确,与系统一次回答就正确,是不同指标;从十份里找出正确那份也有成本。因此报告要同时写明 k、怎样采样,以及验证用了多少预算。

常见做法是先为每题采样 n 个候选,记录其中 c 个通过,再估计较小 k 的结果:1-C(n-c,k)/C(n,k),其中 k 必须在一到 n 之间,C 表示组合数。可以把它读成:从已有候选中均匀、无放回地选 k 个,先算全部失败的比例,再用一减去它。若原始候选是独立同分布采样,这也是目标 pass@k 的无偏估计;对会边试边调整的自适应搜索,不能直接套用同样解释。

跨题目时通常先计算每题分数,再对题目等权平均。把所有题的候选混在一起后算一个成功率,会让候选数多或容易的题权重失控,也把题目难度差异掩盖。比较两种方法时,同一道题的结果天然相关,应该保留配对关系,而不是把每个候选当作完全独立的任务。

文中这几个词,先认一认

无偏估计
在规定的抽样条件下,重复很多次实验,估计结果的平均值等于要估计的目标;单次结果仍可能有误差。
配对比较
让两种方法做相同题目,再逐题比较差值,减少两组题目难度不同带来的干扰。
Bootstrap
从已经观察到的统计单位中有放回地重抽样,再重复计算指标,用来近似估计结果会怎样波动。

还有陌生词?到术语小抄里看一个具体例子 ↗

02看看它怎么工作

组合数可能很大,Java不用阶乘,而把全失败概率写成从i=0到k-1的乘积(1-c/(n-i))。先累加log1p(-c/(n-i)),再用-expm1返回补概率。若失败数小于k,直接返回一;没有成功候选则返回零。这些分支既表达数学边界,也避免对数零和巨大组合数溢出。

Python先枚举n不超过七的所有k子集,将“至少一个成功”的比例与组合公式逐一对照。接着给六道合成题构造两种方法的每题分数,用同一题目索引同时抽取两种方法,计算差值均值的百分位区间。固定随机种子使教学输出可核对,但六题区间只展示流程,不足以证明真实方法优劣。

按题目做 bootstrap,主要回答“换一批题,平均成绩会怎样波动”。但同一道题重新生成一批候选,也可能得到不同结果;如果只固定眼前这批候选再重抽题目,就没有充分计入这部分变化。可以用多个随机种子重复生成,或把题目抽样、候选生成分成不同层来分析,但要保留真实的数据关联。例如多道题都来自同一模板或仓库,它们可能一起变难或变易,应考虑按模板或仓库成组抽样,不能因为题目 ID 不同就认定彼此独立。

先跑通一个最小例子

Python3.14标准库执行experiment.py,枚举小集合并对六道合成题做2000次配对bootstrap。没有运行模型或不受信任程序。

展开收起代码 · experiment.pypython · 34 行

长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。

experiment.py · python
import itertools
import math
import random
def pass_k(n,c,k):
    if not (type(n) is int and type(c) is int and type(k) is int and n>=1 and 0<=c<=n and 1<=k<=n): raise ValueError("counts")
    return 1-math.comb(n-c,k)/math.comb(n,k) if n-c>=k else 1.
def paired_bootstrap(a,b,repeats=2000,seed=44):
    if not a or len(a)!=len(b) or repeats<2: raise ValueError("pairs")
    differences=[y-x for x,y in zip(a,b)]
    rng=random.Random(seed);n=len(a);sample=[]
    for _ in range(repeats):
        # 同一个题目索引同时取两个方法,保留配对相关性。
        sample.append(sum(differences[rng.randrange(n)] for _ in range(n))/n)
    sample.sort()
    return sum(differences)/n,(sample[int(.025*(repeats-1))],sample[int(.975*(repeats-1))])

for n in range(1,8):
    for c in range(n+1):
        for k in range(1,n+1):
            subsets=list(itertools.combinations([1]*c+[0]*(n-c),k))
            observed=sum(any(s) for s in subsets)/len(subsets)
            assert math.isclose(pass_k(n,c,k),observed,abs_tol=1e-12)
a=[pass_k(10,c,2) for c in (1,2,3,4,2,1)]
b=[pass_k(10,c,2) for c in (2,2,4,3,3,1)]
mean,interval=paired_bootstrap(a,b)
assert interval[0]<=mean<=interval[1]
assert paired_bootstrap(a,b)==paired_bootstrap(a,b)
assert paired_bootstrap(a,a)==(0.,(0.,0.))
assert math.isclose(pass_k(10,2,1),.2)
try: pass_k(2,1,3)
except ValueError: pass
else: raise AssertionError("k>n")
print(f"paired_delta={mean:.6f} percentile_interval={interval}")
print("PASS 6 check groups; tiny synthetic tasks, no general significance claim")

核对运行结果输出paired_delta及百分位区间,PASS 6 check groups;不从六题合成数据得出普遍显著性结论。

正在加载流程图…

先在每题内部统计候选成功数,再按任务单位比较,避免把同题样本当独立题目。
查看流程图源码(Mermaid)
flowchart TD
  N0["固定任务集采样规则与k"]
  N1["每题记录候选总数和通过数"]
  N2["稳定计算每题pass概率"]
  N3["同题配对重采样差值"]
  N4["连同区间预算和覆盖报告"]
  N0 --> N1
  N1 --> N2
  N2 --> N3
  N3 --> N4

按顺序看,每一步做了什么

  1. 固定任务集采样规则与k
  2. 每题记录候选总数和通过数
  3. 稳定计算每题pass概率
  4. 同题配对重采样差值
  5. 连同区间预算和覆盖报告

03对照实现代码

pass@k无偏估计的组合表达 (新标签页)↗

Evaluating Large Language Models Trained on Code,2021;公式1与附录A · 1-C(n-c,k)/C(n,k)

原创论文公式模型;不涉及任何商业模型API或当前产品规格。

核心机制代码 · python
import math
def estimate(n, c, k):
    if not 0 <= c <= n or not 1 <= k <= n:
        raise ValueError("counts")  # 不为样本数不足的k外推结果。
    if n-c < k:
        return 1.0  # 全失败子集不存在。
    return 1-math.comb(n-c,k)/math.comb(n,k)  # 原创小整数公式模型,Java另做稳定浮点版。

原论文定义pass@k并讨论利用n个样本中c个正确结果的估计;附录通过二项分布假设说明无偏性。这里明确区分两层含义:给定有限池的无放回比例是组合恒等式,而把它解释成未来独立采样k次的成功概率估计还需要采样假设。

HumanEval作者实现estimate_pass_at_k使用乘积方式避免直接构造组合数,并在失败候选不足k时返回一。本文Java改写为对数与expm1累加,是同一组合表达的数值实现,不复制测试执行器,也不运行生成的任意代码。仓库master作为阅读入口,论文公式提供固定机制基线。

不能简单把这次观察到的 c/n 当成真实成功概率,再代入 1-(1-c/n)^k;它通常不同于前面的估计,n 小时偏差尤其值得注意。束搜索、看到反馈再重试、删除重复候选,也都会改变候选之间的关系。此时仍可计算当前候选池的组合比例,但若要把它解释为独立采样成功概率的无偏估计,就需要额外论证。

统计比较资料强调选择检验需匹配任务、指标和样本依赖。本文百分位bootstrap是明确的教学方案,不替所有基准规定唯一检验,也不把置信区间描述为“真值落入此区间的概率”。解释应回到重复抽样程序及其近似条件,尤其注意小样本与强依赖的局限。

代码先调用谁,再调用谁

  1. 为每题生成并验证n个候选
  2. 统计正确候选c
  3. 按目标k计算组合补概率
  4. 按任务单位聚合并比较方法

04动手写一个例子

稳定的有限候选pass@k

给定整数n、c、k,计算至少一次成功的无放回组合比例;非法计数拒绝,避免阶乘和相近浮点数直接相减。

输入 1
n=10,c=2,k=3
输出 1
0.5333333333333333
输入 2
n=2000000000,c=1,k=1
输出 2
5e-10

全失败概率的第i项分母是n-i,分子是n-c-i;两者比值等于1-c/(n-i)。乘k项即得到组合数比值。循环累积这些项的对数,最后还原补概率,因此不需要存储候选本身,也不需要任意精度阶乘。

第i次循环前logFailure等于前i项失败因子的对数和,添加当前项后保持不变量。若n-c小于k,全失败事件不可能发生;否则所有因子为正,log1p参数严格大于负一。c等于零独立处理,防止不必要计算,并使边界语义清楚。

有限双精度仍有舍入误差,但log1p和expm1对接近零的参数更稳定。测试包含极小成功概率并使用比普通容差更严格的断言,避免一个宽松绝对误差掩盖结果被算成零。循环时间O(k),空间O(1),不把验证候选的模型与测试运行成本算进本函数。

为什么这样写是对的完成i轮时logFailure是前i个无放回失败因子对数之和。

时间 O(k)额外空间 O(1)

至多k次标量对数累加;提前返回分支为常数时间,固定字宽算术不构造组合整数。

保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。

Main.java · 完整可运行程序 · Java
import java.util.*;
public class Main {
    static void near(double expected,double actual) {
        if (!Double.isFinite(actual) || Math.abs(expected-actual)>1e-9*Math.max(1,Math.abs(expected)))
            throw new AssertionError(expected+" != "+actual);
    }
    static void check(boolean ok) { if(!ok) throw new AssertionError(); }
    static void bad(Runnable r) {
        try { r.run(); } catch(IllegalArgumentException e) { return; }
        throw new AssertionError("invalid input accepted");
    }
static double passAtK(int n,int c,int k) {
        if(n<1||c<0||c>n||k<1||k>n) throw new IllegalArgumentException("counts");
        if(c==0) return 0;
        if(n-c<k) return 1; // 失败候选不足k个,任何无放回子集都含成功项。
        double logFailure=0;
        for(int i=0;i<k;i++) logFailure+=Math.log1p(-(double)c/(n-i)); // 累加对数乘积,避免组合数与阶乘溢出。
        return -Math.expm1(logFailure); // 失败概率接近一时,避免直接一减exp的消减误差。
    }
    public static void main(String[] args) {
near(1-56.0/120,passAtK(10,2,3));
        near(0,passAtK(10,0,3));
        near(1,passAtK(10,10,3));
        near(1,passAtK(10,2,9));
        near(.2,passAtK(10,2,1));
        check(Math.abs(passAtK(2_000_000_000,1,1)-5e-10)<1e-22);
        bad(()->passAtK(10,2,0));
        bad(()->passAtK(10,11,2));
        System.out.println("PASS 8 cases");
    }
}

这些特殊情况也要试一试

  • c零直接返回零
  • 失败数不足k直接返回一
  • 极小成功概率使用expm1
  • k大于n或零拒绝
进阶选读 · 多层随机性的研究比较

场景题目来自不同仓库,候选又由多个随机种子生成。

做法预注册主指标与比较方法,按真实独立层次重采样,保留同题配对;额外报告单次成功、调用成本和选择器准确率。

复杂度B次重采样、N题的均值统计O(BN),若每次还重采候选则另乘每题样本规模。

好处与代价更完整方差模型提高解释力但需要更多数据;探索很多配置再选最好者仍产生选择偏差。

05项目里怎么用

评估配置应固定温度、候选数、超时和判定器版本。增加k通常增加预算,方法A的pass@10不能直接与B的pass@1称作同成本提升。若实际服务只有一次机会,主指标应包含pass@1或真实选择策略的成功率;能够事后知道哪个候选通过是一项额外能力假设。

报告效果时区分统计可检测差异与实际价值。一个很小但稳定的提升可能抵不过成倍延迟,某些关键类别退化也可能被总体平均掩盖。应给差值、区间、样本数和任务分层,并记录是否因看过结果而修改k或筛选题目,避免用事后挑选制造优势。

对同一保留集反复试很多方案,会逐渐把它变成开发集,即使每次都计算区间也无法修复选择偏差。最终结论需在冻结方案后确认,必要时保留新的测试集或校正多重比较。本文合成六题没有真实语言模型候选,所得区间只是统计代码证据,不是新方法有效性的实验结果。

容易踩的坑

  • pass@k被写成单次可靠性
  • 把c/n直接代入非线性式称无偏
  • 同题候选被当独立任务
  • k超过n仍外推
  • 只报告最佳种子与显著结果

几种做法,怎么选

表格有四列,窄屏可左右滑动查看代价和适用条件。

方案优势代价与限制适用条件
pass@1接近一次尝试能力忽略多样候选潜力单次调用产品基线
pass@k衡量多候选可解性依赖更多预算与验证程序生成及可验证搜索
配对任务区间利用共同题目难度仍依赖抽样单位和样本覆盖固定题集方法比较

06检验是否读懂了

先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。

先用自己的话解释

01给定池比例与总体估计区别?

展开参考答案与推导

前者是组合计数事实,后者还依赖原始候选的抽样假设。

02为什么用配对bootstrap?

展开参考答案与推导

同一题的两方法结果相关,应共同抽取以保留题目难度关系。

03置信区间包含零说明相同吗?

展开参考答案与推导

不能证明相同,只说明此数据与方法下未排除零差异,仍需看区间宽度和实际阈值。

换个情况,还会处理吗

01pass@10提高但线上没变?

展开参考答案与推导

检查服务是否能生成十个候选并正确选择,比较同预算实际成功。

02很多题共享同一模板?

展开参考答案与推导

考虑按模板聚类抽样,避免夸大独立样本数。

03只某个种子明显领先?

展开参考答案与推导

报告预定多个种子分布,检查训练与采样方差,不挑最好一次。

写代码试一试

01计算组合比例。

输入 / 条件
n10,c2,k3
输出 / 预期
1-56/120

提示 先数全失败子集

展开参考答案与推导

总子集C10,3,失败子集C8,3,取补。

02改进极小概率精度。

输入 / 条件
失败概率接近一
输出 / 预期
保持非零小成功概率

提示 log1p与expm1

展开参考答案与推导

累加失败对数后用-expm1计算补值。

03构造配对重采样。

输入 / 条件
两方法每题结果数组
输出 / 预期
差值均值分布

提示 共同抽索引

展开参考答案与推导

每个抽中的题同时取A和B,再计算该次均值。

读完之后,独立完成

课后作业:自己做一遍

本篇共 12 道作业,先完成 9 道基础与综合任务,再按前置知识挑战进阶题。其中包含 2 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。

这次练什么

Java稳定计算组合概率;Python枚举小集合校验公式,并生成按题目配对的bootstrap差值区间。

本篇重点加练

候选预算、采样依赖和结果选择共同决定 pass@k 的含义,需要额外检查高分来源。

  • pass@k定义与候选预算
  • 独立同分布假设
  • 重复比较与选择偏差

重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。

先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。

基础必做

  1. 作业 01原理问答

    区分“固定候选池至少一个成功的组合比例”与“采样策略生成 k 次的总体成功概率”,列出将二者联系起来所需的假设。

    提交内容
    估计对象与假设表
    怎样算完成
    • 候选预算明确
    • 相关采样限制说明充分

    练到的知识点pass@k定义与候选预算 · 无放回组合估计 · 独立同分布假设

  2. 作业 02代码推演

    某题采样 n=8 个候选,其中 c=3 个成功,分别讨论 k=1、3、8 的组合计算;再审查 n<k 和 c=0 的输入契约。

    提交内容
    手算记录与边界表
    怎样算完成
    • 组合定义完整
    • 非法与合法极端分开

    练到的知识点无放回组合估计 · 数值稳定log1p与expm1

  3. 作业 03原理问答

    某系统把同一个候选复制十次并称为十次生成。定义固定候选池统计和生成过程成功概率,审查重复样本分别影响什么。

    提交内容
    两个估计对象与依赖图
    怎样算完成
    • 候选身份与调用次数分开
    • 独立性假设显式

    练到的知识点pass@k定义与候选预算 · 独立同分布假设

综合应用

  1. 作业 04编程实践

    实现 CPU pass@k 汇总器,支持逐题不同 n 并拒绝不合法 k;对小候选池使用穷举子集独立核对。

    提交内容
    实现及穷举检验
    怎样算完成
    • 每题有效性检查完整
    • 宏平均分母可追踪

    练到的知识点无放回组合估计 · 数值稳定log1p与expm1 · 按题目宏平均

  2. 作业 05排错实验

    生成共享模板的相关题目,比较逐题与按模板配对重采样的差值区间;固定方法 A/B 的逐题配对身份。

    提交内容
    模拟数据与区间报告
    怎样算完成
    • 方法配对关系保留
    • 推断单位与相关结构一致

    练到的知识点独立同分布假设 · 配对bootstrap

  3. 作业 06面试场景

    某模型只在 pass@20 上提升,但用户每次只使用一个答案。设计评价与决策报告,列出候选选择成本和实际使用条件。

    提交内容
    指标与使用场景映射
    怎样算完成
    • 预算可比
    • 统计变化与实用价值分开

    练到的知识点pass@k定义与候选预算 · 重复比较与选择偏差 · 统计与实际效果

  4. 作业 07编程实践

    CPU 模拟相同单次成功率但不同候选相关性的两种采样过程,重复 300 个任务,比较至少一次成功的频率与独立采样预测。

    提交内容
    模拟器与频率报告
    怎样算完成
    • 边际成功率受控
    • 相关结构可复现

    练到的知识点pass@k定义与候选预算 · 独立同分布假设

  5. 作业 08设计评审

    系统为容易题生成二个候选、困难题生成二十个,报告总体 pass@5 时又删除候选不足的题。制定完整任务账本和合法比较协议。

    提交内容
    预算及缺失样本审计
    怎样算完成
    • 预定任务都有去向
    • 条件选择不被隐去

    练到的知识点pass@k定义与候选预算 · 重复比较与选择偏差

  6. 作业 09排错实验

    对相同真效果模拟二十组配置,只保留观察最好的一组;比较选择数据上的结果与独立新样本上的结果,不改变选择规则。

    提交内容
    CPU 选择偏差实验
    怎样算完成
    • 筛选过程固定
    • 独立评价真实隔离

    练到的知识点重复比较与选择偏差

进阶挑战 · 掌握前置后选做

  1. 作业 10设计评审

    预注册两个方法、两个 k 和多个种子的比较计划,规定主指标、探索指标、重采样单位及停止规则;用合成数据跑通分析。

    提交内容
    分析计划与 CPU 演练
    怎样算完成
    • 选择偏差受约束
    • 演练结果不当真实模型结论

    练到的知识点配对bootstrap · 重复比较与选择偏差 · 统计与实际效果

思维拓展

先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。

  1. 作业 11思维拓展 · 跨域迁移

    将 pass@k 迁移到多个解题者协作:各人准确率相同但错误高度相关时,增加人数的收益能否由单人准确率决定?构造两种联合结果分布。

    提交内容
    联合分布与收益比较
    怎样算完成
    • 分布边际一致
    • 相关性而非人数被单独检验

    练到的知识点pass@k定义与候选预算 · 独立同分布假设

  2. 作业 12思维拓展 · 开放探究

    在固定总生成 token 预算下,选择更多短候选还是更少长候选?提出可证伪研究问题,预先定义候选可执行性、选择规则和保留任务。

    提交内容
    研究预注册与成本表
    怎样算完成
    • 总预算可对账
    • 试验前明确主比较

    练到的知识点pass@k定义与候选预算 · 重复比较与选择偏差

完成后,再检验一次

给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。

继续阅读

先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。

打开资料库,找下一份学习材料 ↗