Files
Video-Tree-TRM5/research-wiki/designs/2026-07-16-gate-speedup-design.md
T

6.7 KiB

gate 验证提速设计:预灌 BaselineCache + 双臂并行

2026-07-16。背景:Video-MME 900 训练实测,单题型 gate 双臂验证 ~3h(epoch 1 缓存全冷,base 臂每块新鲜跑 8 题;两臂串行),单 step 约 6 题型排队 → step ~18h,3 epochs 不可行。三方流程对比(SkillOpt / TRM4 / TRM5)确认:TRM5 忠实迁移了 TRM4 的 CE-Gate 机制,慢是设计固有成本 + 冷缓存,不是迁移引入的缺陷。本设计在不碰 e-process 判定逻辑与核心算法语义(保真 #4/#5/#6)的前提下砍验证成本。

1. 问题与机会

事实 出处
BaselineCache 内容寻址:键 task_type|sha1(基线skill正文)|prompts_version|unit_id → 单元级对错 gate_ladder.py:344
seed 基线 infer_adhoc 已用 skills v1 + prompts v1 跑过全部 900 题,predictions 随 seed 入 workspace harness.db scripts/train_videomme.sh Phase 0
内容寻址意义上,seed 基线就是 epoch 1 的 base 臂,却未被灌入缓存 → base 臂全 miss 全重推 实测 b0-b3 base 臂各 8-22 min
gate 块内 base 臂 await 完才 await cand 臂,两臂串行 validate.py:640→661
Redis LLM 缓存盐 = 推理 run_id,派生自 seed 的 baseline_run_id(infer_adhoc_e{e}_s{s}...),与工程 config 的 run_id 无关 inference.py:406,437

2. 改动一:训练启动时自动预灌 BaselineCache

位置:_init_gate_pools(runner.py:976)末尾、BaselineCache 构造后顺手预灌(该函数已持有 900 题基线查询与 gate_pools)。

预灌(gate_pools, baseline_cache, 基线rows, prompts_version):
    rows 查询在现有基础上追加 stop_reason 列
    for 题型 in gate_pools.task_types:
        target = resolve_skill_file(skill_store, 题型)   # slug.md 缺失 → default-strategy.md,
                                                          # 复用 core/evolution/evolve.py:640 同一解析
        s_hash = skill_hash(读取 skills_dir/target)  # 当前(=v1)基线正文
        for unit in gate_pools[题型] 的单元:
            if unit 任一成员题 stop_reason ∈ {error, parse_error}: skip   # INFRA 不入缓存
            entries[键] = unit_correctness_view 折叠对错   # AR pair 双向 AND
    baseline_cache.put_many(entries)                # 一次原子落盘(见 §5)
关键点 说明
正确性由内容寻址自动保证 某题型 skill 被 accept 改写 → hash 变 → 自然 miss 重推;prompts 版本升级同理。无显式失效逻辑
INFRA 排除 seed 基线 900 题中 4 题 stop_reason ∈ {error, parse_error},与 _resolve_baseline_block 的"INFRA 不写缓存"语义完全一致
折叠口径 复用 unit_correctness_view(保真 #5:AR pair 双向 AND 折叠一个布尔)
预灌范围 仅 gate_pools 内单元(8 题型 ~286 单元),不是全 900 题
resume 每次启动重跑预灌:未进化题型同键同值(无害),已进化题型键不同(互不干扰)

新增 BaselineCache.put_many(entries: dict) -> None:批量合并后一次 tmp 写 + os.replace 原子落盘,避免 ~286 次全量 JSON 重写;单条 put 语义不变。

3. 改动二:gate 块内双臂并行

validate.py:_run_local_validation 块循环内,先后 await _resolve_baseline_block / await _run_candidate_block 改为 asyncio.gather(两臂)

并发安全项 结论
run_id 两臂后缀 _base/_cand 不同,predictions 落库不冲突
HarnessLog 单连接 + threading.Lock,线程/协程安全
BaselineCache.put 仅 base 臂协程内发生,cand 臂不触缓存,无竞争
INFRA 护栏 errors/denom 统计与 gate_decision 均在两臂汇合后执行,判定顺序语义不变
并发上限 每臂一块 ≤8 题,双臂 ≤16 并发 < concurrency 32,LLM 端无压力变化

与改动一叠加:epoch 1 base 臂全命中时 gather 退化为只等 cand 臂,零额外开销。

4. 改动三:Redis 复用保障(重启不白烧已耗调用)

措施 说明
缓存键成分全不动 消息内容(skills/prompts v1 正文、冻结题池、build_batches(seed=epoch) 确定性批次)与盐(run_id 派生自 infer_adhoc)在本设计中零变化
.env REDIS_CACHE_TTL 86400→604800 仅改一行;只影响未来写入。今日已写键(24h TTL)在当日重启窗口内有效
已实证 今日运行遥测 6505/15571 次命中(42%),复用机制已在工作;远程 Redis 现存 9.1 万键

5. 非功能四维

维度 保障
持久化 预灌批量构建后一次原子落盘;单条 put 沿用现有"先盘后存"(tmp + os.replace)
幂等 同键同值重复 put/put_many 结果一致;重复启动安全
断点续跑 预灌在每次启动执行,fresh/resume 行为一致且正确(§2 resume 行)
原子性 单文件 tmp 写 + os.replace,无半写窗口

6. 前序行为审计(修改点逐项)

现有行为 处置
base 臂缓存 miss 才新鲜跑、INFRA 不写缓存 保留(预灌只是提前填充,miss 路径原样)
两臂串行执行 替换为 gather 并行(仅调度顺序,数据流/判定不变)
put 逐条原子落盘 保留;新增 put_many 批量原子落盘
e-process 四出口 / 配对翻转 / 阶梯出题 / 防泄漏排除 不动
_init_gate_pools 基线查询 扩展:追加 stop_reason 列(仅预灌用)

7. 测试

  • 单测(预灌):预灌后对 gate 单元逐一 cache.get 非 None(INFRA 单元除外);AR pair 折叠正确;改写某题型 skill 后同单元 miss;put_many 崩溃前后文件完整(读回可解析)。
  • 单测(并行):注入假 run_inference,断言 gather 版 W/L/E/INFRA 计数与串行版逐块一致;base 臂全命中时 cand 臂正常执行。
  • 集成:真实 workspace 启动预灌 → _resolve_baseline_block 对首块零推理调用(假 run_inference 断言未被调用)。
  • 回归:现有 validate/gate_ladder 测试全绿。

8. 预期收益

现状 改后
epoch 1 单题型 gate ~3h(两臂全推,串行) ~1-1.5h(base 免费,cand 独跑)
epoch ≥2 / accept 后 miss 块 两臂串行 双臂并行,≈减半
单 step(~6 题型) ~18h ~6-8h;叠加 batch=40(5 step/epoch),3 epochs 预计 ~2 天

9. 已否决备选

  • 烘焙进 seed:缓存键含运行期才确定的 prompts_version,且已有 seed 须重建 — 否决。
  • 独立预灌脚本:多一个易忘的手工步骤,违背零参数复现 — 否决。
  • 砍 gate_n_max / 放宽 e 阈值:动核心算法参数、损统计严谨性,收益可由本设计免推理获得 — 本轮不做。
  • 题型间并行 gate:accept 会推进 skills 版本形成串行依赖(后过门题型的 base 臂定义依赖前一题型结果),改动大风险高 — 记 future work。