feat: tier-aware diag/val split with val-power repair (design 5.1)

This commit is contained in:
2026-07-16 04:58:41 -04:00
parent 6432ffef9a
commit 5bb8319220
5 changed files with 126 additions and 21 deletions
+15 -5
View File
@@ -13,7 +13,7 @@ from __future__ import annotations
import hashlib
import sqlite3
from collections import Counter
from collections import Counter, defaultdict
from dataclasses import asdict, dataclass, field
from typing import TYPE_CHECKING
@@ -59,6 +59,8 @@ class SplitBuildConfig:
select_seed: 贪心选择器预洗牌种子(打破等增益平局)。
val_ratio: validation 占 trainval 视频组总数的比例。
split_seed: 视频组题级切分的洗牌种子。
val_wrong_min: validation 池最少错题数,切分时保证功效(不足则从 diag 换入
低 T2 错题组补足,耗尽 fail-loud)。
"""
n_trainval: int
@@ -68,6 +70,7 @@ class SplitBuildConfig:
select_seed: int
val_ratio: float
split_seed: int
val_wrong_min: int
@dataclass(frozen=True)
@@ -119,10 +122,10 @@ def build_split(
test → 加载题库并以视频归属切三池 → 原子冻结 pools.json → 写溯源 manifest →
六条防御断言 fail-fast 校验。
契约(Task 11,非疏漏):build_split 有意保持 val_wrong_min-agnostic——内部调
split_by_video_assignment 时不传 val_wrong_min(默认 0,不校验 validation 错题
数)。McNemar 功效护栏是切分**冻结后**的独立校验,由 CLI 的 check_mcnemar_power
在 build_split 返回后执行;切分构造本身不因功效阈失败,二者关注点分离
契约(Task 11):val_wrong_min 前置到切分内保证功效——build_split 计算
wrong_tier_by_video 并连同 config.val_wrong_min 传入 split_by_video_assignment
切分时若 val 错题不足即从 diag 换入低 T2 错题组补足(耗尽 fail-loud)。CLI 的
check_mcnemar_power 作切分冻结后的冗余最终确认
参数:
db_path: harness.db 路径(只读读取 predictions,不改动)。
@@ -181,6 +184,11 @@ def build_split(
# Phase 3: 加载题库 + 视频归属切三池 + 原子冻结。
questions = load_benchmark(questions_dir)
correctness = {pred["question_id"]: pred["correct"] for pred in preds}
tier_by_q = {row["question_id"]: row["tier"] for row in signal_rows}
wrong_tier_by_video: dict[str, int] = defaultdict(int)
for pred in preds:
if not pred["correct"] and tier_by_q.get(pred["question_id"]) == "T2":
wrong_tier_by_video[pred["video_id"]] += 1
pools = split_by_video_assignment(
questions,
assignment,
@@ -188,6 +196,8 @@ def build_split(
config.val_ratio,
config.split_seed,
baseline_run_id=baseline_run_id,
val_wrong_min=config.val_wrong_min,
wrong_tier_by_video=dict(wrong_tier_by_video),
)
save_pools(pools, out_path)