排行榜聚合:把指标数字变成跨模型可比的排名 本节摘要:每任务打分容易,跨异质任务的每模型排名更难,千预测排行榜上的统计显著性则是人人都跳过的部分——本节不跳过。聚合器消费一份 记录(每 对一条,score 在 ),产三张表:每任务透视、每模型均值、每模型胜率;在均值与两两差上做 bootstrap 置信区间;按均值与胜率两种方案排名并说清何时用哪个;输出 JSON 报告加 markdown 表(可贴进 CI 评论)。读完本节,你能说清为什么均值对离群值敏感而胜率丢失信息、配对 bootstrap 为何比非配对更适合「两模型都跑了同样一百任务」,以及为什么排行榜要把区间不含零的两模型判为显著差异、否则判平手。 对应原课程:Phase 19 · Lesson 74 · (原英文 )。
本节摘要:每任务打分容易,跨异质任务的每模型排名更难,千预测排行榜上的统计显著性则是人人都跳过的部分——本节不跳过。聚合器消费一份
EvalRun记录(每(model, task)对一条,score 在[0,1]),产三张表:每任务透视、每模型均值、每模型胜率;在均值与两两差上做 bootstrap 置信区间;按均值与胜率两种方案排名并说清何时用哪个;输出 JSON 报告加 markdown 表(可贴进 CI 评论)。读完本节,你能说清为什么均值对离群值敏感而胜率丢失信息、配对 bootstrap 为何比非配对更适合「两模型都跑了同样一百任务」,以及为什么排行榜要把区间不含零的两模型判为显著差异、否则判平手。
对应原课程:Phase 19 · Lesson 74 ·
leaderboard-aggregation(原英文phases/19-capstone-projects/74-leaderboard-aggregation/docs/en.md)。本节属第 20 章「毕业项目」的评估赛道。
阅读完本节,你应当能够:
聚合器消费一份 EvalRun 记录:
@dataclass class EvalRun: model_id: str task_id: str metric_name: str score: float # 在 [0, 1] category: str
75 节运行器为每 (model, task) 对发一条记录。聚合器不在乎分数怎么产出,它期望归一化已完成——每个 score 都在 [0,1]。
排行榜行含:model_id、mean_score、mean_ci_lo、mean_ci_hi、win_rate、tasks_completed,以及可选的 categories 映射(每类均值)。
若一个任务分在 [0,1],另一个在 [0,100],后者会静默主导均值。聚合器校验每个输入 score 在 [0,1],否则拒绝这次运行。修在上游:指标本就该返回比例,71~73 节强制这个契约。
两种排名方案服务不同目标。均值是一个模型每任务分数的平均,是排行榜报的头条数,但对离群值与任务不平衡敏感。胜率计一个模型在同一任务上击败其他所有模型的频率:每任务最高分者胜(平分均分),胜率 = 胜数 / 该模型有分的任务数,对离群值与尺度差不那么敏感,但丢失信息。
def win_rate(model_id, runs_by_task, all_models): wins, total = 0, 0 for task_id, runs in runs_by_task.items(): scores = {r.model_id: r.score for r in runs if r.model_id in all_models} if model_id not in scores: continue total += 1 best = max(scores.values()) if scores[model_id] >= best: wins += 1 return wins / total if total else 0.0
框架两者都报:75 节运行器默认按均值排名,markdown 里胜率列就在旁边。
每模型均值带一个 bootstrap 重采样置信区间:对任务 id 有放回重采样,算重采样集上的均值,重复 B 次,取 alpha 水平的百分位区间。
两两对比我们对每任务差 score_A - score_B 做 bootstrap,取百分位区间报告。用户读区间是否含零:不含零则差异在 alpha 水平显著;含零则排行榜把两模型判平手。底层助手(bootstrap_mean_ci、bootstrap_pairwise_diff)默认 B=1000;公开聚合器(aggregate、pairwise_diffs)默认 b=500 以让 demo 与测试快;默认 alpha 0.05;bootstrap 纯 numpy,不依赖 scipy。
若 EvalRun.category 有值,聚合器还报每类均值——这是每个排行榜上 math、reasoning、code、safety 那一列,让运行器发现「总体好但代码弱」这种头条均值藏起的信息。
排行榜渲染成 markdown 表:
| Rank | Model | Mean | 95% CI | Win rate | Tasks | |------|-------|------|--------|----------|-------| | 1 | gpt | 0.78 | 0.74-0.82 | 0.62 | 50 | | 2 | claude| 0.75 | 0.71-0.79 | 0.34 | 50 | | 3 | random| 0.10 | 0.07-0.13 | 0.04 | 50 |
按均值排序,CI 渲到两位小数,长 model id 截到 20 字符。
| 排名方案 | 度量 | 对离群值 | 信息量 |
|---|---|---|---|
| 均值 | 每任务分数平均 | 敏感 | 高(保分数) |
| 胜率 | 击败他模型的频率 | 鲁棒 | 低(只保序) |
| 两两 bootstrap 差 | score_A - score_B 区间 |
中 | 显著性判定 |
业界对比:HELM 报均值加 bootstrap CI;LMSYS Chatbot Arena 用胜率(Elo);Open LLM Leaderboard 用均值。它们的共识与本节一致:均值是头条,胜率是鲁棒补充,两两 CI 是显著性判定。Chatbot Arena 的人类投票本质是配对胜率,与本节配对 bootstrap 同源。
aggregate + pairwise_diffs:本节是 75 节运行器的聚合端,运行器把每模型 EvalRun 喂进来,拿回排行榜与两两差。bootstrap_mean_ci / bootstrap_pairwise_diff:纯 numpy,可独立用于任何「数值列表求均值 CI」或「两模型配对差显著性」。main.py 定义 EvalRun、LeaderboardRow、aggregate、bootstrap_mean_ci、bootstrap_pairwise_diff、render_markdown;demo 构建三模型十二任务的合成套件,聚合,打印排行榜加两两差表;code/tests/test_leaderboard.py 钉死 bootstrap、markdown 渲染、胜率边界、空输入行为。数据形状在前、聚合次之、bootstrap 第三、渲染最后,每个函数契约聚焦。
本节不跑模型、不调指标层、不实现自适应 ECE 等校准变体(那些是 73 节)、不做任务加权——此处每任务等权,生产排行榜加权,我们通过 weight 字段留钩子在聚合器里忽略;需要时在后续课加。
💡 自然下一步是配对任务显著性而非非配对 bootstrap:若 A 与 B 都跑了同样一百任务,合适的检验是任务逐差的配对 bootstrap(我们实现了)。再往下是尊重任务族的分层 bootstrap(数学题彼此不独立,一个算术错误模式影响十道)——那是后续。本节的要点是把地基做对,让评估报一个你能为之辩护的数。
category 分层,层内重采样,对比与不分层的 CI。weight 字段,让 code 类权重翻倍,看排名如何变。[0,1],否则拒绝;修在上游(71~73 节指标返回比例)。weight 钩子;配对 bootstrap 优先于非配对。下一节,我们将进入「端到端评估运行器」——把 70~74 节粘成一条流水线,跑固定任务集、出 JSON 报告与 markdown 表、自终止退出码,这是评估赛道的收官。