拓展阅读
成功率、配对比较与真实成本口径
计算pass@k、独立重复假设下的至少一次与全部成功概率,并对合成配对任务做Bootstrap示范。
沿着对象与状态理解本章
两个平均成功率之间的差值,不能告诉你哪些题退化。先看同题配对表:两边都成功、新版独胜、旧版独胜、两边都失败,再看成本、方差和评分器误差。
下方图解的固定输入在“输入与适用条件”中声明。它用于解释本章机制,不与本章其他独立例子的数值暗中接续。
动态图解 · F36
涨了10个百分点,为什么还不能急着下结论?
一次实验怎样区别真实改进与偶然波动?
像给同一组题换两种解法:同题对照能减少题目难度不同带来的干扰。统计方法帮助表达不确定性,不会自动告诉你退化是否可接受、成本是否值得。实际决策还要回到任务类型、失误严重性和重复表现。
配对同一任务同时有新旧方案结果。
百分点两个百分率直接相减,如70%−60%=10个百分点。
随机波动在相同条件下多次运行也可能有不同结果。
输入与适用条件:构造的20题配对表:10都成、4新成旧败、2旧成新败、4都败。不把它当真实模型胜率。
1 / 5 · 同20题,两版各跑一次
第1步,共5步
窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。
配对意味着旧版和新版面对同一题。先列出两者在每道题上的成功与失败关系,而不是只看两个总分。
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
}