Files
Robot_Arm/docs/ML运行手册审查.md
T

74 KiB
Raw Blame History

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_<tag>.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. 没问题再「开始训练」从「预训练」跑到「验收汇总」。


第四轮:对第三轮(14d72a9)与训练控制台的审查

审查对象:B-1~B-8 的处置代码 + 新增控制台(ml/monitor.py 315 行、ml/monitor.html 795 行、ml/pstate.py、ml/console.bat|sh)+ ml/pipeline.sh 改动。 方式:逐行读代码;只跑了三个秒级 shell/python 验证(不碰 GPU、不跑流水线)。控制台与第 7 步之后仍然零实测。

一、B-1~B-8 处置核对

条目 代码事实 判定
B-1 护栏 ik-policy.js:174 margin 回 5;stats.cjs:7 与 build_dataset.cjs:30 同一 MIN_GAP_MM=3 过滤,gapDrop 两边都报;增广地板同步到 3(:49) 方向对,已落地。尾巴见 N-1
B-2 hinge 量纲 apg_finetune.py:104 由 sum(0) 改 mean(0) → 与只查端点同量纲,w_col 无需重标 已解决
B-3 check_r2 fin.sum()<20 → pass:null 提前返回;默认 5000×50000、分块 256(一块 256×50000 fp32 ≈ 51 MB)、masked_fill_ 原地;pipeline.sh:72 在 pass is False 时 exit 4 崩溃与显存问题解决了;判据本身新增误拒风险,见 N-2
B-4 R7 条件 gate.py:47 分母改 det.sum(),不再以到达为条件 解决了,但口径名不副实,见 N-3
B-5 r8 起点 policy_bench.cjs:57 起点也要求 nearColumn 已落地;出题成本未知,见 N-4
B-6 估时 对拍改「估算:300 条实测 7.6 s,流程跑 2000 条」;M5/bench 标「估算」、APG/mine 标「未实测」 已解决
B-7 R4 前置 export_onnx.py:24 新增 --random <配置>(meta.random_init),pipeline.sh:100 的 r4 阶段,README 表新增「2′ R4 延迟」 思路对(延迟只取决于结构)。但导出路径会覆盖真模型,见 N-5
B-8 杂项 起步失败 ≤0.2%(acceptance.py:44 带理由)、column_gap 分块 256、set -e -o pipefail、consistency.json 先 .tmp 再 mv 已解决
附带 pstate 原子写(os.replace)、训练/APG 日志加 total/t、all 支持 FROM/TO 结构合理

我这条也纠正自己一处:静态读 pipeline.sh:117 时我认为「端口已被占用就不重复启动」的判断写反了,实测我错了——端口空闲时 guard 退 0(启动)、被占用退 1(跳过),逻辑正确。

二、控制台的问题(按严重度,N-x 供下一轮出方案)

N-7(最高:会真丢数据)并发无锁,且重启后"已在跑"守卫在最危险的阶段失效

  • monitor.py:212 判断「终端里是不是另有一条在跑」的依据是 ml/runs/*/log.jsonl 120 秒内有更新。但 gate1 / gen / prep / m5 / bench / accept 这些阶段根本不写 log.jsonl(只有 train/apg 写)→ 在 4–5 小时的 gen 期间,网页「开始训练」会被放行。
  • 一旦二次启动:run_parallel.sh:8 用同一批种子(pipeline.sh:57 固定 S0=1)生成同名 ml/data/shard_%04d.jsonl,而 gen_dataset.cjs:23 是 fs.openSync(OUT,'w') → 两边互相截断,谁都没法要。
  • 反方向同样裸奔:pipeline.sh 内部没有任何锁,网页在跑时终端敲 bash ml/pipeline.sh all 照样起跑。
  • 触发概率不低:README 与审查文档都明确鼓励「网页可以关、重新打开接着看」,而 monitor 重启后 self.p=None,它自己的「已经在运行」判据(:207)也一起失效。

N-8(安全)/api/start、/api/stop 没有 Origin / CSRF 校验

monitor.py:288 的门槛是 client_address ∈ {127.0.0.1,::1},--remote-control 只管远端 IP。浏览器里任意第三方网页发往 http://localhost:8100/api/stop 的 POST,源地址就是 127.0.0.1 → 陌生网页可以停掉你跑了几小时的训练,或塞进一条新流水线(响应被同源策略挡住,读不走数据,但破坏成立)。文档里「开始/停止只接受本机请求」给出的保证比代码实际提供的强。

N-9(性能:整轮都在白付 IO)每次刷新把全部 shard 全文读两遍数行数

monitor.py:157-161 无条件对每个 shard 做 sum(1 for _ in fh),stage_progress 的 gen 分支(:100-101)再读一遍;网页 5 秒轮询一次(monitor.html:664)。按 ~5 KB/条 × 5 万条 ≈ 250 MB 估算(未实测,口径是记录里带 traj 顶点数组),即每 5 秒约 500 MB 顺序读,而且训练/APG 阶段那段统计并没有关掉 → 与 28 个 node 写盘、训练数据加载抢同一块盘。

N-10(环境)console.bat 找 Python 的方式会"看起来正常、三小时后炸"

  • .bat:6 用 where python 判存在。Windows 的 WindowsApps 假壳也能通过 where(本机 python3 -c 实测 exit 1 就是它的行为),存在性检查形同虚设;pipeline.sh:25 用的是更强的 command -v && python -c "import sys",两个启动器强弱不一致。
  • 双击 .bat 不会激活 conda/venv → monitor 只用标准库,照样起得来;:218 把自己的 sys.executable 当 PYTHON 传给流水线 → gen/prep 全是 node 不受影响,到 train 才 ImportError,最坏的失败姿势。
  • console.sh 认 $PYTHON,.bat 不认。

N-11(生命周期)几小时的训练挂在"别关那个黑窗口"上,且中间步骤没有续跑能力

CREATE_NEW_PROCESS_GROUP 不脱离控制台,关窗口即杀整棵树。train 有 --resume,但 apg 没有 resume 参数、mine 轮内无 resume、gen 补量会从头重扫全部 shard。Linux 侧文档给了 nohup,Windows 侧没有等价做法。

N-16(可观测性 + 潜在死循环)生成阶段网页看不到任何节拍

run_parallel.sh:8 把每个进程的进度 2> ml/data/log_$s.txt 重定向到文件,不流向 console.log → 4–5 小时的 gen 期间,网页「终端输出」面板是空的,只能看行数进度。叠上 sampler.cjs:19-20 的 C 类采样是无上限 for(;;)(nearColumn 命中率一旦因 collide.json 重拟合塌掉就永远出不来),故障表现就是"网页上一个字都没有、任务数不再增长",最难判断是慢还是死。

N-5(数据踩踏)r4 的随机模型和真模型同名同路径

pipeline.sh:100 是 --random $CFG --out webapp/models/traj_policy.onnx,与第 10 步导出的是同一个文件。meta.random_init 没有任何消费方检查(ik-policy-glue.js:30 只查 fp/feat_ver/tcp0,policy_bench.cjs:22 同)→ 训完之后补做 R4 会把真模型盖掉,随后 bench/accept/查看器 ?policy=1 会把随机策略当真模型跑,徽标照样显示「策略模式就绪」。

N-2(新闸门可能误拒)R2 判据与搜索池耦合,现在是硬停

池从 20 万降到 5 万(1.7 M 样本的约 3%)→ 最近邻系统性更远 → label_std 系统性偏大;而阈值 0.10 是按哪个池定的没有任何记录。pass:false 现在以 exit 4 停掉一键流程,等于在最可能出假阴性的配置上加了一道会拦掉 3 小时训练的闸。代码已经输出 nn_dist_threshold 但没有任何地方看它。

N-3(口径名不副实)R7 的"左右"不是首次方向

closed_loop.py:172 的 side = j1_max >= -j1_min,是整条展开的 J1 累计行程正负极值比较(净方向),而 gate.py:46 注释与第三轮文档写的是「首次 J1 方向」。来回摆动的题会被记成净方向;R7 想抓的「一侧被压缩」在"两侧都走但先左后右收回来"这种情况下会被误判。另外 left(sd, ok) 的 ok 形参现在没人用。

N-4(成本未知)r8 三重拒绝采样的出题耗时没测

起点贴柱 + 终点贴柱 + |ΔJ1|≥140° + 直线被挡,四个条件串联,tries 上限 4e6、内层再 200 次找起点。README §11 的「R8 出题约 2 秒/题」是估的;更要紧的是 while (cases.length<N && tries<4e6) 到上限就静默返回不足 50 题(不报错),R8 判据 ≥95% 的分母随之缩水。

N-13(显示不可信)补量时的 target_tasks 可能算错但不报错

pipeline.sh:65 用内层 python -c 读旧 target 再相加。内层失败(例如 pstate begin 因 || true 没写成、pipeline.json 缺 gen 记录)→ 空替换 → 实测 $(( + per2*NPROC )) 得 56 这类值(我验证过:不中断、不报错)→ 旧目标丢失,进度条 >100% 或倒退。

N-14(总百分比偏乐观)网页权重与修订后的耗时表不一致

monitor.html:341 的 STAGE_META:gen=1.0 / train=3.0 / mine=9.0。共识分支的 gen 实际 4–5 小时(README 自己写的),权重仍按 1 个单位 → 「全流程总百分比」「剩余粗估」在最可能走的那条分支上系统性偏乐观。

N-12(状态竞态)pipeline.json 有两个写者,其中一个非原子

pstate.save() 走 .tmp + os.replace(:20-24);monitor.stop() 却在 :247 手工 open(PJ,'w') 直写,且与 :244 的读之间没有锁 → 与子进程 begin/end 撞车会丢阶段记录。另:Ctrl+C / SIGKILL 时 bash 的 trap EXIT 不执行 → current 悬空 → 网页显示幻影「进行中」,同时 :210 的 7 天窗口把它当"终端可能还在跑"的候选。

N-1(配置面)MIN_GAP_MM 没有贯通

默认两边都是 3 ✓。但它是两个 node 脚本各自读的环境变量:只给 gen(影响 stats/补量口径)不给 prep(影响实际进训练集的条数),或反过来,就会让「补量算的样本数」和「真实样本数」分叉,而且没有任何一致性检查。monitor.py:175 的 env 白名单里也没有它,网页上根本设不了。

N-6(次要)分块尺寸自相矛盾

column_gap 默认 256、min_gap_chunked 默认 512,注释给的理由分别是「约 20 MB/位形」和「约 1.5 MB/位形」——同一台机器上同一个 kin.gaps 的峰值差 13 倍,说明其中至少一个是沙箱小内存机器上看来的数,24 GB 显存下 256 会把出题拖慢。未实测。

N-15(误导风险,低)file:// 双击就是"看起来在训练"

monitor.html:216 在 file: 协议下自动进 demo 模式并按刷新推进假数据;有 示例数据 标签(:470)但 README 没强调"别双击 html"。对不熟悉的人有误读空间。

(说明:/api/log 的 run 参数用 re.fullmatch(r'[\w.\-]+') 卡住了 / 与 \,路径逃不出去——这条我核过,不是问题。)

三、第四轮的可行性判断

  1. B-1~B-8 全部处置到位,方向都对(尤其 B-1 改成"数据侧过滤 + 护栏回 5 mm"是我更认可的那条路)。剩下的 N-1/N-3/N-5/N-6 属于口径与副作用的尾巴,不影响能不能跑。
  2. 控制台:读侧可用,写侧现在不能用。 进度/日志/统计的接口设计干净(纯标准库、无危险输入、增量读日志、覆盖检测都对),拿来在终端跑阶段、网页只看进度是合适的。但 N-7(同名 shard 互相截断 + 守卫在最坏阶段失灵)与 N-8(浏览器可驱动的无校验控制口)这两个洞,恰好都长在「一键开始/停止」这条路径上;不补掉就别用它起训练,只把它当仪表盘。
  3. 后 6 步依然零实测,而且现在多了两道新闸:R2 的 exit 4(N-2 有误拒概率)和 M5 的 exit 3。前 6 步的可行性结论不变。
  4. 不改代码就能规避的用法(供你这两天先往下走):终端按阶段跑;网页只看进度不点开始;r4 手工加 --out ml/runs/r4_probe.onnx;MIN_GAP_MM 别设(保持两边默认 3);console.bat 换成在激活过的环境里 python -m ml.monitor --open。

四、给下一轮的待处置清单(按优先级)

# 要解决的问题 判据(怎样算解决)
1 N-7 流水线互斥 一条真锁(ml/runs/pipeline.lock + pid + 心跳,或改 shard 文件名为 shard_<seed>_<runid>),任何入口(网页/终端/all)都走同一把锁;并发起第二条时立刻拒绝而不是等日志判活
2 N-8 控制口鉴权 start/stop 校验 Origin/Sec-Fetch-Site 或首次访问生成的本机 token;文档口径与代码一致
3 N-9 刷新代价 行数统计只在 gen 阶段且带缓存(增量 tail 或按文件大小比例),其他阶段不扫 shard;给出一次 /api/status 的实测耗时
4 N-5 随机模型踩踏 r4 输出独立路径(或不写 webapp/models/),并在 glue/bench/acceptance 里对 random_init 显式拒绝或红字警示
5 N-10 启动器环境 .bat 改用 python -c "import torch" 作可用性判据并尊重 %PYTHON%;monitor 启动时自检并把"当前 Python / 有无 torch / GPU"显示在网页上
6 N-11 生命周期 给出 Windows 等价的脱离控制台起法(或训练进程自托管 + 端口锁),apg/mine 补 --resume 语义或至少文档化"断了从哪重跑"
7 N-16 gen 阶段可观测 gen 的每进程节拍汇入 console.log(或网页读 ml/data/log_*.txt),并给 C 类采样加 tries 上限与告警
8 N-2 R2 阈值 判据与池大小解耦(报告 nn_dist_threshold 并按配对距离归一,或池改成全量的固定比例并写明);pass:false 先降为"警告 + 需人工确认"
9 N-3 / N-4 / N-14 / N-13 / N-12 / N-1 / N-6 口径对齐(首次方向 vs 净方向)、r8 不足额时报 n<50、网页权重与耗时表同源、target_tasks 读失败可见、状态文件单一写者、MIN_GAP_MM 贯通并校验、分块尺寸按本机实测统一

五、修法分层(阻塞 / 必要 / 可选)

判据口径:阻塞 = 不修就会毁掉已有产出或让几小时白跑;必要 = 不修会让你误判或断一次损失一整轮;可选 = 只是显示不准,不损失工作。

A. 阻塞:修完这 4 条才可以用网页「开始训练」,或用任何方式挂 4 小时以上的流程

# 要修的 落点 算修好了的判据
A-1 N-7 流水线互斥 monitor.py:207-213 的判活依据、run_parallel.sh:8 的 shard_%04d.jsonl 命名、pipeline.sh(终端入口也要一把锁) ① 已经有一条在跑时,第二个入口(网页或终端)立刻被拒并指名锁文件;② 两轮生成的输出文件名不重叠(带 run-id),故意并发一次,两边的 shard 都不被截断
A-2 N-5 随机模型踩真模型 pipeline.sh:100 的 --out、ik-policy-glue.js:29-32、policy_bench.cjs:22、acceptance.py 跑一次 r4 后:webapp/models/traj_policy.onnx 仍是训练产物(或 r4 走独立文件名),且 random_init:true 的模型在查看器徽标 / bench 输出 / acceptance 任一处都被显式标成"随机探针"而不是"策略模式就绪"
A-3 N-10 启动器拿不到带 torch 的 Python console.bat:6-7、monitor.py:218、monitor 启动自检 用一个没有 torch 的解释器起 monitor 时,网页顶栏直接标红"当前 Python 无 torch,不能开始训练"并拒绝 /api/start;.bat 尊重 %PYTHON%,与 console.sh 行为一致;用 WindowsApps 假壳路径能测出"拒绝"而不是"跑起来到 train 才炸"
A-4 N-8 控制口鉴权 monitor.py:286-289 带外站 Origin(或无 Sec-Fetch-Site: same-origin)的 POST /api/stop 返回 403;本机网页正常;--remote-control 语义不变

B. 必要:在对应那一步之前完成(不挡 A 组之前的操作)

# 要修的 什么时候必须完成 落点 / 判据
B-1 N-16 gen 阶段没有节拍 + C 类采样无上限 第一次挂 gen(4–5 小时)之前 run_parallel.sh:8 的 2> ml/data/log_$s.txt 汇入 console 或网页可读;sampler.cjs:19-20 的 for(;;) 加 tries 上限并打印原因。判据:故意造一个命中率塌掉的 collide.json,gen 要么报"采样不出"要么报错退出,不会静默空转
B-2 N-2 R2 阈值与搜索池耦合,且已改成 exit 4 硬停 第一次跑 prep→train 之前 check_r2.py 把 nn_dist_threshold 与池大小一起判,或阈值按配对距离归一;pipeline.sh:72 先降为"警告 + 需确认"。判据:同一份数据用 --pool 50000 与 --pool 200000 两跑,结论一致,否则判据不成立
B-3 N-11 apg/mine 无 resume,训练挂在"别关黑窗口"上 第一次跑第 8 步之前 apg 支持 --resume、loop.sh 支持从 r<k> 续跑,或文档逐阶段写清"断了从哪重跑、损失多少";Windows 给出等价 nohup 的脱离控制台起法。判据:中途强杀一次,续跑不重复已完成的轮次

C. 可选(先记账,等实测数出来再排)

N-9 刷新的全量行数统计、N-14 网页权重与耗时表同源、N-13 补量 target_tasks 丢失、N-12 pipeline.json 双写者与悬空 current、N-3 R7 净方向 vs 首次方向、N-4 r8 不足额静默、N-1 MIN_GAP_MM 贯通、N-6 分块尺寸不统一、N-15 file:// demo 误导。

建议的并行方式

  • 你(或另一台机器)现在做 A-1…A-4;A 组四条都不需要 GPU、不碰训练,改动面小。
  • 与此同时本机按终端逐阶段跑 r4(用 --out ml/runs/r4_probe.onnx)→ kin → gate1 → gen → prep → train → m5,把四个未知数拿回来:gapDrop 比例、R2 在 1.7 M 样本上的实际判定、samples_per_s 与 eta_h、M5 双口径数字。
  • 拿到这四个数,B-1/B-2/B-3 的优先级和 C 组要不要做才有依据;现在给 C 组排序只是猜。

六、目标改成「一条命令无人值守跑完」,需要怎么改

上一节按"能不能马上开始跑"分层,那是把流程切成两段的做法;如果目标是 bash ml/pipeline.sh all(或网页一个按钮)从 kin 一路到 accept 无人值守跑完、中间不需要人在终端前守着,那么 A/B/C 的排法不适用——需要的是下面四个结构性缺口一起补,缺任何一个都谈不上"完整流程"。约束我不放松:5 mm 护栏、M5 ≤2%、collisionFrames=0 三道门槛照旧。

缺口 1:状态机不闭环——"续跑"要变成一个动作,而不是让人挑起始阶段

现状:all 每次 pstate reset 把计划内阶段的记录清空重跑;FROM/TO 要人判断该从哪续;gen 重跑会把所有 seed 从头再生;loop.sh 重跑会把已完成的轮次再挖一遍;只有 train 有 --resume。所以一次中断(断电、关窗口、OOM、门禁停)= 人工重建上下文。

改成:

  • pstate reset 加 --keep-done:all 启动时读 ml/runs/pipeline.json,rc==0 的阶段跳过并保留用时,从第一个未完成阶段继续;网页「开始训练」不再问起始阶段(只问参数)。
  • 阶段内幂等:run_parallel.sh 跳过已存在且行数达标的 shard_%04d.jsonl;loop.sh 跳过已存在 gate.json 的轮次并复用 current.pt;gen 补量的目标条数落进 pipeline.json(重入时不重复补)。
  • train 的 --resume ml/runs/$TAG/last.pt 由 pipeline.sh 自动判断(存在就带),apg 加同样的 --resume(last.pt 已经在存)。
  • 判据:任意时刻强杀整棵进程树,再点一次「开始训练」,日志与网页显示"跳过 k 个已完成阶段,从第 j 步继续",且各产物文件不被重复生成。

缺口 2:入口不互斥、进程不脱离控制台

现状即 N-7 + N-11:靠"120 s 内有日志更新"猜有没有别的实例(gen/prep/m5/bench 阶段猜不到),shard 同名互相 openSync(OUT,'w') 截断;CREATE_NEW_PROCESS_GROUP 不脱离控制台,关黑窗口就杀整条链。

改成:

  • 一把真锁 ml/runs/pipeline.lock(pid + 心跳时间戳),网页/终端/all 三个入口都拿,超时未刷新即视为陈旧可抢占并打印提示。
  • monitor 起子进程时加 DETACHED_PROCESS(POSIX 侧 start_new_session 已有),训练与控制台进程解耦;控制台窗口关掉只关面板。
  • 判据:故意并发两个入口,第二个立刻被拒并指名持锁 pid;关闭控制台窗口 10 分钟后训练进程仍在跑且网页重开能接上。

缺口 3:三道"要人拍板"的门槛,要变成流程内的自动分支

现在这三处会把无人值守的长跑停死在人不在场的时刻:

门槛 现状 改成
R2(check_r2 exit 4) 判据与池大小耦合(5 万池 = 1.7 M 的 3%,最近邻系统性偏远 → label_std 系统性偏大),误拒就停 3 小时训练 阈值与池解耦(同时看已有的 nn_dist_threshold,或按配对距离分位归一),并在 pipeline.json 记 pool;不通过时默认警告并继续,R2_STRICT=1 才拦
M5(exit 3) 不过就停下等人手敲两条 CFG=wta 命令 流程内自动分支:base 不过 → 自动 train CFG=wta + m5 CFG=wta;两头都不过才停(并把两个 m5_gate_*.json 一并落盘)。目录已分开了,这条纯接线
gate1 → CONSENSUS 已经自动 ✓ 保留,但把 consensusDrop/gapDrop 双阈值写进 pipeline.json,让 gen 的补量次数按实际保留率放开(现在硬编码最多 2 次,共识 + 间隙过滤叠加时 2 次可能补不满 170 万 → 让它按 TARGET_SAMPLES 收敛到 4 次)
R4(浏览器延迟) 需要人开浏览器看 __POLICY.latency(),于是被排除在 all 之外 → 流程天生不完整 加自动探针:ml/eval/r4_probe.py 起 ml/serve.py(已有 COOP/COEP)→ headless Edge 打开 ?policy=1 → 用页面暴露的 __POLICY.setGoal(T,R,tool) 注入 30 个贴柱目标 → 回收 __POLICY.latency()/stats() 写 ml/runs/r4.json 并判 P99 < 15 ms;探针用 --random 导出的独立文件名模型,不碰真模型(顺手解掉 N-5)

判据:all 的计划里含 r4,且全程不需要人做任何一次浏览器/终端判断;三道门槛的"停"都只在两种头/两种配置都失败时才发生。

缺口 4:无人值守 = 失败要能被发现,长阶段要有心跳

现状:gen 的每进程节拍被 2> ml/data/log_$s.txt 吞掉;sampler.cjs:19 的 C 类采样是无上限 for(;;)(collide.json 重拟合后命中率塌掉就是静默死循环);补量的 target_tasks 读失败会算成 56 这类值而不报错(我验过)。

改成:

  • 每个阶段在 pipeline.json 带 timeout_h(按表里估时 ×3)与心跳(info.lastBeat);超时 → 非零退出 → 阶段标失败,网页顶栏红字,锁释放。
  • gen 的 log_$s.txt 尾行汇入 console(或网页直接读这些文件),并给 C 类采样加 tries 上限 + 失败原因落盘。
  • monitor 启动自检并把"当前 Python 路径 / 有无 torch / GPU 名 / 锁持有者"显示在顶栏(解 N-10:不能等到 train 才 ImportError)。

这样改完之后的机时与剩余风险(明说,不当已解决)

  • 一次成功的完整流程下界约 15–28 小时:gate1 0.5 + gen 1–5 + prep 0.5 + train 3 + m5 0.3(选中 wta 再加 3.3)+ apg 2–4 + mine 6×1–2 + export/bench/accept 1.5。其中 apg 与 mine 每轮的机时仍是零实测,补量/过滤叠加时 gen 可能更长。
  • 补了这四组缺口,"跑不完"的风险从"中途停了没人知道、只能人肉重建上下文"降到"某一步真失败且已落盘可查";但 跑通 ≠ 达标:M5 的 2%、held-out 98%、collisionFrames=0 能不能过,仍然要这一次真跑才知道。这是我不会用改造掩盖掉的唯一一件事。
  • 改动量估计(都不动模型结构与数据口径):pipeline.sh ~60 行、monitor.py ~70 行、pstate.py ~15 行、run_parallel.sh/loop.sh 各 ~10 行、check_r2.py ~15 行、新增 r4_probe.py ~80 行、sampler.cjs ~5 行。
  • 顺序建议(一次提交内做完,不分批放出去):缺口 1 + 2 是"敢挂长跑"的前提,先做;缺口 4 的心跳与超时和它们同一个改动面;缺口 3 的 r4 探针最大但独立,可以并行走。

七、R4 实测(本机 2026-10-18,随机初始化模型,不需要训练)

复现:python -m ml.export_onnx --random ml/configs/base.json --out webapp/models/traj_policy_r4.onnx(3.6 s,fp32 19.77 MB,ONNX↔torch 最大误差 4.8e-7)→ python ml/serve.py 8000 → 打开 ?policy=models/traj_policy_r4.onnx。 环境:RTX 3090 机器、28 核、Edge(Qoder Browser Connector)、onnxruntime-web 1.20.1、crossOriginIsolated=true、wasm EP、graphOptimizationLevel:'all'。模型 = configs/base.json(d192/heads8/ff768/enc4/dec4,4.92 M 参数),batch=8 即部署的 8 个候选。

配置 batch=8 P50 P95 P99 说明
部署现状(4 线程,在真查看器里) 33.2 ms 36.1 37.0 150 次;徽标"wasm,4 线程"
4 线程(空白页,无渲染) 32.2 — 35.9 与上一行差 ~1 ms → 查看器渲染不抢这块
8 线程 22.2 24.5 24.9 140 次
16 线程 13.7 — 19.5 160 次,最好的线程数
24 线程 19.2 23.8 25.8 比 16 差 → 超线程反而挤

batch 缩放(4 线程,同一会话):B=1 → 6.2/7.2;B=2 → 10.3;B=4 → 18.2/20.1;B=8 → 33.2/37.0;B=16 → 60.1;B=64 → 80.1(P50/P99)。

结论:

  1. R4 判据不过,差 2.4 倍(P99 37.0 vs 要求 < 15)。而且这不是"训练完再优化"的项——延迟只取决于结构与线程,随机权重与真权重同速。
  2. 一行改 ik-policy-glue.js:35 的 Math.min(4, hardwareConcurrency)(本机 16 最优)能把 P99 从 37 拉到 19.5,仍然不达标。所以达标必须动结构,两个方向都不是"降精度换速度"的取舍:① 部署的 8 行输入是同一观测复制 8 份,encoder 被重复算 8 次(Runner._plan 把 f 复制 K 遍、Policy.sample_k 里 mem.repeat_interleave(K,0))→ 改成"编码一次、解码 8 次"是纯冗余消除;② K=8 降到 4(B=4 在 16 线程下大概率 <10 ms),这是候选数与选优质量的权衡,要用 M5/闭环数字判损失,不能拍。
  3. 周期账:部署每 3 步(60 ms)一次推理,安全层单次预算 safetyMs:30。4 线程下推理 33 ms 已吃掉一半以上——安全层只要超过 27 ms 就必然丢步;16 线程下 13.7 + ≤30 才谈得上有余量。也就是说 R4 与 R9(贴柱处安全层全程 P99 < 5 ms)是同一个 60 ms 周期的两半,必须一起测。
  4. 没拿到的数(如实):闭环实际倍率/丢步率没采到——我挂的探针一运行,CDP 连 15 s 都取不到执行槽(连 list_console_messages 都超时),标签页要到切走才恢复。这本身就是主线程(投影有限差分 + 认证递归都在主线程)在 4 线程 wasm 下已经很挤的证据。R9 因此仍无数据。
  5. 残留:webapp/models/traj_policy_r4.onnx(+.meta.json,共 19.8 MB) 是未跟踪文件,meta.random_init=true;serve.py 已停,浏览器标签已释放。它同时验证了 N-5 的隐患是真的——这个文件与第 10 步 export 的默认输出同名同目录,只差文件名后缀。


第五轮:对第四轮的处置(实现方回复,2026-10-18)

处置对象:第四轮 N-1~N-16、第六节「无人值守跑完」四个缺口、第七节 R4 实测。 结论先说:第四轮全部认可,按第六节的目标一次改完(不走 A/B/C 分批)。三道门槛(运行时 5 mm 护栏、M5 ≤ 2%、collisionFrames = 0)不动;「跑通 ≠ 达标」原样保留。

〇、分歧裁决规则(用户 2026-10-18 确定)

审查方与实现方意见不一致、讨论后仍定不下来时,以实现方(Hark)的意见为准,按它落地,不再停下来等第三方拍板。 审查方的异议照常写进下一轮文档,附上判据;下一轮如果实测数据证明实现方错了,按数据改,不另起争论。 本轮实际只有两处分歧:N-6 和 R4 的改法(见第二节)。按上述规则,两处均按实现方意见落地。

一、逐条处置

条目 处置(代码落点) 自测
N-7 并发无锁 ml/pstate.py 新增互斥锁 ml/runs/pipeline.lock(O_EXCL 原子创建;pid + 每 60 s 心跳;5 分钟无心跳视为失效可接管)。pipeline.sh 在任何阶段入口先拿锁,all 的子阶段继承;监控页判断是否在跑只看锁,不再看日志新鲜度。生成改为先写 shard_XXXX.jsonl.part、写完才改名 先起 gate1,再起 gen → 立即拒绝(返回码 9,报出持锁 pid);TERM 后阶段记 143、锁释放
N-8 控制口无校验 /api/start、/api/stop 要求自定义头 X-Console: 1,Origin 和 Sec-Fetch-Site 有值时必须同源。跨站页面加自定义头会触发 CORS 预检,本服务不应答 OPTIONS,浏览器就不会发出请求 不带头 → 403;带头但 Origin: https://evil.com → 403;同源 → 200
N-9 刷新全量读 shard 行数按文件增量统计(记住偏移,只读新增字节),只在 gen 阶段做;其他阶段用 stats.json 的 tasks —
N-10 启动器 Python console.bat 尊重 %PYTHON%,判据改为 import torch;console.sh 依次试 PYTHON / python / python3,判据相同。monitor 启动后后台自检(训练用 Python、torch 版本、GPU、node、bash),不过则网页「开始训练」不可点、/api/start 拒绝;all 开头再查一次 import torch 沙箱自检输出正确(CPU torch 给出「看不到 GPU」警告)
N-11 生命周期 Windows 子进程用 `CREATE_NO_WINDOW CREATE_NEW_PROCESS_GROUP(自带隐藏控制台,关掉控制台黑窗口不连带结束);锁里记 Windows pid(Git Bash 的 /proc/$$/winpid),重开的控制台可按锁停止。APG 新增 --resume(last.pt 带优化器状态);loop.sh跳过已有gate.json的轮次、本轮 APG 断了从该轮last.pt 续跑;train自动带--resume`,测速 2000 步做过就跳过
N-16 gen 无节拍 / 死循环 run_parallel.sh 每 60 s 往控制台打一行节拍(已写条数 + 各进程最新进度),逐个 wait 子进程查返回码(原来的裸 wait 恒为 0),任一种子失败即返回码 6 并打印该种子日志尾部。sampler.cjs 所有拒绝采样设次数上限,超限抛错并写明「哪一类位形连续 N 次未命中」 4 条任务小跑:重跑时已完成种子跳过;补量第 1 次触发并写对 target_tasks
N-5 随机模型踩真模型 r4 导出到 ml/runs/r4/traj_policy_r4.onnx,不再写 webapp/models/;查看器遇到 random_init 默认拒绝(&probe=1 才放行,徽标标红「随机探针模型」);policy_bench.cjs 拒绝;acceptance 新增「部署模型是训练产物」一项 bench 加载随机模型 → 拒绝退出
N-2 R2 与池耦合 判据改为 label_std_norm:在距离较近的一半近邻对上做「标签差 ~ 近邻距离」线性回归,取距离 → 0 的截距。池变大只会让近邻更近、点更多,截距估的都是同一输入下的固有分歧;旧口径 label_std_mean 保留为参考,并记录 pool。不通过默认警告并继续,R2_STRICT=1 才停;验收里 R2 改为参考项 小样本可跑通(数值无意义);池无关性要用 1.7 M 样本的 --pool 50000 与 --pool 200000 两跑验证(差 < 0.02 为成立)
N-3 R7 口径 closed_loop.py 新增 j1_first:J1 累计偏离首次超过 5° 时的方向,即真正的首次方向;全程不到 5° 的退回净方向。gate.py 去掉无用形参 语法检查
N-4 r8 不足额静默 bench 记录 requested 与出题耗时 genS,不足额时打印警告;acceptance 对 n < requested 判不通过 见第三节
N-12 双写者 停止后的状态改写走 pstate.mark_stopped()(原子替换);pipeline.json 只有 pstate 一个写者 网页停止 → 阶段记 −15、current 清空、锁删除
N-13 target 读失败 新增 pstate get,读不到时非零退出;set -e 下直接失败,不再算出 56 这类值 get gen target_tasks 缺失 → rc 1
N-14 权重不同源 阶段名、估时、超时上限统一在 pstate.STAGES,由 /api/status 下发给网页;gen 权重 1.0 → 5.0(共识分支上界) 示例页 4 个场景无报错
N-1 MIN_GAP_MM pipeline.sh 统一 export MIN_GAP_MM=3,并作为参数记进 pipeline.json(改了会使生成及之后阶段作废);prep 开头比对 stats.json 的 minGapFilterMm,不一致返回码 8;网页可设 MIN_GAP_MM=4 跑 prep → 报错退出
N-6 分块 分歧,见第二节。改为按空闲显存自动定块(用 1/4,256~8192) —
N-15 file:// demo 示例模式在页面顶部显示黄色提示条;README 写明不要直接双击 html —

第六节四个缺口的对应:

缺口 落地
1 状态机闭环 pstate reset 默认保留 rc=0 的阶段;all 只跑 pstate todo 列出的阶段。参数变了只作废受影响的阶段(AFFECTS 表:生成参数 → gen 起;CFG → r4 和 train 起;挖错参数 → mine 起),被作废阶段的产物移到 ml/archive/<时间>/,不删除;FRESH=1 全部作废。网页不再问起始阶段,只选「续跑 / 从头重跑」
2 互斥 + 脱离控制台 见 N-7、N-11
3 门槛自动分支 R2 默认警告继续;M5 base 不过 → 流程内自动训 WTA 再判,两头都不过才停(AUTO_WTA=0 关闭);补量上限 2 → 4 次,补完仍不足 90% 目标时警告并继续;R4 自动探针(第二节)
4 心跳与超时 每阶段 timeout_h = 估时 ×3(m5 按含 WTA 分支计),all 用 timeout -k 120 包住每个阶段,超时整组进程收到 TERM,阶段记 timed_out、放锁;网页显示「本步粗估 / 上限」与「超过时间上限,判定卡死」

自测(沙箱、秒级):用 TIMEOUT_SCALE=0.002 把 gate1 上限压到 10 s → 返回码 124,阶段标 timed_out,node 子进程全部结束,锁释放。

二、两处分歧(按第〇节规则以实现方意见落地)

N-6:不是自相矛盾

kin.gaps 按 d² ≤ (胶囊对下限 + cap)² 筛候选对。min_gap_chunked 用 cap = 0.5 mm,每个随机位形幸存约 2800 对(≈ 1.5 MB);column_gap 用 cap = 40 mm,40 mm 以内幸存的对多一个量级(≈ 20 MB)。两处的峰值差 13 倍是 cap 不同造成的,两个默认块大小在各自的 cap 下都对。 第四轮说得对的是「24 GB 显存上 256 偏小、拖慢出题」,所以块大小改为按空闲显存自动计算(tasks.auto_chunk),两处只需传入各自的每位形峰值。

R4:33 ms 里不全是模型本身的开销

第四轮的结论是「必须动结构,二选一:去掉重复 encoder,或 K=8 → 4」。我的处置不同:

  1. 推理移出主线程(第四轮没提,最重要):原来 ik-policy-glue.js 没开 ort.env.wasm.proxy,wasm 推理在主线程同步执行。第七节第 4 条里「CDP 15 s 拿不到执行槽」就是这个原因;50 Hz 的 tick 也被一起拖住,所以丢步不只来自推理时长本身。现在默认开代理 Worker(&proxy=0 关),创建失败时自动退回主线程并在徽标上写明。
  2. 编码一次:同意第四轮的方向 ①。ONNX 接口改为 feat[1,83]、z[K,16,6](Policy.sample_shared,meta.enc_once);导出时断言与旧接口逐元素一致(误差 < 1e-4);运行时按 meta.enc_once 只送 1 行,旧模型照常可用。按 base.json 粗算,encoder 约占总计算 29%,可省约 25%;沙箱 CPU 实测(onnxruntime 2 vCPU,200 次):旧接口 P50 10.03 / P99 10.34 ms → 新接口 8.03 / 8.18 ms,省约 20%。
  3. 线程数:默认 min(16, ⌊0.6×逻辑核⌋)(28 核机 = 16,即第七节实测最优),&threads=N 可覆盖。
  4. K=8 不降:降 K 损失的是选优质量,要有 M5 / 闭环数字才能判,现在没有依据。
  5. WebGPU 必须测:方案 R4 行本来就写了要同测 WebGPU,第七节没测。探针现在 4 种配置都测:默认线程 + Worker(部署口径)、4 线程(对照)、默认线程不开 Worker(看 tickGapMax,即推理是否卡住 20 ms 节拍)、WebGPU。

判定顺序:部署口径 P99 < 15 ms 即通过;wasm 不过而 WebGPU 过,也判通过并记 deploy_ep: webgpu;两者都不过,流程在训练前停下(返回码 5),改 CFG=ml/configs/small.json(d=128)重跑。 预估(未实测,不当已解决):16 线程旧接口 P99 19.5 ms × (1 − 0.2~0.25) ≈ 14.6~15.6 ms,正好踩在线上;WebGPU 在 3090 上的数字是这一轮最关键的未知数。 R9:探针只测纯推理。贴柱处安全层耗时(R9)仍以 bench 的 safetyMsP99 为准;推理移出主线程后,安全层独占主线程,R4 与 R9 合起来要小于 60 ms 周期这笔账,等真机数据再核。

三、断点续跑(用户 2026-10-18 追加要求)

要求:长时间的步骤意外中断(断电、蓝屏、误关、手动停止)后能接着跑,不从头来。阶段级跳过上一版已有,这一版把阶段内部也做成可续:

阶段 续跑粒度 做法
gen 每条任务 第 i 条任务的随机数只由(种子, i)决定(sampler.reseed);.part 里保留完整的行、截掉写了一半的最后一行,从下一条接着写。沙箱验证:截断后续写的结果与一次写完逐条一致(id、q0、keep 全同)。
prep 每个 shard tasks.jsonl 最后写且先写 .tmp 再改名,它存在且比 shard 新就跳过;shard 已不存在的旧打包删掉。
train / WTA 最多 10 分钟 每 10 分钟原子写 last.pt(ckpt_min,先写 .tmp 再 os.replace,写到一半断电旧档仍完整),含优化器、步数、最好成绩、早停计数;发散时不再用发散权重覆盖 last.pt / best.pt。
apg / 挖错 最多 10 分钟 同上;挖错已过门禁的轮次跳过。
m5 半个门禁 比检查点新的 m5_<头>_policy.json / _safety.json 跳过;结果文件改为原子写;检查点不存在按不通过处理(直接进入 WTA 分支)。

停止也不丢进度:网页「停止」在训练类阶段先写 ml/runs/STOP,训练进程 2 秒内看到后存档、以 143 退出,最多等 90 秒才硬杀(Windows 的 taskkill /F 收不到信号,所以靠文件)。终端 Ctrl+C / kill 走信号,同样先存档。新一次运行拿到锁时清掉旧的 STOP。 沙箱验证:STOP 文件 → rc 143、last.pt 在第 180 步,续训从 180 到 200 正常收尾;kill -TERM → rc 143 并存档;kill -9(模拟断电)→ 定时存档的 last.pt 完好可读;打包第二次运行「待打包 0 个」。 不续的只有几分钟级的步骤(kin、gate1、r4、export、bench、accept),整步重跑。

四、仍未验证(如实)

  • Windows 侧全部没有实测:CREATE_NO_WINDOW 下关掉黑窗口训练能否继续跑;/proc/$$/winpid 与 taskkill /T 能否结束整棵树;Git Bash 自带的 timeout 能否把 TERM 送到 python.exe / node.exe 子进程(Linux 下已验证会结束整个进程组)。
  • R4 探针没在真浏览器跑过(沙箱没有 Chromium):onnxruntime-web 1.20 的 proxy 从 jsDelivr 加载能否建出 Worker(建不出会自动退回并写明 proxyError)、无头模式下 WebGPU 是否可用。
  • R2 新判据的池无关性只能在 1.7 M 样本上验证(判据见第一节表)。
  • R8 出题耗时:沙箱 2 vCPU 出 2 题 9 分钟仍未出完,已停掉,改在本机测;policy_bench.json 的 r8 段会记录请求题数、实际题数和出题耗时。
  • 训练 / APG / 挖错的机时仍然零实测,第七节之后的结论不变。

五、请在本机按顺序验证

  1. git apply review5_fixes.patch(基于 e3bb783)。
  2. 激活训练环境,双击 ml/console.bat:页面「全流程」卡片底部应显示 Python / torch / GPU;用 set PYTHON=C:\不存在\python.exe 再双击,应直接提示「不能 import torch」。
  3. 终端单跑 bash ml/pipeline.sh r4,把 ml/runs/r4.json 发我:4 种配置的 P50/P99、proxy 是否为 true、tickGapMax、WebGPU 数字。
  4. 网页「开始训练」(续跑),在 gen 节拍出现后:① 另开 Git Bash 跑 bash ml/pipeline.sh gen,应被拒绝(返回码 9);② 关掉控制台黑窗口,任务管理器里 node 进程应仍在;③ 重新双击 console.bat,页面应显示「由上一个控制台窗口启动」,点「停止」应结束全部 node 进程。
  5. 其余照常跑到 prep,把 stats.json(gapDrop)与 r2.json(label_std_norm 和 label_std_mean 两个口径)发我。
  6. 断点续跑:gen 跑到一半点「停止」再点「续跑」,ml/data/log_*.txt 应出现「续写:已有 N 条」;train 跑过 10 分钟后点「停止」,终端应出现「已存档 last.pt(第 N 步)」,续跑后出现「续训:从第 N 步继续」。