拓展阅读

成功率、配对比较与真实成本口径

计算pass@k、独立重复假设下的至少一次与全部成功概率,并对合成配对任务做Bootstrap示范。

状态:自检已实际通过 · 环境:Python标准库 · 性质:合成/模拟教学实验
对应讲义 12.11 · 保存Python脚本

沿着对象与状态理解本章

两个平均成功率之间的差值,不能告诉你哪些题退化。先看同题配对表:两边都成功、新版独胜、旧版独胜、两边都失败,再看成本、方差和评分器误差。

下方图解的固定输入在“输入与适用条件”中声明。它用于解释本章机制,不与本章其他独立例子的数值暗中接续。

动态图解 · F36

涨了10个百分点,为什么还不能急着下结论?

一次实验怎样区别真实改进与偶然波动?

像给同一组题换两种解法:同题对照能减少题目难度不同带来的干扰。统计方法帮助表达不确定性,不会自动告诉你退化是否可接受、成本是否值得。实际决策还要回到任务类型、失误严重性和重复表现。

配对同一任务同时有新旧方案结果。
百分点两个百分率直接相减,如70%−60%=10个百分点。
随机波动在相同条件下多次运行也可能有不同结果。

输入与适用条件:构造的20题配对表:10都成、4新成旧败、2旧成新败、4都败。不把它当真实模型胜率。

1 / 5 · 同20题,两版各跑一次

第1步,共5步

窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。

涨了10个百分点,为什么还不能急着下结论?:同20题,两版各跑一次都成功10只有新版本成功4只有旧版本成功2都失败4相同题集旧版:12/20新版:14/20改善4,退化2

配对意味着旧版和新版面对同一题。先列出两者在每道题上的成功与失败关系,而不是只看两个总分。

20个配对任务;不是40个独立任务

默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。

为什么需要

避免因一个小样本的好看结果就把优化推向全部任务。

怎样实现

固定题集、版本和预算,配对重复,记录退化,并检查置信区间等不确定性。

可能的代价与误解

只保留成功重跑、反复调参又用同一评测集验收,会高估能力。

如何判断是否做对

重算全部配对原始记录,报告净提升、退化和成本,而不是只报均值。

怎样观察与验证

p=0.8、k=3得到0.992与0.512。所有任务结果和费用是虚构的;区间不是对任何产品的统计结论。

运行命令

python3 reading-notes/experiments/08_evaluation.py

完整教学代码

"""合成样本评测:pass@k、独立重复假设、配对Bootstrap、成功成本。"""
import math
import random


def pass_at_k(n: int, c: int, k: int) -> float:
    if any(not isinstance(x,int) or isinstance(x,bool) for x in [n,c,k]):
        raise TypeError("n/c/k必须是整数")
    if n<=0 or not 0<=c<=n or not 1<=k<=n:
        raise ValueError("要求 n>0, 0<=c<=n, 1<=k<=n")
    return 1.0 if n-c<k else 1-math.comb(n-c,k)/math.comb(n,k)


def repeated_probabilities(p: float,k: int) -> dict:
    if not math.isfinite(p) or not 0<=p<=1 or not isinstance(k,int) or k<=0:
        raise ValueError("p需在[0,1],k需为正整数")
    return {"at_least_one":1-(1-p)**k,"all_success":p**k}


def paired_bootstrap(old: list[int], new: list[int], samples: int=2000, seed: int=17) -> dict:
    if not old or len(old)!=len(new) or any(x not in (0,1) for x in old+new):
        raise ValueError("需要等长非空的0/1配对结果")
    if not isinstance(samples,int) or samples<100:
        raise ValueError("演示至少使用100次重采样")
    rng=random.Random(seed); n=len(old)
    diffs=[b-a for a,b in zip(old,new)]
    estimates=sorted(sum(diffs[rng.randrange(n)] for _ in range(n))/n for _ in range(samples))
    return {"difference":sum(diffs)/n,"percentile_95_interval":[estimates[int(.025*(samples-1))],estimates[int(.975*(samples-1))]],
            "assumption":"按任务配对重采样;不是对真实产品的显著性结论"}


def cost_per_success(costs: list[float], successes: list[int]) -> float | None:
    if len(costs)!=len(successes) or any(not math.isfinite(x) or x<0 for x in costs):
        raise ValueError("成本需有限非负且与结果等长")
    if any(x not in (0,1) for x in successes):
        raise ValueError("成功标记需为0/1")
    total=sum(successes)
    return sum(costs)/total if total else None


def self_test() -> None:
    assert abs(pass_at_k(10,2,1)-.2)<1e-12
    assert pass_at_k(10,0,3)==0 and pass_at_k(10,10,3)==1
    p=repeated_probabilities(.8,3)
    assert abs(p["at_least_one"]-.992)<1e-12
    assert abs(p["all_success"]-.512)<1e-12
    assert cost_per_success([1,2,3],[1,0,1])==3
    assert cost_per_success([1],[0]) is None
    assert paired_bootstrap([1,0],[1,0])["percentile_95_interval"]==[0.0,0.0]


def run_demo() -> dict:
    old=[1,1,0,1,0,1,0,1,1,0]
    new=[1,1,1,1,0,1,1,1,0,1]
    return {"性质":"全部结果为合成数据;费用单位是假想单位",
            "独立p08_k3":repeated_probabilities(.8,3),
            "n10_c2_k3":pass_at_k(10,2,3),"配对比较":paired_bootstrap(old,new),
            "含失败成本的每成功任务成本":cost_per_success([1.0]*10,new)}


if __name__ == "__main__":
    import json
    self_test()
    print(json.dumps(run_demo(),ensure_ascii=False,indent=2))

运行结果示例

以下结果由随包脚本产生。它们不是LLM或产品性能数据。

{
  "性质": "全部结果为合成数据;费用单位是假想单位",
  "独立p08_k3": {
    "at_least_one": 0.992,
    "all_success": 0.5120000000000001
  },
  "n10_c2_k3": 0.5333333333333333,
  "配对比较": {
    "difference": 0.2,
    "percentile_95_interval": [
      -0.2,
      0.6
    ],
    "assumption": "按任务配对重采样;不是对真实产品的显著性结论"
  },
  "含失败成本的每成功任务成本": 1.25
}

输入关键词即可检索。

↑ ↓ 选择 Enter 打开Esc 关闭 · 仅本地搜索

阅读设置

阅读设置仅保存在本机浏览器,不上传任何数据。