7.1 KiB
模型训练前端(training-ui)
本地回环 Web UI,用于实时观察一次 HRL 高层 PPO 训练:子进程状态、步进进度、
参数收敛曲线、逐回合记录与完整 stdout 日志。设计与评测前端(referee/ WebUI)
同构——同一套暗色面板语言、同一个「只监听 127.0.0.1 + 静态白名单 + JSON 轮询
API」模式——差别在于本页托管的是一个训练子进程而不是评测会话。
启动
uv run python -m mcm26b.training_ui --ui-port 8036 # 打开 http://127.0.0.1:8036
uv run python scripts/train_hrl_ui.py --ui-port 8036 # 等价薄入口(支撑材料命令)
uv run python -m mcm26b.training_ui --print-schema # 只看配置契约,不起服务
uv run python -m mcm26b.training_ui --print-preview # 只看离线示例快照
--no-browser 阻止自动打开浏览器;Ctrl+C 退出时会先停止训练子进程。
无训练进程时,界面顶栏「离线预览」可渲染三组合成快照(上升 / 平台期 / KL 过猛), 用于检查曲线、指标卡、时间线与日志着色——预览不启动任何进程、不写任何文件。
界面结构
| 区块 | 内容 |
|---|---|
| 顶栏 | 任务摘要、进程/进度/墙钟/设备/连接五个状态 chip、刷新·预览·停止 |
| 执行进度与进程 | 步进进度条、剩余时间与预计结束、PID/吞吐/退出码、PPO 阶段时间线、完整命令行 |
| 训练配置 | 由 /api/train/schema 生成的五组表单 + 四个预设,运行中锁定 |
| 参数收敛状态 | 判读 chip + 判据说明,每个指标一张卡片(最新值、增量、迷你趋势线、异常高亮) |
| 曲线 | 主指标(平均回合奖励)+ 可切换诊断量,悬停十字线读数、KL=0.05 等参考线 |
| 实时运行日志 | 关键字过滤、错误/进度 JSON 开关、自动跟随、最近 4000 行 |
| 逐回合记录 | Monitor CSV 的每回合奖励/长度/时刻 |
数据来源与契约
三个端点都在 src/mcm26b/training_ui/server.py:
| 端点 | 说明 |
|---|---|
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 |
进度数据的两个来源,服务端都已解析:
- progress JSON(主来源):
train_hrl_high.py --progress-json每若干环境步打印 一行{"event": "progress", "timesteps": ..., "ep_rew_mean": ...},字段最全; 热身阶段间隔会自动缩小,短程 smoke 也能看到完整曲线。 - SB3 verbose 表格(补充来源):解析
| key | value |表格,用于补clip_fraction/learning_rate等诊断量。表格按段分组缓存、等本表的total_timesteps到位后统一打横轴刻度;与 JSON 同一步数的表格版本会被跳过, 避免重复点。
timesteps 是整个前端的横轴:服务端不伪造刻度(缺刻度的表宁可丢弃,截断的表
按末尾已知刻度兜底并在收尾提交),前端不再自行编号。
收敛判读口径
summarize_convergence() 只做可解释的启发式判读,不替代正式学习曲线分析:
| 状态 | 触发条件 |
|---|---|
| 等待首个 rollout | 尚无任何指标 |
| 预热阶段 | 进度点 < 3,无法判断趋势 |
| KL 偏大,策略更新可能过猛 | 最近 approx_kl > 0.05(判定优先级最高) |
| 价值函数尚未拟合 | 最近 explained_variance < 0 |
| 奖励未形成上升趋势 | 最近窗口奖励斜率为负 |
| 已趋于平稳(疑似收敛) | 窗口对半位移 ≤ 逐点噪声、变异系数 ≤ 0.15、极差 ≤ 15% 量级 |
| 收敛趋势良好 | 其余(斜率正向) |
平台判据特意用「对半窗口位移 / 逐点噪声」而不是单看斜率绝对值:慢速但持续的
提升(斜率很小)不会被误判成收敛。所有判据数字都会写进 notes,便于人工复核。
安全边界
- 只监听
127.0.0.1;静态资源是固定白名单,不提供目录遍历。 - 命令由服务端以参数列表构造,不经过 shell;字符串参数只拒绝控制字符。
- 同一时刻只允许一个训练任务;停止用独立进程组 SIGTERM,避免残留子进程。
- 产物路径限定在仓库根下解析,模型输出沿用训练脚本自身的
--force/--resume语义(两者互斥,由后端校验)。
停止与落盘保证
训练前端的「停止」是对独立进程组发 SIGTERM。train_hrl_high.py 有三层兜底,
点一下「停止」不再等于这一局白跑:
| 机制 | 行为 |
|---|---|
| 定期 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 并抛出 |
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) 复现前端的停止路径。
测试
.venv/bin/python -m pytest tests/test_training_ui.py tests/test_training_ui_js.py -q
tests/test_training_ui.py:配置契约同源、预设可直接提交、参数校验、SB3 表格 刻度打点、游标分页、describe_process进度/ETA、示例快照与真实快照同键、 以及用桩训练脚本真起子进程的启动/轮询/停止/退出码全链路。tests/js/training_ui_harness.cjs(经tests/test_training_ui_js.py调起): DOM 桩加载发布的charts.js/app.js原文,用服务端生成的真实快照驱动渲染, 断言表单/指标卡/曲线/日志/逐回合表都拿到数据,覆盖增量游标三条分支与classList、HTML 转义、极端数值等边界。
已知取舍
- 进度点保留上限 5000(服务端 deque 与前端本地缓存同值),超长训练只保留最近一段。
- 表格解析依赖 SB3 的段名(
rollout//time//train//system/);SB3 若改表格 格式,退化路径是「只显示 progress JSON」,不会导致界面崩溃。 - 收敛判读是启发式;正式结论仍需用
scripts/benchmark_hrl.py的配对评测与 学习曲线人工复核。