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:
@@ -0,0 +1,198 @@
|
||||
---
|
||||
id: action-recognition-training
|
||||
title: Action Recognition 单题型首次训练实验设计
|
||||
type: design
|
||||
created: 2026-07-14
|
||||
status: approved
|
||||
---
|
||||
|
||||
# Action Recognition 单题型首次训练实验设计
|
||||
|
||||
## 1. 目标
|
||||
|
||||
用 Action Recognition 单题型验证训练管线端到端可用性。选择此题型因其:
|
||||
- Video-MME 准确率 65.1%(倒数第三),提升空间大
|
||||
- 属于 RETRIEVAL family(权重最高 0.30)
|
||||
- v2-360 有 30 道生成题可用于 train/val
|
||||
|
||||
## 2. 数据划分
|
||||
|
||||
| 池 | 来源 | 数量 | 用途 |
|
||||
|----|------|------|------|
|
||||
| train (diagnosis) | generated-v2-360 | 20 题 | 错误归因 + 进化 |
|
||||
| val (validation) | generated-v2-360 | 10 题 | CE-Gate 验证 + best argmax |
|
||||
| test | Video-MME benchmark | 63 题 | 训练结束后终评(epoch 内跳过四向 held-out) |
|
||||
|
||||
切分方式:`PerCategoryPoolStrategy`,`train_ratio=0.667`,按 correctness 分层。
|
||||
|
||||
## 3. 前置步骤
|
||||
|
||||
```
|
||||
步骤 1: baseline infer
|
||||
main.py --mode infer \
|
||||
--questions generated-v2-360 \
|
||||
--task-types "Action Recognition" \
|
||||
--run-id v2ar_baseline
|
||||
→ workspaces/default/harness.db 新增 30 题 predictions
|
||||
|
||||
步骤 2: create seed
|
||||
2a. extract_run_db(
|
||||
src_db="workspaces/default/harness.db",
|
||||
dst_db="/tmp/v2ar_baseline.db",
|
||||
run_id="infer_v2ar_baseline")
|
||||
2b. init_seed(
|
||||
store_dir=Path("store"),
|
||||
name="v2ar-baseline",
|
||||
skills_dir=Path("store/skills/v1"),
|
||||
prompts_dir=Path("store/prompts/v1"),
|
||||
baseline_db=Path("/tmp/v2ar_baseline.db"),
|
||||
baseline_run_id="infer_v2ar_baseline",
|
||||
parent=None,
|
||||
description="v2-360 Action Recognition 30 题 baseline (skills/v1)")
|
||||
→ store/seeds/v2ar-baseline/
|
||||
|
||||
步骤 3: fresh workspace
|
||||
main.py --mode train --fresh \
|
||||
--seed v2ar-baseline \
|
||||
--run-id train_ar_v1 \
|
||||
--config config/train_action_recognition.yaml
|
||||
→ workspaces/train-action-recognition/
|
||||
|
||||
前提: workspaces/default/ 已存在(含 harness.db),步骤 1 复用该 workspace。
|
||||
```
|
||||
|
||||
## 4. 训练配置
|
||||
|
||||
| 参数 | 值 | 理由 |
|
||||
|------|-----|------|
|
||||
| workspace_dir | workspaces/train-action-recognition | 独立 workspace |
|
||||
| mode | train | 训练模式 |
|
||||
| run_id | train_ar_v1 | 显式命名,observation 可追溯 |
|
||||
| questions | generated-v2-360 | v2-360 题目集 |
|
||||
| task_types | ["Action Recognition"] | 单题型 |
|
||||
| pool_split_mode | per_category | 按类切分 |
|
||||
| train_ratio | 0.667 | 20 train / 10 val |
|
||||
| test_questions | benchmarks/Video-MME | test 池来源(过滤后 63 题) |
|
||||
| run_holdout_eval | false | epoch 内跳过四向评估 + shadow_gate |
|
||||
| epochs | 3 | 6 step + 3 轮慢更新 |
|
||||
| batch_size | 10 | 20 题 / 2 batch per epoch |
|
||||
| min_class_per_batch | 2 | 最小值 |
|
||||
| concurrency | 24 | 推理并发 |
|
||||
| max_steps | 40 | 沿用默认 |
|
||||
| early_stop_patience | 4 | 3 epoch 内有意义 |
|
||||
| use_slow_momentum | true | 验证 momentum |
|
||||
| skill_mode | auto | 自动加载 skill |
|
||||
|
||||
其余 gate 参数沿用 `config/default.yaml` 默认值。
|
||||
|
||||
## 5. 训练循环预期
|
||||
|
||||
```
|
||||
Epoch 1-3 每轮:
|
||||
├── build_batches: 20 题 → 2 batch
|
||||
├── Step 0: rollout → diagnose → evolve → CE-Gate
|
||||
├── Step 1: rollout → diagnose → evolve → CE-Gate
|
||||
└── Slow Update:
|
||||
├── 全 val 重跑 (10 题)
|
||||
├── probation 结算
|
||||
├── best argmax
|
||||
├── momentum
|
||||
├── system/tool 慢更新
|
||||
└── 四向 held-out: 跳过 (run_holdout_eval=false)
|
||||
→ 同时跳过 _pick_mixed_best 的 shadow val 评估
|
||||
收尾:
|
||||
├── _deliver_best: manifest 指向最佳版本
|
||||
└── _final_test_eval: test 池 63 题终评 (保留,仅跑 1 次)
|
||||
```
|
||||
|
||||
预计 LLM 调用 ~500-600 次(去掉每 epoch 四向 held-out 后大幅减少)。
|
||||
|
||||
## 6. 代码修改
|
||||
|
||||
### 6.1 PerCategoryPoolStrategy test 池按 task_types 过滤
|
||||
|
||||
**问题**: `PerCategoryPoolStrategy.build()` Phase 4 加载 test 池时调用
|
||||
`load_benchmark(config.test_questions_dir)` 会加载全部题目(如 Video-MME 900 题),
|
||||
不受 `config.task_types` 过滤。而 train/val 在 Phase 1 已按 task_types 过滤,
|
||||
test 池应保持一致。
|
||||
|
||||
**修改位置**: `app/harness/pools.py` `PerCategoryPoolStrategy.build()` Phase 4
|
||||
|
||||
**修改内容**:
|
||||
|
||||
```python
|
||||
# Phase 4: test 池(从外部目录加载,无则空)
|
||||
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)
|
||||
# 与 train/val 的 Phase 1 过滤保持一致
|
||||
if config.task_types is not None:
|
||||
allowed = set(config.task_types)
|
||||
test = [q for q in test if q.task_type in allowed]
|
||||
```
|
||||
|
||||
**边界情况**:
|
||||
- `config.task_types=None`(全题型): 不过滤,行为不变
|
||||
- 过滤后 test 为空: 不报错(test 池可为空,`_final_test_eval` 会拿到空列表并跳过)
|
||||
- 过滤后 test 仅含目标题型: 本次为 63 道 Action Recognition
|
||||
|
||||
**对 pools.json 冻结的影响**: test 池过滤后的结果写入 pools.json,
|
||||
resume 时直接加载无需重新过滤。fresh workspace 每次从头构建。
|
||||
|
||||
**测试**: 新增单测验证 test_questions_dir 混合题型时过滤正确。
|
||||
|
||||
### 6.2 RunConfig 新增 run_holdout_eval
|
||||
|
||||
**修改位置**: `app/harness/config.py` RunConfig 字段区
|
||||
|
||||
```python
|
||||
run_holdout_eval: bool = True
|
||||
```
|
||||
|
||||
**YAML 暴露**: 实验 YAML 中配置(科研实验参数归 YAML)。
|
||||
|
||||
**CLI 覆盖**: `main.py` 新增 `--no-run-holdout-eval` 标志,符合 CLAUDE.md §4.5
|
||||
"CLI 仅用于单次临时覆盖"。
|
||||
|
||||
**Runner 修改**: `app/harness/runner.py` `_slow_update_cycle` Phase 9
|
||||
|
||||
```python
|
||||
# Phase 9: epoch_report(保留)+ 四向 held-out(可选)
|
||||
write_epoch_report(...)
|
||||
if self._config.run_holdout_eval:
|
||||
await self._holdout_four_way(...)
|
||||
```
|
||||
|
||||
**副作用说明**: `run_holdout_eval=false` 同时跳过 `_pick_mixed_best` 的
|
||||
shadow val 评估(`best_mixed` 版本选择)。首次试跑只看 `best_hard`,
|
||||
shadow/mixed 指标不需要。
|
||||
|
||||
**不影响**: `_final_test_eval` 不受此开关控制,训练结束后仍跑一次终评。
|
||||
|
||||
**测试**: 新增 runner 单测验证 `run_holdout_eval=false` 时跳过 `_holdout_four_way`。
|
||||
|
||||
## 7. 观测产出
|
||||
|
||||
| 产出 | 位置 | 内容 |
|
||||
|------|------|------|
|
||||
| step 报告 | analyses/step_reports/ | 每步 gate 决策(accept/reject/skip) |
|
||||
| epoch 报告 | analyses/epoch_reports/ | 慢更新结果、best 变化 |
|
||||
| dual_metric | harness.db dual_metric 表 | val 准确率变化 |
|
||||
| skill 版本链 | skills/v1→v2→... | 可 diff 查看改动 |
|
||||
| checkpoint | checkpoint.json | 断点续训支持 |
|
||||
| 终评 | analyses/final_test_eval.json | test 63 题最终准确率 |
|
||||
|
||||
## 8. 非功能性需求
|
||||
|
||||
| 维度 | 必答问题 | 设计 |
|
||||
|------|---------|------|
|
||||
| 持久化 | 何时落盘?崩溃丢多少?覆盖还是追加? | predictions 逐题 INSERT 即时落库(追加);checkpoint 每 step 结束后原子写入(覆盖);崩溃最多丢当前 step 的推理结果,resume 从上一 step 重跑 |
|
||||
| 幂等性 | 重复执行安全吗? | 同 seed + 同配置 + fresh → 确定性结果(RNG seed 固定);predictions INSERT OR IGNORE 防重复 |
|
||||
| 断点续跑 | 中断后如何恢复? | `--resume` 读 checkpoint.json 的 epoch/step_completed/phase,从断点下一 step 继续;epoch_done 阶段恢复到下一 epoch 开头 |
|
||||
| 原子性 | 部分写入会损坏数据吗? | checkpoint.json 整文件写入(非追加);predictions 逐条 commit 无事务风险;skills 版本目录先 copytree 再改 manifest 指针 |
|
||||
|
||||
## 9. 新增文件
|
||||
|
||||
- `config/train_action_recognition.yaml` — 实验配置
|
||||
- `scripts/train_action_recognition.sh` — 自包含实验脚本(写死参数,零参数复现)
|
||||
Reference in New Issue
Block a user