# ML 运行手册审查(ml/README.md) 审查对象:`ml/README.md`(12 阶段运行手册)+ `ml/pipeline.sh` + `ml/gen|prep|model|sim|apg|mining|eval` 全部代码。 判据来源:`docs/Transformer轨迹拟合训练方案.md`(方案)。 硬件:本机 CBB-003,RTX 3090 24 GB(driver 617.42)、28 核 CPU、Python 3.12.10 + torch 2.10.0+cu130 + onnxruntime 1.22.0、Node v24.15.0。 方法:代码逐行读 + 在本机真跑第 2/4/5/6 步(两种头)与闭环仿真。**APG 及之后(8–11 步)未跑**,被中断,不声称可用。 结论:流程骨架是通的(对拍 PASS、数据生成、打包、预训练、闭环仿真都实测跑过)。问题集中在**判据自相矛盾**和**指标口径不一致**两类,不是代码写错。 --- ## 一、实测记录(可复现) 冒烟产物落在 gitignore 目录 `ml/data/_smoke/`、`ml/runs/_smoke/`,工作树干净,`ml/assets/kin.json` 未重新生成。 | 步 | 命令 | 实测结果 | 手册声称 | 判定 | |---|---|---|---|---| | 2 对拍 | `node ml/eval/dump_parity.cjs 300` + `python -m ml.eval.check_parity` | `PASS: true`,fk 3.4e-13 mm、orient 3.3e-16、间隙 2.5e-13 mm、83 维特征 6.0e-8、间隙梯度相对误差 1.4e-6,7.6 s | 2 分钟,须 PASS | 符合 | | 4 生成 | `node ml/gen/gen_dataset.cjs --n 80 --seed 7` | **1.56 s/条**,80 条 122 s;老师 plan p50 291 ms / p90 1490 ms,认证 p50 247 ms / p90 944 ms | 约 2 秒/核 | 符合(偏乐观余量) | | 4 统计 | `node ml/gen/stats.cjs`(88 条) | keepRate **0.977**、samplesPerTask 36.5、projectedSamplesFor50k **178.4 万**、exec_s p50 2.3 / p90 3.0 / max 3.7 s、travel p50 350°、**cert.minGap p50 4.4 / p90 5.9 mm**、尾部补零样本占比 14.7% | keepRate≈0.95、projected≈180 万 | 符合,但 minGap 没进检查项(见 P0-2、口径-4) | | 5 打包 | `node ml/prep/build_dataset.cjs` + `python -m ml.data --index` | 86 任务 → train 3004 / val 136 样本,`index.json` 键名与手册一致;增广 847 提交 / 5 拒绝 | train 170 万 / val 9 万(5 万条口径) | 比例自洽(5% split) | | 6 预训练 MeanFlow | `python -m ml.train --steps 250 --no-eval --config ml/configs/base.json` | 参数量 **4.92 M**,吞吐 **6 208 样本/s**(step 200,batch=1024) | 1.5–6 小时;方案 R10 要 ≥2 万样本/s | 与 1.5–6 h 自洽(60 000 步 ≈ **2.8 h**),**与 R10 判据冲突** | | 6 预训练 WTA | `--config ml/configs/wta.json --steps 40` | 跑通,参数量 4.93 M(同骨干换头成立) | 回退约 1 天 | 通路成立 | | 闭环仿真 | `python -m ml.sim.closed_loop --split val --n 3` | 跑通 9.7 s,按 cat/mode 出 success/collision(未训模型 0/0,符合预期) | — | 通路成立 | 吞吐换算:60 000 步 × 1024 = 6 144 万样本 ÷ 6 200 = **9 910 s ≈ 2.75 h**(不含 eval)。2 万样本/s 意味着 51 分钟跑完 60 000 步——当前配置在 3090 上不可能达到。 --- ## 二、P0:会让人在终端前停住或误判 ### P0-1 训练吞吐判据自相矛盾(方案 R10 vs 手册) - 方案 §10.1 R10:`2000 步吞吐 ≥ 2 万样本/s`,缓解写的是"降 epoch 或增大 batch"。 - 实测 6 208 样本/s → 按 R10 判"不达标",但 60 000 步 2.75 h 完全落在手册 1.5–6 h 的预算内。 - 训练是算力受限(MeanFlow 的 `torch.func.jvp` 双前向 + FK 损失),增大 batch 只拉长每步墙钟,samples/s 不上去。R10 给的缓解措施对它自己定的阈值无效。 - 手册 §6 只写"测吞吐",**没写通过线**:`ml/train.py:98` 打出 `samples_per_s`,读者看到 6k 无从判断该继续还是改配置。 建议:R10 阈值改为终态判据("60 000 步 ≤ 3 h")或把 samples/s 线降到 ~5 千;手册 §6 补一行"实测参考:本机 3090 batch=1024 → 6.2 k 样本/s ≈ 2.8 h/60 k 步"。 ### P0-2 共识过滤分支的期望数字整段缺失 - `ml/pipeline.sh:27-31`:`consistentByGateRule < 0.9` 时自动加 `--consensus 4`。 - 方案 §10.1 R1 记录首测一致率 **78%**(100 任务 / 373 对邻居,门禁 90%)→ **默认走的就是 consensus 分支**。 - `ml/gen/gen_dataset.cjs:51`:邻居多数不同意就 `keep=false`(why=consensus)→ keepRate 与样本量双双下降。 - 手册 §4 的 `keepRate≈0.95`、`projectedSamplesFor50k≈180 万`(实测无共识 0.977 / 178 万)是**关共识**的口径;开共识后没有对应期望值,也没写"样本不够要不要加 N_TASKS"。 - §13 回传表要求按 `stats.json` 判下一步 → 判据缺一条分支。 建议:§4 增加共识分支的两行期望(keepRate≈? / projected≈?),并明确"低于 X 就把 N_TASKS 提到 8 万"这类动作。 ### P0-3 WTA 回退会覆盖 MeanFlow 的训练产物 - `ml/pipeline.sh:34-35`:`train` 阶段输出目录写死 `ml/runs/bench` 与 `ml/runs/base`,`CFG` 只换头不换目录。 - `ml/pipeline.sh:37`(m5)与 `:44`(apg)写死读 `ml/runs/base/best.pt`。 - `ml/train.py:57`:`log.jsonl` 以 append 打开 → 两次不同头的日志混在同一文件。 - 后果:手册 §7 的 `CFG=ml/configs/wta.json bash ml/pipeline.sh train` 会盖掉已训好的 MeanFlow `best.pt/last.pt`,对比物消失;而方案 §1.1 选头的依据恰恰是两者对比(沙箱里两者 100%/0%,"分不出高下")。 建议:`OUT=${OUT:-ml/runs/base_$HEADTAG}`,m5/apg/bench 用 `CKPT=` 变量指;两头的 run 分目录共存。 ### P0-4 手册要求 Linux/WSL2,但本机 Windows 原生可跑,只差一处 - 实测第 2/4/5/6 步与闭环仿真在 Windows + Git Bash 下全部跑通(Python 用 `python`)。 - `ml/prep/run_prep.sh:8` 硬用 `python3`,而 Windows 的 `python3` 是 `WindowsApps` 假壳(`python3 -c` 直接 exit 1)→ 第 5 步在**最后一步**失败,`set -e` 会让 `pipeline.sh all` 在此中止(前面的 npy 已写好,重跑代价大)。 - 同类:`ml/pipeline.sh` 内联的 `python -c`(gate1 的 CONSENSUS 判定、m5 的打印)用的是 `python`,在 Windows 没问题 → 两个脚本各用一种,本身就是不一致。 建议:统一 `${PYTHON:-python}`(或 `${PYTHON:-python3}`);手册 §1 补一句"Windows 原生实测可跑,需 Git Bash + `python` 在 PATH"。 --- ## 三、口径不一致("这一步通过"不等于"部署能过") ### 口径-1 闭环仿真的安全层 ≠ 部署运行时的安全层 - 仿真只有"逐步候选干涉则换下一条"(`ml/sim/closed_loop.py:118-130`)。 - 运行时多出:1 步胶囊梯度投影(`webapp/js/ik-policy.js:107`)、刹停段 + 每条候选 40 次距离查询预算(`:177`)、安全层单次推理 30 ms 预算(`:322`)、卡死检测与换向锁 1 s(`:296-304`)。 - §6 EVAL、§7 M5 门禁、§9 挖错/门禁**全部**用仿真那套(默认 `safety=False`)。 - 于是 §7 的"穿柱率 ≤2%"和 §11 的"collisionFrames 必须为 0"是两把尺,互不预示;手册没有任何一处说明这点。 建议:§7 明说"M5 是纯策略指标,§11 是策略+安全层指标";m5 对同一门禁集加跑一遍 `--safety`,两个口径都写进 `m5_gate.json`。 ### 口径-2 门禁集/挖错题的时长预算是假的 - `ml/mining/tasks.py:47` 每题写死 `exec_s: 3.0, travel: 0.0`。 - `ml/sim/closed_loop.py:67` 超时 = `clamp(2*exec_s, min=3)` = 6 s;`ml/mining/mine_round.py:38` 又用 `max_sec=6.0` 覆盖。 - 后果:§7 M5 的 1000 题、§9 的门禁集里,"到达"一律 6 s 硬顶、`time_ok_1p3` 一律 3.9 s;而 §6 训练期 EVAL 用的是老师真实时长 ×1.3。两个"到达率"不可比却共用同名指标。`travel=0` 还使 `travel_ratio_median`(`closed_loop.py:154`)在门禁集上直接返回 None。 - 方案 §10.1 R8"3 s 内恢复 ≥95%、活锁=0"在 12 步里没有执行点。 建议:`tasks.py` 出题时带上老师时长(或明确写"门禁集按定长 6 s 口径");R8 补对抗用例集与命令。 ### 口径-3 APG 的"到达"是定长 3.6 s 窗口内的距离塑形,且碰撞只查端点 - `configs/apg.json`:`plans:60`,每次推理提交 3 步 × `DT=0.02` → 展开 **3.6 s**(`ml/apg/apg_finetune.py:67`),无"到达即停";到达项只在每个 10 块段末用 `((T-pe)/100)²`(`:104-109`)。 - 实测这批老师轨迹 exec_s p50 2.3 / p90 3.0 / max 3.7 s → 样本刚好压线;真数据里老师时长 >3.6 s 的绕远长任务(正是 C/B 难题)APG **永远看不到绕出段**。 - 碰撞 hinge 只打每步末端点(`:95-97`),仿真与运行时都查端点+中点(`closed_loop.py:142-144`、`ik-policy.js:222`)。 - 手册 §8 写"直接优化到达、碰撞余量",读者会以为是全 horizon 到终点。 建议:§8 补一句"定长 3.6 s 滚动 + 段末距离替代到达项";把 `plans` 提到覆盖 exec_s p90(≥55 s 不现实,折中:对 exec_s>3.6 s 的题按比例加 plans,或在 README 说明这是已知截断)。 ### 口径-4 训练余量 5 mm 压在老师轨迹自身的间隙分布上 - 实测 88 条 keep 轨迹 `cert.minGap` **p50 4.4 mm / p90 5.9 mm**(`ml/gen/stats.cjs:18` 已算出 `vertexMinGapMm`)。 - `docs/collide.json` 的 `margin_mm = 0.3`,`kin.gaps()` 的 gap 已减掉它 → 这 4.4 mm 是实打实的几何间隙。 - `configs/apg.json` 的 `margin_train_mm = 5.0` + 运行时投影目标 `margin:5`(`ik-policy.js:174`)→ **约一半 BC 标签落在 5 mm 以下**,APG 碰撞项与 BC 项方向相反。 - 手册 §4 让检查的三项(keepRate / byCatMode / projectedSamples)里没有 `vertexMinGapMm`;方案 §7.1 只写"margin=认证+5",没和老师的间隙分布对齐。 建议:§4 检查项加 `vertexMinGapMm` 并给期望区间;要么 `margin_train_mm` 降到老师分布 p50 附近(~4 mm),要么承认 BC/APG 有对抗并把它当成设计取舍写下来。 --- ## 四、缺项与细节 1. **验收表落不了地**:方案 §9 要求 held-out 可达 ≥98%、D 类停靠 ≤老师+5 mm、刹停发生率 <2%、R7 左右比例与行程门禁、R8 对抗用例——12 步里都没有对应命令。`ml/mining/gate.py:41` 只查 3 项(到达增益 / A 类降幅 / 上轮仍对);`tests/policy_bench.cjs:81` 已经算出 `brakes/locks/uncertifiedSegs` 但没进任何门禁。 2. **挖错池累积**:`ml/mining/loop.sh:20` `ls ml/runs/mine/r*/pool.jsonl` 把**所有历史轮**的错题池喂给每轮 APG,池子逐轮变大,且 `mine_round.py:57` 对 E1 复制 ×2、每题再带 10 个邻居。手册 §9 只说"错题与邻居进采样池",没说会跨年累积 → 后期轮次的采样分布严重偏向老错题(A 类降幅门禁是唯一的兜底)。 3. **fp16 导出方向可疑**:手册 §10 把 `--fp16` 报错当偶发("去掉 --fp16 手动导出 fp32")。但部署端是 wasm(`webapp/js/ik-policy-glue.js:36` 默认 wasm)和 Node CPU(`tests/policy_bench.cjs:55` `executionProviders:['cpu']`),fp16 在 CPU EP 上要么缺 kernel 要么靠大量 Cast 兜底,不保证比 fp32 快。参数量实测 4.92 M → fp32 ≈ 20 MB(与手册一致),fp16 省的 10 MB 对浏览器收益有限。**本机 `onnxconverter_common` 未装,此条未实测。** 4. **`--n` 同时控制两个集合**:`tests/policy_bench.cjs:85-86` 的 `+opt('--n')` 对 nc 和 route 一起生效,手册说的"30 例 / 40 例"只在默认值成立。 5. **val 为空时崩**:小样本摸底(几百条、5% split 可能 0 个 val 任务)下 `ml/sim/closed_loop.py:161` 的 `evaluate` 返回空 dict,`ml/train.py:106` 取 `res['success']` KeyError。真实数据不会撞,但手册鼓励先小跑摸底。 6. **文件地图缺 4 个真实文件**:`ml/gen/cert.cjs`(认证实现在 ik-policy.js,这里是跳转壳)、`ml/gen/pathdev.cjs`(同伦类判定,共识过滤与门禁都依赖它)、`ml/prep/npy.cjs`、`ml/toy/toy_bimodal.py`(方案 §沙箱 的依据)。 7. **R2 无执行点**:方案要求"近邻样本标签方差 <10% 步长、起步失败率=0",手册 §3–§5 的检查项里没有。 --- ## 五、按优先级的改动清单 | 优先级 | 改什么 | 落点 | |---|---|---| | 1 | 吞吐判据对齐:R10 改成终态判据或降线,§6 写实测参考值 | `docs/Transformer轨迹拟合训练方案.md` §10.1、`ml/README.md` §6 | | 2 | train 阶段输出目录随头分开,m5/apg/bench 用 CKPT 变量 | `ml/pipeline.sh:34-37,44,48` | | 3 | `python3` → `${PYTHON:-python}`,§1 说明 Windows 原生可跑 | `ml/prep/run_prep.sh:8` | | 4 | §4 补共识分支的 keepRate/样本量期望与"加 N_TASKS"动作;检查项加 `vertexMinGapMm` | `ml/README.md` §3–§4 | | 5 | §7 说明 M5 与 §11 两个口径,m5 加 `--safety` 对照 | `ml/pipeline.sh:36-42`、`ml/README.md` §7 | | 6 | 出题带真实老师时长(或明写定长 6 s),补 R7/R8 执行点 | `ml/mining/tasks.py:47`、`ml/mining/gate.py:41`、`ml/README.md` §9 | | 7 | §8 明写 APG 的 3.6 s 截断与只查端点;`margin_train_mm` 与老师间隙分布对齐 | `ml/README.md` §8、`ml/configs/apg.json` | | 8 | §10 默认 fp32 + `__POLICY.latency()` 实测后才考虑 fp16(仅 WebGPU);loop.sh 池子只取本轮;文件地图补全;evaluate 空任务保护 | `ml/README.md` §10/文件地图、`ml/mining/loop.sh:20`、`ml/sim/closed_loop.py:161` | 未验证范围:第 7(m5)、8(apg)、9(mine)、10(export)、11(bench)步与浏览器 `?policy=1` 第 12 步都没跑过(`ml/node_modules` 未安装、`webapp/models/` 无模型、`onnxconverter_common` 未装)。这些通路只做过静态代码核对。 --- --- # 第二轮:对修订版(提交 `d3b7c7b`)的可行性评估 评估对象:另一台机器按上述审查做的修订(18 文件、+493/−156)。方式:**只读代码 + 只读 diff,本轮没有跑任何东西**,第一轮的实测数字(6208 样本/s、keepRate 0.977、minGap p50 4.4 mm、1.56 s/条)继续沿用。上文第一轮的正文按 0218d7f 时的代码写,路径与章节号(`ml/runs/base`、12 步)在修订后已变,不逐条订正,以本节为准。 ## 一、第一轮 13 条的处置 | 第一轮条目 | 状态 | 落点 | |---|---|---| | P0-1 R10 吞吐判据矛盾 | 已修 | 方案 §10.1 R10 改为"60000 步 ≤ 3.5 h,即 ≥5000 样本/s",缓解改成 `fk_caps` 96→48 / `steps`→40000(增大 batch 明确标注无效);`ml/train.py:101` 加 `eta_h` | | P0-2 共识分支缺期望数字 | 已修 | README §4 加两张表(不共识实测 / 共识估算 + 异常动作);`ml/gen/stats.cjs:20` 加 `consensusDrop`、分位数加 p10;`ml/pipeline.sh` gen 阶段按实测"样本/任务"自动补量到 `TARGET_SAMPLES=1700000`(最多 2 次) | | P0-3 WTA 回退覆盖 MeanFlow 产物 | 已修 | `pipeline.sh`:输出 `ml/runs/$TAG`、`ml/runs/bench_$TAG`;m5 通过后写 `ml/runs/selected_ckpt`,apg/export/accept 经 `ckpt()` 取;`ml/train.py:58` 与 `apg_finetune.py:51` 日志改为新训覆盖、续训追加 | | P0-4 `python3` 在 Windows 断第 5 步 | 已修 | `run_prep.sh:8` 与各脚本统一 `${PYTHON:-python}`;README §1 加"Windows 原生 + Git Bash 实测可跑",并写明 Windows 不要用 `python3` | | 口径-1 仿真安全层 ≠ 运行时 | 已修(说明层) | m5 同跑 `--safety` 一遍,产出 `m5_gate_.json` 的 `policy_only` / `with_sim_safety` 两块;README §7 明写"三口径互不替代,部署只看第 11 步" | | 口径-2 门禁题假时长 | 已修 | `ml/mining/tasks.py:54` `exec_s/travel` 置 None;`ml/sim/closed_loop.py:24` `NOREF_SEC=6.0`,`time_ok_1p3`/`travel_ratio` 只在有老师参考的题上统计,新增 `n_ref`、`time_budget` 字段 | | 口径-3 APG 定长 3.6 s + 只查端点 | 已修(但引入新问题,见下 B-2) | `apg_finetune.py:70` 展开长度随批取 `exec_s p90 × 1.2`,夹在 60–150 次推理;`:102` 铰链改端点 + 中点 | | 口径-4 5 mm 余量压老师间隙分布 | 已改,方向不认同 | `configs/apg.json` 与 `webapp/js/ik-policy.js:174` 一起降到 3 mm;方案决策表"训练余量 5 mm"改为 3 mm,R3 回退改"提高到 4–6 mm" | | 缺项-1 §9 验收表无执行点 | 已补 | 新增 `ml/eval/acceptance.py`(第 12 步 `accept`):held-out 到达率 ≥98%、D 类停靠 ≤老师+5 mm、起步失败=0,并汇总 R2/R7/R8/R10/M5/部署形态 | | 缺项-2 挖错池累积 | 已修 | `ml/mining/loop.sh:21` 改为 `--pool $D/pool.jsonl` 只用本轮 | | 缺项-3 fp16 部署 | 已修 | `pipeline.sh` export 去掉 `--fp16`,README §10 改默认 fp32,只在 `&ep=webgpu` 实测受益才另出 fp16 对比 | | 缺项-4 `--n` 混控两集合 | 已修 | 拆成 `--nc-n` / `--route-n` / `--r8-n`,positional 过滤改用白名单 `VALOPT` | | 缺项-5 val 为空崩 | 已修 | `closed_loop.evaluate` 空任务返回 NaN 字典;`train.py:59` 无 val 任务时提示并自动 `--no-eval` | | 缺项-6 文件地图缺 4 文件 | 已修 | README 文件地图补 cert.cjs / pathdev.cjs / npy.cjs / toy_bimodal.py | | 缺项-7 R2 无执行点 | 已补 | 新增 `ml/eval/check_r2.py`,prep 末尾自动跑 | | 附带 | 新增 | R7(左右比例 + 行程)进 `ml/mining/gate.py:47-53`;R8 对抗集 + `brakeRate` 进 `tests/policy_bench.cjs`;出题与间隙计算分块防 OOM(`tasks.py:9` `min_gap_chunked`) | ## 二、修订新引入的问题(按严重度) ### B-1(最高)3 mm 是拿真机护栏迁就数据分布 依据是本轮实测"老师最小间隙 p50 4.4 mm",但正解是在认证环节把 `cert.minGap < 3 mm` 的老师样本过滤掉(`stats.cjs` 已经算这个数,逐条记录里就有),而不是把**运行时投影余量**一起降下来。认证只保证扫掠间隙 > 0,`collide.json` 的 `margin_mm` 只有 0.3 mm,`project()` 是唯一主动把臂往外推的力量(`ik-policy.js:279`)。方案 R3 原文写的回退是"训练余量**提高到** 3–6 mm 以缓解认证过保守",修订把这个值定成 3 = 把回退方向当成了默认。贴柱真实距离从 ≥5 mm 变 ≥3 mm 是机械/安全决定,需要单独确认,不该由 ML 口径推出。 ### B-2 APG 两处改动耦合且没重标定 - `apg_finetune.py:102-104`:端点与中点两个 hinge 直接 `view(2, B).sum(0)` 相加 → 碰撞项数值约为旧版 **2 倍**,而 `w_col=2.0` 未动。 - `:70` + `plans_max=150`:展开最坏 60→150 次推理(2.5 倍),每步 `kin.gaps` 的位形数又 ×2(端点+中点)→ 单个 APG step 代价上界约 **4 倍**。 - 同时 margin 5→3 让铰链激活的样本数下降。一增一减方向相反,幅度都没实测。 - README §8 的"2–4 小时"沿用旧口径,没随改动更新。 后验表现无法预判:APG 之后若行程变长/贴柱反而更近,第一嫌疑是这个组合而不是学习率。 ### B-3 新代码路径一次没跑,check_r2 自身可能成为闸门 - `check_r2.py:39`:`m.sum()==0` 时对空张量取 `torch.quantile(std, 0.9)` 直接报错(小样本摸底必撞;`label_std_mean` 也是 NaN)。 - 默认 `--queries 20000 --pool 200000`,分块距离矩阵 1024×200000 ≈ 0.8 GB/块,`torch.where` 再复制一份 → 峰值 2–3 GB 显存;`Samples` 本身又把 X/Y/SG 全量上卡(约 1.5 GB)。CPU 兜底时 20000×200000 fp32 = 16 GB 内存,docstring 说"降到 50000"仍然不轻。 - 它挂在第 5 步末尾,README 写"不通过先发我,不要直接训练"→ 一个自身可能 OOM/崩的硬门禁。 ### B-4 R7 的左右比例被成功率污染,会漏报它想抓的东西 `gate.py:47` 的 `left(...)` 以"到达"为条件掩码。APG 把某一侧绕行全做失败时,剩余成功集里的左右比例可以纹丝不动 → 而 R7 的目标(方案:APG 压缩多模态)恰恰是这种情况。建议按全部题的首次 J1 方向统计,不筛到达。 ### B-5 r8 集合没实现"两侧都要绕远" `policy_bench.cjs:47-64` 只约束终点贴立柱 + |ΔJ1|≥140° + 直线被挡,**起点没做 `nearColumn` 检查**。方案 §10.1 R8 的口径是"两侧都要绕远",现在起点对侧无约束 → 恢复率会比设计意图偏松(判据偏松是危险的偏)。 ### B-6 估时里"实测"两字一处用错、两处仍是拍的 - §2 写"1 分钟(实测 7.6 s 对拍)"——7.6 s 是 `dump_parity 300` 的数,pipeline 跑 2000 条,不同口径。 - §6 的"测速 6 分钟 / 正式 3 小时"由 6.2k 样本/s 换算,可信。 - §7 的 10–20 分钟(1000 题 ×2 遍 × 定长 6 s)、§11 的"约 1 小时"(题数已从 1070 增到 1120,且 r8 采样每条最多 200 次 `collideDetail` 重试)、§8 的 2–4 小时(见 B-2)都无依据。 ### B-7 方案自标的最大不确定项 R4 仍然是空的 策略推理在 wasm 多线程、batch=8 下的 P99 从未量过:`webapp/models/` 只有一个 README.md(至今没导出过任何模型),方案 §1.1 表里的 0.19 ms 是 2D 玩具沙箱(`ml/toy/toy_bimodal.py`)的数,不是这个 4.92 M 模型。而 60 ms 一次推理的部署节奏、`safetyMs:30` 预算、`certBudget:40` 全靠它。R4 不过就要改模型规模(d=128)或提交步数(3→2),是伤筋动骨的返工,却排在第 13 步才第一次被测。 ### B-8 杂项 - `build_dataset.cjs:46` 噪声增广的接受地板仍是 `S.clearance(qn, 5) > 1`,与新的 3 mm 投影不同步(一松一紧,不是矛盾但没对齐)。 - `acceptance.py:44` 用 `start_fail == 0` 硬判:held-out 约 2500 题里任何一题 0.5 s 内行程 <1° 就整项未过,判据脆。 - `pipeline.sh` m5 不通过 `sys.exit(3)` → `all` 会中止(有意,但应写进"常见问题")。 - `requirements.txt` 仍列 `onnxconverter-common`(fp16 已非默认,留着无害)。 - `tasks.py:36` 的 `column_gap(kin, b)` 仍在 2048 行整批上算(`free_q`/`straight_blocked` 已分块),单卡可过但和"已分块防 OOM"的说法不完全一致。 ## 三、可行性判断 1. **前段(第 1–6 步)可执行**:数据生成、打包、两种头、闭环仿真、对拍都在本机实测过,数字与手册自洽。修订把 R10、共识补量、目录覆盖、`python3` 这几个会真卡住人的点解决了,这部分我认可。 2. **后段(第 7–13 步)逻辑闭合但无证据**:判据齐全、口径终于分开了,可"APG+挖错能不能把纯策略穿柱率压到 ≤2%"、"机时是不是 2–4 h"、"held-out 能不能 98%"一个都没跑过。别把 §8–§12 的估时当预算。 3. **不建议按现值直接跑第 7 步之前**:B-1(3 mm)是需要你拍板的安全决定,B-2(w_col 没跟着重标)会让后段任何异常都无法归因。 ## 四、第 7 步之前建议先做的两件事(都不需要训练) 1. **结掉 R4**:用一个随机初始化的 4.92 M 模型导出 fp32 ONNX,走 `ml/serve.py` + `?policy=1`,在目标浏览器实测 `__POLICY.latency()` 的 batch=8 P99 与安全层 P99(方案要求 <15 ms)。不过就要改 `d`/`commit`,此时改配置的成本远低于训完 3 小时再改。 2. **把 margin 决定单独拍板**:在"过滤老师数据保 5 mm"与"放宽护栏到 3 mm"之间选一个,再把 `w_col` 与 §8 估时按选择重算。这两条是本轮唯一拦住往下跑的理由。 --- --- # 第三轮:对第二轮 B-1~B-8 的处置 + 新增训练控制台 修订方:沙箱(Hark)。基于 `655e1fb`。沙箱只做了 CPU 小数据冒烟(见第四节),所有耗时、keepRate、gapDrop 比例仍以你机器上的实测为准。 本轮改动以补丁形式交付(`git apply review2_and_monitor.patch`),确认后再推送。 ## 一、B-1~B-8 处置 | 条目 | 处置 | 改动位置 | |---|---|---| | B-1 3 mm 拿护栏迁就数据 | **采纳你的方向**:运行时投影护栏恢复 **5 mm**;改为在数据侧剔除认证最小间隙 `cert.minGap < 3 mm` 的老师轨迹;APG 碰撞项余量 3 mm 与过滤线一致(碰撞项不与 BC 标签对抗)。护栏比数据更保守,二者不再绑定 | `webapp/js/ik-policy.js:174` margin 3→5;`ml/gen/stats.cjs` 新增 `gapDrop`、`minGapFilterMm`,样本数与补量只按过滤后计;`ml/prep/build_dataset.cjs` 同一过滤(`MIN_GAP_MM=3`,可用环境变量改),打包输出也报 `gapDrop` | | B-2 端点+中点 hinge ×2、w_col 未重标 | 两处 hinge 由求和改为**取均值**,与只查端点同量纲,`w_col=2.0` 不用动。展开 60–150 次的单步代价不再拍估时:手册 §8/§9 改为"未实测,以监控页前 50 步换算的 ETA 为准" | `ml/apg/apg_finetune.py:104`;README 阶段表 | | B-3 check_r2 空匹配崩、显存峰值 | 近邻对 < 20 时输出 `pass: null` 不报错;默认 5000 查询 × 50000 池、分块 256、`masked_fill_` 原地,GPU 峰值 < 0.5 GB。`pass: false` 时 prep 以返回码 4 结束、一键流程停下;`null` 不拦 | `ml/eval/check_r2.py`;`ml/pipeline.sh` prep | | B-4 R7 以到达为条件会漏报 | 左右比例改为按**全部** B/C 题的首次 J1 方向统计,不筛到达 | `ml/mining/gate.py:47` | | B-5 r8 起点没贴柱 | 起点也要求 `nearColumn`,两侧都要绕远 | `tests/policy_bench.cjs` r8Cases | | B-6 估时口径 | 对拍改为"约 1 分钟(估算:300 条实测 7.6 s,流程跑 2000 条)";M5、部署评测标"估算";APG、挖错标"未实测" | README 阶段表 | | B-7 R4 排在最后 | **提到训练前**:新增 `bash ml/pipeline.sh r4`,用随机初始化模型导出 fp32 ONNX(延迟只与结构有关),`python ml/serve.py` + `?policy=1` 测 `__POLICY.latency()`;不过就把 `model.d` 192→128、`ff` 768→512 后重测 | `ml/export_onnx.py` 新增 `--random <配置>`;`pipeline.sh` r4;README §2;方案 R4 行"M1 前" | | B-8 杂项 | 噪声增广接受地板 1 mm → 3 mm(与过滤线一致);验收起步失败 `= 0` → `≤ 0.2%`;`column_gap` 分块 256(沙箱实测约 20 MB/位形,整批 2048 会爆);m5 返回码 3 中止 `all` 写进常见问题;`set -o pipefail` + 门禁结果先写 `.tmp` 再改名(中断不留残缺 `consistency.json`) | `build_dataset.cjs`、`acceptance.py`、`tasks.py`、`pipeline.sh`、README 常见问题 | | `onnxconverter-common` | 保留(`--fp16` 仍可用于 WebGPU 对比) | — | 方案文档同步:决策表"训练余量"改为"数据过滤与 APG 碰撞项 3 mm、运行时护栏 5 mm";APG 碰撞项写明端点与中点取均值;R4 检测时点改为训练前。HTML 已重新生成。 **需要你确认的一处**:3 mm 过滤会剔除多少老师轨迹没实测过。沙箱 30 条小样本里剔除了 9/29(p10 = 1 mm),量太小不具代表性;你那边第一轮实测 p50 4.4 mm。生成完看 `stats.json` 的 `gapDrop`:> 25% 说明老师整体太贴柱,把 `stats.json` 发我再定。自动补量会按过滤后的样本数补,所以样本总量不受影响,只是生成时间相应变长。 ## 二、新增:训练控制台(一键运行 + 全程可视化) ### 怎么用 1. Windows 双击 `ml/console.bat`(Linux/WSL:`bash ml/console.sh`),浏览器自动打开 `http://localhost:8100/`。 2. 点右上角「▶ 开始训练」,在对话框里选起始阶段、结束阶段和参数,点「开始」。 3. 之后整个流程(对拍 → 老师门禁 → 生成 → 打包 → 预训练 → M5 → APG → 挖错 → 导出 → 部署评测 → 验收)都在这一页看;「■ 停止」随时停,再开始默认从停下的那一步继续。 网页可以关,训练不受影响;关闭控制台窗口会结束训练。R2、M5 不通过时流程会停在那一步。 ### 组成与接口 | 文件 | 作用 | |---|---| | `ml/console.bat` / `ml/console.sh` | 一键启动:`python -m ml.monitor --open` | | `ml/monitor.py` | Python 标准库 HTTP 服务,默认只监听 127.0.0.1:8100 | | `ml/monitor.html` | 网页,零依赖(原生 canvas 画图),不连外网;`?demo=1` 或直接双击打开时用内置示例数据 | | `ml/pstate.py` | 写 `ml/runs/pipeline.json`:`pipeline.sh` 每个阶段开始/结束时自动调用(终端里跑也会写) | | 接口 | 方法 | 说明 | |---|---|---| | `/` | GET | 网页 | | `/api/runs` | GET | `ml/runs/*/log.jsonl` 列表(名称、类型 train/apg/bench、更新时间、是否发散) | | `/api/log?run=&offset=` | GET | 增量读日志:只返回 offset 之后的完整行,日志被新训练覆盖时自动从头读 | | `/api/status` | GET | GPU、`pipeline.json` + 当前阶段进度、数据统计、R2、M5、挖错各轮、部署评测、验收 | | `/api/job` | GET | 控制台启动的流水线进程:是否在跑、PID、起止阶段、返回码、是否手动停止 | | `/api/console?offset=` | GET | `ml/runs/console.log` 增量(首次只给最后 64 KB) | | `/api/start` | POST | `{"from":"kin","to":"accept","env":{...}}` → 后台 `bash ml/pipeline.sh all`;env 只接受白名单:`N_TASKS NPROC TARGET_SAMPLES CFG CONSENSUS ROUNDS APG_STEPS MINE_N`,逐个正则校验 | | `/api/stop` | POST | 结束整个进程树(Windows `taskkill /T /F`,其他系统进程组 SIGTERM,10 秒后 SIGKILL),当前阶段记为返回码 -15 | 安全:开始/停止只接受本机请求(`--host 0.0.0.0` 开放给局域网时其他机器只能看,`--remote-control` 才放开);已经有流水线在跑(控制台自己起的,或终端里起的、日志 2 分钟内有更新)时拒绝再开始。 Windows 找 bash 的顺序:环境变量 `BASH` → `C:\Program Files\Git\bin\bash.exe` → `C:\Program Files (x86)\Git\...` → `%LOCALAPPDATA%\Programs\Git\...` → `git` 所在目录推断;不用 `System32\bash.exe`(那是 WSL)。控制台把自己的 `sys.executable` 作为 `PYTHON` 传给流水线(路径含空格时不传,回落到 `python`)。 ### 流水线脚本配套改动 - `bash ml/pipeline.sh all` 支持 `FROM` / `TO` 只跑一段(如 `FROM=gen TO=train`);在终端里用 `all` 时也会在后台自动起控制台(`MONITOR=0` 关闭)。 - 每个阶段开始写 `begin`(生成阶段带 `target_tasks`,补量时更新;预训练带 `tag`;挖错带 `rounds`),退出时由 `trap EXIT` 写返回码。 - `pipeline.json` 在从中间续跑时保留计划之外各步的记录,所以流水线卡片能显示"之前已完成"。 - 训练与 APG 日志每条记录新增 `total`(总步数)与 `t`(时间戳),评估记录也带 `t`,用于进度条、已用时间与 ETA。 ### 网页内容 流水线阶段条 → 全流程卡(总百分比按各步粗估耗时加权、当前步骤及其进度、总用时、本步用时、剩余粗估、开始/停止)+ GPU 仪表 → 当前运行(自动跟随正在跑的训练,可手动切换)→ 指标卡(带趋势小图)→ 4 张曲线(预训练:总损失含原始值淡线、各分量、闭环评估带"最好"标记、吞吐带 R10 5000 线;APG:到达误差、损失分量、闭环评估、展开长度)→ 数据 / M5 / 挖错 / 部署评测 / 验收面板 → 终端输出(报错标红)。顶栏有平滑系数、对数纵轴、自动刷新开关。 当前步骤进度的口径:生成 = 已写任务条数 / 目标条数;打包 = 已打包分片 / 分片数;预训练 = (测速 2000 步 + 正式步数) / (2000 + 总步数);APG = 步数;挖错 = 已完成轮数 + 本轮 APG 步数;M5 = 两遍评估;对拍、老师门禁、导出、部署评测、验收无法计量,显示用时与粗估。 ## 三、本轮改动文件清单 ``` 新增 ml/monitor.py ml/monitor.html ml/pstate.py ml/console.bat ml/console.sh 修改 ml/pipeline.sh r4 / monitor 阶段、all 支持 FROM/TO 与自动起控制台、阶段状态记录、pipefail、R2 不通过停下、补量更新目标 ml/train.py 日志加 total、t ml/apg/apg_finetune.py 日志加 total、t;端点+中点 hinge 取均值;docstring 余量说明 ml/gen/stats.cjs gapDrop / minGapFilterMm,样本数按过滤后计 ml/prep/build_dataset.cjs 剔除 minGap < 3 mm 的轨迹;增广地板 3 mm;输出 gapDrop ml/eval/check_r2.py 空匹配保护、默认规模、分块 256 ml/eval/acceptance.py 起步失败 ≤ 0.2% ml/mining/gate.py R7 左右比例不以到达为条件 ml/mining/tasks.py column_gap 分块 ml/export_onnx.py --random tests/policy_bench.cjs r8 起点贴柱 webapp/js/ik-policy.js 投影护栏 5 mm ml/README.md 一键运行、R4、训练控制台、过滤口径、估时标注、回传文件、常见问题、文件地图 docs/Transformer轨迹拟合训练方案.md / .html 余量决策、APG 碰撞项、R4 时点 ``` ## 四、沙箱验证(CPU,2 核,小数据) | 项 | 结果 | |---|---| | `stats.cjs` 过滤 | 30 条任务:keep 20、gapDrop 9;`MIN_GAP_MM=4.4` 时 keep 16、gapDrop 13,口径一致 | | `build_dataset.cjs` | 20 条任务 761 样本,gapDrop 9,增广拒绝 5/226 | | `check_r2.py` | 小样本可跑,不再报错 | | APG 2 步 + 门禁(含新 R7) | 通过 | | `column_gap` 分块 | 300 位形 / 块 100 正常;块 512 在 7 GB 内存下被杀 → 默认 256 | | `export_onnx --random` | 走到导出一步,沙箱缺 `onnx` 包未完成,需在你机器上跑 `r4` | | 控制台接口 | 网页开始 `gen→prep`(6 条任务):进度 2/6 → 4/6 → 打包 0/2 → 完成,返回码 0;停止 `gate1`:进程树结束、该步记为已停止、不留残缺 `consistency.json`、"已停止"提示不被覆盖 | | 网页 | jsdom 跑 4 个示例场景 + 开始/停止流程,无脚本错误;沙箱没有浏览器,没有截图 | | 未验证 | Windows 下 `console.bat` 与 Git Bash 查找、`taskkill` 停止;`policy_bench.cjs` 完整运行(沙箱无 `onnxruntime-node`) | ## 五、你机器上的建议顺序 1. `git apply review2_and_monitor.patch`(或等我推送后 `git pull`)。 2. `bash ml/pipeline.sh setup`(如依赖已装可跳过)→ `bash ml/pipeline.sh r4` + 浏览器测延迟,把 `__POLICY.latency()` 发我。 3. 双击 `ml/console.bat` → 「开始训练」,起始「对拍」、结束「打包样本」,跑完看 `gapDrop` 与 R2。 4. 没问题再「开始训练」从「预训练」跑到「验收汇总」。