feat(harness): add Action Recognition training experiment
- PerCategoryPoolStrategy: filter test pool by task_types - RunConfig: add run_holdout_eval toggle (default true) - load_config: fix YAML task_types list-to-tuple conversion - Runner: conditionally skip _holdout_four_way when disabled - CLI: add --no-run-holdout-eval flag - New config/train_action_recognition.yaml (3 epochs, per_category) - New scripts/train_action_recognition.sh (baseline + seed + train)
This commit is contained in:
@@ -90,6 +90,7 @@ class RunConfig:
|
||||
pool_split_mode: 池划分策略,"global"(全局统一划分)/ "per_category"(按类别独立划分)。
|
||||
train_ratio: 训练集占比,范围 (0, 1)。
|
||||
test_questions: 测试题目集路径(相对路径)。
|
||||
run_holdout_eval: 是否在 epoch 末执行 held-out 四向评估,默认 True。
|
||||
"""
|
||||
|
||||
# ── 必填字段(无默认值,来自 YAML 或 CLI) ──
|
||||
@@ -145,6 +146,7 @@ class RunConfig:
|
||||
pool_split_mode: str = "global"
|
||||
train_ratio: float = 0.667
|
||||
test_questions: str = "benchmarks/Video-MME"
|
||||
run_holdout_eval: bool = True
|
||||
|
||||
|
||||
def _validate(config: RunConfig) -> None:
|
||||
@@ -439,6 +441,10 @@ def load_config(
|
||||
if field_name in yaml_data:
|
||||
yaml_data[field_name] = Path(yaml_data[field_name])
|
||||
|
||||
# Phase 4b: 类型转换 — task_types list → tuple
|
||||
if "task_types" in yaml_data and yaml_data["task_types"] is not None:
|
||||
yaml_data["task_types"] = tuple(yaml_data["task_types"])
|
||||
|
||||
# Phase 5: 构造并校验
|
||||
config = RunConfig(**{k: v for k, v in yaml_data.items() if k in valid_fields})
|
||||
_validate(config)
|
||||
|
||||
@@ -205,6 +205,7 @@ def _q_to_dict(q: GeneratedQuestion) -> dict:
|
||||
"answer": q.answer,
|
||||
"source_nodes": list(q.source_nodes),
|
||||
"difficulty": q.difficulty,
|
||||
"family": q.family,
|
||||
"skill_target": q.skill_target,
|
||||
"difficulty_steps": q.difficulty_steps,
|
||||
}
|
||||
@@ -228,6 +229,7 @@ def _dict_to_q(d: dict) -> GeneratedQuestion:
|
||||
answer=d["answer"],
|
||||
source_nodes=tuple(d.get("source_nodes", ())),
|
||||
difficulty=d.get("difficulty", "medium"),
|
||||
family=d.get("family"),
|
||||
skill_target=d.get("skill_target"),
|
||||
difficulty_steps=d.get("difficulty_steps"),
|
||||
)
|
||||
@@ -595,12 +597,15 @@ class PerCategoryPoolStrategy:
|
||||
all_train.extend(train)
|
||||
all_val.extend(val)
|
||||
|
||||
# Phase 4: test 池(从外部目录加载,无则空)
|
||||
# Phase 4: test 池(从外部目录加载,无则空;按 task_types 过滤)
|
||||
test: list[GeneratedQuestion] = []
|
||||
if config.test_questions_dir is not None:
|
||||
from app.question_gen import load_benchmark
|
||||
|
||||
test = load_benchmark(config.test_questions_dir)
|
||||
if config.task_types is not None:
|
||||
allowed = set(config.task_types)
|
||||
test = [q for q in test if q.task_type in allowed]
|
||||
|
||||
# Phase 5: 计算 baseline_val_accuracy
|
||||
val_correct = sum(1 for q in all_val if correctness.get(q.question_id, False))
|
||||
|
||||
@@ -1368,7 +1368,10 @@ class Runner:
|
||||
momentum_updated_task_types=momentum_task_types,
|
||||
best_val_acc=state.best_val_acc,
|
||||
)
|
||||
await self._holdout_four_way(epoch, pools, state, eval_skills_version, eval_prompts_version)
|
||||
if self._config.run_holdout_eval:
|
||||
await self._holdout_four_way(
|
||||
epoch, pools, state, eval_skills_version, eval_prompts_version
|
||||
)
|
||||
|
||||
# Phase 10: gate 阶梯刷新
|
||||
self._refresh_gate_ladder(
|
||||
|
||||
Reference in New Issue
Block a user