holy fuking shit
This commit is contained in:
+53
-14
@@ -23,7 +23,7 @@ uv run python -m mcm26b.training_ui --print-preview # 只看离线示例快照
|
||||
|
||||
| 区块 | 内容 |
|
||||
| --- | --- |
|
||||
| 顶栏 | 任务摘要、进程/进度/墙钟/设备/连接五个状态 chip、刷新·预览·停止 |
|
||||
| 顶栏 | 任务摘要、进程/进度/墙钟/设备/连接五个状态 chip、刷新·预览·暂停/继续·停止 |
|
||||
| 执行进度与进程 | 步进进度条、剩余时间与预计结束、PID/吞吐/退出码、PPO 阶段时间线、完整命令行 |
|
||||
| 训练配置 | 由 `/api/train/schema` 生成的五组表单 + 四个预设,运行中锁定 |
|
||||
| 参数收敛状态 | 判读 chip + 判据说明,每个指标一张卡片(最新值、增量、迷你趋势线、异常高亮) |
|
||||
@@ -40,7 +40,15 @@ uv run python -m mcm26b.training_ui --print-preview # 只看离线示例快照
|
||||
| `GET /api/train/schema` | 字段/分组/预设/指标字典,**与 `TrainConfig` 同源**(`schema.py`) |
|
||||
| `GET /api/train/state` | 快照;支持 `log_tail`、`metric_limit`、`after_metric` 游标与 `preview=1` |
|
||||
| `POST /api/train/start` | 允许只提交部分字段(其余取默认值),校验失败返回 409 |
|
||||
| `POST /api/train/stop` | 向独立进程组发送 SIGTERM |
|
||||
| `POST /api/train/pause` | 写控制文件请求在 step 边界挂起(跨平台) |
|
||||
| `POST /api/train/resume` | 解除挂起,从暂停处继续 |
|
||||
| `POST /api/train/stop` | 请求优雅停止;宽限期(`STOP_GRACE_S`,默认 15s)后强杀进程树 |
|
||||
|
||||
暂停/停止走**控制文件**而不是信号,原因与平台差异见
|
||||
[`training-ui-windows-audit.md`](training-ui-windows-audit.md):Windows 没有
|
||||
`SIGSTOP`,且 `os.kill(pid, SIGTERM)` 会退化成 `TerminateProcess` 硬杀,训练脚本
|
||||
"保存模型再退出"的收尾分支不会执行。控制文件在 POSIX 与 Windows 上都只是普通
|
||||
文件 IO,语义完全一致;信号(POSIX `SIGTERM/SIGINT`、Windows `SIGBREAK`)只作兜底。
|
||||
|
||||
进度数据的两个来源,服务端都已解析:
|
||||
|
||||
@@ -76,41 +84,66 @@ uv run python -m mcm26b.training_ui --print-preview # 只看离线示例快照
|
||||
|
||||
- 只监听 `127.0.0.1`;静态资源是固定白名单,不提供目录遍历。
|
||||
- 命令由服务端以**参数列表**构造,不经过 shell;字符串参数只拒绝控制字符。
|
||||
- 同一时刻只允许一个训练任务;停止用独立进程组 SIGTERM,避免残留子进程。
|
||||
- 同一时刻只允许一个训练任务;暂停中的任务仍算「在跑」,必须先停止才能开新一局。
|
||||
- 暂停/停止走控制文件(`--control-file`,默认 `output/hrl/ui/control/<job_id>.control.json`),
|
||||
进程组隔离按平台选择(POSIX `setsid` / Windows `CREATE_NEW_PROCESS_GROUP`),
|
||||
超时强杀按平台选择(POSIX `killpg(SIGKILL)` / Windows `taskkill /F /T`)。
|
||||
- 产物路径限定在仓库根下解析,模型输出沿用训练脚本自身的 `--force`/`--resume`
|
||||
语义(两者互斥,由后端校验)。
|
||||
|
||||
## 停止与落盘保证
|
||||
## 暂停、停止与落盘保证
|
||||
|
||||
训练前端的「停止」是对独立进程组发 SIGTERM。`train_hrl_high.py` 有三层兜底,
|
||||
点一下「停止」不再等于这一局白跑:
|
||||
「暂停」是**协作式**的:前端原子写控制文件,`train_hrl_high.py` 的
|
||||
`_ControlPoll` 每 32 个环境步抽样读一次;暂停时训练阻塞在 `_on_step` 的轮询里,
|
||||
进程与显存保留、不消耗算力,点「继续」从同一个 step 边界恢复。暂停时长不计入
|
||||
`--max-wall-s`(否则"挂起一小时回来直接判超时")。
|
||||
|
||||
「停止」同样是控制文件请求(**跨平台**)。`train_hrl_high.py` 有多层兜底,
|
||||
点一下「停止」不再等于这一局白跑;而**急停**(强杀,例如 `SIGKILL` /
|
||||
`taskkill /F`,进程没有任何收尾机会)也保证可恢复:
|
||||
|
||||
| 机制 | 行为 |
|
||||
| --- | --- |
|
||||
| 定期 checkpoint | `--checkpoint-every`(默认 20000 环境步)在 `<模型目录>/checkpoints/` 落 `*_ckpt_<步数>.zip` + 同名元数据;`--checkpoint-keep` 只保留最近 K 个(默认 3),`--checkpoint-dir` 可改目录 |
|
||||
| 优雅停止 | subproc worker 忽略 SIGTERM/SIGINT,主进程收到信号后在 step 边界收尾,保存最终模型与元数据(`stopped_by_signal=true`、`stop_signal`、`checkpoint_files`)并正常关闭环境;收尾阶段恢复默认处理器,再按一次可立即强杀 |
|
||||
| 异常兜底 | `learn()` 抛出任何异常时先落 `*_emergency.zip` + 元数据,再写 `training_failure.json` 并抛出 |
|
||||
| 滚动恢复点 | `--recovery-every`(默认 2000 环境步)把 `<checkpoints>/<stem>_latest.zip` **原子覆盖写**一次;训练开始前先写一份**起步基线**。因此急停后最多只丢 `--recovery-every` 个环境步,且磁盘上始终有一份完整、可加载、可 `--resume` 的产物 |
|
||||
| 原子落盘 | 模型 zip、旁挂 json、`resume.json` 都是「同目录临时文件 + `os.replace`」:保存途中被强杀不会留下半截文件,也不会破坏上一份完整快照 |
|
||||
| 恢复指针 | `<run_dir>/resume.json` 记录当前最佳恢复目标 + 步数(增量语义)+ 生效过的 PPO 超参;前端顶部的「用最近检查点续训」按钮直接读它 |
|
||||
| 定期 checkpoint | `--checkpoint-every`(默认 20000 环境步)另留 `<stem>_<步数>.zip`,`--checkpoint-keep` 只保留最近 K 个(默认 3),供返祖对比与多档恢复;滚动恢复点与起步基线不受清理影响 |
|
||||
| 优雅停止 | 控制文件 `stop=true`(或 POSIX 信号 / Windows `SIGBREAK`)后,主进程在 step 边界收尾,保存最终模型与元数据(`stopped_by_signal=true`、`stop_signal`、`checkpoint_files`、`recovery_model`)并正常关闭环境 |
|
||||
| 超时强杀 | 宽限期(`STOP_GRACE_S`,默认 15s)内没退出则强杀整棵进程树,快照 `forced_kill=true`,并在 `<run_dir>/forced_stop.json` 写明 `recovery.model`(可直接续训的目标) |
|
||||
| 异常兜底 | `learn()` 抛出任何异常时先落 `*_emergency.zip` + 元数据 + `resume.json`,再写 `training_failure.json` 并抛出 |
|
||||
|
||||
急停后恢复的操作方式:界面点「用最近检查点续训」;命令行则
|
||||
`--resume --model <resume.json 里的 model>`,`--timesteps` 按**追加**步数理解。
|
||||
|
||||
checkpoint 与最终模型共用同一份 json 契约,因此 `--resume <checkpoint.zip>` 直接
|
||||
续训(`--timesteps` 为增量步数)。注意 `--resume`/`--init` 会沿用 checkpoint 内的
|
||||
PPO 超参(`n_steps`/`batch_size`/`n_epochs`):命令行这三项被忽略,脚本会打一条
|
||||
`hyperparams_from_checkpoint` 事件说明差异,元数据 `ppo_hyperparams` 记录**实际生效**值。
|
||||
回归测试见 `tests/test_hrl_checkpoint.py`,
|
||||
其中端到端用例真起 subproc 子进程并用 `killpg(SIGTERM)` 复现前端的停止路径。
|
||||
回归测试见 `tests/test_hrl_checkpoint.py`,其中端到端用例真起 subproc 子进程,覆盖
|
||||
控制文件停止、POSIX 信号停止、**强杀后仍可续训**与暂停→继续(信号路径在 Windows
|
||||
上自动跳过)。
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m pytest tests/test_training_ui.py tests/test_training_ui_js.py -q
|
||||
.venv/bin/python -m pytest tests/test_training_ui.py tests/test_training_ui_js.py tests/test_hrl_checkpoint.py -q
|
||||
```
|
||||
|
||||
- `tests/test_training_ui.py`:配置契约同源、预设可直接提交、参数校验、SB3 表格
|
||||
刻度打点、游标分页、`describe_process` 进度/ETA、示例快照与真实快照同键、
|
||||
以及用**桩训练脚本真起子进程**的启动/轮询/停止/退出码全链路。
|
||||
刻度打点、游标分页、`describe_process` 进度/ETA、示例快照与真实快照同键;
|
||||
用**桩训练脚本真起子进程**覆盖启动/轮询/暂停/继续/停止/退出码全链路;并覆盖
|
||||
跨平台进程控制——控制文件往返与损坏容错、`spawn_kwargs` 平台分支、Windows 走
|
||||
`taskkill`、**无 `killpg` 环境下强杀不抛异常**(旧 bug 的直接回归)、控制文件被
|
||||
忽略时强杀并写 `forced_stop.json` 恢复指引、`discover_recovery` 的
|
||||
`resume.json`/扫描/悬空指针三分支、HTTP `pause/resume`。
|
||||
- `tests/js/training_ui_harness.cjs`(经 `tests/test_training_ui_js.py` 调起):
|
||||
DOM 桩加载发布的 `charts.js`/`app.js` 原文,用服务端生成的真实快照驱动渲染,
|
||||
断言表单/指标卡/曲线/日志/逐回合表都拿到数据,覆盖增量游标三条分支与
|
||||
`classList`、HTML 转义、极端数值等边界。
|
||||
- `tests/test_hrl_checkpoint.py`:落盘与恢复的端到端回归——原子保存失败不破坏
|
||||
上一份快照、JSON 原子替换、历史清理不误删滚动恢复点、起步基线可加载、
|
||||
**强杀急停后 `resume.json` 与滚动恢复点仍可继续训练**、控制文件停止落盘、
|
||||
暂停→继续、POSIX 信号停止。
|
||||
|
||||
## 已知取舍
|
||||
|
||||
@@ -119,3 +152,9 @@ PPO 超参(`n_steps`/`batch_size`/`n_epochs`):命令行这三项被忽略
|
||||
格式,退化路径是「只显示 progress JSON」,不会导致界面崩溃。
|
||||
- 收敛判读是启发式;正式结论仍需用 `scripts/benchmark_hrl.py` 的配对评测与
|
||||
学习曲线人工复核。
|
||||
- 暂停粒度是 `poll_every` 个环境步(默认 32),不是即时挂起;停止宽限期默认 15s。
|
||||
- Windows 强杀依赖系统自带的 `taskkill`;若被策略禁用会退回 `Popen.kill()`
|
||||
(只杀主进程)。更严格的 Job Object 方案需要额外依赖,未引入。
|
||||
|
||||
Windows 兼容性问题的完整根因与修复记录见
|
||||
[`training-ui-windows-audit.md`](training-ui-windows-audit.md)。
|
||||
|
||||
Reference in New Issue
Block a user