docs: ml 审查第四轮(对 14d72a9 与训练控制台)+ R4 实测——控制台 N-7 并发无锁(守卫只看 log.jsonl,gen/prep/m5/bench 阶段失灵,二次生成用同名 shard 互相截断)、N-8 开始/停止无 Origin 校验(本机网页即可停掉长跑)、N-9 每次刷新全量重读 shard、N-5 r4 与真模型同名同路径、N-10 console.bat 用 where python 判存在且不带 conda 环境、N-16 gen 节拍被重定向吞掉且 C 类采样 for(;;) 无上限;修法按完整流程分层(状态机可续跑/入口互斥且脱离控制台/门槛改自动分支/阶段超时心跳);R4 随机模型实测 batch=8:4 线程 P50 33.2 P99 37.0(要求 <15,差 2.4×),16 线程 13.7/19.5,24 线程反而劣化,闭环倍率未采到(主线程已挤到 CDP 取不到槽),故 K 与重复 encoder 是必改项;附 .gitignore 忽略 20 MB 级模型产物
This commit is contained in:
@@ -39,3 +39,7 @@ web/
|
||||
webapp/data/
|
||||
webapp/generated/
|
||||
定位图/
|
||||
|
||||
# 策略模型产物(由 python -m ml.export_onnx 生成,20 MB 级,不入库)
|
||||
webapp/models/*.onnx
|
||||
webapp/models/*.meta.json
|
||||
|
||||
@@ -332,3 +332,227 @@ Windows 找 bash 的顺序:环境变量 `BASH` → `C:\Program Files\Git\bin\b
|
||||
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` 的默认输出同名同目录,只差文件名后缀。
|
||||
|
||||
Reference in New Issue
Block a user