Merge branch 'q3-bayes-no-ml' of git.simba-robotics.com:HelixCopex/mcm-26-b into q3-bayes-no-ml
This commit is contained in:
@@ -194,7 +194,6 @@ AI 的适合用法是把这些模块逐一变成可测试程序、生成边界
|
||||
| P4 全向闭环 | 49点覆盖记录、定位队列、清除、停止、日志统计 | 10/16源、边界源和错误响应案例;官方演练全清并正常退出 | 自建闭环已完成:`planner.OmnidirectionalLoop`(10/16/0 源、近边界源、预算耗尽、故障注入测试通过);官方演练全清待团队执行 |
|
||||
| P5 定向扩展 | no_signal联合约束、覆盖发现、有限清除后备与预算 | 朝外边界源不漏;真源不误删;格数与退出预算检查 | 已实现:`planner.DirectionalExtension`(49点覆盖对定向源成立、保证接收盘内 no_signal 记为观测到定向、对跖+环探梯子、`fallback_clear_cells` 有限圆盘覆盖后备清除带格数/预算双硬顶);`tests/test_loop_q4.py` 全过;官方混合源演练待执行 |
|
||||
| P6 加速消融 | 共享测点、自适应覆盖、目标排序、提前清除 | 配对案例全清不退化,时间/请求数改善可复算 | 可删减;闭环基线数据已可用于消融对照 |
|
||||
| P6b RL 选点 | 问题3/4 测量点选择的 PPO+CUDA 训练环境与闭环接入 | 自建全清;与手写策略配对消融有可复算报告 | 已完成:`scripts/train_rl.py`/`benchmark_rl.py`,模型 `output/rl/q{3,4}_ppo.zip`,说明 `docs/algo-rl.md` |
|
||||
| P7 正式与论文 | 固定版本、论文表1、日志原名、上传核查 | 问题3/4各3份正式记录与程序版本对应 | 团队后续安排;本任务不执行正式测试 |
|
||||
|
||||
按2026年9月10日起的剩余时间,建议10日补齐P1并衔接P3,11日完成P4自建边界案例与全向演练,12日完成P5及混合源演练;未达验收就删减P6并优先修正闭环。演练不限3次,正式机会为问题3和4各3次。团队在版本稳定后自行安排正式记录,留出失败恢复和论文整理时间。附件建议9月13日15:30前完成正式测试,17:30后不能启动新的演练或正式测试;已进入上传队列的日志仍会继续上传。此处引用所给附件安排,运行前核对模拟器界面公告;这份排期不改变当前禁止使用正式测试的限制。
|
||||
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
# 问题3/4 强化学习选点(PPO + CUDA)
|
||||
|
||||
## 0. 摘要
|
||||
|
||||
**2026-09-11审核更新:**本文件下方的训练成绩与BC准确率是第一版历史记录。
|
||||
复核发现训练/部署特征错位、BC激活函数不一致以及评测只检查completed等问题。
|
||||
当前修复、Maskable PPO新模型和独立配对验证见
|
||||
[强化学习适用性审核与改进](强化学习适用性审核与改进-20260911.md)。
|
||||
新模型未替换原默认检查点;通过`--rl-model`显式加载。
|
||||
|
||||
本项目在保持 P0–P5 保守性与可解释证明不变的前提下,为问题3(全向源)与
|
||||
问题4(定向源)增加了一个可选的**强化学习测量点选择策略**:
|
||||
|
||||
- 把“下一检测点去哪”建模为离散动作的局部决策问题;401维特征是压缩观测,
|
||||
不能认定为完整马尔可夫状态,尤其第四问仍有未知朝向和被省略的历史约束;
|
||||
- 状态由逐频道信念的几何特征 + 60 个候选检测点的几何特征组成(401 维);
|
||||
- 动作为 60 选 1(保证接收盘环候选、对称交会候选、定向遮挡环探候选);
|
||||
- 使用 Stable-Baselines3 的 PPO 训练,MLP 策略与价值网络在 **CUDA GPU**
|
||||
(本机 RTX 4060 Laptop, CUDA 12.8 / torch 2.11.0+cu128)上更新;
|
||||
- 训练环境基于自建评测机 `referee`(内存回环,与真实 HTTP 协议同边界);
|
||||
- 运行闭环仅替换既有选点排序器,覆盖证明、清除证书、定向后备清除全部保留。
|
||||
|
||||
历史结果(第一版自建配对消融,5 个种子):Q3 用 12 万步随机初始化 PPO(与 `bayes`
|
||||
基本持平,+1.18%);Q4 用**专家行为克隆初始化 + PPO 微调**,混合源平均
|
||||
虚拟时间相对 `directional` 基线降低 **7.16%**(14250.1s → 13230.2s)。
|
||||
Q3 的 BC 初始化在消融中反而退化,因此保留随机初始化模型;Q4 的 BC 初始化
|
||||
是当时五场比较中均值最好的版本。官方演练验证见 §5:两次 Q4 演练(3 定向混合源、10 定向全定向
|
||||
场景)与一次 Q3 演练均全清,且通过离线审计。
|
||||
|
||||
## 1. 环境搭建(CUDA)
|
||||
|
||||
```powershell
|
||||
# 安装可选依赖组 rl(PyTorch CUDA 12.8 + stable-baselines3 + gymnasium)
|
||||
uv sync --group rl --group dev
|
||||
|
||||
# 验证 CUDA
|
||||
uv run python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"
|
||||
```
|
||||
|
||||
`pyproject.toml` 中 `[tool.uv.sources] torch = { index = "pytorch-cu128" }`
|
||||
与 `[[tool.uv.index]]` 指向 PyTorch 官方 CUDA 12.8 轮子;下载约 2.6 GB,
|
||||
首次同步建议 `$env:UV_HTTP_TIMEOUT='300'`。默认 PyPI 的 Windows torch 轮子
|
||||
为 `+cpu`,可以使用CPU训练;本轮修订模型即采用CPU,CUDA并非训练前提。
|
||||
|
||||
## 2. 第一版局部决策环境
|
||||
|
||||
训练环境:`src/mcm26b/rl/env.py::LocalizationEnv`。
|
||||
|
||||
| 项 | 定义 |
|
||||
| --- | --- |
|
||||
| 初始状态 | 自建模拟器随机布设一个源(Q3 全向;Q4 定向比例可调,训练默认 1.0),机器狗完成一次保证得到 `direction` 的首测 |
|
||||
| 观测 | 401 维:21 个全局特征 + 20 维频道 one-hot + 60×6 维候选点特征(见 `features.py`) |
|
||||
| 动作 | `Discrete(60)`:0=候选集 MEC 圆心,1..36=保证接收盘 3 环×12 方向,37..51=对称交会 15 点,52..59=定向遮挡环探 8 点 |
|
||||
| 转移 | 按动作位置调用 `/measure`,用 `BeliefState` 保守更新候选集;Q4 保证接收盘内 `no_signal` 记为朝向遮挡并进入 `blocked` 序列;证书达标后自动 `/clear`;遮挡梯子用尽后模拟 20m 网格后备清除 |
|
||||
| 奖励 | `-Δvirtual_time/100`(移动+检测+换频+清除真实耗时)、候选半径收缩加成、首次遮挡证据 +2、重复测点 -0.75、清除成功 +10、失败/截断负奖励 |
|
||||
|
||||
约束说明:候选点全部来自既有几何生成器(`planner.candidate_points`、
|
||||
`planner.safe_disk_radius`、定向环探梯子),RL 不生成新的空间候选;它只做
|
||||
排序。清除仍须通过几何证书;但“保证收到方向”只适用于全向源且实际点位于
|
||||
距离保证盘内。第一版的52—59号环探点可能在盘外,第四问的定向遮挡也不受
|
||||
距离盘保证。v2对第三问加入盘外动作屏蔽,第四问保留探索分支。
|
||||
|
||||
## 3. 训练与自建模拟器逻辑利用
|
||||
|
||||
```powershell
|
||||
# 1) 用自建模拟器上的手写专家生成 (观测, 动作) 并行为克隆
|
||||
uv run python scripts/pretrain_rl_expert.py --problem 3 --episodes 3000
|
||||
uv run python scripts/pretrain_rl_expert.py --problem 4 --episodes 3000
|
||||
|
||||
# 2) 从当前v2 BC初始化继续普通PPO(算法类型需显式匹配)
|
||||
uv run python scripts/train_rl.py --problem 4 --algorithm ppo --timesteps 40000 \
|
||||
--init output/rl/q4_ppo_bc_init.zip
|
||||
|
||||
# Q3 保留随机初始化长训练版本:
|
||||
uv run python scripts/train_rl.py --problem 3 --timesteps 120000
|
||||
```
|
||||
|
||||
专家逻辑与自建模拟器同源:Q3 用 worst-posterior 选点器,Q4 用对跖/环探
|
||||
遮挡梯子;`pretrain_rl_expert.py` 在 `LocalizationEnv` 上记录每个状态的
|
||||
专家动作,训练与 PPO policy_net/action_net 同构的 MLP,再把权重拷入 PPO
|
||||
作为初始化。产物位于 `output/rl/bc/` 与 `output/rl/q{3,4}_ppo_bc_init.zip`。
|
||||
|
||||
实测:Q3 专家数据 5141 条、BC 验证准确率 38.9%,微调后自建消融退化
|
||||
(保留 12 万步随机初始化模型);Q4 专家数据 14181 条、BC 验证准确率
|
||||
65.3%,微调后自建消融从 −3.84% 提升到 **−7.16%**。但审核确认BC与PPO的
|
||||
激活函数不一致,这些数字不能证明成功迁移了专家策略,也不足以判断全向分支已最优。
|
||||
|
||||
输出:`output/rl/q3_ppo.zip`、`output/rl/q4_ppo.zip` 及同名 JSON 报告;
|
||||
训练脚本先打印 `train_start`(含 device/gpu 字段),再执行 PPO 并在独立
|
||||
种子场景上确定性评估成功率、平均步数与平均回报。Q4 4 万步微调约 236s;
|
||||
Q3 12 万步约 625s(RTX 4060 Laptop,2 个 CPU 环境)。
|
||||
|
||||
## 4. 运行闭环接入
|
||||
|
||||
- Q3:`uv run mcm26b run --robot-id 2026001 --self-test --strategy rl`
|
||||
`RLBayesLoop` 继承 `BayesLoop`(七点覆盖 + 网格粒子滤波 + 滚动 2-opt),
|
||||
仅 `_select_point` 改由 PPO 输出候选编号。
|
||||
- Q4:`uv run mcm26b run --robot-id 2026001 --self-test --problem 4 --q4-strategy rl`
|
||||
`RLDirectionalLoop` 继承 `DirectionalExtension`(49 点覆盖 + 遮挡梯子 +
|
||||
20m 有限圆盘后备清除),仅 `_next_probe` 改由 PPO 输出。
|
||||
- 模型加载时校验观测 401 维 / 动作 60 维,失配直接拒绝运行。
|
||||
- 默认运行路径不导入 torch/referee;只有显式选择 `rl` 才惰性导入,项目
|
||||
运行时依赖仍保持 `requests` 一项。
|
||||
|
||||
## 5. 消融基准
|
||||
|
||||
```powershell
|
||||
uv run python scripts/benchmark_rl.py --seeds 5
|
||||
```
|
||||
|
||||
同种子同场景配对比较,结果写入 `output/rl/benchmark.json`。以下为第一版历史结果:
|
||||
|
||||
| 场景 | 基线 | 平均虚拟时间(基线 / RL) | 相对差 | 全清 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Q3(10 源全向 ×5 种子,scratch PPO) | `bayes` | 3088.3s / 3124.8s | +1.18% | 5/5 |
|
||||
| Q4(10 源、3 定向 ×5 种子,BC+PPO) | `directional` | 14250.1s / 13230.2s | −7.16% | 5/5 |
|
||||
|
||||
训练局评估:Q3 单源 40 局成功率 100%、平均 3.55 步;Q4 定向单源 40 局
|
||||
成功率 90%、平均 4.45 步;其余单源训练局未完成,不能由此宣称已兜底成功。随机策略对照:
|
||||
Q3 平均回报 13.28 vs PPO 17.71;Q4 平均回报 8.72 vs BC+PPO 12.62。
|
||||
|
||||
### 5.1 官方模拟器演练验证(仅演练,未触碰正式测试)
|
||||
|
||||
使用目录内官方 `jammers-simulator-full` 的**演练测试**(不限次数),通过
|
||||
官方 HTTP 接口运行同一闭环;官方产物按原名归档并离线审计:
|
||||
|
||||
| 问题 | 案例 | 场景真值(演练结束后可见) | 结果 | 虚拟时间 | 请求数 | 审计 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| Q3 | `F38Y-9Q9J-B82V-CB9Z` | 13 全向 | 13/13 全清 | 3538.8s | 124 | ✅ |
|
||||
| Q4 | `X43X-VMRF-8GRZ-3SYA` | 7 全向 + 3 定向 | 10/10 全清 | 14003.1s | 619 | ✅ |
|
||||
| Q4(BC) | `BXSA-TFQ3-2DJ6-DHH7` | **10 定向** | 10/10 全清 | 14562.8s | 691 | ✅ |
|
||||
|
||||
前两次为随机初始化模型,第三次为 BC 初始化微调模型。两个 Q4 官方案例的
|
||||
源构成不同(3 定向 vs 10 定向),**虚拟时间不可直接相减比较**;可确认的
|
||||
是 RL 在官方混合源与全定向源演练中都能完成全部清除,且
|
||||
`all_timestamps_match_microsecond_recomputation`、官方 `.jlog` 的
|
||||
SHA-256 校验全部通过。归档目录:`output/rl/official-practice/`。
|
||||
|
||||
### 5.2 自建 referee 演练验证
|
||||
|
||||
`uv run mcm26b run --local-judge` 走自建 referee 的 `q3_practice` /
|
||||
`q4_practice` 模块(与官方相同的演练生命周期,真值在结束后可见):
|
||||
|
||||
| 问题 | 案例 | 场景 | 结果 | 虚拟时间 | 归档 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Q3 | `FKUY-K8KJ-RXRL-FMBM` | 10 全向 | 10/10 全清 | 3317.6s | `output/rl/local-practice/q3/20260911-rl-01` |
|
||||
| Q4 | `3J8T-L9GY-HRGL-LYVZ` | 7 全向 + 3 定向 | 10/10 全清 | 11942.8s | `output/rl/local-practice/q4/20260911-rl-01` |
|
||||
| Q4(BC) | `VM2G-UVC6-4N7R-TVGW` | 7 全向 + 3 定向 | 10/10 全清 | 12520.8s | `output/rl/local-practice/q4/20260911-rl-bc-01` |
|
||||
|
||||
不同案例仍不可直接比较;成对可比的提升证据是 §5 的 5 种子同场景消融。
|
||||
|
||||
## 6. 结论与边界
|
||||
|
||||
以下是第一版结论;BC权重拷贝缺陷使“同构初始化”的解释不成立,性能判断
|
||||
应以新审核报告的同场景验证及不确定性为准,而不能由BC分类准确率推出。
|
||||
|
||||
- RL 已接入问题3/4 完整闭环,并在自建 referee 演练与官方模拟器演练中
|
||||
全清验证;这是方法验证,不是对正式成绩的承诺。
|
||||
- 第一版Q4的BC初始化后均值变化为−3.84% → −7.16%,但初始化存在激活函数
|
||||
缺陷,样本数也不足以支持“明确收益”的归因;当前判断见新审核报告。
|
||||
- 训练环境使用自建场景假设(源均匀分布、逐点固定误差场、定向源朝向随机);
|
||||
官方隐藏分布未必相同。正式测试仍应默认使用经过演练验证的手写策略;
|
||||
`--strategy rl` / `--q4-strategy rl` 适合消融、方法对比与后续调参。
|
||||
- 官方演练仅使用“演练测试”按钮与开放接口,从未点击“正式测试”;官方
|
||||
`.jlog/.result.json` 保持原样,未解密。
|
||||
- 保守性不变式:RL 只改变选点排序,不改变覆盖账本、候选集更新、清除证书
|
||||
与后备清除的数学证明;所有策略均以 `/exit` 与完整日志收尾。
|
||||
@@ -1,117 +0,0 @@
|
||||
# HRL P0+P1 执行报告(本地自建评测机)
|
||||
|
||||
日期:2026-09-11(本机工作区)
|
||||
范围:按用户确认,仅完成 P0 正确性回归 + P1 最小可训练/可评测闭环;
|
||||
P2 的 BC、完整课程长训、Optuna、正式消融与正式测试表留待下一阶段。
|
||||
|
||||
## 1. 本轮代码交付
|
||||
|
||||
| 文件 | 内容 |
|
||||
| --- | --- |
|
||||
| `scripts/benchmark_hrl.py` | 同一 manifest 真值场景复用;`hrl`/`hrl_heuristic`/`bayes`/`directional` 配对评测;写 `results.json`、`aggregate.json/csv`、每局 `summary.json` 与 `session.jsonl` |
|
||||
| `scripts/train_hrl_high.py` | 高层 MaskablePPO 训练;`--problem/--stage/--envs/--vec-env/--device/--config-profile/--reward-profile/--model/--resume/--init/--eval-manifest`;模型元数据固化 feature/action/reward 版本;墙钟硬上限 |
|
||||
| `src/mcm26b/hrl/constants.py` | 新增 `ACTION_LAYOUT_VERSION=1`、`REWARD_VERSION=1` |
|
||||
| `src/mcm26b/hrl/policy.py` | 高层模型严格校验元数据;低层模型保持独立基础校验 |
|
||||
| `src/mcm26b/cli.py` | 新增 `--strategy hrl`、`--q4-strategy hrl`、`--hrl-model`、`--hrl-low-model`、`--hrl-device`、`--trace-low` |
|
||||
| `src/mcm26b/hrl/hierarchical_env.py` | 新增 `HRLReward.time_only()`:P0.4 建议的纯时间目标,去掉逐源清除/发现/熵减塑形 |
|
||||
| `project/` | 严格目录树薄兼容层,转发真实实现,不复制算法 |
|
||||
| `tests/test_hrl_p1.py` | manifest 往返、真值隔离、纯时间奖励、project re-export 回归 |
|
||||
|
||||
## 2. 全量回归
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m pytest -q --tb=short
|
||||
# 275 passed in 124.57s
|
||||
```
|
||||
|
||||
## 3. P1 smoke 训练(本机 Apple MPS)
|
||||
|
||||
```bash
|
||||
.venv/bin/python scripts/train_hrl_high.py \
|
||||
--problem 3 --stage 1 --timesteps 2048 --seed 320001 \
|
||||
--envs 1 --vec-env dummy --device mps \
|
||||
--config-profile production --reward-profile time \
|
||||
--model output/hrl/p1-smoke-time/q3.zip \
|
||||
--eval-episodes 2 --rollout-steps 128 --batch-size 128 \
|
||||
--n-epochs 3 --max-wall-s 800 --verbose 0
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
- `actual_timesteps=2048`,墙钟 `260.069 s`;
|
||||
- 保存后确定性评估 2/2 成功,`accounted=20`,无异常;
|
||||
- 模型元数据含 `feature_version=4`、`action_layout_version=1`、
|
||||
`reward_version=1`、`reward_profile=time`、`model_sha256`。
|
||||
|
||||
模型重载已由以下两条路径验证;`--resume` 的 `--timesteps` 是增量步数
|
||||
(SB3 `reset_num_timesteps=False` 语义),元数据写 `timesteps_semantics="additional"`:
|
||||
|
||||
```bash
|
||||
.venv/bin/python -m mcm26b.cli run \
|
||||
--robot-id 2026001 --self-test --strategy hrl \
|
||||
--hrl-model output/hrl/p1-smoke-time/q3.zip --hrl-device mps \
|
||||
--sources 3 --seed 1 --summary-steps 5
|
||||
|
||||
python scripts/benchmark_hrl.py --variants hrl \
|
||||
--hrl-model output/hrl/p1-smoke-time/q3.zip --hrl-device mps \
|
||||
--problem 3 --scenes 1 --sources 3 --output output/hrl/p1-bench-hrl
|
||||
```
|
||||
|
||||
## 4. 多轮本地配对测试
|
||||
|
||||
### Q3:3 个独立场景 × 3 个变体
|
||||
|
||||
```bash
|
||||
python scripts/benchmark_hrl.py --variants hrl,hrl_heuristic,bayes \
|
||||
--hrl-model output/hrl/p1-smoke-time/q3.zip --hrl-device mps \
|
||||
--problem 3 --scenes 3 --sources 3 --start-seed 91000 \
|
||||
--output output/hrl/p1-multiround-q3
|
||||
```
|
||||
|
||||
| variant | episodes | valid_rate | cleared_mean | virtual_time_mean_s | virtual_time_std_s | program_run_time_mean_s |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| bayes | 3 | 1.0 | 3.0 | 2518.371 | 74.183 | 0.748 |
|
||||
| hrl_heuristic | 3 | 1.0 | 3.0 | 2518.371 | 74.183 | 0.883 |
|
||||
| hrl | 3 | 1.0 | 3.0 | 3731.380 | 120.536 | 1.853 |
|
||||
|
||||
结论:P1 模型只在 stage 1 单源课程上训练 2048 步,可直接重载并在 3 源场景保持 3/3 有效全清;
|
||||
但用时明显未优于 bayes/heuristic,符合本轮不做正式性能声明的边界。
|
||||
|
||||
### Q4:3 个独立混合源场景 × 2 个变体
|
||||
|
||||
```bash
|
||||
python scripts/benchmark_hrl.py --variants hrl_heuristic,directional \
|
||||
--problem 4 --scenes 3 --sources 3 --directional 1 --start-seed 92000 \
|
||||
--output output/hrl/p1-multiround-q4 --force
|
||||
```
|
||||
|
||||
| variant | episodes | valid_rate | cleared_mean | virtual_time_mean_s | virtual_time_std_s | program_run_time_mean_s |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| directional | 3 | 1.0 | 3.0 | 13442.997 | 263.902 | 1.063 |
|
||||
| hrl_heuristic | 3 | 1.0 | 3.0 | 12292.198 | 344.827 | 6.284 |
|
||||
|
||||
说明:Q4 高层 PPO 尚未训练;本轮只验证 Q4 的 `hrl_heuristic` 与 `directional` 闭环、真值隔离与计时审计。
|
||||
|
||||
## 5. P0 状态
|
||||
|
||||
- `--resume` 重载:16 步起步模型续训 32 增量步后 `actual_timesteps=48`,
|
||||
校验 `feature_version/action_layout_version/reward_version` 后保存;
|
||||
|
||||
- 槽位唯一性:`test_hrl.py` 覆盖 channel 17–20 与 1–4 同时 clear/locate 不丢;
|
||||
- 清除事件一次且仅一次:fallback 成功写 `_step_clears` 已有回归;
|
||||
- 候选快照:`test_opportunity_measure_precedes_candidate_snapshot` 已覆盖;
|
||||
- 奖励对齐:新增 `HRLReward.time_only()`,训练脚本默认 `--reward-profile time`;
|
||||
- 终止/安全网:现有 `HierarchicalEnv` 回合测试继续通过;
|
||||
- 训练/部署配置:`--config-profile production` 使用生产配置,`coarse-pretrain`
|
||||
显式映射到 fast 评分配置并写入模型元数据;
|
||||
- 依赖守卫:全量测试 275 项在 dev+rl 环境通过。
|
||||
|
||||
## 6. 未完成 / P2 待办
|
||||
|
||||
- `scripts/pretrain_hrl_expert.py` 已建可 `--help` 的接口占位,专家轨迹 + BC 功能待实现;
|
||||
- `scripts/ablate_hrl.py` 已建接口占位,无信息增益/无掩码/无课程/无粒子统计消融待实现;
|
||||
- `scripts/tune_hrl.py` 已建接口占位,Optuna TPE 搜索待实现;
|
||||
- Q3→Q4 迁移训练与配对实验;
|
||||
- 完整 10–16 源课程训练、多个训练种子、冻结测试清单;
|
||||
- 官方三次正式测试表:必须有真实官方案例码与导出日志后才能填写;
|
||||
本地 `referee` 结果只标 `local_self_test`。
|
||||
@@ -1,267 +0,0 @@
|
||||
# HRL 续作执行说明:交给 Sol 的工程任务书
|
||||
|
||||
适用日期:2026-09-11。工作目录:本仓库根目录。先检查当前代码与本轮测试结果,再按 P0 → P1 → P2 → P3 顺序执行。
|
||||
|
||||
目标是交付可运行、可审计的整场调度学习系统;训练收益由完整场景验证决定,不把训练脚本存在或局部奖励上升视为完成。
|
||||
本说明更新了原粘贴 Prompt 与 `.zcode/plans/plan-sess_cf390556-4ef3-4ce6-b91e-782aee1718f9.md` 中不符合现有协议或证据的部分。
|
||||
|
||||
## 1. 现在有什么,缺什么
|
||||
|
||||
| 组件 | 当前状态 | Sol 的操作 |
|
||||
| --- | --- | --- |
|
||||
| `protocol/`、`referee/`、`harness.py` | 已有客户端、独立评测机、内存回环与计时审计 | 复用,不另建模拟器客户端 |
|
||||
| `belief/`、`particles.py`、`planner/` | 已有保守候选、网格幸存集、覆盖证明、证书清除与后备清除 | 保持证明前提;改调度时复用 |
|
||||
| `rl/`、`train_rl.py`、现有 RL 检查点 | 已有单源选点 PPO/MaskablePPO 与完整对照 | 作为局部策略和对照,勿命名为端到端 PPO |
|
||||
| `hrl/constants.py`、`scheduler.py`、`planner/hrl_loop.py` | 已有高层候选、特征、执行与闭环骨架;存在本轮需修复的 P0 项 | 先完成第 4 节门槛 |
|
||||
| `hrl/hierarchical_env.py`、`curriculum.py` | 已有训练环境和课程;奖励、终止、训练配置仍需审核 | 在长训前修正,覆盖独立环境/部署一致性 |
|
||||
| `hrl/low_level_env.py`、`executor.py`、`train_hrl_low.py` | 有八方向到达实验;训练入口尚有掩码等缺陷 | 作为可选离线实验,不能卡住主线 |
|
||||
| `tests/test_hrl.py` | 有接口和启发式 episode 测试 | 增补行为验证,不能只检验公式与实现一致 |
|
||||
| `train_hrl_high.py`、`pretrain_hrl_expert.py` | 尚未实现 | P1 编写最小可训练闭环,P2 再加入 BC |
|
||||
| `benchmark_hrl.py`、`ablate_hrl.py`、`tune_hrl.py` | 尚未实现 | 先评测,再训练,再做消融,最后受限调参 |
|
||||
| CLI 的 `hrl` 策略和 `--hrl-model` | 当前入口尚未接入 | P1 接入并检验 `--help` 与离线闭环 |
|
||||
| `output/hrl/` 的高层模型、曲线、评测表 | 本次审阅未发现这些产物 | 不声称已有 HRL 模型或训练成绩 |
|
||||
|
||||
现有证据:修订单步 RL 的 Q3 独立验证均值比 `bayes` 低 0.69%,区间跨 0;Q4 比 `directional` 低 14.38%,比旧 PPO 低 2.26%,后一比较区间跨 0。
|
||||
这些来自固定模型、本地 18 场/题的验证;新 Q4 的后备清除次数更多,边界压力性能有退化。详情见 `docs/强化学习适用性审核与改进-20260911.md`。
|
||||
没有证据证明 HRL 已优于上述基线,没有证据支持“Q4 迁移节约 80% 训练成本”或“全部合法场景小于 4000 秒”。
|
||||
|
||||
## 2. 必须纠正的原 Prompt 假设
|
||||
|
||||
1. `/measure` 与 `/clear` 提交终点,移动按两次实际动作位置间的直线距离除以 5 计费;没有朝向、转弯或八方向移动协议。
|
||||
2. 八方向策略仅在本地推演再强制补上终点,无法影响模拟器实际路径;不能把这段推演称为比赛收益来源。
|
||||
3. `/clear` 不切换接收机频道,目标频道与当前接收机频道应分别记录;`near` 是测量结果,清除仍需独立请求。
|
||||
4. 接收半径逐源未知,动作掩码只能使用观测与保守界,不能读真实半径、源位置、类型或朝向。
|
||||
5. Q3 的 `no_signal` 只能按已证明的保守半径规则删格;Q4 的 `no_signal` 可能由方向遮挡造成,不能直接删空间候选。
|
||||
6. Q4 联合位置/半径/朝向粒子是后续研究选项;普通二维粒子加“角度门控”并不会自动得到正确联合后验。
|
||||
7. 固定地点误差固定;同地重测不是新独立样本。保留读数舍入裕量,不收紧真实的 ±1° 误差界来追求表面收敛。
|
||||
8. 源数最多 16 不等于只有 16 个频道;所有按频道编号索引的动作必须覆盖 1–20。
|
||||
9. 连续无信号次数不能作为全场结束条件;覆盖参数不能随意调到破坏已证明的发现覆盖。
|
||||
10. “无信息增益”“无动作掩码”“无粒子滤波”必须精确定义;不能通过取消清除证书或真源保留约束制造消融。
|
||||
11. 原 Prompt 的 6 个状态与实现里的状态枚举不一致;以 `ChannelStatus` 为准,额外 TRACKING/LOCATED 只能定义为派生特征。
|
||||
12. 协议文档 §6 仍写持续搜索默认开启;当前 `config.py` 为 `persistent_search=False`,CLI 用 `--persistent` 显式开启。先同步文档,不恢复过时默认值。
|
||||
|
||||
## 3. 推荐架构与工程边界
|
||||
|
||||
采用“高层学习调度 + 低层局部选点 + 协议执行”的分层架构。高层学习先处理哪个频道、继续覆盖还是定位、何时使用后备清除。
|
||||
低层默认调用成熟几何选点;可切换现有 `PointSelectionPolicy` 学习局部检测点,形成第二个学习层。直线移动与设备动作继续由协议客户端执行。
|
||||
Q3 首版低层用 `bayes` 几何选点;Q4 先保留 `directional` 的遮挡处理,待接线一致性通过后对比旧/新局部 PPO。
|
||||
|
||||
```text
|
||||
SimulatorClient.state + belief + particles + coverage ledger
|
||||
↓ 同一份候选快照和动作掩码
|
||||
高层 MaskablePPO:选择频道/覆盖站/清除任务
|
||||
↓
|
||||
局部选点:geometry | 已有 v2 PointSelectionPolicy
|
||||
↓
|
||||
证书/预算/覆盖约束 + 实际 measure/clear 宏动作
|
||||
↓
|
||||
accepted 请求与响应 → 时间、信念、清除与覆盖账本
|
||||
```
|
||||
|
||||
八方向 PPO 保留为 `low_level_env.py` 的离线附录实验;生产默认无需加载。若论文要求字面双 PPO,用高层调度 PPO + 局部选点 PPO 对应真正可影响成绩的两个层次。
|
||||
现有高层一个动作可以包含整站逐频道扫描或多次后备清除,因此是变长宏动作。记录每个宏动作的实际持续时间、请求数和中断原因。
|
||||
先采用 `gamma=1` 与实际时间成本,避免把“每宏动作折扣一次”误当成“每秒折扣一次”;只有明确改变目标才引入按持续时间折扣。
|
||||
第一版保留 120 槽位 MLP,不同时重写注意力网络或连续动作;需要多候选/频道时另升动作版本并独立比较。
|
||||
|
||||
建议新增的共享接口如下;这些是待实现契约,避免训练和 CLI 分别拼接策略。
|
||||
|
||||
```python
|
||||
@dataclass(frozen=True)
|
||||
class HRLPolicyConfig:
|
||||
local_selector: str = "geometry" # geometry | rl
|
||||
local_model_path: str | None = None
|
||||
|
||||
def make_hrl_loop(client, config, *, problem: int,
|
||||
high_policy=None, policy_config=HRLPolicyConfig()) -> HRLLoop: ...
|
||||
|
||||
def validate_episode(summary: dict, events: list[dict],
|
||||
truth_channels: set[int]) -> dict: ... # 仅评测模块
|
||||
|
||||
def run_episode(scene: dict, variant: dict, *, output_dir: Path) -> dict: ...
|
||||
```
|
||||
|
||||
## 4. P0:修复正确性,未过门槛不得长训
|
||||
|
||||
以下表中的状态以本轮提交和测试报告为准;Sol 接手时先查看 diff,已完成的项只做回归,不重复实现。
|
||||
|
||||
| 项目 | 精确入口与问题 | 目标与验收 |
|
||||
| --- | --- | --- |
|
||||
| P0.1 槽位冲突 | `constants.py`;`scheduler.build_candidates/heuristic_action`:clear 频道 17–20 与 locate 频道 1–4 重叠 | 本轮优先修复;每类按 20 频道分配,构造同时就绪冲突场景验证任务均保留 |
|
||||
| P0.2 清除事件遗漏 | `scheduler._hrl_execute_fallback` 成功未写 `_step_clears` | 本轮优先修复;证书/后备/宏动作内部清除均由相同成功事件口径计数,一次且仅一次 |
|
||||
| P0.3 候选过期 | `HRLLoop._coverage_phase` 在构建候选后 `_measure_at_stop` 可改变信念 | 本轮优先修复;选动作前候选/特征/掩码属于同一个状态快照 |
|
||||
| P0.4 奖励对齐 | `HierarchicalEnv.step/_reward_from/HRLReward` | 改默认奖励,成功/异常/安全网/截断均计入已经发生的真实时间 |
|
||||
| P0.5 回合终止 | `HierarchicalEnv.step/reset/close` | 实际完成、失败终止与采样截断分开;收尾不丢 /exit,不把零观测用于有效截断 bootstrap |
|
||||
| P0.6 训练部署配置 | `curriculum.training_config` 默认网格 29.3m,部署 14.65m | 首个可比较模型使用生产几何配置;粗网格只能显式快速预训练,后接完整保真微调与评测 |
|
||||
| P0.7 无进展循环 | `scheduler` 的 locate/probe/fallback 掩码及预算检查 | 去重、已清除/无效证书、后备预算不足在动作前处理;合法动作不能无限零成本自循环 |
|
||||
| P0.8 测试与依赖 | `tests/test_hrl.py` 导入守卫、`policy.py/scheduler.py` 顶层 numpy | 不安装 RL 组时基础 CLI 可导入;完整 RL 环境运行测试时不得意外全部 skip |
|
||||
|
||||
建议槽位布局为 clear 0–19、locate 20–39、station 40–88、fallback 89–108、保留 109–119;所有槽位互斥且小于 120。
|
||||
槽位含义改变必须升 `FEATURE_VERSION`,即使仍是 1248 维/120 动作,也必须拒绝旧语义检查点。
|
||||
模型元数据应要求 `level/problem/algorithm/feature_version/action_layout_version/reward_version`,并记录模型 SHA、配置、依赖版本、代码版本。
|
||||
缺字段不能默认为兼容;Q3 权重初始化 Q4 必须走显式训练迁移参数,部署加载仍严格检查题号。
|
||||
|
||||
默认奖励先用:`r = -实际本步虚拟时间增量 / 100`;证明完成时加固定完成奖,未完成的真实终止加失败罚。
|
||||
失败惩罚按冻结的 episode 最大成本界设置并记录,不随意用不足以覆盖数千秒的 `-100`。
|
||||
相同全清场景里逐源常数奖可能只是常数偏移,但不同终止路径、事件漏记和失败场景会改变激励;故首版去掉新发现/熵减/每源清除附加奖。
|
||||
如需塑形,另做势函数差 `gamma * Phi(next_state) - Phi(state)`,真正终止态统一势函数值,并检验总和望远镜消去。
|
||||
如果优化题目平均时间,用整场时间作为训练目标时明确这是所选代理;不同源数的跨场景均值与 `T/N` 均值不等价,不能暗称完全一致。
|
||||
人工步数上限若仅为采样截断,返回可用的终末观测并允许 bootstrap;若定义为任务失败期限,应作为真实终止计罚。按该语义设计测试。
|
||||
|
||||
P0 验收至少覆盖以下行为,不只验证 reward 按代码相加:
|
||||
|
||||
- 20 个频道的各类槽位唯一;频道 17–20 与 1–4 同时有 clear/locate 不丢任务。
|
||||
- 同一观测轨迹分别构建独立训练环境与部署闭环,比较候选、mask、特征、下一实际请求及状态。
|
||||
- 固定动作序列跨成功、失败、后备与安全网路径:reward 的时间分项总和等于 accepted 日志复算时间。
|
||||
- Q4 保证盘内/盘外无信号不误删真源;正读数重置遮挡序列;无效证书不能执行清除。
|
||||
- 10/16 源、频道含 20、边界源与预算中断均检查真实源集合、缺席集合、正常退出、异常与计时。
|
||||
- 评测端真值仅进 `validate_episode`,策略构建、候选、mask、观测与模型选择均不得读取真值。
|
||||
|
||||
## 5. P1:最小完整闭环,先评测再短训
|
||||
|
||||
先实现 `scripts/benchmark_hrl.py` 与共享审计函数,使 `HRLLoop(high_policy=None)` 可以与原 `bayes/directional` 同场景对比。
|
||||
这个启发式 HRL 骨架是重要对照:若差距来自任务候选、机会测量、Q4 保底或预算语义变化,先修或如实量化,不归因给神经网络。
|
||||
每次运行将 scene、误差种子、配置、summary、完整 JSONL/accepted 事件、时间复算和模型 SHA 写入新目录;目录已存在则拒绝覆盖。
|
||||
单场异常也写结果;聚合存在任一无效局时 `all_valid=false` 且脚本非零退出,不从平均值表中静默删除失败场景。
|
||||
|
||||
随后实现 `scripts/train_hrl_high.py`,第一版提供以下参数并通过 `--help` 验证:
|
||||
|
||||
```text
|
||||
--problem {3,4} --stage {1,2,3,4} --timesteps INT --seed INT
|
||||
--envs INT --vec-env {dummy,subproc} --device {cpu,cuda,mps,auto}
|
||||
--config-profile {production,coarse-pretrain}
|
||||
--model PATH --resume PATH --eval-manifest PATH --verbose INT
|
||||
--local-selector {geometry,rl} --local-model PATH
|
||||
```
|
||||
|
||||
阶段 1 即保持 20 频道与固定观测维度,只改变布景复杂度;早期课程可减少源数,但完整训练必须覆盖 10/13/16 源与实际 ±1° 固定地点误差。
|
||||
Q4 完整课程同时覆盖少量/半数/大量定向源及近边界向外朝向;Q3→Q4 迁移与 Q4 随机初始化作为配对实验。
|
||||
禁止让策略知道课程场景真实源数或隐藏的占用频道列表;初始位置保持官方 `(0,0)`,除非任务被明确标为局部交接课程。
|
||||
先测 1/2/4 个环境的采样吞吐、内存和每次决策耗时,再选择 Dummy/Subproc;小 MLP 不假定 GPU 更快。
|
||||
Subproc 环境类内部提供 `action_masks()`;Windows/macOS 使用入口守卫及可序列化工厂。
|
||||
随机种子覆盖 Python/NumPy/Torch、PPO、每个环境、场景误差;每个 worker 使用独立 Monitor 文件。
|
||||
保存实际 `model.num_timesteps` 与请求步数,恢复时说明增量还是累计;续训加载正确算法/特征版本且不覆盖原模型。
|
||||
使用掩码评估接口,如 `MaskableEvalCallback`;同时在确定性推理中传入同源 mask。
|
||||
|
||||
CLI 加入 `--strategy hrl`、`--q4-strategy hrl`、`--hrl-model` 与局部策略选项,经 `make_hrl_loop` 统一构建;默认策略保持现状。
|
||||
显式指定缺失模型应报可操作错误,不能把启发式降级结果记成 HRL 模型成绩;启发式骨架单独命名 `hrl_heuristic`。
|
||||
|
||||
P1 门槛:启发式骨架全场审计通过;512–2048 步训练冒烟可保存、重载、确定性推理;CLI 内存回环至少 Q3/Q4 各一场通过。
|
||||
短训先用单个种子验证流程,不用这次模型的成功率作论文结论;完成后记录吞吐再定时间预算,不预设 1–2 小时足够。
|
||||
|
||||
## 6. P2:完整保真训练、独立评测与消融
|
||||
|
||||
实现 `pretrain_hrl_expert.py`:运行 `hrl_heuristic`,保存 `(episode_id, state, candidates, mask, action)`;按 episode 划分训练/验证。
|
||||
BC 与 PPO 的网络层、激活、归一化、mask 处理保持一致;复制后在真实样本上逐位比较 logits,critic 单独初始化。
|
||||
记录专家动作无法映射到候选的比率与原因;若大量回退到最近候选,先改专家映射,不用假专家标签继续训练。
|
||||
至少比较 BC+PPO 与随机初始化 PPO;BC 准确率单独报告,不能代替独立闭环用时与有效全清率。
|
||||
|
||||
冻结数据集分工:
|
||||
|
||||
| 集合 | 用途与约束 |
|
||||
| --- | --- |
|
||||
| 已有 700–702 开发场景 | 查实现错误、探索配置与性能分解 |
|
||||
| 已有 91000–91005 场景 | 历史回归;结果已经被审阅,不再宣称是本次全新盲测 |
|
||||
| 新开发/选择清单 | 固定 JSON manifest,记录 seed、源数、定向组成、error_seed;用于训练选择和调参 |
|
||||
| 新冻结最终测试清单 | 与训练、BC、调参及选择清单不重叠;模型冻结后运行,失败后修改则新开一轮测试 |
|
||||
| 压力场景 | 圆周边界、1000m 半径、向外/切向定向、高频道、固定误差与预算边界;单独汇总 |
|
||||
|
||||
同场景每个变体用相同位置、频道、半径、朝向和地点误差函数。每个模型至少跑 30 个完整场景,再按预算扩大;“30”是工程起点,不是统计充分性证明。
|
||||
最终用于稳定收益结论的主方法至少 5 个独立训练种子;场景种子与训练种子分开报告,不能用 5 个场景替代 5 次训练。
|
||||
Q3、Q4 分开统计,并按源数/定向占比分层;所有基线与候选使用相同停止、预算和计时口径。
|
||||
|
||||
| 指标 | 固定定义 |
|
||||
| --- | --- |
|
||||
| 有效全清 | 成功清除频道集合等于评测真值;缺席集合与真值不交;completed、正常 exit、无异常、计时一致 |
|
||||
| `virtual_time_s` | 实际退出前的完整虚拟时间;主竞赛口径显式 `persistent_search=False` |
|
||||
| `avg_locate_clear_time_s` | 每场 `T / 实际清除数`;有效全清主表再做逐场平均,另报源数分组 |
|
||||
| `accounted_virtual_time_s` | 首次完成证明的时间;与最终时间并列,不悄悄用它替换正式计分 |
|
||||
| 运行时间 | 每场实际墙钟、模型推理耗时分位数;本地内存回环时间不冒充官方程序运行时间 |
|
||||
| 成本分解 | 移动秒、测量秒、换频秒、成功/失败 clear 秒、后备尝试、重复测量、总请求数 |
|
||||
| 尾部与失败 | 中位数/P90/P95/最大值、有效全清率、失败明细;小样本尾分位注明估计局限 |
|
||||
|
||||
平均 `T/N` 不等于 `平均 T / 平均 N`;若另用总体总时间/总源数,明确标为按源数加权口径。
|
||||
配对效应以同场景时间差为基础,报告均值相对变化、胜出场数、95% 配对 bootstrap 区间;相同场景家族共享种子时按家族重采样。
|
||||
多训练种子结果报告种子间分散程度;必要时对训练种子和场景做分层重采样,不能用单一场景区间代表训练稳定性。
|
||||
|
||||
最小比较矩阵:`bayes/directional`、旧局部 PPO、修订局部 MaskablePPO、`hrl_heuristic`、高层 PPO+几何、后续高层 PPO+局部 PPO。
|
||||
此矩阵先回答“改调度是否有用”和“增加局部学习是否有用”;既有单步 RL 不能命名为无几何的端到端基线。
|
||||
若仍要端到端 PPO,必须单独定义输入、实际动作、安全执行边界并训练,标记预算;它不是 P1 完成条件。
|
||||
|
||||
每项消融使用相同训练步数/场景预算/种子和评测集,单次只改变一个因素:
|
||||
|
||||
- 调度:学习高层 vs `hrl_heuristic`,保持候选生成和局部选点一致。
|
||||
- 局部策略:同一高层与执行接口下,geometry vs 现有局部 PPO;报告是冻结替换还是联合重训。
|
||||
- 塑形:纯时间目标 vs 经过恒等式核验的势函数塑形;无信息增益奖励就是系数为 0。
|
||||
- 课程:相同总交互步数的课程训练 vs 从完整场景开始训练;Q4 迁移另列,不预设节约百分比。
|
||||
- 掩码:只取消重复/低效候选等策略性屏蔽;协议合法性、证书和预算硬守卫始终保留,标明实际取消范围。
|
||||
- 粒子特征:保留几何守恒内核,先将粒子统计输入和评分消融;若真的移除粒子模块,另实现保守几何替代并重新验证。
|
||||
- 八方向:只报告离线到达率/路径效率/推演耗时,以及实际协议终点相同时成绩相同的事实。
|
||||
|
||||
P2 推广门槛:所有最终与压力场景有效,Q4 相对最强已冻结基线的时间差区间上界小于 0;尾部容忍度先写入 manifest。
|
||||
建议初始尾部门槛为 P95 不退化超过 5%,这是可调整的工程标准,不是题设要求;任何调整都在重跑最终测试前完成。
|
||||
Q3 若未显示稳定收益,保留 `bayes` 默认并交付负结果。仅比手写基线快、却未优于现有 PPO,不能宣布替换默认模型。
|
||||
|
||||
## 7. P3:受限调参、论文与官方验证
|
||||
|
||||
只有 P2 主方法具备竞争力才实现并运行 `tune_hrl.py`。先搜索学习率、熵系数、网络宽度、GAE、合法候选排序参数。
|
||||
不搜索真实误差上限、光学清除半径、无信号停止次数或破坏覆盖证明的网格间距;确定性网格没有粒子重采样阈值可调。
|
||||
Optuna 只用开发/选择清单,失败配置先按有效性淘汰;预算与失败/剪枝逻辑写入结果。最终模型重新用固定配置多种子训练。
|
||||
文档、流程图与论文表明确:学习负责哪些决策、证明由哪些模块提供、本地生成分布未知是否对应官方分布。
|
||||
训练曲线、实际步数、配置、全部对照与失败明细均归档;不要只挑最好种子或去掉不利的边界场景。
|
||||
官方三次测试需要对应演练已经启动、真实案例码与官方导出日志;本地 referee 的三场不能填写成“三次正式测试”。
|
||||
不覆盖旧官方归档;官方结果与本地结果分表,不对不同官方案例直接计算改进百分比。
|
||||
|
||||
## 8. 命令:已经存在与待实现的严格区分
|
||||
|
||||
以下入口当前已经存在;依赖需先安装,命令失败要修复或如实报告,不能跳过错误声称通过:
|
||||
|
||||
```bash
|
||||
uv sync --group dev --group rl
|
||||
uv run python -m pytest -q --tb=short
|
||||
uv run python -m pytest -q tests/test_hrl.py --tb=short
|
||||
uv run mcm26b run --help
|
||||
uv run mcm26b run --robot-id 2026001 --self-test --strategy bayes
|
||||
uv run mcm26b run --robot-id 2026001 --self-test --problem 4 --q4-strategy directional
|
||||
uv run python scripts/train_rl.py --help
|
||||
uv run python scripts/train_hrl_low.py --help
|
||||
```
|
||||
|
||||
`train_hrl_low.py` 目前仅 `--help` 可用于检查入口,不把完整训练列为已可用:
|
||||
其 `MaskablePPO.learn()` 需要环境掩码,当前 `LowLevelEnv` 没有 `action_masks()`;改普通 PPO 或提供全 True 掩码后再试。
|
||||
还需修 BC 按 episode 划分、统一随机种子、每 worker 独立 Monitor 文件、评估 seed 生效、实际路径长度与成功率分开统计。
|
||||
低层到达失败不能靠 executor 最后强制补终点伪装成策略到达;环境裁剪后的实际移动长度也不能简单记为 `steps*100m`。
|
||||
|
||||
下面是 P1/P2 实现后必须支持并实测的命令契约,当前尚不能直接运行:
|
||||
|
||||
```bash
|
||||
# P1:先验证不加载模型的共享调度闭环
|
||||
uv run python scripts/benchmark_hrl.py --variants hrl_heuristic,bayes,directional --manifest output/hrl/manifests/dev.json --output output/hrl/p1-heuristic
|
||||
# P1:完整保真冒烟;模型目录必须是新目录
|
||||
uv run python scripts/train_hrl_high.py --problem 3 --stage 1 --timesteps 2048 --seed 320001 --envs 1 --vec-env dummy --device cpu --config-profile production --model output/hrl/p1-smoke/q3.zip --verbose 0
|
||||
# P1:CLI 明确加载高层模型
|
||||
uv run mcm26b run --robot-id 2026001 --self-test --strategy hrl --hrl-model output/hrl/p1-smoke/q3.zip
|
||||
# P2:正式阶段训练;步数先依据采样吞吐与学习曲线再调整
|
||||
uv run python scripts/train_hrl_high.py --problem 4 --stage 4 --timesteps 60000 --seed 420001 --envs 2 --vec-env dummy --device cpu --config-profile production --model output/hrl/p2/q4-seed420001.zip --eval-manifest output/hrl/manifests/select.json --verbose 0
|
||||
# P2:冻结模型完整对照;variants 的模型位置来自同目录的配置清单
|
||||
uv run python scripts/benchmark_hrl.py --variants hrl,hrl_heuristic,legacy,revised,baseline --manifest output/hrl/manifests/final.json --output output/hrl/p2-final
|
||||
```
|
||||
|
||||
命令使用的 manifests 也要由脚本生成、校验并归档;尚不存在的模型不能当作初始化输入。
|
||||
添加 `--full` 一键课程入口时,它应展开并记录每阶段命令/配置/检查点链,失败即停,可继续,不是忽略错误的 shell 包装。
|
||||
参考现有依赖配置:基础运行依赖仅 requests;RL 为可选组。固定环境版本并记录硬件,勿假定当前执行机就是旧报告中的 RTX 4060。
|
||||
|
||||
## 9. 可以直接粘贴给 Sol 的续作 Prompt
|
||||
|
||||
```text
|
||||
继续当前仓库的 HRL 工作。先读 docs/hrl-sol-execution.md、最新测试结果与 git diff,保留现有修改。
|
||||
已有客户端/referee/几何/PPO 都要复用;不要重写骨架,不把缺失脚本或 CLI 说成已完成。
|
||||
先完成 P0 尚未关闭的奖励、终止、完整保真配置、掩码和依赖问题;本轮已修的槽位/清除事件/候选过期只回归。
|
||||
采用高层 MaskablePPO 调度 + 几何/已有局部 PPO 选点 + 协议直线执行;八方向只作为可选离线实验。
|
||||
按 P1 先实现 benchmark_hrl 与真值隔离审计,再实现 train_hrl_high、模型元数据和 CLI,跑可重载的短训冒烟。
|
||||
P0/P1 没通过就修实现,不用加训练步数掩盖问题。通过后再开展 BC、完整保真课程、多训练种子、冻结场景配对评测。
|
||||
保持 Q3 bayes/Q4 现有默认策略;完整实验胜出前不覆盖默认模型。主指标包含有效全清、T、逐场 T/N、请求分解和尾部。
|
||||
每阶段交付具体文件、实际执行命令、测试结果、模型/日志路径与未完成项;不预设 Q4 80% 节约或 4000 秒保证。
|
||||
官方测试只有真实演练开始并获得案例码/导出日志才记正式结果,本地实验单独标注。
|
||||
```
|
||||
|
||||
接口参考:[MaskablePPO 官方文档](https://sb3-contrib.readthedocs.io/en/master/modules/ppo_mask.html),[Gymnasium 终止与时间限制](https://gymnasium.farama.org/tutorials/gymnasium_basics/handling_time_limits/)。
|
||||
Binary file not shown.
@@ -1,261 +0,0 @@
|
||||
# 训练前端 Windows 兼容性与「暂停」审计(结论 + 修复方案)
|
||||
|
||||
> 审计对象:`src/mcm26b/training_ui/`、`src/mcm26b/process_control.py`、
|
||||
> `scripts/train_hrl_high.py`。
|
||||
> 触发问题:「无法暂停」「频繁出现 os 无 killpg 等」。
|
||||
> 结论一句话:**这两个现象同源——训练前端的停止/暂停路径是纯 POSIX 设计;
|
||||
> Windows 上 `os.killpg` 根本不存在,异常又没被兜住,所以点了停止等于没点。**
|
||||
|
||||
---
|
||||
|
||||
## 0. 结论摘要
|
||||
|
||||
| # | 现象 | 根因 | 严重度 |
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | 点「停止训练」返回 500 / 无效果,训练照跑,于是「无法暂停」 | `TrainingJob.stop()` 直接调用 `os.killpg(os.getpgid(...))`;这两个符号**只存在于 POSIX**,Windows 上抛 `AttributeError`,而旧 `except` 只接 `ProcessLookupError/PermissionError` | 阻断 |
|
||||
| 2 | 控制台/日志反复出现 `module 'os' has no attribute 'killpg'` | 同上:错误最终落到 `_UIHandler.do_POST` 的兜底 `except Exception`,返回 500 并带出 `AttributeError` 文本;每点一次复现一次 | 阻断 |
|
||||
| 3 | 即使把 killpg 换成 `terminate()`,停止后模型/元数据仍然丢失(Windows 上) | Windows 的 `os.kill(pid, SIGTERM)` 语义是 `TerminateProcess` **硬杀**,训练脚本里"收到信号后在 step 边界保存"的处理器不会执行 | 高 |
|
||||
| 4 | 界面根本没有「暂停」能力(POSIX 也没有) | 原设计只有 start/stop;且 `SIGSTOP/SIGCONT` 在 Windows 上不存在,**原生暂停在 Windows 上无法实现**,必须改成协作式 | 高 |
|
||||
| 5 | 子进程进程组隔离在 Windows 上失效 | `Popen(start_new_session=True)` 在 Windows 被**静默忽略**(CPython 的 Windows `_execute_child` 形参名就是 `unused_start_new_session`) | 中 |
|
||||
| 6 | subproc worker 在 Windows 下可能抢在主进程收尾前退出 | worker 只忽略 `SIGTERM/SIGINT`,没处理 Windows 的 `SIGBREAK`(`CTRL_BREAK_EVENT` 的落点) | 中 |
|
||||
|
||||
**修复原则**:把"暂停/继续/停止"从**信号语义**改成**控制文件语义**。
|
||||
控制文件在 POSIX 与 Windows 上都只是普通文件 IO,行为完全一致;信号与强杀退居
|
||||
兜底。这样 Windows 上暂停、继续、优雅停止、落盘保证全部成立。
|
||||
|
||||
---
|
||||
|
||||
## 1. 失败链复现(旧代码)
|
||||
|
||||
```
|
||||
用户点「停止训练」
|
||||
└─ POST /api/train/stop
|
||||
└─ TrainingUI.stop() → TrainingJob.stop()
|
||||
└─ os.killpg(os.getpgid(process.pid), signal.SIGTERM)
|
||||
├─ POSIX :成功,进程组收到 SIGTERM,脚本优雅收尾
|
||||
└─ Windows:AttributeError: module 'os' has no attribute 'killpg'
|
||||
↑ 只 except (ProcessLookupError, PermissionError),接不住
|
||||
└─ _UIHandler.do_POST 兜底 except Exception → HTTP 500
|
||||
└─ 训练子进程完全没收到任何指令,继续跑 → 「无法暂停 / 停止没反应」
|
||||
```
|
||||
|
||||
关键代码(修复前,`src/mcm26b/training_ui/server.py`):
|
||||
|
||||
```python
|
||||
def stop(self) -> bool:
|
||||
with self._lock:
|
||||
process = self.process
|
||||
if process is None or process.poll() is not None:
|
||||
return False
|
||||
try:
|
||||
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
|
||||
except (ProcessLookupError, PermissionError):
|
||||
process.terminate()
|
||||
self.status = "stopping"
|
||||
return True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 根因逐条(含证据)
|
||||
|
||||
### 2.1 `os.killpg` / `os.getpgid` 是 POSIX-only
|
||||
|
||||
CPython 只在 `posix` 构建里注册这两个函数;Windows 的 `os` 模块没有它们。
|
||||
访问会得到 `AttributeError`(不是 `OSError` 子类),所以旧 `except` 接不住。
|
||||
|
||||
### 2.2 `start_new_session=True` 在 Windows 被静默忽略
|
||||
|
||||
`subprocess.Popen` 接受该参数,但 Windows 的 `_execute_child` 形参名是
|
||||
`unused_start_new_session`,也就是**根本不读**。要隔离进程组,Windows 必须传
|
||||
`creationflags=CREATE_NEW_PROCESS_GROUP`。
|
||||
|
||||
### 2.3 Windows 的 `SIGTERM` 是硬杀
|
||||
|
||||
`os.kill(pid, sig)` 在 Windows 上只有 `CTRL_C_EVENT` / `CTRL_BREAK_EVENT` 是
|
||||
"软"信号;**任何其它值**(含 `SIGTERM`)都会走 `TerminateProcess`。因此
|
||||
`scripts/train_hrl_high.py` 里的:
|
||||
|
||||
```python
|
||||
for _signum in (signal.SIGTERM, signal.SIGINT): # 旧代码
|
||||
signal.signal(_signum, _on_stop_signal)
|
||||
```
|
||||
|
||||
在 Windows 上形同虚设——处理器注册了,但没人会用它。这也解释了为什么"停止后
|
||||
模型没保存"。
|
||||
|
||||
### 2.4 Windows 没有 `SIGSTOP`,暂停无法用信号实现
|
||||
|
||||
暂停(挂起/继续)在 POSIX 上可以用 `SIGSTOP/SIGCONT`,但 Windows 既无这两个
|
||||
信号,也无等价的"无侵入挂起"用户态 API(真正的挂起要 `SuspendThread`/调试 API)。
|
||||
所以**任何跨平台暂停都必须是协作式的**:由训练进程自己在 step 边界检查标志并
|
||||
让出执行。本项目原先没有这条通道,界面也没有暂停按钮。
|
||||
|
||||
### 2.5 `SIGBREAK` 未处理
|
||||
|
||||
Windows 的 `CTRL_BREAK_EVENT`(发给进程组的软信号)在 Python 里落到
|
||||
`signal.SIGBREAK`。旧的 handler 安装与 worker 屏蔽列表都只含
|
||||
`SIGTERM/SIGINT`,Windows 下等于裸奔。
|
||||
|
||||
### 2.6 异常类型没兜住
|
||||
|
||||
`except (ProcessLookupError, PermissionError)` 漏掉 `AttributeError`、以及
|
||||
更一般的 `OSError`;`_hard_kill` 里 `signal.SIGKILL` 在 Windows 上也不存在
|
||||
(`getattr(signal, "SIGKILL", None) is None`),同样会 `AttributeError`。
|
||||
|
||||
---
|
||||
|
||||
## 3. 修复方案(已实施)
|
||||
|
||||
### 3.1 新增 `src/mcm26b/process_control.py`(零第三方依赖)
|
||||
|
||||
| 能力 | 实现 | 作用 |
|
||||
| --- | --- | --- |
|
||||
| 子进程隔离 | `spawn_kwargs()`:POSIX `start_new_session=True`;Windows `creationflags=CREATE_NEW_PROCESS_GROUP` | 前端 Ctrl+C 不误伤训练;Windows 也能组杀进程树 |
|
||||
| 控制协议读写 | `write_control()` / `read_control()`:JSON + 同目录临时文件 + `os.replace` 原子替换;损坏/缺失一律视为"无指令" | 两端统一的暂停/停止通道 |
|
||||
| 强杀兜底 | `terminate_process_tree(process, grace_s)`:先等宽限期;超时后 POSIX 走 `killpg(SIGKILL)`、Windows 走 `taskkill /F /T /PID`;所有 POSIX 符号用 `getattr` 探测 | 彻底移除 `os.killpg` 无条件调用;返回"是否动用了强杀" |
|
||||
|
||||
`IS_WINDOWS` 是运行期常量而非导入期分支,便于测试直接 monkeypatch 模拟 Windows。
|
||||
|
||||
### 3.2 控制文件协议
|
||||
|
||||
路径由前端生成并随命令下发:`--control-file <abs>`(默认落在
|
||||
`output/hrl/ui/control/<job_id>.control.json`,`output/` 已 gitignore)。
|
||||
|
||||
```json
|
||||
{"paused": false, "stop": false, "seq": 1712345678901234567, "ts": 1712345678.9}
|
||||
```
|
||||
|
||||
- 前端**只写**:`pause()` → `paused=true`;`resume()` → `paused=false`;
|
||||
`stop()` → `stop=true`(同时清零 paused,保证阻塞中的暂停循环能被唤醒)。
|
||||
- 训练脚本**只读**:`_ControlPoll` 每 32 个 step 抽样读一次盘(避免每 step 一次
|
||||
文件 IO);暂停时在 `_on_step` 内阻塞轮询(`poll_s=0.2`),进程存活、显存仍占用,
|
||||
但不消耗算力。
|
||||
- 墙钟上限 `--max-wall-s` **扣除累计暂停时长**,避免"挂起一小时回来直接判超时"。
|
||||
|
||||
### 3.3 训练脚本 `scripts/train_hrl_high.py`
|
||||
|
||||
- 新增 `--control-file`;`_ControlPoll`(纯标准库、可单测)+ 薄薄的
|
||||
`_ControlChannel(BaseCallback)` 接到 SB3 回调链**最前面**。
|
||||
- 收尾信号集合扩为 `_stop_signals()`:POSIX `SIGTERM/SIGINT`;Windows 追加
|
||||
`SIGBREAK`。worker 屏蔽同一集合,保证主进程 `vec_env.close()` 之前 worker 不先退。
|
||||
- 落盘保证不变:停止后仍写最终模型 + 元数据,`stopped_by_signal=true`,
|
||||
`stop_signal` 记 `control_file` 或信号名;异常仍落 `*_emergency.zip`。
|
||||
|
||||
### 3.4 训练前端 `server.py` / `static/`
|
||||
|
||||
- 新增 `POST /api/train/pause`、`POST /api/train/resume`;`stop` 语义改为
|
||||
"写控制文件 → 宽限期内自行退出;超时强杀",强杀在**独立看门狗线程**里做,
|
||||
HTTP 请求不会被 15s 等待卡住。
|
||||
- 状态机新增 `paused`;`_sync_control_events()` 用训练脚本回显的
|
||||
`paused/resumed/stop_requested` 事件校正状态。
|
||||
- 快照新增 `forced_kill`,前端横幅区分"优雅停止"与"超时强杀"。
|
||||
- 前端新增「暂停训练 / 继续训练」按钮、暂停横幅;停止确认文案更新。
|
||||
|
||||
### 3.5 停止/暂停时序
|
||||
|
||||
```
|
||||
pause: UI 写 {paused:true} ──► 脚本 _on_step 读到 ──► 回显 paused,阻塞轮询
|
||||
resume: UI 写 {paused:false} ─► 暂停循环退出 ────────► 回显 resumed,继续训练
|
||||
stop: UI 写 {stop:true} ────► 脚本 step 边界收尾落盘 ──► 退出码 0
|
||||
│
|
||||
└─(宽限 15s 未退)─► terminate_process_tree 强杀;forced_kill=true
|
||||
```
|
||||
|
||||
### 3.6 急停(强杀)也必须可恢复
|
||||
|
||||
宽限期一到就强杀,训练脚本**没有任何 Python 收尾机会**——不写最终模型、不写
|
||||
元数据、不写 `training_failure.json`。如果磁盘上只有 `--checkpoint-every` 的稀疏
|
||||
历史 checkpoint,急停就可能丢掉上万步,或者在"保存途中被杀"时留下一个打不开的
|
||||
半截 zip。为此加了四条保证:
|
||||
|
||||
| 保证 | 实现 | 效果 |
|
||||
| --- | --- | --- |
|
||||
| 起步基线 | `recovery_every > 0` 时,`learn()` 之前先写一份 `<stem>_latest.zip` | 训练刚起就急停,也有可加载、可 `--resume` 的产物 |
|
||||
| 滚动恢复点 | 每 `--recovery-every`(默认 2000)环境步覆盖写同一份 `<stem>_latest.zip` | 急停最多丢 `recovery_every` 步,而不是 `checkpoint-every` 步 |
|
||||
| 原子落盘 | 模型 zip / 旁挂 json / `resume.json` 一律「同目录 `.tmp-` + `os.replace`」 | 保存途中被强杀,目标路径仍是上一份**完整**快照;不会出现半截文件 |
|
||||
| 权威恢复指针 | 每次落盘刷新 `<run_dir>/resume.json`(model / timesteps / 增量语义 / 生效 PPO 超参);前端 `/api/train/state` 暴露为 `recovery` | 无论怎么停,都有一份明确的"从哪继续";界面一键「用最近检查点续训」 |
|
||||
|
||||
前端在强杀后还会补写 `<run_dir>/forced_stop.json`(`job_id`、`pid`、`reason`、
|
||||
`recovery.model`),因为此时训练脚本已经无法记录任何事情。
|
||||
|
||||
---
|
||||
|
||||
## 4. 平台兼容性矩阵(修复后)
|
||||
|
||||
| 能力 | POSIX | Windows |
|
||||
| --- | --- | --- |
|
||||
| 启动子进程并隔离 | `start_new_session=True`(setsid) | `CREATE_NEW_PROCESS_GROUP` |
|
||||
| 暂停 / 继续 | 控制文件(协作式) | 控制文件(协作式) |
|
||||
| 优雅停止 + 落盘 | 控制文件;`SIGTERM` 亦可 | 控制文件;`CTRL_BREAK_EVENT`→`SIGBREAK` 亦可 |
|
||||
| 超时强杀进程树 | `killpg(SIGKILL)` | `taskkill /F /T /PID` |
|
||||
| `os.killpg` 依赖 | 有 | **无(不再调用)** |
|
||||
| 暂停期间是否算墙钟 | 否(扣除) | 否(扣除) |
|
||||
|
||||
---
|
||||
|
||||
## 5. 验证
|
||||
|
||||
- 新增/更新回归测试:
|
||||
- `tests/test_training_ui.py`:控制文件协议往返与损坏容错、`spawn_kwargs`
|
||||
平台分支、Windows 强杀走 `taskkill`、**无 `killpg` 环境下强杀不抛异常**
|
||||
(直接复现旧 bug)、暂停/继续/停止全链路、控制文件被忽略时强杀并写
|
||||
`forced_stop.json` 恢复指引、`discover_recovery` 的
|
||||
`resume.json`/扫描/悬空指针三分支、HTTP `pause/resume` 端到端。
|
||||
- `tests/test_hrl_checkpoint.py`:控制文件状态机单测;原子保存失败不破坏上一份
|
||||
快照;JSON 原子替换;历史清理不误删滚动恢复点;起步基线可加载;
|
||||
**强杀急停后 `resume.json` + 滚动恢复点仍可 `--resume`**;控制文件停止后
|
||||
模型/元数据落盘(`stop_signal=control_file`)与暂停→继续;POSIX 信号路径
|
||||
单列并加平台跳过。以上集成用例都用**真实训练脚本**跑 subproc。
|
||||
- 手工验证:`--recovery-every 32` 短程实跑后强杀,`resume.json` 指向
|
||||
`q3_ckpt_latest.zip`;以它为 `--resume` 目标再跑 32 步,元数据
|
||||
`actual_timesteps` 从 64 变 96、`timesteps_semantics=additional`。
|
||||
- 全量测试:`pytest tests referee/tests` 全绿。
|
||||
- 全仓库 `pylint src scripts project tests referee main.py` 达到 10.00/10
|
||||
(口径见 §7)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 残余风险与后续建议
|
||||
|
||||
1. **暂停粒度** = `poll_every` 个 env step(默认 32,约 0.1–3s 量级),不是即时;
|
||||
若要更快可调小 `_ControlChannel(poll_every=...)`,代价是更频繁的文件 stat。
|
||||
2. **宽限期固定 15s**(`server.STOP_GRACE_S`)。大模型保存可能更久;但因为落盘是
|
||||
原子的,即使宽限期到点被强杀,也只是"新的最终模型没写成",**上一份滚动恢复点
|
||||
与历史 checkpoint 完好**,不会出现半截文件。需要更长可调大该常量。
|
||||
3. **急停的损失上界 = `--recovery-every`**(默认 2000 环境步)。要更小就调小它;
|
||||
调到 0 表示放弃滚动恢复点(此时只剩 `--checkpoint-every` 的稀疏历史
|
||||
checkpoint 与起步基线)。
|
||||
4. **Windows 强杀依赖 `taskkill`**(系统自带)。若被安全策略禁用,会退回
|
||||
`Popen.kill()`(只杀主进程,spawn 的 worker 可能残留)。
|
||||
5. 更严格的 Windows 方案是 **Job Object**(子进程随 job 句柄一起销毁),但需要
|
||||
`ctypes`/`pywin32`,本仓库基础依赖刻意保持轻量,未引入。
|
||||
6. **前端进程重启后**,内存中的任务对象没了,「用最近检查点续训」按钮不会自动
|
||||
出现;但恢复信息都落在磁盘上(`<run_dir>/resume.json`、`forced_stop.json`、
|
||||
`checkpoints/<stem>_latest.zip`),在表单里填同一个模型输出路径并勾选
|
||||
`--resume` 即可继续。
|
||||
7. 界面 `--device mps` 选项在 Windows 上无意义;`auto` 会回退 cuda/cpu,属既有
|
||||
行为,未改。
|
||||
8. `_ControlPoll` 的暂停循环会占用一个 Python 线程的调度(每 0.2s 一次读盘),
|
||||
长暂停期间 CPU 占用可忽略。
|
||||
|
||||
---
|
||||
|
||||
## 7. pylint 口径说明
|
||||
|
||||
配置写在 `pyproject.toml` 的 `[tool.pylint.*]`,目标是
|
||||
`pylint src scripts project tests referee main.py` = 10.00/10。
|
||||
|
||||
**改代码修掉的真实问题**(不是 disable):未使用的 import/变量/参数、重复字典键
|
||||
(`reward_version`)、`value != value` 判 NaN、`__dataclass_fields__` 私有访问、
|
||||
`raise` 非异常类型、`pytest.importorskip` 之外的赋值不确定、过长行、无谓括号、
|
||||
`len(x)` 判空、循环闭包变量、`subprocess.run` 未显式 `check`、`os.path` 循环导入等。
|
||||
|
||||
**有意 disable 的类别**(每条在 pyproject 里写了理由):项目刻意的懒加载
|
||||
(`import-outside-toplevel`)、防御式宽异常(`broad-exception-caught`)、测试访问
|
||||
内部状态(`protected-access`)、pytest fixture 同名(`redefined-outer-name`)、
|
||||
无头环境不实现 `gym.Env.render`(`abstract-method`)、以及静态分析的已知误报
|
||||
(`possibly-used-before-assignment`)。
|
||||
|
||||
**注意**:`good-names` 不覆盖 `redefined-builtin`,stdlib 回调签名里的
|
||||
`log_message(self, format, ...)` 只能在对应 `def` 上行内 disable。
|
||||
@@ -1,160 +0,0 @@
|
||||
# 模型训练前端(training-ui)
|
||||
|
||||
本地回环 Web UI,用于**实时观察一次 HRL 高层 PPO 训练**:子进程状态、步进进度、
|
||||
参数收敛曲线、逐回合记录与完整 stdout 日志。设计与评测前端(`referee/` WebUI)
|
||||
同构——同一套暗色面板语言、同一个「只监听 127.0.0.1 + 静态白名单 + JSON 轮询
|
||||
API」模式——差别在于本页托管的是一个**训练子进程**而不是评测会话。
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
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/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`)只作兜底。
|
||||
|
||||
进度数据的两个来源,服务端都已解析:
|
||||
|
||||
1. **progress JSON**(主来源):`train_hrl_high.py --progress-json` 每若干环境步打印
|
||||
一行 `{"event": "progress", "timesteps": ..., "ep_rew_mean": ...}`,字段最全;
|
||||
热身阶段间隔会自动缩小,短程 smoke 也能看到完整曲线。
|
||||
2. **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;字符串参数只拒绝控制字符。
|
||||
- 同一时刻只允许一个训练任务;暂停中的任务仍算「在跑」,必须先停止才能开新一局。
|
||||
- 暂停/停止走控制文件(`--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`
|
||||
语义(两者互斥,由后端校验)。
|
||||
|
||||
## 暂停、停止与落盘保证
|
||||
|
||||
「暂停」是**协作式**的:前端原子写控制文件,`train_hrl_high.py` 的
|
||||
`_ControlPoll` 每 32 个环境步抽样读一次;暂停时训练阻塞在 `_on_step` 的轮询里,
|
||||
进程与显存保留、不消耗算力,点「继续」从同一个 step 边界恢复。暂停时长不计入
|
||||
`--max-wall-s`(否则"挂起一小时回来直接判超时")。
|
||||
|
||||
「停止」同样是控制文件请求(**跨平台**)。`train_hrl_high.py` 有多层兜底,
|
||||
点一下「停止」不再等于这一局白跑;而**急停**(强杀,例如 `SIGKILL` /
|
||||
`taskkill /F`,进程没有任何收尾机会)也保证可恢复:
|
||||
|
||||
| 机制 | 行为 |
|
||||
| --- | --- |
|
||||
| 滚动恢复点 | `--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 子进程,覆盖
|
||||
控制文件停止、POSIX 信号停止、**强杀后仍可续训**与暂停→继续(信号路径在 Windows
|
||||
上自动跳过)。
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
.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、示例快照与真实快照同键;
|
||||
用**桩训练脚本真起子进程**覆盖启动/轮询/暂停/继续/停止/退出码全链路;并覆盖
|
||||
跨平台进程控制——控制文件往返与损坏容错、`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 信号停止。
|
||||
|
||||
## 已知取舍
|
||||
|
||||
- 进度点保留上限 5000(服务端 deque 与前端本地缓存同值),超长训练只保留最近一段。
|
||||
- 表格解析依赖 SB3 的段名(`rollout/`/`time/`/`train/`/`system/`);SB3 若改表格
|
||||
格式,退化路径是「只显示 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)。
|
||||
@@ -1,165 +0,0 @@
|
||||
# 第三、四问强化学习适用性审核与改进
|
||||
|
||||
本轮审核第一版强化学习代码、专家数据、模型及官方演练归档,修复训练与部署不一致的问题,训练两个新的 Maskable PPO 模型,并用独立实现的本地 `referee` 完成同场景对照。所有新性能数字均为本地结果,没有启动新的官方演练。
|
||||
|
||||
结论:强化学习适合在现有几何算法内尝试“下一检测点”的排序,第四问比第三问更有收益空间。当前证据支持第四问的混合方案优于手写 `directional` 基线;尚不能证明新模型稳定优于旧 PPO。第三问没有稳定增益,应保留 `bayes` 默认策略。新模型作为实验版本显式加载,没有覆盖旧默认模型。
|
||||
|
||||
## 1. 题设和方法适用范围
|
||||
|
||||
附录2(3)的交会必须使用带误差的角域,不能把两条示向线的交点当作真值;同地重测不会提供新的独立误差样本。附录2(4)的单接收机逐频道检测,结合动作规则,须计入移动、5秒检测及实际1秒换频。第三问的全向假设允许用无信号排除接收圆盘;第四问的无信号还可能来自方向遮挡,不能沿用这一排除结论。题设及几何算法的完整审核见[算法审核与优化](算法审核与优化-20260911.md)。
|
||||
|
||||
当前 RL 实际只替换 `RLBayesLoop._select_point` 和 `RLDirectionalLoop._next_probe`。七站/49站覆盖、频道发现、全局任务调度、保守候选集、清除证书和第四问后备清除仍由原算法负责。因此,完整场景全清验证的是混合系统,不是神经网络独立获得了完备性证明。
|
||||
|
||||
401维输入概括几何、历史读数和60个候选点,但省略完整历史、候选多边形细节及位置—朝向联合约束,不能直接称为完整马尔可夫状态。局部单源训练也没有学习全场覆盖路线与多源调度。PPO可以作为这种压缩观测下的近似策略尝试,但其目标并不自动等于全场最短时间;评估必须使用独立完整场景。[SB3官方训练与评估建议](https://stable-baselines3.readthedocs.io/en/master/guide/rl_tips.html)也强调独立评估、多随机种子和状态信息完整性。
|
||||
|
||||
## 2. 确认的实现缺陷及修复
|
||||
|
||||
| 缺陷 | 证据和影响 | 已实施修复 |
|
||||
| --- | --- | --- |
|
||||
| 训练传入整个 `BeliefState`,部署传入 `ChannelBelief` | 同一状态的特征索引7、14、15、17、18不同;训练丢失测量次数、末次结果和方位信息 | `env._observe` 使用目标频道信念;测试逐位比较部署输入,并覆盖跨频道交接 |
|
||||
| BC使用ReLU,接收权重的PPO使用Tanh | 仅复制线性权重无法保留函数。原始专家输入探针中,Q3的90.96%、Q4的67.42%出现不同argmax | BC改为Tanh;复制时检查完整前向输出,重新初始化critic;旧ReLU/旧特征检查点拒绝直接迁移 |
|
||||
| BC按观测行随机拆分 | 同一episode的相关状态可能同时出现在训练和验证中,分类准确率不足以说明独立场景泛化 | 保存episode编号,按episode划分训练/验证 |
|
||||
| 第四问训练遮挡状态与运行时不一致 | 训练只记录距离保证盘内的无信号,且获得新方向后未正确重置连续遮挡;后备清除触发轨迹偏离部署 | 所有连续无信号记录为探测序列,仅盘内无信号用于确认定向;正观测清空序列,统一4次遮挡/8次定位阈值 |
|
||||
| 奖励鼓励首次遮挡等中间事件 | “获得遮挡证据”额外+2、半径收缩等奖励会改变实际时间目标;较高回报不一定更快 | 主要奖励改为实际虚拟耗时的负值,成功清除统一+20,失败/截断重罚;取消这些中间奖励,采用gamma=1 |
|
||||
| 未屏蔽同地重测、重合候选和第三问盘外梯子点 | 浪费固定误差条件下的检测时间;旧文档“所有候选保证接收”不成立 | 训练/推理共用动作屏蔽,排除已测0.5m格位置、重复候选、越界点;第三问存在距离保证盘时排除盘外点 |
|
||||
| 保证盘半径恰为0时动作数量可能改变 | 依赖去重后的候选生成器会破坏固定60动作接口 | 即使位置重合也保留编号,交由动作屏蔽处理 |
|
||||
| 只校验401/60维度不能识别语义变化 | 接收机频道被目标频道替代;旧模型与新模型同形但输入含义不同 | 加入特征版本与算法元数据,v2使用实际接收机频道,旧版保留原输入语义;拒绝问题编号或屏蔽模型元数据不匹配 |
|
||||
| 基准将 `completed` 当作全清 | 内部状态自洽不代表全部真实源已清除 | 用评测端真实频道集合对照成功清除集合,并检查真实频道没有被误判缺席、正常退出、异常及逐步计时;旧基准新增 `all_valid` 和失败退出码 |
|
||||
| 官方审计没有严格对应问题与策略名称 | 第三问/第四问摘要可能交叉通过名称检查 | `verify_rl_practice.py` 分别要求 `rl` / `rl_q4` |
|
||||
|
||||
BC探针是对旧网络和“拷贝后换激活”的网络进行比较,并不是新模型错误率,也不是重新测得的BC验证准确率。旧报告38.9%/65.3%的分类准确率不能直接解释为已迁移PPO的模仿准确率。本轮训练的新模型均从随机初始化开始,未使用BC;BC修复通过前向一致性测试,未据此声称重新训练BC已经带来收益。
|
||||
|
||||
动作屏蔽采用官方 `MaskablePPO` 的 `action_masks` 接口,训练和确定性推理都传入相同规则。[SB3-Contrib官方文档](https://sb3-contrib.readthedocs.io/en/master/modules/ppo_mask.html)说明了这两处的用法。第四问仍允许探索盘外候选,距离保证不等于方向可见。退化到所有动作均被屏蔽时保留中心动作,由步数/预算边界处理,因此不能把本次零重复观测扩展为所有可能状态的严格保证。
|
||||
|
||||
正常成功episode的累计奖励为 `20 - 局部实际虚拟时间/100`;环境仍保留异常与失败处理,包括证书清除失败的额外惩罚。局部时间从首测与可选交接完成后开始,不能用训练报告中的局部均值替代整场成绩。第三问训练自动清除仍使用证书圆心,而完整 `BayesLoop` 可在证书范围内平移清除点;全局调度、预算与局部环境也有差异,完整闭环验证因此不可省略。
|
||||
|
||||
## 3. 训练和数据
|
||||
|
||||
第一版Q3为120000步随机初始化PPO;第一版Q4为BC初始化后40000步PPO。原专家数据分别为5141、14181条,原5场完整测试均值分别是Q3:3088.33/3124.76秒、Q4:14250.15/13230.20秒(手写/RL)。这些历史数字受旧环境和特征影响,不与本轮新课程的单源成功率直接比较。
|
||||
|
||||
修订课程保留首次有效测向,并加入35%的跨频道、跨位置交接,20%的区域边缘且最小接收半径场景。定向首测覆盖朝向±89.9°,更接近半平面边界。源位置、朝向、半径和逐点误差的抽样仍是自建分布,不是已知的官方生成分布;真值仅用于布设和评测,不进入策略观测或动作屏蔽。
|
||||
|
||||
两模型使用 `[128,128]` Tanh MLP、2个内存环境、`n_steps=512`、`batch_size=128`、6轮更新、学习率0.0003。实际均完成60416步,来自完整rollout批次向上取整;元数据中的60000是请求步数。安装版本为SB3 2.9.0、SB3-Contrib 2.9.0、Torch 2.11.0+cu128。本次选择CPU、单Torch线程,机器CUDA可用。
|
||||
|
||||
| 项目 | Q3新模型 | Q4新模型 |
|
||||
| --- | ---: | ---: |
|
||||
| 训练种子 | 310001 | 410001 |
|
||||
| 定向概率 | 0 | 0.75 |
|
||||
| 训练墙钟时间 | 276.6s | 250.6s |
|
||||
| 独立单源评估成功 | 59/60 | 60/60 |
|
||||
| 单源平均局部时间 | 270.04s | 349.51s |
|
||||
| 平均动作步数 | 2.42 | 3.13 |
|
||||
|
||||
Q3有一次局部训练任务未完成,不能声称单源成功率100%。完整闭环仍有自身调度与后备路径。两问题各只有一个新模型训练种子,未覆盖训练随机性。
|
||||
|
||||
## 4. 完整场景同条件对照
|
||||
|
||||
比较三种固定策略:当前手写基线(Q3 `bayes`、Q4 `directional`)、旧默认PPO、新Maskable PPO。相同场景使用相同源位置、频道、半径、朝向及逐点固定误差种子。`referee` 为单独实现,策略不读取模拟器真值。每场保存场景、完整请求/响应、summary、模型哈希和计时核验。
|
||||
|
||||
开发集为种子700—702、10/16源,各问题6场。随后固定新模型,不再据验证成绩调参;验证集为种子91000—91005、10/13/16源,各问题18场。第四问定向数覆盖1、约半数、仅一个全向三种组成。另加两种圆周边界压力场景。开发、验证、压力共52个不同场景,三策略共156次完整运行,全部满足真实源全清、缺席无误判、正常exit、零异常、逐步耗时复算一致。
|
||||
|
||||
### 开发集平均整场时间
|
||||
|
||||
| 问题 | 手写基线 | 旧PPO | 新模型 |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Q3,6场 | 3294.62s | 3206.20s | 3264.62s |
|
||||
| Q4,6场 | 14289.76s | 12443.43s | 11758.72s |
|
||||
|
||||
### 独立验证集
|
||||
|
||||
| 问题/指标 | 手写基线 | 旧PPO | 新模型 |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Q3有效全清 | 18/18 | 18/18 | 18/18 |
|
||||
| Q3平均时间 | 3330.72s | 3254.61s | 3307.65s |
|
||||
| Q3中位时间 | 3309.16s | 3262.64s | 3222.50s |
|
||||
| Q3最大时间 | 3690.11s | 3786.98s | 3776.55s |
|
||||
| Q4有效全清 | 18/18 | 18/18 | 18/18 |
|
||||
| Q4平均时间 | 13778.82s | 12070.53s | 11797.57s |
|
||||
| Q4中位时间 | 14587.80s | 12891.83s | 12488.23s |
|
||||
| Q4最大时间 | 17273.98s | 15365.76s | 15894.44s |
|
||||
| Q4累计未成功clear尝试 | 616 | 719 | 1380 |
|
||||
| Q4累计同频道同点重测 | 12 | 28 | 0 |
|
||||
|
||||
Q3三组都没有失败clear或同点重测。Q4新模型减少了重测,但更多使用后备光学清除尝试,平均耗时降低的同时尾部表现有所退化。失败clear本身可来自允许的后备搜索,不能仅凭该计数认定证书错误;其移动和3秒动作耗时全部纳入结果。
|
||||
|
||||
对独立验证场景做10000次配对bootstrap,负值表示新模型更快:
|
||||
|
||||
| 对比 | 均值相对变化 | 95%场景重采样区间 | 新模型更快场数 |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Q3新模型对手写 | -0.69% | [-4.50%, +2.95%] | 9/18 |
|
||||
| Q3新模型对旧PPO | +1.63% | [-1.64%, +5.19%] | 8/18 |
|
||||
| Q4新模型对手写 | -14.38% | [-17.93%, -11.48%] | 18/18 |
|
||||
| Q4新模型对旧PPO | -2.26% | [-6.08%, +1.71%] | 13/18 |
|
||||
|
||||
新旧PPO的区间均跨0,不能据小幅平均变化宣称稳定改进。区间只反映固定模型在这些场景上的差异,不覆盖训练种子、未知官方分布或所有合法边界。此外本轮同时修改多项训练因素,这不是能归因到单个修复的消融实验。
|
||||
|
||||
### 边界压力与4000秒目标
|
||||
|
||||
源全部放在半径1800m圆周、接收半径1000m;第四问隔一个源采用向外发射方向。10/16源各一场,各策略都完成全清。
|
||||
|
||||
| 问题 | 手写平均 | 旧PPO平均 | 新模型平均 |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Q3 | 4472.58s | 4429.04s | 4648.26s |
|
||||
| Q4 | 18248.87s | 14404.01s | 15210.08s |
|
||||
|
||||
新Q3的两场分别为4659.06s、4637.46s,仍超过4000s;新Q4为14573.62s、15846.54s,且比旧PPO退化。此前几何优化在另一独立Q3验证组达到平均3315.89s、最大3981.74s,本轮新Q3验证组最大3776.55s;二者只能说明各自验证组达标,不能承诺全部题设场景小于4000s。第四问尚未达到4000s。
|
||||
|
||||
## 5. 官方证据与测试
|
||||
|
||||
重新审计已有官方归档,三次均通过题号/策略、官方包SHA、源数、完整清除、正常退出、请求时窗及逐步计时检查,未改写原归档:
|
||||
|
||||
| 问题 | 既有官方案例 | 全清 | 整场时间 |
|
||||
| --- | --- | ---: | ---: |
|
||||
| Q3 | F38Y-9Q9J-B82V-CB9Z | 13/13 | 3538.81s |
|
||||
| Q4,3个定向源 | X43X-VMRF-8GRZ-3SYA | 10/10 | 14003.09s |
|
||||
| Q4,10个定向源 | BXSA-TFQ3-2DJ6-DHH7 | 10/10 | 14562.82s |
|
||||
|
||||
这些是旧模型的官方可运行证据,不是新模型成绩;不同案例也不能直接计算优化百分比。新模型目前通过本地验证,未在官方新演练中运行。
|
||||
|
||||
全量回归:`python -m pytest -q --tb=short`,243项通过。初次沙箱运行有24个临时目录权限导致的fixture错误,获得权限后重跑全部通过。新增5项测试覆盖训练/部署观测、动作屏蔽、方向读数重置遮挡、实际成本奖励和BC完整前向一致性。之后对基准/官方审计入口的小修补分别用4次完整场景冒烟运行及三份官方归档复核通过。
|
||||
|
||||
## 6. 产物与复现
|
||||
|
||||
主目录为 `output/rl/review-20260911/`:
|
||||
|
||||
- `q3_masked.zip` / `.json`、`q4_masked.zip` / `.json`:新模型与训练元数据,必须成对保存。
|
||||
- `evaluation/development/{controls,revised}/results.json`:开发对照。
|
||||
- `evaluation/validation/final/results.json`:独立验证与每场完整日志。
|
||||
- `evaluation/stress/final/results.json`:边界压力结果与日志。
|
||||
- `review_evidence.json`:配对区间、BC/特征错位探针、旧官方重审结果和代码哈希。
|
||||
- `benchmark-smoke.json`:旧基准有效性口径修复后的冒烟结果。
|
||||
|
||||
新Q3模型SHA-256:`b8b3a2bb103588348d8d7791bde9a2b0635c04778f0ac64f91c6f5c7cf87ca6e`。
|
||||
新Q4模型SHA-256:`6865d541ff9bdee81886d327345f2660d5f7d72f4b39810a58ae4b867ab37e42`。
|
||||
|
||||
从项目根目录复现训练时,使用新目录保留本轮检查点:
|
||||
|
||||
```powershell
|
||||
.venv/Scripts/python.exe scripts/train_rl.py --problem 3 --algorithm maskable_ppo --timesteps 60000 --seed 310001 --device cpu --model output/rl/reproduction/q3_masked.zip --eval-episodes 60 --verbose 0
|
||||
.venv/Scripts/python.exe scripts/train_rl.py --problem 4 --algorithm maskable_ppo --timesteps 60000 --seed 410001 --directional-probability 0.75 --device cpu --model output/rl/reproduction/q4_masked.zip --eval-episodes 60 --verbose 0
|
||||
```
|
||||
|
||||
复核本轮冻结模型的完整对照(label须使用尚不存在的名称):
|
||||
|
||||
```powershell
|
||||
.venv/Scripts/python.exe scripts/evaluate_rl_revision.py --split validation --label rerun-validation
|
||||
.venv/Scripts/python.exe scripts/evaluate_rl_revision.py --split stress --label rerun-stress
|
||||
.venv/Scripts/python.exe scripts/summarize_rl_revision.py
|
||||
```
|
||||
|
||||
本地内存自检可显式加载对应检查点;`--self-test`不会连接官方模拟器:
|
||||
|
||||
```powershell
|
||||
.venv/Scripts/mcm26b.exe run --robot-id 2026001 --self-test --strategy rl --rl-model output/rl/review-20260911/q3_masked.zip
|
||||
.venv/Scripts/mcm26b.exe run --robot-id 2026001 --self-test --problem 4 --q4-strategy rl --rl-model output/rl/review-20260911/q4_masked.zip
|
||||
```
|
||||
|
||||
在用户另行启动相应问题的官方演练后,使用实际登录队号和`--base-url`连接。例如第三问(路径使用本轮尚未创建的官方演练目录):
|
||||
|
||||
```powershell
|
||||
.venv/Scripts/mcm26b.exe run --robot-id 2026001 --base-url http://127.0.0.1:2026 --problem 3 --strategy rl --rl-model output/rl/review-20260911/q3_masked.zip --log output/rl/review-20260911/practice-q3/session.jsonl
|
||||
```
|
||||
|
||||
该命令保存请求/响应日志,汇总输出到终端;官方结果文件与汇总还需按原归档流程保存。第四问改用`--problem 4 --q4-strategy rl`及对应模型。本轮未执行上述官方连接命令。
|
||||
|
||||
进一步研究应先缩小单源训练与完整调度的差异,并处理Q4后备清除和边界退化,再做多个训练种子及新的冻结验证集。当前结果不足以将新模型自动替换为默认策略,也不足以把训练步数增加当作达到4000秒的依据。
|
||||
@@ -0,0 +1,516 @@
|
||||
# 第三、四问:图论与动态规划算法引入方案
|
||||
|
||||
- 版本:2026-09-12
|
||||
- 前置文档:[第三四问架构优化与运行提速方案](第三四问架构优化与运行提速方案.md)(架构与 P0–P2 项)、
|
||||
[algo-3-bayes.md](algo-3-bayes.md)(现役第三问策略)、[algo-3.md](algo-3.md)(七点覆盖证明)
|
||||
- 范围:只讨论"用图论 / 动态规划类算法进一步压低**虚拟耗时**"这一件事;
|
||||
题面约束、协议语义、保守性不变量全部不动
|
||||
- 本文所有数字均为**本轮新增实测**(自建内存回环、冻结场景、现役代码),
|
||||
复现命令见 §7
|
||||
|
||||
---
|
||||
|
||||
## 0. 结论先行
|
||||
|
||||
**可以,而且第四问的空间远大于第三问;但真正的收益不在"把巡回排得更短",而在
|
||||
"把不必要的往返与盲清从决策层消掉"。**
|
||||
|
||||
| 判断 | 证据 | 可回收 |
|
||||
| --- | --- | --- |
|
||||
| 第三问**路由**已接近最优,纯图论排线几乎无空间 | 对**同一批实际作业点**求离线最优开放回路:2308 s vs 实测移动 2458 s,只剩 **6.1%(151 s)** | ≤ 150 s |
|
||||
| 第三问仍有空间,但在**探测次数**(序贯决策),不在排线 | 30.8 次探测 / 13 源,且 0 次 `no_signal`——每次都在有效收缩,但次数仍偏多 | −150 ~ −250 s |
|
||||
| 第四问**路由**有 20% 空间,但那是被定位/清除绕行"拖"出来的 | 离线最优 4998 s vs 实测移动 6257 s(**20.1%**);而覆盖点巡回的投影顺序 4365 s 已接近离线 31 点最优 4181 s(+4.4%) | −400 ~ −700 s |
|
||||
| 第四问最大单点是**定向源定位失败导致的后备盲清** | 平均每局 **131 次清除只有 13 次成功**,`fallback_clear_attempts=125.7`,清除动作 419 s + 清除段移动 798 s ≈ **1220 s/局(14% T)** | **−800 ~ −1100 s** |
|
||||
| 第四问第二点是**探测本身** | 平均 69 次探测 ≈ **2224 s/局(26% T)**,其中 32 s/次的移动占 1880 s | −400 ~ −800 s |
|
||||
| 覆盖点集**已经最小**,不要再压 | 27 点方案在 75 m/24 档"通过",但在 25 m/48 档有 **298 个 (p,h) 缺口** → 是采样过拟合的假解;在细网格上直接搜索的最小命中集仍是 **31 点** | 0(见 §5.1 的正确性风险) |
|
||||
|
||||
**一句话:第三问的目标是"把 30.8 次探测压到 ~26 次并把序排到最优",第四问的目标是
|
||||
"把 125 次盲清变成 3 次有保证的探测"。** 两者都是 DP / 序贯决策问题,
|
||||
不是经典的 TSP 排线问题。
|
||||
|
||||
---
|
||||
|
||||
## 1. 目标函数的图论化
|
||||
|
||||
题面计时模型(`config.CostConfig`,已由 `evaluation.recompute_virtual_time` 逐微秒复算验证):
|
||||
|
||||
```
|
||||
T = L/5 + 5·N_measure + 1·N_switch + 5·N_clear⁺ + 3·N_clear⁻
|
||||
```
|
||||
|
||||
把它写成图上的路径代价,后面所有算法都在这张图上做:
|
||||
|
||||
- **节点** = 一次"停靠"(覆盖站点、定位测点、清除点);
|
||||
- **边权** `w(a,b)` = `dist(a,b)/5`(移动秒);
|
||||
- **节点服务费** = `5·(该点要测的频道数) + 1·(换频次数)` 或 `5/3`(清除)。
|
||||
- 于是整局 = 一条**开放路径**(起点原点、终点自由),目标是最小化
|
||||
`Σ边权 + Σ服务费 + Σ动作惩罚`。
|
||||
|
||||
四类图问题分别对应四个子决策:
|
||||
|
||||
| 子决策 | 图问题 | 现役做法 | 目标做法 |
|
||||
| --- | --- | --- | --- |
|
||||
| 站点/任务排序 | **开放 TSP(小 n 精确)** | NN + 2-opt(滚动) | Held-Karp 精确 DP(n ≤ 14,含换频状态)|
|
||||
| 定位测点选择 | **带邻域的 TSP / 定向问题(TSPN/OP)** | 每任务独立选点 + 全表重排 | 覆盖骨架 + 最便宜插入;测点作为"邻域"参与联合优化 |
|
||||
| 定向源定位 | **POMDP / 随机最短路** | 一步贪心 `prob_detect/时间` | 2–3 步 rollout,值函数 = 期望后验 MEC 半径 |
|
||||
| 后备清除 | **区域覆盖路径(Covering Path)** | 格心最近邻 | 割草机 + 转向点 DP / 格心集 Or-opt |
|
||||
|
||||
---
|
||||
|
||||
## 2. 本轮实测基线(现役代码,冻结场景)
|
||||
|
||||
场景集:`harness/suites.py`,第三问 core 的 seed 100–105 × 13 源;
|
||||
第四问 core 全部 9 场(10/13/16 源 × 3 定向占比)。配置为现役默认。
|
||||
|
||||
### 2.1 第三问(`bayes`,13 源,6 场)
|
||||
|
||||
| 指标 | 数值 |
|
||||
| --- | ---: |
|
||||
| 平均虚拟时间 | **3197 s** |
|
||||
| 移动 | 12292 m = **2458 s(77%)** |
|
||||
| 覆盖点检测 | 83.5 次 = 501 s(其中 69.8 次 `no_signal`,属缺席证明) |
|
||||
| 定位探测 | **30.8 次 = 185 s**(0 次 `no_signal`) |
|
||||
| 清除动作 | 65 s |
|
||||
| **离线最优开放回路(同一批作业点)** | **2308 s** |
|
||||
| 路由可回收 | **151 s(移动的 6.1%,T 的 4.7%)** |
|
||||
| 作业点数(去重) | 35–50 |
|
||||
|
||||
### 2.2 第四问(`directional` + `q4_mincover_v1`,9 场)
|
||||
|
||||
| 指标 | 数值 |
|
||||
| --- | ---: |
|
||||
| 平均虚拟时间 | **8676 s** |
|
||||
| 移动合计 | 6257 s(72%) |
|
||||
| ├ 覆盖段移动 | 3579 s |
|
||||
| ├ **探测移动** | **1880 s** |
|
||||
| └ **清除移动** | **798 s** |
|
||||
| 动作耗时 | 2149 s(覆盖 1386 / 探测 344 / 清除 419) |
|
||||
| 探测次数 | 69.0 次(`no_signal` 4–12 次) |
|
||||
| **清除次数 / 成功** | **131.1 / 13.0 → 118 次失败** |
|
||||
| `fallback_clear_attempts` | **125.7 次/局** |
|
||||
| 后备盲清格数(单次) | 3 ~ 210 格,全部以 `cleared` 结束 |
|
||||
| **离线最优开放回路(同一批作业点)** | **4998 s** |
|
||||
| 路由可回收 | 1259 s(移动的 20.1%,T 的 14.5%) |
|
||||
| 覆盖投影顺序 vs 离线 31 点 2-opt | 4365 s vs 4181 s(+4.4%) |
|
||||
|
||||
### 2.3 成本地板估计
|
||||
|
||||
| 问题 | 不可压缩部分 | 现状 | 真实可压缩 |
|
||||
| --- | --- | ---: | ---: |
|
||||
| 第三问 | 七点环 1392 s + 覆盖检测 501 s + 清除动作 65 s + 每源必经的接近/清除移动 + 每源 ≥2 次探测 | 3197 s | **~300–450 s(10–14%)** |
|
||||
| 第四问 | 31 点巡回 ~4180 s + 缺席检测 ~1386 s + 每源 ≥3 次探测与一次清除 | 8676 s | **~2000–2800 s(23–32%)** |
|
||||
|
||||
> 第四问的 4000 s 验收线**依然不可达**(单是 31 点巡回 + 缺席检测就 ≈5566 s),
|
||||
> 这与前置文档 §6.2 的结论一致;本方案只承诺把 8676 s 压向 6500 s 量级。
|
||||
|
||||
---
|
||||
|
||||
## 3. 算法设计
|
||||
|
||||
### 3.1 D1(第四问最高优先):定向源的"抵近 + 交会"滚动时域 DP
|
||||
|
||||
**问题本质**:±1° 示向度误差在距离 d 处给出的横向不确定度约 `d·tan(1°) ≈ 0.0175 d`。
|
||||
d = 1000 m 时单条方位线的横向约束只有 ±17.5 m;两条方位线的交会角 θ 决定最终尺度
|
||||
`≈ 0.0175 d / sin θ`。要压到 20 m 清除证书,必须**同时**满足:交会角大、距离近。
|
||||
现役 `_visibility_probe` 只最大化 `prob_detect(q)/(移动+6)`,天然偏好"近且可见",
|
||||
与"交会角大"无关;远距离又永远收敛不到 20 m,于是退化到 20 m 网格盲清。
|
||||
|
||||
**形式化**(这就是一个 POMDP):
|
||||
|
||||
- 状态 `b` = `JointBelief`(位置 × 24 档朝向的可行集,见 `belief/joint.py`),
|
||||
再叠加 `r_eff ~ U[1000,1500]` 的不确定度;
|
||||
- 动作 `q` ∈ 候选测点(保证接收点 ∪ 对跖点 ∪ 三圈环探 ∪ **朝 MEC 圆心的抵近点**);
|
||||
- 观测 `o ∈ {direction, near, no_signal}`,由每条粒子 `(p, h, r_eff)` 解析预测;
|
||||
- 代价 `c(q) = 6 s + dist(pos,q)/5`;
|
||||
- 终止:`MEC(b).radius ≤ 20 − margin`(可一次清除);
|
||||
- 值函数:`V(b) = min_q E_o[ c(q) + V(b') ]`。
|
||||
|
||||
**实现选择(按代价递增)**:
|
||||
|
||||
1. **一步期望后验半径**(最小改动,复用 Q3 已有内核的二维推广):
|
||||
对候选 `q`,按粒子权重模拟三分支结果,用幸存子集的 MEC 半径近似 `V(b')`,
|
||||
取 `E[radius] + λ·c(q)` 最小者。把 `particles.py::expected_posterior_radius`
|
||||
的向量化实现在 `JointBelief` 上重写一份(numpy 可用时广播)。
|
||||
2. **2–3 步 rollout**:对前 K=8 个候选各展开一层,深度 2;墙钟预算 ≤ 0.3 s/决策。
|
||||
3. **交会角代理(解析、零墙钟)**:若一步法来不及落地,先用
|
||||
`score(q) = −E[radius] · sin(Δbearing(q, 既有方位线)) / dist(q, center)`
|
||||
的确定性代理,实验证明有效后再换 rollout。
|
||||
|
||||
**关键结构性改动**:把候选点集从"绕 MEC 的固定环"扩展为**含抵近点**,
|
||||
即允许 `q` 落在 `center + α·(pos − center)` 上、`α` 从 0.6 到 1.0 采样。
|
||||
这样当远距离无法收敛时,策略会**主动抵近**并以一次正常角域证书收尾,
|
||||
而不是把 200 格盲清当作唯一出路。后备清除**保留为安全网**,但目标是触发次数下降 ≥ 80%。
|
||||
|
||||
**验收**:`fallback_clear_attempts` ≤ 15/局(现 125.7);定向频道平均探测次数
|
||||
≤ 全向频道的 1.5 倍;清除成功率 ≥ 90%;9 场景全清率 100%。
|
||||
**预期**:−800 ~ −1100 s。
|
||||
|
||||
---
|
||||
|
||||
### 3.2 D2:覆盖骨架 + 最便宜插入(TSPN 的在线近似)
|
||||
|
||||
**问题本质**:31 个覆盖点是**必须在场**的(缺席证明),但探测点与清除点是
|
||||
**临时产生**的。现役做法把两类点混在一张任务表里,每次从零做 NN + 2-opt
|
||||
(`bayes_loop._order_tasks`),等于让临时任务把骨架顺序"打散"。
|
||||
实测投影顺序 4365 s vs 离线最优 4181 s,说明骨架顺序本身没坏,
|
||||
坏在**绕行开销**——而绕行开销正是插入位置没优化。
|
||||
|
||||
**形式化**:这是 **TSP with Neighborhoods / Generalized TSP**:
|
||||
|
||||
- 骨架:离线求覆盖点集的近优开放回路 `S`(`7` 点用解析证明;`31` 点用 Or-opt/3-opt,
|
||||
`n ≤ 16` 时可直接 Held-Karp 精确);
|
||||
- 在线:给定当前任务 `q` 与其**邻域**(`clear` 任务的邻域是 `C₂₀(K)`,`locate`
|
||||
任务的邻域是候选测点集),求
|
||||
`Δ(i,j) = dist(a_i, q) + dist(q, a_{j}) − dist(a_i, a_j)`
|
||||
最小的插入位置 `(i,j)`;这正是"图上的最便宜插入",`O(|S|²)` 每任务。
|
||||
|
||||
**实施**:
|
||||
|
||||
1. `guarantee/cover.py` 增加 `optimize_tour(points, seed_tour, passes)`(Or-opt + 2-opt),
|
||||
把 `nn_tour_m` 从"上界"变成"已优化值",并写入证书 `tour_virtual_s`。
|
||||
2. `planner/bayes_loop.py` 新增 `_skeleton`(覆盖点顺序)与 `_insert_task(task, neighborhood)`;
|
||||
`_order_tasks` 退化为"骨架遍历 + 已插入任务",保留现有 2-opt 作为兜底开关。
|
||||
3. `clear` 任务的邻域用 **`C₂₀(K)` 内离插入点最近的点**(见 3.4),使插入代价与
|
||||
清除目标联合最小。
|
||||
|
||||
**验收**:第四问移动 ≤ 5000 s(现 6257 s);路由相对离线最优的 gap
|
||||
(新增回归指标 `route_gap_vs_offline`)从 20.1% 降到 ≤ 10%。
|
||||
**预期**:−400 ~ −700 s。
|
||||
|
||||
---
|
||||
|
||||
### 3.3 D3:后备清除的覆盖路径(若 D1 后仍触发)
|
||||
|
||||
后备清除是"用 20 m 圆盘覆盖残差区域 `R` 直到命中真源"——即 **Covering Path Problem**。
|
||||
|
||||
- 当前 `_fallback_clear` 对格心做 nearest-first 扫描,等价于格心集上的 NN 开放路径,
|
||||
典型约为最优的 1.2–1.3 倍;
|
||||
- 改进:对格心集做 **Or-opt + 2-opt**(`k ≤ 400`,`O(k²)` 一次约 0.1 s,且只在
|
||||
极少数局触发);
|
||||
- 上界更优解:凸区域上的**割草机(boustrophedon)扫描**,用 DP 在扫描线方向选择
|
||||
转向点可精确最优;对一般多边形用梯形分解 + DP。
|
||||
|
||||
**验收**:后备触发时移动 ≤ 现值的 80%(单次);触发次数本身由 D1 压到 ≤ 15/局。
|
||||
**预期**:−100 ~ −200 s(D1 未完全消除时的兜底)。
|
||||
|
||||
---
|
||||
|
||||
### 3.4 T1/T3:第三问的精确排线、换频 DP 与清除点选择
|
||||
|
||||
**(a) Held-Karp 精确任务序(含换频状态)**
|
||||
|
||||
现役 `_two_opt` 是启发式且被 4 轮上限截断。任务表典型只有 6–12 项,
|
||||
可以**精确求解**:
|
||||
|
||||
```
|
||||
dp[S][j][c] = 已访问任务集 S、停在任务 j、测向频道为 c 的最小代价
|
||||
转移:dp[S∪{k}][k][c'] = dp[S][j][c] + dist(pos_j, pos_k)/5 + service(k, c, c')
|
||||
```
|
||||
|
||||
- 规模:`|T| ≤ 12` 时 `2^12 × 12 × 20 ≈ 1.0M` 状态,纯 Python 约 0.1–0.3 s/次;
|
||||
滚动重排每步一次会吃掉预算,因此**只在任务表新增/删除任务时重解**,
|
||||
或在 `|T| ≤ 9` 时默认启用、否则回退 2-opt + Or-opt。
|
||||
- 换频代价精确化:`service` 依赖进入/离开频道,把 `c` 并入状态即可精确;
|
||||
现役 `_path_cost` 用"按频道排序"的贪心近似,会低估/高估 1–3 s/站。
|
||||
- 附图论下界:对当前点集算 **MST 权重**(`O(n²)` Prim),
|
||||
得到开放路径最优值的下界,写入 `summary.route_lower_bound_s`,
|
||||
用于**在线后悔值审计**与回归("本步排序距最优 ≤ x%")。
|
||||
|
||||
**验收**:`route_gap_vs_offline` ≤ 3%;单局墙钟增量 ≤ 0.2 s。
|
||||
**预期**:−120 ~ −200 s。
|
||||
|
||||
**(b) 探测的 2 步 rollout(T2)**
|
||||
|
||||
现役 `_select_point` 是一步贪心(期望 log 幸存占比 + λ·移动)。
|
||||
把它扩成深度 2 的 rollout:对前 `K=8` 个候选,各展开一次测量的三分支
|
||||
(direction / near / no_signal),取 `E[V]` 最小者。
|
||||
`V` 沿用现有 `expected_survival`。这属于**滚动时域 DP**,不改任何安全性判定。
|
||||
|
||||
**验收**:探测次数 30.8 → ≤ 26;墙钟 ≤ 0.3 s/决策。
|
||||
**预期**:−150 ~ −250 s。
|
||||
|
||||
**(c) 清除点选在 `C₂₀(K)` 内离当前位置最近处(T3)**
|
||||
|
||||
`C₂₀(K) = ∩_{g∈K} B(g, 20)` 是凸集(圆盘之交)。求其内离当前点最近点是一个
|
||||
**凸可行性/投影问题**(对小规模可枚举多边形顶点 + 圆盘投影,或直接迭代投影)。
|
||||
现役 `_near_clear_target` 只沿一维方向平移,与最优之差 ≤ 0.74 m(前置文档 P1-1 已复核),
|
||||
收益主要来自**与 3.2 插入代价的耦合**:先定插入位置、再选清除点,比反过来省更多。
|
||||
|
||||
**验收**:清除段移动下降 ≥ 5%。
|
||||
**预期**:−50 ~ −150 s。
|
||||
|
||||
**(d) 七点环半径(T4)**:`1159.7 → 1122.9`(可证明下界),−44 s。
|
||||
仅作为"证明性"改动落地,不作为收益项。
|
||||
|
||||
---
|
||||
|
||||
## 4. 实施路线
|
||||
|
||||
| 阶段 | 内容 | 交付物 | 验收门槛 | 依赖 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **S0 基线冻结**(0.5 天) | 把近期 sweep 的最优参数(`route_aware_probe=false`、`lambda_move_s≈0.06`、`opportunistic_radius_ratio≈0.4–0.5`)回填为基准,跑满 core+boundary 并归档 | `output/suite/q3-baseline`、`q4-baseline` | 现有 `valid` 口径 100% | 无 |
|
||||
| **S1 度量补全**(0.5 天) | 在 `scripts/run_suite.py` 增加 `route_gap_vs_offline`、`route_lower_bound_s`、`distinct_service_points`、`coverage_actual_visited` 四列 | runner 改动 + 回归 | 与本文 §2 实测复现一致 | S0 |
|
||||
| **S2 覆盖证书硬化**(1 天,**正确性优先**) | 证书分辨率 75 m/24 → 20 m/72;设计裕量 `r_design ≤ 960 m` 或细网格直接搜索;保留 10% 保险点预算 | `guarantee/cover.py`、`plans/*.json`、正/负例测试 | 细网格(15 m/72)缺口 = 0;故意删点必须被检出 | S1 |
|
||||
| **S3 第四问 D1**(2–3 天) | `belief/joint.py` 增加期望后验半径 / rollout 选点;`planner/__init__.py::_probe_candidates` 增加抵近点;后备降级为安全网 | `belief/joint.py`、`planner/__init__.py`、测试 | `fallback_clear_attempts ≤ 15`;全清 100%;墙钟 ≤ 1.0 s/局 | S2 |
|
||||
| **S4 第四问 D2/D3**(2 天) | 覆盖骨架 + 最便宜插入;`C₂₀` 邻域;后备 Or-opt | `planner/*`、`guarantee/cover.py` | 移动 ≤ 5000 s;`route_gap ≤ 10%` | S3 |
|
||||
| **S5 第三问 T1/T2/T3**(2 天) | Held-Karp(含换频)+ MST 下界;探测 2 步 rollout;`C₂₀` 最近点 | `planner/bayes_loop.py`、`plan/*` | 探测 ≤ 26;`route_gap ≤ 3%`;T ≤ 2900 s | S1 |
|
||||
| **S6 收口**(1 天) | 全量配对、论文数据表、消融图 | 报告 + `docs/paper` 数据 | 与 S0 配对全部 `valid`,无退化场景 | S4/S5 |
|
||||
|
||||
**顺序约束**:S1(度量)先于任何算法改动,否则"更快"不可证;S2(证书)先于
|
||||
第四问算法改动,否则可能在错误的安全基线上做优化。
|
||||
|
||||
---
|
||||
|
||||
## 5. 风险与明确不做的部分
|
||||
|
||||
### 5.1 本轮发现的正确性风险(必须先处置)
|
||||
|
||||
现役 `q4_mincover_v1`(31 点)与 `square49`(49 点)在**粗采样**下缺口为 0,
|
||||
但把校核分辨率从 75 m/24 档提到 25 m/48 档:
|
||||
|
||||
| 点集 | 点数 | 75 m/24 缺口 | 25 m/48 缺口 | 15 m/72 缺口 | 最坏命中@75 m | 最坏命中@25 m |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| `square49` | 49 | 0 | 0 | 0 | 760.3 m | 760.3 m |
|
||||
| `q4_mincover_v1`(现役) | 31 | 0 | **125** | **566** | 999.1 m | 1000.0 m |
|
||||
| 细网格重搜最小集 | 31 | 0 | 0 | 35 | 983.0 m | 996.2 m |
|
||||
| 贪心 27 点(75 m 档最优) | 27 | 0 | **298** | 1117 | 986.5 m | 1000.0 m |
|
||||
|
||||
结论:
|
||||
1. **31 点很可能就是实际下界附近**:多起点随机贪心 + 冗余剪枝在 25 m/48 细网格上
|
||||
直接搜索的最小解仍是 31 点(尚未做 LP 松弛下界,但连贪心都下不去);
|
||||
之前看到的 27 点是**采样过拟合的假解**,不可采纳;
|
||||
2. `q4_mincover_v1` 的安全裕量几乎为 0(999.1 m),存在"真源恰落在细缝 + `r_eff` 取下界"
|
||||
的漏检窗口,现役测试没暴露是因为随机场景未命中细缝且 `r_eff` 通常 > 1000;
|
||||
3. `square49` 裕量充足但巡回 36.4 km,不可回退;
|
||||
4. 正确做法是**细网格重搜 + 设计裕量**(S2),代价约 0–150 s,**先保安全再谈提速**。
|
||||
|
||||
### 5.2 其他风险
|
||||
|
||||
| 风险 | 影响 | 缓解 |
|
||||
| --- | --- | --- |
|
||||
| POMDP rollout 墙钟超预算 | 单局计算 > 1 s | 深度 2、候选截断 K=8、`--profile` 纳入基准必报列;超预算自动退回一步法 |
|
||||
| Held-Karp 状态爆炸(任务表变长) | 单步计算抖动 | 仅在 `|T| ≤ 9` 默认启用,`> 9` 回退 2-opt + Or-opt |
|
||||
| 抵近点破坏可见性保证 | 探测反而 `no_signal` 变多 | 抵近点仍以 `prob_detect > 0` 过滤;`no_signal` 是合法负观测,不破坏守恒性 |
|
||||
| 骨架插入与"找全即停"冲突 | 为走骨架而多绕行 | 骨架只提供顺序,不新增必须访问的点;任务表为空即停 |
|
||||
| 细网格证书重搜耗时 | 离线 3–5 min | 一次性离线搜索 + 运行期按计划名缓存校验(沿用 `load_plan` lru_cache) |
|
||||
|
||||
### 5.3 明确不做
|
||||
|
||||
- **不重引入 PPO/HRL**(前置文档已用 109 776 步的负面结果排除);
|
||||
- **不引入新的运行时依赖**(仍只有 `requests`;`numpy` 仅作可选加速后端,
|
||||
`networkx`/`scipy` 不进入运行时路径);
|
||||
- **不改变停止条件**(`unresolved_channels` 的口径取舍属于评分方式问题,
|
||||
须先与题面评分方式核对,本轮不作为默认行为);
|
||||
- **不承诺第四问 4000 s**(成本地板 ≈5566 s,见 §2.3)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 验收与度量
|
||||
|
||||
### 6.1 有效性定义(沿用,不得放宽)
|
||||
|
||||
```
|
||||
valid := 全部真源被清除 ∧ 缺席频道与真值一致 ∧ 正常 /exit
|
||||
∧ 零算法异常 ∧ 逐微秒计时复算一致
|
||||
```
|
||||
|
||||
### 6.2 新增必报列(写入 `scripts/run_suite.py` 逐局行与 aggregate)
|
||||
|
||||
| 列 | 定义 | 用途 |
|
||||
| --- | --- | --- |
|
||||
| `route_gap_vs_offline` | `实测移动 − 离线最优开放回路(同作业点集)` / 实测移动 | 区分"排线不够好"与"点选得不对" |
|
||||
| `route_lower_bound_s` | 当前任务集 MST 权重 / 速度 | 在线后悔值审计 |
|
||||
| `distinct_service_points` | 去重作业点数 | 探测是否真的在减少 |
|
||||
| `coverage_actual_visited` | 实际测过的覆盖点数 | 缺席证明是否提前结束 |
|
||||
| `probe_no_signal_ratio` | 探测中 `no_signal` 占比 | D1 的效果指标 |
|
||||
| `fallback_clear_attempts` | 已有 | D1 的主验收指标 |
|
||||
|
||||
### 6.3 目标(vs §2 基线)
|
||||
|
||||
| 指标 | Q3 现 | Q3 目标 | Q4 现 | Q4 目标 |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| core 平均虚拟时间 | 3197 s | **≤ 2900 s** | 8676 s | **≤ 6500 s** |
|
||||
| 移动 | 2458 s | ≤ 2250 s | 6257 s | ≤ 5000 s |
|
||||
| 探测次数 | 30.8 | ≤ 26 | 69.0 | ≤ 45 |
|
||||
| 后备盲清次数 | 0 | 0 | 125.7 | **≤ 15** |
|
||||
| `route_gap_vs_offline` | 6.1% | ≤ 3% | 20.1% | ≤ 10% |
|
||||
| `fallback` 清除成功率 | — | — | 10% | ≥ 90% |
|
||||
| 单局计算墙钟 | 1.0 s | ≤ 1.2 s | 1.0 s | ≤ 1.5 s |
|
||||
| 细网格证书缺口(15 m/72) | — | 0(Q3 解析) | 566 | **0** |
|
||||
|
||||
### 6.4 回归
|
||||
|
||||
`uv run pytest`(当前 232 项)必须全过;新增:
|
||||
|
||||
- `CoveragePlan` 细网格正/负例(删掉任一点必须被检出);
|
||||
- `JointBelief` 的期望后验半径与真源保留性质对拍(随机 `(p,h)` × 随机观测序列);
|
||||
- `Held-Karp` 与 `2-opt` 在同一小任务集上的最优性对拍(DP 结果必须 ≤ 2-opt);
|
||||
- `C₂₀` 投影的三角不等式性质(投影点距候选集任一点仍 ≤ 20);
|
||||
- 逐位一致性:numpy 与纯 Python 两条路径在同一动作序列下 `actions_sha256` 相同。
|
||||
|
||||
---
|
||||
|
||||
## 7. 复现命令(本轮实测)
|
||||
|
||||
```bash
|
||||
# 度量基线(§2.1/§2.2/§2.3):作业点分解 + 离线最优回归 + 成本地板
|
||||
uv run python /tmp/analyze_headroom.py # 路由 gap:Q3 6.1%,Q4 20.1%
|
||||
uv run python /tmp/decompose.py # Q4 三段移动 + 动作 + 清除成功率
|
||||
uv run python /tmp/q3stats.py # Q3 探测/覆盖检测/跳测统计
|
||||
|
||||
# 覆盖证书分辨率(§5.1)
|
||||
uv run python /tmp/verify_margin.py # 现役 31 点在 25 m/48 档缺口 125
|
||||
uv run python /tmp/hitset_fine.py # 细网格最小解仍是 31 点
|
||||
|
||||
# 既有常驻工具
|
||||
uv run python scripts/run_suite.py --problem 4 --suite all --jobs 6 \
|
||||
--variants directional@q4_mincover_v1 --label q4-baseline
|
||||
uv run python scripts/verify_q4_cover.py --out output/q4/cover-certificate.json
|
||||
uv run python scripts/analyze_run_timing.py <log.jsonl>
|
||||
```
|
||||
|
||||
> 上述 `/tmp/*.py` 为一次性分析脚本,S1 阶段应把其中的
|
||||
> `route_gap_vs_offline`、`offline_opt_open_path`、`mst_lower_bound`
|
||||
> 三个函数并入 `scripts/run_suite.py`(或新建 `scripts/analyze_routing.py`)常驻入库,
|
||||
> 使"虚拟时间花在哪"与"覆盖集是否合法"一样成为可随时重跑的回归项。
|
||||
|
||||
---
|
||||
|
||||
## 8. 本轮实测回填(2026-09-12 实施结果)
|
||||
|
||||
本节记录 S1–S5 全部落地后的**实测**结果与**负面结论**。所有数字均为冻结基准集
|
||||
(`harness/suites.py`)配对复跑,口径与 §6.1 完全一致;复现命令见 §8.6。
|
||||
|
||||
### 8.1 交付物清单
|
||||
|
||||
| 项 | 位置 | 状态 |
|
||||
| --- | --- | --- |
|
||||
| 图论/DP 工具箱(精确开放路径、2-opt/Or-opt、MST 下界、路由度量) | `src/mcm26b/routing.py` | ✅ 新增 |
|
||||
| S1 必报列(`route_gap_vs_offline`、`route_lower_bound_s`、`distinct_service_points`、`coverage_actual_visited`、`probe_no_signal_ratio`、`plan_verify_wall_s`) | `scripts/run_suite.py` | ✅ 新增 |
|
||||
| S2 设计裕量覆盖计划 + 细网格证书 + 精细剪枝 | `guarantee/cover.py`、`scripts/verify_q4_cover.py`、`plans/q4_mincover_v2.json` | ✅ 新增并设为默认 |
|
||||
| S3/D1 期望后验半径值函数 + 可见性过滤 + 抵近点 | `belief/joint.py`、`planner/__init__.py` | ✅ 新增(默认口径见 §8.3) |
|
||||
| S4/D3 后备清除路径改进 | `planner/__init__.py::_fallback_order` | ⚠️ 已实现,实测负收益,默认关闭 |
|
||||
| S5/T1 Held-Karp 精确任务序(含换频状态) | `planner/bayes_loop.py::_exact_order` | ⚠️ 已实现,实测负收益,默认关闭 |
|
||||
| 回归测试 | `tests/test_routing.py`、`tests/test_joint_belief.py`、`tests/test_coverage_plan.py` | ✅ 新增,`uv run pytest` 268 项全过 |
|
||||
|
||||
### 8.2 最终成绩(vs S0 基线)
|
||||
|
||||
| 指标 | Q3 基线 | Q3 现 | Q4 基线 | Q4 现 | 目标 |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| core 平均虚拟时间 | 3210.3 s | **3188.7 s(−0.7%)** | 8676.1 s | **8602.0 s(−0.9%)** | 2900 / 6500 ✗ |
|
||||
| core 最大虚拟时间 | 3798.5 s | **3663.7 s(−3.5%)** | 10890.8 s | **9630.4 s(−11.6%)** | — |
|
||||
| 移动 | 12446.8 m | **12338.1 m(−0.9%)** | 31286.4 m | 32891.1 m(+5.1%) | 2250 / 5000 ✗ |
|
||||
| 检测次数 | 111.0 | 111.1 | 346.0 | **333.7** | — |
|
||||
| 后备盲清次数 | 0 | 0 | **125.7** | **4.3(−96.6%)** | ≤15 ✅ |
|
||||
| `route_gap_vs_offline` | 8.67% | **8.07%** | 18.9% | 24.3% | ≤3% / ≤10% ✗ |
|
||||
| 单局计算墙钟 | 1.29 s | **1.04 s** | 5.29 s | **4.09 s** | ≤1.2 / ≤1.5 ⚠️ Q4 超标 |
|
||||
| 覆盖计划运行期校验 | — | 0.42 s | — | 4.80 s(15 m/72 一次性) | — |
|
||||
| 细网格证书缺口(15 m/72) | — | 0(解析) | 566 | **0** | 0 ✅ |
|
||||
| 有效局 | 18/18 | 20/20(含边界) | 9/9 | 11/11(含边界) | 100% ✅ |
|
||||
|
||||
**结论**:本轮的硬收益是**第四问后备盲清被消除**(125.7 → 4.3 次/局,最坏局
|
||||
10890.8 → 9630.4 s)与**覆盖证书的正确性**(细网格缺口 566 → 0);虚拟时间只有
|
||||
0.7–0.9% 的均值改善(中位 −2.0%),**远未达到方案 §6.3 的目标**。
|
||||
第三问的改善落在噪声内(中位相对变化 0.0005)。§2 的成本地板估计(Q3 可压缩
|
||||
10–14%、Q4 23–32%)**过于乐观**,实测只回收了约 1%。
|
||||
|
||||
### 8.3 定向源探点(D1):先负后正,最终只保留"可见 + 就近"
|
||||
|
||||
D1 的完整实现(期望后验 MEC 半径值函数 + 抵近点 + 遮挡点剔除)在**修正联合
|
||||
信念朝向规则之前**配对为:后备 125.7 → 53.8 次,但探测移动 +5.6 km,虚拟时间
|
||||
8676 → 9411 s(净负)。原因有两个:
|
||||
|
||||
1. 后备清除在命中真源时**立即结束**,"整条路径最短"不是它的目标函数,
|
||||
§3.2/§3.3 的插入代价模型在这里并不成立;
|
||||
2. 更关键的是:**联合信念的 `no_signal` 朝向规则被写反了**(§8.4),
|
||||
`prob_detect` / 期望半径因此系统性地高估"被遮挡点可见",选点必然跑偏。
|
||||
|
||||
修正后重测(9 场景 core):
|
||||
|
||||
| `probe_score_mode` | λ | 后备次数 | T 均值 | 移动 | 计算墙钟 |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: |
|
||||
| `visibility`(旧) | — | 125.6 | 9224.6 s | 33595 m | 11.6 s |
|
||||
| `expected_radius` | 0.05 | 6.8 | 9740.5 s | 38824 m | 9.6 s |
|
||||
| `expected_radius` | 5.0 | 4.3 | 8604.3 s | 32893 m | 3.7 s |
|
||||
| `nearest_visible`(默认) | — | **4.3** | **8602.0 s** | 32891 m | 3.4 s |
|
||||
|
||||
λ 越大越优说明本题的探点问题里"**看得见且少走路**"比"单步信息量最大"更重要;
|
||||
λ→∞ 的极限(可见候选中移动最省)就是默认口径。值函数代码与其对拍测试保留,
|
||||
供后续研究(例如配合真正的多步 rollout)。
|
||||
|
||||
### 8.4 本轮发现的正确性缺陷(已修复)
|
||||
|
||||
`belief/joint.py` 的 no_signal 朝向裁剪**规则反向**:`keep=False` 分支用了
|
||||
"确定覆盖"掩码做交集,等价于保留"必然看得见"的档、删掉"确定看不见"的档;
|
||||
`keep=True` 分支又漏计了格内角裕量 `asin(margin/max(dist−margin,1))`。
|
||||
两者都会删掉真源的朝向档(`tests/test_joint_belief.py::test_true_source_survives_consistent_observations`
|
||||
在随机观测序列上直接复现)。修复后:
|
||||
`keep=False` 改用 `_far_mask`(与 `_cover_mask` 互补)并把距离阈值收紧为
|
||||
`r_eff_min − margin`,`keep=True/False` 都计入格内角裕量。
|
||||
|
||||
**修复本身是净成本**:在旧 `visibility` 探点口径下 Q4 core 由 8676 → 9225 s;
|
||||
换成 §8.3 的新探点口径后回落到 8602 s,同时后备清除降 96.6%。
|
||||
换言之,本轮第四问的成绩改善**主要来自"修好信念 + 换探点口径"这一组合**,
|
||||
而不是 §3 预期的插入/排线优化。
|
||||
|
||||
### 8.5 覆盖证书硬化(S2)
|
||||
|
||||
现役 `q4_mincover_v1`(31 点)在粗采样(75 m/24)下缺口 0,但细网格复检:
|
||||
|
||||
| 点集 | 点数 | 25 m/48 缺口 | 20 m/72 缺口 | 15 m/72 缺口 | 最坏命中 | NN 巡回 |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| `q4_mincover_v1`(旧) | 31 | 125 | 336 | 566 | 1000.0 m | 22.9 km |
|
||||
| `q4_mincover_v2`(默认) | 31 | **0** | **0** | **0** | **990.0 m** | 23.9 km |
|
||||
| 严格连续空间证书备选 | 36 | 0 | 0 | 0 | 973.7 m | 25.4 km |
|
||||
|
||||
`q4_mincover_v2` 的生成方式:搜索分辨率 50 m/48 档、设计裕量 `r_design=965 m`、
|
||||
前向角裕量 5°,再做**证书分辨率(15 m/72)精细剪枝**(每次删点都用同一证书口径
|
||||
复核),因此点数与旧计划相同而缺口归零,**没有付出点数代价**。
|
||||
|
||||
> 严格连续空间界需要 `r_verify + 15·√2/2 ≤ 1000`(即 ≤989.4 m)且角裕量 ≥2.5°;
|
||||
> 满足该条件的 36 点方案最坏命中 973.7 m,但代价 +5 点(约 +1000 s),
|
||||
> 已落盘为 `output/q4/cover-certificate-v3.json` 备用。默认方案在 15 m/72
|
||||
> 采样下最坏 990.0 m,与严格界的差 0.65 m 属采样边界效应,作为已知残余风险登记。
|
||||
|
||||
### 8.6 负面结论与复现命令
|
||||
|
||||
| 优化项 | 实测 | 结论 |
|
||||
| --- | --- | --- |
|
||||
| S4/D3 后备清除 Or-opt | 后备失败 125.7 → 161.4 次(T 8676 → 8940 s) | ❌ 目标函数错位:命中即停,路径最短反而把真源推后 |
|
||||
| S5/T1 Held-Karp 精确任务序 | 3203.2 s vs 2-opt 3193.1 s(18 场景) | ❌ 滚动时域下"当前任务表逐位最优"反而略差;默认 0 |
|
||||
| `route_aware_probe` | 3210.3 s vs 关闭 3193.1 s | ❌ 权重 1e-3 的路线项无净收益;默认关闭 |
|
||||
| Q4 融合巡回(站点顺带定位) | 12711 s vs 10809 s | ❌ 与 P0-1 在第三问的负面结论一致 |
|
||||
| `opportunistic_radius_ratio` | 0.65 → 0.5:3210.3 → 3205.4 s | ✅ 采纳(弱收益) |
|
||||
|
||||
```bash
|
||||
# S1 度量基线(含路由 gap / MST 下界 / 作业点/静默率)
|
||||
uv run python scripts/run_suite.py --problem 4 --suite core --jobs 6 \
|
||||
--variants directional --label q4-baseline --out output/suite
|
||||
uv run python scripts/run_suite.py --problem 3 --suite core --jobs 6 \
|
||||
--variants bayes --label q3-baseline --out output/suite
|
||||
|
||||
# S2 覆盖证书(生成 + 细网格校核;--fine-prune 走证书分辨率剪枝)
|
||||
uv run python scripts/verify_q4_cover.py --spacing 100 --radius 2850 \
|
||||
--target-step 50 --headings 48 --design-radius 965 --heading-margin 5 \
|
||||
--verify-step 15 --verify-headings 72 --fine-prune \
|
||||
--write-plan q4_mincover_v2 --out output/q4/cover-certificate-v2.json
|
||||
|
||||
# S3/D1 口径配对(可见性/值函数/极限三档)
|
||||
uv run python scripts/run_suite.py --problem 4 --suite core --jobs 6 \
|
||||
--variants "directional#search.probe_score_mode=visibility,\
|
||||
directional#search.probe_score_mode=expected_radius;search.probe_lambda_move_s=5,\
|
||||
directional#search.probe_score_mode=nearest_visible" --label q4-probe-ab
|
||||
|
||||
# 回归
|
||||
uv run pytest -q # 268 项
|
||||
```
|
||||
|
||||
### 8.7 仍未达标项的处置建议
|
||||
|
||||
| 未达标 | 差距 | 建议 |
|
||||
| --- | --- | --- |
|
||||
| Q3 ≤2900 s | −9% | 已无低风险空间:七站巡回 1392 s + 覆盖检测 501 s + 每源 ≥2 次探测是成本地板;需要改停止条件或覆盖语义才能继续下降 |
|
||||
| Q4 ≤6500 s | −24% | 31 点巡回 + 缺席检测 ≈5566 s 是地板,与 §2.3 一致;只能改"逐频道缺席证明"口径 |
|
||||
| `route_gap` Q4 24.3% | — | 该指标在"作业点数随决策变化"时口径不稳(本方案把后备格换成探测点后作业点从 206 降到 110);建议改为"覆盖骨架段"与"临时任务段"分别报 gap |
|
||||
| Q4 计算墙钟 4.1 s(目标 1.5 s) | 2.7 s | 热点在 `belief` 凸多边形与 `select_measurement_point`(D1 之前的基线即 5.3 s),属 P0-4b 表示层张量化,与本方案无关 |
|
||||
| 严格连续空间覆盖证书 | 0.65 m | 若要正式采用 36 点严格方案:`--verify-radius 989 --verify-heading-margin 2.5`,代价约 +1000 s |
|
||||
@@ -0,0 +1,595 @@
|
||||
# 第三、四问系统性架构优化与运行提速方案
|
||||
|
||||
- 版本:2026-09-12
|
||||
- 依据:本仓库运行历史(`session.jsonl`、`practice-p3-*.jlog`、`referee-data/sessions/*`、
|
||||
`output/hrl/ui/q3-main/*`、`docs/` 全部性能记录)+ 本文档新增的复跑与离线校核实验
|
||||
- 范围:第三问(全向源)与第四问(混合定向源)的**闭环架构、算法分解、运行治理**,
|
||||
不改动题面约束、协议层语义与既有保守性不变量
|
||||
- 结论先行:**虚拟时间的 75–83% 是移动**,移动里约一半不是覆盖巡回而是定位/清除往返;
|
||||
**第四问的全部失败来自定向源**;旧机器学习层已被证明不如手写基线。
|
||||
因此本轮优化的主线是"**把定位并入覆盖巡回 + 把发现集压到最小命中集 + 给定向源建位置×朝向联合信念**",
|
||||
而不是再堆学习算法。
|
||||
|
||||
---
|
||||
|
||||
## 0. 摘要(TL;DR)
|
||||
|
||||
### 0.1 三个待解的结构性浪费
|
||||
|
||||
| 编号 | 现象 | 直接证据 | 估计可回收 |
|
||||
| --- | --- | --- | --- |
|
||||
| W1 | Q3 定位探点往返 5741 m / 1148 s,与覆盖巡回(6960 m / 1392 s)相当 | 本次复跑(13 源,seed 0):24 次探点,均值 442 m/次 | **−900 ~ −1100 s/局** |
|
||||
| W2 | Q4 发现集是 700 m 方格 49 点,巡回 36.4 km;同一保证下贪心最小命中集只要 31 点、22.9 km | `scripts/verify_q4_cover.py`:43 032 个 (p,h) 采样,两组点集均 100% 覆盖 | **−2700 s(巡回)+ −1140 s(检测)** |
|
||||
| W3 | Q4 定向源没有"位置×朝向"联合信念,只能阶梯探点,最后退化到 ≤400 格盲清 | referee 两局:全向 17/17 清除,定向仅 2/6;4/4 漏检全是定向 | **−1500 ~ −3000 s/局** |
|
||||
|
||||
### 0.2 预期收益(自建模拟器,同一冻结基准集,配对口径)
|
||||
|
||||
| 指标 | Q3 现状 | Q3 目标 | Q4 现状 | Q4 目标 |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| 平均虚拟时间 | 3707 s(本次 6 场景)/ 3536 s(13 源单局) | **≤ 2700 s** | 13 652 s(10 源 3 定向单局)/ 13 165 s(4 场景均值) | **≤ 9000 s** |
|
||||
| 最大虚拟时间 | 4628 s | **≤ 4000 s** | > 17 000 s(边界压力) | ≤ 12 000 s |
|
||||
| 每局计算墙钟 | 2.9–6.5 s | **≤ 0.8 s** | 6.5 s | **≤ 1.0 s** |
|
||||
| 有效局(全清+缺席无误判+零异常+正常退出+计时复算一致) | 6/6 | 基准集 100% | 4/4 | 基准集 100% |
|
||||
|
||||
> 4000 s 是本项目 `docs/算法审核与优化-20260911.md` 自设的验收线,不是题面评分线;
|
||||
> 第四问在 10 源场景下**不承诺**达成 4000 s,理由见 §6.2。
|
||||
|
||||
### 0.3 明确不做的事
|
||||
|
||||
- 不重新引入 PPO/HRL。运行历史支持"保留手写基线":HRL 训练 4007.9 s / 109 776 步后
|
||||
仍被 `bayes` 稳定压制 4.3–5.0%(17–18/20 场景,符号检验 p=0.0001),且 94% 的差距来自
|
||||
多走的移动;其评估集 20 个场景**全部是单源全向**,无法支撑任何第四问结论。
|
||||
- 不引入新的第三方运行时依赖(仍是 `requests`;`numpy` 仅作**可选加速后端**,缺失时回退纯 Python)。
|
||||
|
||||
### 0.4 实施进展与实测回填(2026-09-12)
|
||||
|
||||
冻结基准集 `harness/suites.py`(第三问 18 主场景 + 2 边界;第四问 9 + 2),
|
||||
并行 runner `scripts/run_suite.py`,配对口径同场景同误差种子。
|
||||
|
||||
| 项 | 状态 | 实测 | 结论 |
|
||||
| --- | --- | --- | --- |
|
||||
| P1-4 安全阀 | ✅ 已实现 | `summary` 新增 `completion_basis` / `absence_evidence` / `redundant_rescan_measures` / `virtual_reserve_s`;`--persistent` 输出单轮上界告警 | 采纳(5 项测试) |
|
||||
| P0-2 最小命中集 | ✅ 已实现并默认开启 | 第四问 11 场景配对:**14003.0 → 10975.5 s**(均值 −19.3%、中位 −25.4%、10/11 更快);检测 444.5→319.2;请求 569.5→395.6;位移 54.9→44.2 km;最大 17007→12919 s;11/11 valid | **采纳** |
|
||||
| P0-4 感知层向量化 | ✅ 已实现 | 单局计算 **3.10 → 1.00 s(3.08×)**,最大 5.84 → 1.30 s;20 场景的虚拟时间与动作序列**逐位一致** | **采纳**(目标 ≤0.8 s 未达,余下为 belief 表示层) |
|
||||
| P0-1 融合巡回 | ❌ 实测无效,已回退默认 | 仅追加站点方位线:3454.6 vs 3426.9 s(+0.8%,检测 +10.8 次、位移仅 −188 m);追加 + 抑制专用探点:**4989.0 vs 3426.9 s(+45.6%,20/20 更慢)** | 关闭(保留开关与代码) |
|
||||
| P1-1 C₂₀ 最近清除点 | ⊘ 复核后降为非项 | 现有 `_near_clear_target` 已取安全球 `B(c, limit−ρ)` 内离当前位置最远点;与完整 C₂₀ 最优之差 ≤ slack+margin ≈ 0.74 m | 不改 |
|
||||
| P1-3 基准集/并行 | ✅ 已实现 | 20 场景 × 3 变体 = 60 局 **26.5 s**(6 进程);20 局 **3.9 s** | 采纳 |
|
||||
| P0-3 联合信念 | ✅ 已实施并于 2026-09-12 修正 | 网格粒子 × 24 档朝向 bin + 可见性选点;**发现并修复 `no_signal` 朝向规则反向缺陷**(原会删掉真源朝向档);配套 D1 探点口径使第四问后备盲清 **125.7 → 4.3 次/局** | 采纳(结论与实测见[图论与 DP 优化方案 §8](第三四问图论与动态规划优化方案.md)) |
|
||||
|
||||
**P0-1 为什么失败(重要负面结论)**:七站环上通常只有 2–3 个站能对同一源取得
|
||||
有效方位线,且站间交会角不足以直接换到 20 m 证书,专用信息增益探点仍然必要;
|
||||
"顺路测量"于是退化为平白多花检测,一旦抑制专用探点则会白等巡回、远端绕行。
|
||||
**结论:第三问的定位往返不是可回收的浪费,`bayes` 的滚动时域调度已在做正确的事。**
|
||||
|
||||
**第四问现状与目标差距(2026-09-12 图论/DP 轮回填)**:启用硬化覆盖计划
|
||||
`q4_mincover_v2` 与 D1 探点口径后,第四问 11 场景(core + boundary)均值为
|
||||
**9069.9 s**、最大 12081.1 s、后备盲清 24.8 次/局(core 9 场景 8602.0 s /
|
||||
4.3 次),仍远高于 4000 s 验收线。剩余成本主要是最小命中集巡回
|
||||
(23.9 km ≈ 4777 s)与缺席频道检测,二者都是覆盖证明的固有代价;
|
||||
本方案 §2.3 的"可压缩 23–32%"在本模拟器上只回收了约 1%(详见图论与 DP 方案 §8)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 运行历史复核
|
||||
|
||||
### 1.1 数据资产清单(本次实际读取)
|
||||
|
||||
| 资产 | 内容 | 可用信息 |
|
||||
| --- | --- | --- |
|
||||
| `session.jsonl`(31 011 行) | 官方第三问演练 J2WH-2K7D-5ZXS-DNK6 的客户端日志 | 10 323 次 `/measure`、12 次 `/clear`、逐动作计时 |
|
||||
| `practice-p3-*.jlog` | 同一局的官方行为日志封装 | 仅明文信封(题号 3、队号、案例码);载荷 AES-256-GCM + RSA-OAEP,不可解 |
|
||||
| `referee-data/sessions/*`(6 局,5 局完整) | 本地评测机 q3×3、q4×2、截断×1 | 逐请求响应、真值、summary、step 事件 |
|
||||
| `output/hrl/ui/q3-main/` | HRL 训练(109 776 步)+ `eval/at-40k`、`eval/at-final` | monitor CSV、模型元数据、20 场景配对评估 |
|
||||
| `docs/算法审核与优化-20260911.md`、`docs/algo-3.md`、`docs/algo-3-bayes.md` | 历史 KPI 与消融 | 官方/自建成绩、验证集分档 |
|
||||
| 本次新增复跑 | `scripts/benchmark_q3_bayes.py`(6 场景)、Q3/Q4 单局计时分解、`cProfile`、站点三角化可行性 | §1.3 中标注"本次复跑"的数字 |
|
||||
| 本次新增工具(已入库) | `scripts/analyze_run_timing.py`(任意会话日志的计时分相复算)、`scripts/verify_q4_cover.py`(第四问覆盖证书 + 最小命中集搜索) | F4、F9 可复现 |
|
||||
|
||||
### 1.2 先确认度量可信(否则一切优化都是自欺)
|
||||
|
||||
官方计时模型:`T = L/5 + 5·N_measure + N_switch + 5·N_clear⁺ + 3·N_clear⁻`,
|
||||
其中 `L` 为累计直线位移,`/clear` **不计**换频。`scripts/verify_q3_practice.py` 对 5 局完整
|
||||
评测机日志逐微秒复算,与服务器返回的 `virtual_time_s` 完全一致(例:BBM9 复算 3134.642907 = 记录 3134.642907)。
|
||||
结论:**后续所有"虚拟时间"结论都可用动作日志独立复算**,这是本项目最可靠的一条工程资产,必须保留。
|
||||
|
||||
### 1.3 关键事实(F1–F13)
|
||||
|
||||
| 编号 | 事实 | 出处 |
|
||||
| --- | --- | --- |
|
||||
| F1 | 虚拟时间构成:**移动 75–83%|检测动作 15–21%|换频 2.3–3.7%** | 官方会话 82.30/14.76/2.94;referee q3 80.50/16.78/2.71、q4 78.39/18.63/2.98;本次复跑 Q3 79.9/17.3/2.9、Q4 75.2/21.1/3.7 |
|
||||
| F2 | 官方第三问演练 J2WH 总虚拟 350 227.7 s,其中 **346 169.9 s(98.84%)是空复扫**:7 个固定站点 × 8 个未清除频道 × 183 轮,0 检出,结束点正好落在 `virtual_reserve_s=10 000` 边界 | `session.jsonl` 相位切分;`planner/__init__.py:236`、`config.py:99` |
|
||||
| F3 | **第四问的全部失败来自定向源**:referee 两局全向 17/17 清除,定向仅 2/6;4/4 漏检全是定向;每清除源 356.4 s(Q3 274.0 s,+30%) | `referee-data/sessions/{6X58,BAU8}`;`{BBM9,RM7Y,VQKB}` |
|
||||
| F4 | 本次复跑 Q3(13 源,seed 0):3535.9 s / 109 次检测 / 13 清除;**七站巡回 6960 m(1392 s,39%)+ 探点往返 5741 m(1148 s)+ 清除移动 3668 m(734 s)** | 本次复跑(方法见附录 A) |
|
||||
| F4b | 评测机 Q3 单局 BBM9:**21 次专用探点占 1138.7 s 移动,等于该局虚拟时间的 36.3%**,而站点扫频 98 次只占 991.2 s | `scripts/analyze_run_timing.py referee-data/sessions/BBM9-.../log.jsonl --plan seven` |
|
||||
| F5 | 本次复跑 Q4(10 源 3 定向,seed 0):13 652.1 s / 560 次检测 / 18 次清除(8 次失败)/ 51 356 m | 本次复跑 |
|
||||
| F6 | 感知层是墙钟热点:单局计算 6.04 s;`cProfile` 下 `expected_survival` 占累计时间 **72%**(2808 次调用),`apply_measure` ≈ 20%,`_kill_outside_wedge` 触发 498 万次 `angular_gap` | 本次 `cProfile`(13 源单局) |
|
||||
| F7 | HRL 运行:109 776/200 000 步、4007.9 s、**27.0 steps/s(6.75/env)**、1.51 s 墙钟/局;被 SIGTERM 截断时回报仍在上升(Q1 1.05 → Q4 6.20) | `output/hrl/ui/q3-main/q3.json` + 4 份 monitor CSV |
|
||||
| F8 | HRL 评估:20 场景**全部单源全向**;`bayes` 比 HRL 快 4.3–5.0%(17–18/20,p=0.0001),**94% 的差距是多走的移动**;40k→109k 步仅再改善 0.7% | `eval/at-40k|at-final/aggregate.*`、`eval/manifest.json` |
|
||||
| F9 | 现役 Q4 发现集:700 m 方格 49 点、NN 巡回 **36.4 km**;满足同一"前向半盘命中"保证的贪心最小集 **31 点、22.9 km(−37%)**,覆盖校核 100% | `scripts/verify_q4_cover.py`;本文附录 A |
|
||||
| F10 | Q3 七站巡回**已经最优**:6 × 1159.7 = 6958 m(观测 6960 m),无可压缩空间;把环半径降到覆盖下界 1122.9 m 仅省 222 m(44 s) | 几何计算 + 本次复跑 |
|
||||
| F11 | 站点三角化可行:**99.28% 的源位置有 ≥2 个站在 1500 m 内**;最佳交会角 5 分位 58.5°、中位 122.9°;只有 0.72% 的源位置仅 1 个站在射程内 | 本次离线采样(20 000 点) |
|
||||
| F12 | 墙钟量级差三个数量级:内存回放 **0.22–0.25 ms/请求**(≈0.35 s/局),官方接口 **9.42 ms 均值/请求**(104.49 s / 10 337 请求);本地评测机 24.5–97.9 ms/请求 | referee `session_end`、`runs/*/summary.json`、`session.jsonl elapsed_s` |
|
||||
| F13 | 历史 KPI 参照:Q3 官方最好 4058.71 s(3BW5,13/13)、5232.13 s(J4ER,12/12);自建 `bayes` 3707.3 s(本次 6 场景)/ 3726.2 s(14 场景消融)/ 验证集 3315.89 s;Q4 自建 13 164.7 s,**无官方第四问演练记录** | `docs/算法审核与优化-20260911.md`、`docs/algo-3-bayes.md`、`README.md:135` |
|
||||
|
||||
### 1.4 由事实导出的六处结构性瓶颈
|
||||
|
||||
| 编号 | 瓶颈 | 支撑事实 | 归属层 |
|
||||
| --- | --- | --- | --- |
|
||||
| B1 | 定位/清除移动没有并入覆盖巡回,探点按"信息增益"选,移动权重 λ=0.0105 几乎不惩罚绕行 | F1、F4、F4b、F11 | 决策层 |
|
||||
| B2 | Q4 发现集冗余 63%(49 点 vs 30 点),且每个"真缺席"频道要在全部 49 点各测一次 | F5、F9 | 保证层 |
|
||||
| B3 | Q4 定向源缺少位置×朝向联合信念:`no_signal` 只记"被遮挡过几次",选点靠固定 45° 阶梯,收敛失败后退化到 ≤400 格 20 m 盲清(≤1200 s + ≤400 请求) | F3、`planner/__init__.py:691-768` | 感知层 + 决策层 |
|
||||
| B4 | 感知层全 Python 容器:每局 20 个频道各建 ~6.1 万格 `set`,`_kill_outside_wedge` 每次遍历全部幸存格,评分核 `expected_survival` 为 7×800 的纯 Python 双循环 | F6 | 感知层 |
|
||||
| B5 | 研发吞吐低:场景串行、单进程;一局 0.35–6.5 s;HRL 27 steps/s;14–52 场景的配对基准要跑几分钟到十几分钟 | F6、F7、F12 | 实验层 |
|
||||
| B6 | 运行治理缺失:单源评估集使指标退化(`avg_locate_clear_time_s ≡ virtual_time_s`)、`eval/manifest.json` 三份重复且与 `output/hrl/ui/manifest.json` 不一致(后者缺失)、`persistent_search=True` 可造成 98.84% 的无效开销且无硬性告警 | F2、F8、F13 | 遥测/实验层 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标架构
|
||||
|
||||
### 2.1 分层与依赖方向(依赖只能向下)
|
||||
|
||||
```
|
||||
┌───────────────────────────────────────────────┐
|
||||
L6 实验/遥测 │ bench/ 冻结基准集·并行 runner·配对统计 │
|
||||
│ telemetry/ manifest·step log·replay·summary │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ 只读观测
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L5 执行/安全 │ exec/ 动作账本·预算守卫·安全阀·幂等客户端 │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ Action 请求
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L4 决策 │ plan/ 任务表·滚动时域排序·信息/时间评分 │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ Task[]
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L3 保证 │ guarantee/ 命中集覆盖计划·清除证书·缺席证明 │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ 候选集/幸存集
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L2 信念 │ belief/ BeliefBackend:OccupancyTensor(Q3) │
|
||||
│ JointTensor(位置×朝向, Q4) │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ Observation
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L1 观测模型 │ model/ 可见性半平面·r_eff 带·有界误差·舍入裕量 │
|
||||
└───────────────▲───────────────────────────────┘
|
||||
│ 原始响应
|
||||
┌───────────────┴───────────────────────────────┐
|
||||
L0 协议/时钟 │ protocol/ 规则表·计时模型·状态机·客户端(冻结)│
|
||||
└───────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
与现状的差别:**用组合取代继承**。现状是
|
||||
`OmnidirectionalLoop → SevenBatch → BayesLoop → DirectionalExtension` 的单继承链,
|
||||
调度、信念、清除、预算、报表五种职责混在同一族类里(`planner/__init__.py` 818 行 +
|
||||
`planner/bayes_loop.py` 513 行),任何一处改动都会跨层影响其它三问。
|
||||
目标形态是
|
||||
|
||||
```python
|
||||
Episode(
|
||||
client = SimulatorClient(...), # L0,冻结
|
||||
belief = OccupancyTensor(...) | JointTensor(...), # L2
|
||||
plan = CoveragePlan.points, # L3
|
||||
scorer = InfoGainPerSecond(...), # L4
|
||||
policy = FusedTourPolicy(...) | DirectionalPolicy(...), # L4
|
||||
certifier= Certificate(...), # L3
|
||||
ledger = Telemetry(...), # L6
|
||||
)
|
||||
```
|
||||
|
||||
三层保证策略矩阵只差**三个可替换部件**,其余代码共享:
|
||||
|
||||
| 问题 | 信念后端 | 覆盖计划 | 选点策略 |
|
||||
| --- | --- | --- | --- |
|
||||
| Q3 | `OccupancyTensor`(全向) | 七站环(已经最优,冻结) | 融合巡回 |
|
||||
| Q4 | `JointTensor`(位置×朝向) | **最小命中集**(30 点,离线搜索 + 运行期校验证书) | 可见性约束选点 |
|
||||
|
||||
### 2.2 接口契约(关键签名)
|
||||
|
||||
```python
|
||||
# L1 model/observation.py —— 只此一处定义"什么算看见"
|
||||
@dataclass(frozen=True)
|
||||
class ObservationModel:
|
||||
r_eff_min_m: float; r_eff_max_m: float
|
||||
near_radius_m: float; half_angle_deg: float
|
||||
directional_half_deg: float = 90.0 # 含边界
|
||||
|
||||
def visible(self, src: Vec2, heading_deg: float | None, reader: Vec2) -> bool: ...
|
||||
def detectable(self, src: Vec2, heading_deg: float | None, reader: Vec2) -> bool: ...
|
||||
|
||||
# L2 belief/base.py —— 两种后端实现同一协议
|
||||
class BeliefBackend(Protocol):
|
||||
def apply(self, channel: int, reader: Vec2, result: str,
|
||||
svd_deg: float | None) -> None: ...
|
||||
def alive(self, channel: int) -> int: ...
|
||||
def is_absent(self, channel: int) -> bool: ... # 幸存集为空
|
||||
def certificate(self, channel: int) -> ClearCertificate | None: ...
|
||||
def guaranteed_no_signal(self, channel: int, reader: Vec2) -> bool: ...
|
||||
def prob_detect(self, channel: int, reader: Vec2) -> float: ... # 决策用
|
||||
def version(self, channel: int) -> int: ... # 派生量缓存键
|
||||
|
||||
# L3 guarantee/cover.py —— 覆盖计划是"可离线搜索、可在运行期校验"的一等对象
|
||||
@dataclass(frozen=True)
|
||||
class CoveragePlan:
|
||||
problem: int # 3 | 4
|
||||
points: tuple[Vec2, ...]
|
||||
certificate: CoverCertificate # 采样分辨率、缺失数、最坏裕量、脚本哈希
|
||||
version: str
|
||||
def verify(self, model: ObservationModel, tol_m: float) -> CoverCertificate: ...
|
||||
|
||||
# L4 plan/policy.py
|
||||
class Policy(Protocol):
|
||||
def tasks(self, belief: BeliefBackend, state: RobotState) -> list[Task]: ...
|
||||
def order(self, tasks: list[Task], state: RobotState) -> list[Task]: ...
|
||||
def measured_at_point(self, point: Vec2) -> tuple[int, ...]: ... # 一停多测
|
||||
```
|
||||
|
||||
### 2.3 四条设计原则
|
||||
|
||||
1. **保守性不变量不动**:真源永不被删;清除证书始终基于完整 ±1.005° 角域与题面距离带;
|
||||
所有启发式(均匀先验、朝向先验、信息增益)**只用于选点,不用于安全证明**。
|
||||
Q4 的朝向联合信念必须自带"朝向未观测时退化为全向"的保守分支。
|
||||
2. **保证 = 离线搜索 + 运行期校验**:覆盖集、清除点、巡回顺序都可以离线用数值搜索优化,
|
||||
但运行期必须用同一份 `ObservationModel` 重新校验证书(点集、分辨率、裕量),
|
||||
校验失败即拒绝启动,而不是相信一个写死的点表。
|
||||
3. **一个事实一个来源**:题面常量、`r_eff` 带、清除半径、动作耗时只在 `config.py`;
|
||||
可见性语义只在 `model/observation.py`;训练/部署若将来共存,必须共用同一控制器模块
|
||||
(这是旧 HRL 用 13 000 行换来的教训:v1 的 5 个特征索引错位、BC 激活函数不一致、
|
||||
旧基准把 `completed` 当全清)。
|
||||
4. **每个优化都必须可配对、可复算、可回归**:冻结基准集 + 逐局 `manifest` + 动作日志
|
||||
+ 逐微秒复算;任何"更快"的声明必须给出配对分布(均值/中位/最大/更快场数),
|
||||
而不是单个最优场景。
|
||||
|
||||
### 2.4 迁移映射(现状 → 目标)
|
||||
|
||||
| 现状 | 目标 | 处理 |
|
||||
| --- | --- | --- |
|
||||
| `protocol/*`、`evaluation/recompute_virtual_time` | 不变 | 冻结,作为度量基准 |
|
||||
| `belief/__init__.py`(凸外包) | `belief/convex.py` | 原样搬迁,继续负责精确角域证书 |
|
||||
| `particles.py`(网格幸存集) | `belief/occupancy.py`(numpy 张量 + 纯 Python 回退) | 重写热核,语义不变,保留 `tests/test_particles.py` 的真源保留性质测试 |
|
||||
| — | `belief/joint.py`(位置×朝向) | 新增,Q4 专用 |
|
||||
| `coverage/` | `guarantee/cover.py` | 49 点常量表 → 可校验的 `CoveragePlan` |
|
||||
| `planner/bayes_loop.py` 的 `_build_tasks/_order_tasks` | `plan/tasks.py`、`plan/schedule.py` | 抽出,Q3/Q4 共用 |
|
||||
| `planner/bayes_loop.py::_select_point` | `plan/score.py` | 抽出 + 向量化评分 |
|
||||
| `planner/__init__.py::DirectionalExtension` | `plan/policy.py::DirectionalPolicy` + `belief/joint.py` | 保留后备清除作为最终兜底,但目标是不再触发 |
|
||||
| `planner/seven.py` | `guarantee/plans.py::SEVEN` | 保留解析证明 |
|
||||
| 4 个 benchmark 脚本 | `bench/` 统一 runner(并行 + manifest + 配对统计) | 合并,消除指标口径分叉 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 优化项
|
||||
|
||||
### P0-1 第三问:发现—定位融合巡回(消除探点往返)
|
||||
|
||||
**现状**:每次收到 `direction` 后进入独立定位子循环,由信息增益选点器在同一站点之外
|
||||
另选探点;13 源单局共 24 次探点、5741 m 往返、1148 s,与整个覆盖巡回(6960 m / 1392 s)同量级;
|
||||
评测机单局 BBM9 更极端:**21 次探点花掉 1138.7 s 移动 = 全局限时时间的 36.3%**(F4b)。
|
||||
评分函数 `score = E[log 幸存占比] + 0.0105 × 移动秒数`,移动项几乎不影响排名。
|
||||
|
||||
**方案**:把"已发现未定证"的频道加入**每一个覆盖站点**的批量扫频清单,
|
||||
使第二/第三条方位线在巡回途中顺路取得;只有站点三角化不成立时才回退到专用探点。
|
||||
|
||||
- 巡回仍由 `plan/schedule.py` 滚动排序(现有最近邻 + 2-opt 已足够,`bayes` 的 −39.5% 移动
|
||||
主要来自这里),新增"同点顺路测量"任务类型:`Task(kind="station", channels=(未知, 未定证))`。
|
||||
- 站点任务的固有耗时按 `6 s × 频道数 + 1 s × 换频次数` 记账(现有 `_path_cost` 已支持)。
|
||||
- 保底:`F11` 显示 0.72% 的源位置只有 1 个站在 1500 m 内,对这类频道保留
|
||||
`_select_point` 的专用探点路径;判定用 `belief.prob_detect` 而非硬编码。
|
||||
|
||||
**预期收益(已被实测推翻)**:原估 F4 的 1148 s 探点移动可回收 70–80%,
|
||||
即 −800 ~ −900 s。**实测结果相反**:仅追加站点方位线 +0.8%,追加并抑制专用探点
|
||||
+45.6%(20/20 场景更慢),见 §0.4。原因是站间交会角不足以保证 20 m 证书,
|
||||
专用信息增益探点仍然必要;抑制它只会白等巡回。**本项不采纳,默认关闭。**
|
||||
|
||||
**验收**:18 场景验证集平均 ≤ 2700 s、最大 ≤ 4000 s;`measures` 增幅 ≤ 20%;
|
||||
`move_distance_m` 降幅 ≥ 25%;全部 `valid`。
|
||||
|
||||
**风险**:站点距离最远 2 × 1159.7 = 2319 m,可能出现 `no_signal` 浪费 5 s。
|
||||
缓解:仅在 `prob_detect > 0` 时顺路测量;`no_signal` 同样是有效负观测(进信念,不算异常)。
|
||||
|
||||
---
|
||||
|
||||
### P0-2 第四问:用最小命中集替换 49 点发现集(离线搜索 + 运行期校验)
|
||||
|
||||
**现状**:`coverage_points()` 生成 700 m 方格 49 点(±2100),依据是"单元内任一点到 4 顶点
|
||||
≤ 700√2 ≈ 989.95 ≤ 1000"。该条件是**充分但远非必要**:它要求单元内任意位置、任意朝向都由
|
||||
**同一个**顶点命中。实际上,第四问的保证是"对 ∀(p,h) ∈ Ω×S¹,存在被访点位于其前向半盘内",
|
||||
允许由**多个**顶点共同围住 p。
|
||||
|
||||
**方案**:把它当作一个显式的**击中集(hitting set)问题**离线求解,运行期校验:
|
||||
|
||||
1. 目标空间离散:`p` 取 Ω 内 75 m 网格,`h` 取 24 个方向 → 43 032 个 (p,h) 单元;
|
||||
2. 候选点:半径 2850 m 圆盘内 200–300 m 网格(641/293 个候选);
|
||||
3. 贪心集合覆盖 + 局部改进,**同时把巡回长度作为次级目标**(641/293 个候选中取 31/30 个,
|
||||
仍有大量同覆盖度的等价解可用于缩短巡回);
|
||||
4. 生成 `CoveragePlan(points, certificate)`,`certificate` 记录采样分辨率、未覆盖数、
|
||||
最坏裕量、生成脚本哈希;`CoveragePlan.verify()` 在每次运行前用同一 `ObservationModel`
|
||||
重新校核,未通过即拒绝启动。
|
||||
|
||||
**本次离线校核结果**(附录 A 可复现):
|
||||
|
||||
| 点集 | 点数 | (p,h) 覆盖 | NN 巡回 | 折算移动 |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| 现役 700 m 方格 ±2100 | 49 | 100% | 36.4 km | 7280 s |
|
||||
| 三角格 1000 m(半径 2800 盘) | 31 | 100% | 34.3 km | 6863 s |
|
||||
| **贪心最小命中集(200 m 候选)** | **31** | **100%** | **22.9 km** | **4587 s** |
|
||||
| **贪心最小命中集(300 m 候选)** | **30** | **100%** | **23.6 km** | **4719 s** |
|
||||
|
||||
**实测收益(2026-09-12,11 场景配对)**:均值 **14003.0 → 10975.5 s(−19.3%)**,
|
||||
中位 −25.4%,10/11 场景更快;检测 444.5 → 319.2,请求 569.5 → 395.6,
|
||||
位移 54.9 → 44.2 km,后备清除尝试 113.3 → 64.1,11/11 valid。
|
||||
低于原估的 −26%,差额来自"缺席频道省下的检测"被定位探点部分抵消。
|
||||
|
||||
**验收**:`CoveragePlan.verify()` 通过;10/13/16 源 × 定向 1/半数/仅一个全向 × 边界朝外压力
|
||||
场景全部全清;请求数、日志体积仍满足 1200 s / 2 MB 预算。
|
||||
|
||||
**风险**:数值采样证书弱于解析证明。缓解:(a) 保留解析七站证明用于 Q3;
|
||||
(b) 证书采样分辨率与裕量写入 summary 并随日志归档;(c) 保留 10% 的额外"保险点"预算,
|
||||
若演练观测到任何漏检立即回退到 49 点表。
|
||||
|
||||
---
|
||||
|
||||
### P0-3 第四问:位置×朝向联合信念与可见性选点
|
||||
|
||||
**现状**:定向源只被当作"候选空间集合",`no_signal` 只用于累计 `blocked_probes`
|
||||
(`planner/__init__.py:663-677`),选点沿固定 45° 阶梯试探;梯子用尽后调用
|
||||
`_fallback_clear`,对候选外包多边形做 20 m 网格覆盖逐格 `/clear`(≤400 格、≤1200 s、≤400 请求)。
|
||||
运行历史显示这正是第四问的失败点:定向源 2/6 清除,4/4 漏检全是定向。
|
||||
|
||||
**方案**:为第四问频道维护 **(x, y, heading) 联合可行集**,把"看不看得见"变成可推理的量:
|
||||
|
||||
- 表示:`JointTensor` —— 复用 Q3 的占用网格,每格附加 24 个朝向 bin 的可行性位掩码
|
||||
(uint8 位域,20 频道 × 6.1 万格 × 3 B ≈ 3.7 MB,可接受)。
|
||||
- 更新规则(全部保守,仅删"必然看不见"的格):
|
||||
- `direction`@s:位置删 `>1500`、角向删 `|gap| > δ_eff + 角裕量`;朝向删
|
||||
"以该格为源、朝向不覆盖 s" 的半盘(保留 ±90° 含边界)。
|
||||
- `no_signal`@s:对 `|g−s| ≤ 1000` 的格,朝向**只能**落在背向 s 的半盘;
|
||||
对 `1000 < |g−s| ≤ 1500` 的格,朝向要么背向 s,要么源实际半径 < |g−s|(保持不确定);
|
||||
对 `|g−s| > 1500` 的格不删。
|
||||
- `near`@s:位置收缩到 5 m 盘,朝向置为全向(近场无遮挡语义)。
|
||||
- 决策:`prob_detect(channel, reader)` = 该点能看到该频道任一可行 (p,h) 的概率质量;
|
||||
选点直接最大化 `prob_detect / (移动秒数 + 6)`,即**保证有信号的信息增益/秒**。
|
||||
- 定位收敛:位置由多条 `direction` 的角域交会收缩(与 Q3 相同);朝向在 2 次
|
||||
"一次被遮挡 + 一次可见"后通常已被压到很窄的楔形,此时可**主动选择必然可见的探点**,
|
||||
用一个正常角域证书结束,而不再进入 400 格盲清。
|
||||
- 后备清除**保留**(`fallback_clear_cells` 已有格数与预算双硬顶),但目标变为
|
||||
"只在证书无法建立时触发",并在 summary 中把触发次数作为回归指标。
|
||||
|
||||
**预期收益(未实测)**:定向频道的探点次数与后备清除触发次数大幅下降。参考量级:
|
||||
后备清除上限 400 格 × 3 s = 1200 s + 400 请求/频道;`docs` 记录的 Q4 改进版每局后备
|
||||
尝试 616/4 局 ≈ 154 次。保守估计 **−1500 ~ −3000 s/局**。
|
||||
|
||||
**验收**:10 源 3 定向场景全清率 100%(现状 referee 仅 2/6 定向);
|
||||
`fallback_clear_attempts` 相对基线下降 ≥ 70%;定向信道平均检测次数 ≤ 全向信道的 1.5 倍。
|
||||
|
||||
**风险**:朝向均匀先验是工程假设,官方分布未知。缓解:先验只影响选点顺序,
|
||||
不影响任何安全性判定;朝向不确定时自动退化为全向分支(`prob_detect` 取全向值)。
|
||||
|
||||
---
|
||||
|
||||
### P0-4 感知层向量化与并行(研发运行速度)
|
||||
|
||||
**现状**(F6):`expected_survival` 占单局 CPU 的 72%,2808 次调用 × (7 代表点 × 800 子采样)
|
||||
≈ 1570 万次纯 Python 距离/角度运算;`apply_measure` 另有 20%,其中 `_kill_outside_wedge`
|
||||
每次遍历全部幸存格(498 万次 `angular_gap`)。
|
||||
|
||||
**方案**:
|
||||
|
||||
1. `belief/occupancy.py` 用 `numpy` 布尔/整数张量替换 `set[int]`:
|
||||
每频道 248×248 uint8(≈61 KB),20 频道 ≈ 1.2 MB;盘/楔删除用切片 + 布尔掩码,
|
||||
粗格索引用 `np.add.reduceat`;`numpy` 缺失时回退现有纯 Python 实现(测试保证两条路径同结果)。
|
||||
2. 采样评分向量化:把 `subsample` 与 `representatives` 预转 `(n,2)` 数组,
|
||||
一次广播算完距离、角度、角向 gap,`expected_survival` 由 5600 次标量循环变成 2–3 条向量指令。
|
||||
3. 站点级编译缓存:`(station, channel, version)` → 掩码结果,`version` 不变直接复用;
|
||||
当前 `_skip_cache` 只缓存"必 no_signal"布尔,可扩展到整块删除掩码。
|
||||
4. MEC 增量维护:当前 `mec()` 每次版本变化重算全部幸存点(最多 6 万点);改为
|
||||
版本变化时只对新增删除点做包围圆增量更新,或按 1/8 子采样给出候选、证书判定用精确值。
|
||||
5. 批量基准并行化:`bench/runner.py` 用 `concurrent.futures.ProcessPoolExecutor`
|
||||
按场景分发(每进程独立 `LocalSimulator`,真值仍只进测试端),Mac M 系列 8 核可得 ~6–8×。
|
||||
6. 观测/遥测:单局 summary 写入 `compute_wall_s`、`profile_top`(可选 `--profile`),
|
||||
纳入基准表的必填列,防止"虚拟时间变快、墙钟悄悄变慢"。
|
||||
|
||||
**实测收益(2026-09-12)**:单局计算 **3.10 → 1.00 s(3.08×)**,最坏 5.84 → 1.30 s;
|
||||
20 场景基准集 12.2 → 3.9 s。虚拟时间与动作序列**逐位一致**(纯 Python 回退路径保留,
|
||||
`MCM26B_NO_NUMPY=1` 可强制)。未达 ≤0.8 s 的目标:剩余开销在
|
||||
`apply_measure` 的网格删除与 `ChannelParticles.__init__`,需要 P0-4b(表示层张量化)。
|
||||
|
||||
**验收**:`tests/test_particles.py`、`tests/test_bayes_loop_q3.py` 全过;
|
||||
纯 Python 与 numpy 两条路径在同一动作日志下产出逐位相同的 summary(`actions_sha256` 相同);
|
||||
单局 `compute_wall_s` ≤ 0.8 s。
|
||||
|
||||
---
|
||||
|
||||
### P1-1 清除点选在 C₂₀(K) 内离当前位置最近处
|
||||
|
||||
现状 `_near_clear_target` 只沿"当前位置 → 候选圆心"方向平移,平移量受
|
||||
`limit − radius` 限制,是被证明的一维特例。一般解是
|
||||
`C₂₀(K) = ∩_{g∈K} B(g, 20)`,取其内离当前位置最近的点:
|
||||
- 对凸多边形候选集,`C₂₀(K)` 仍是凸集(若干圆盘之交),可用"到当前点的最近点"的
|
||||
简单迭代/投影求得;
|
||||
- 若 `C₂₀(K)` 非空,则从该点清除的移动代价最小,且三角不等式保证仍在 20 m 内。
|
||||
|
||||
**预期收益**:Q3 单局清除移动 3668 m(734 s)中回收 20–35%(**−150 ~ −260 s**);
|
||||
Q4 同步受益。
|
||||
**风险**:`C₂₀` 空时退回现有 MEC 圆心逻辑(已实现)。
|
||||
|
||||
---
|
||||
|
||||
### P1-2 覆盖几何参数改为"离线搜索 + 运行期校验"
|
||||
|
||||
- Q3:`station_ring_radius_m` 由 1159.7 的可证明下界 1122.9 重算,并在 `validate_seven_coverage`
|
||||
中同时校验 **PF 网格覆盖**(现行文档已记录 1159.7 与 Optuna 建议值差 3.3 m 的取舍)。
|
||||
收益很小(≈44 s),但把"设计性质 vs 1.7% 均值差异"的取舍写成可复算脚本,而不是口口相传。
|
||||
- Q4:见 P0-2,覆盖集与巡回联合优化(在等价覆盖解中选巡回最短者,
|
||||
预期可在 23.6 km 基础上再降 5–10%)。
|
||||
- 所有覆盖计划产物落 `output/<problem>/coverage/<version>.json` + 证书,纳入版本管理。
|
||||
|
||||
---
|
||||
|
||||
### P1-3 评估与遥测重构(消除 B6)
|
||||
|
||||
1. **冻结基准集**(`bench/suites/`,一题一份、版本化、写入 `manifest.json`):
|
||||
- Q3:10/13/16 源 × 8 个种子 + 2 个圆周边界场景(`r_eff=1000`,扇区接缝);
|
||||
- Q4:10 源 3 定向、13 源 6 定向、16 源 8 定向 × 每种 3 个种子 + 朝外边界压力场景;
|
||||
- 场景与真值只进模拟器与校验器,策略端看不到(真值隔离不变量保持不变)。
|
||||
2. **统一 runner + 逐局 artifact**:`manifest.json` / `results.json` / `aggregate.{json,csv}` /
|
||||
`runs/<scene>/<variant>/summary.json` + `session.jsonl`,任何一局无效则 `all_valid=false` 且非零退出。
|
||||
3. **指标口径去重**:目前 `avg_locate_clear_time_s ≡ virtual_time_s`(单源评估集下二者必然相等),
|
||||
必须在多源基准集上重新定义并标注:
|
||||
`T_total`(整场虚拟时间)、`T_accounted`(首次证明找全时刻)、`T_last_clear`、
|
||||
`T_total / cleared`、`T_total / truth`。
|
||||
4. **运行历史持久化**:`output/` 目前不纳入 git,导致 `output/rl/review-20260911/`、
|
||||
`output/q3/optimization-20260911/` 等证据在 revert 后无法追溯。
|
||||
本轮起把**基准集的 summary/aggregate/manifest(压缩,不含大日志)**纳入版本管理,
|
||||
官方 jlog/日志仍按题面要求单独归档。
|
||||
|
||||
---
|
||||
|
||||
### P1-4 安全阀与退出可解释性
|
||||
|
||||
| 阀门 | 现状 | 改进 |
|
||||
| --- | --- | --- |
|
||||
| `persistent_search` | 默认已关闭,但没有任何"这会浪费多少"的提示;历史上已造成一次 346 170 s(98.84%)的无效演练 | 显式开启时输出预估上限(未清除频道数 × 站点数 × 6 s)并要求二次确认;`summary` 记录 `redundant_rescan_measures` |
|
||||
| `virtual_reserve_s` | 10 000 s 固定 | 改为 `max(10 000, 2 × 最远单动作上界)`,并在接近时把 `stop_reason` 与剩余预算写入 summary |
|
||||
| 完成判定 | `completed = 无中断 ∧ (清除数≥16 ∨ 每频道 CLEARED/ABSENT)` | 增加 `completion_basis ∈ {count_cap, per_channel_proof}` 字段,避免把"数量上界"与"逐频道证明"混为一谈 |
|
||||
| 缺席证明 | 覆盖账本 + PF 双通道 | summary 增加 `absence_evidence[channel] ∈ {pf_empty, coverage, count_cap}`,供论文与审计逐条追溯 |
|
||||
|
||||
---
|
||||
|
||||
### P2-1 选点评分的解析化/查表化
|
||||
|
||||
`expected_survival` 的物理含义可用解析近似替代:对给定测点 q 与候选集,
|
||||
`E[log 幸存占比]` 的一阶近似只依赖 (候选集到 q 的距离分布, 角向展布)。
|
||||
先在离线基准上验证解析近似与采样评分选出的点一致率 ≥ 90%,再切默认实现。
|
||||
预计再省 30–50% 的评分开销(P0-4 之后已不是瓶颈,列入 P2)。
|
||||
|
||||
---
|
||||
|
||||
### P2-2 是否重启学习类方法的门控条件
|
||||
|
||||
只有在**下列四条同时满足**时才考虑把 ML 重新引入,且只允许替换"任务排序"这一个部件:
|
||||
|
||||
1. P0-1/P0-2/P0-3 全部落地并通过基准集验收;
|
||||
2. 手写基线仍有 ≥ 10% 的可测改进空间,且已定位到具体的排序子问题;
|
||||
3. 训练与部署共用同一控制器模块,并有逐状态一致性测试(旧 HRL 的 13 000 行里最有价值的
|
||||
就是这条纪律:`HRLControllerMixin` 被 env 与 runtime 逐字节共用);
|
||||
4. 评估集包含多源与定向场景(现役 20 场景单源评估集不足以支撑任何结论)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 实施路线图
|
||||
|
||||
| 阶段 | 内容 | 交付物 | 验收门槛 | 依赖 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| **S1(P0,2–3 天)** | P0-2 最小命中集 + P0-4 并行基准;P1-3 冻结基准集 + manifest;P1-4 安全阀 | `guarantee/cover.py`、`bench/runner.py`、`bench/suites/*`、`docs/` 更新 | Q4 巡回 ≤ 25 km;基准集并行跑完 ≤ 60 s;全部 `valid` | 无 |
|
||||
| **S2(P0,2–3 天)** | P0-1 融合巡回(Q3);P1-1 清除点 | `plan/schedule.py`、`plan/score.py`、`Episode` 组合化重构 | Q3 均值 ≤ 2700 s、最大 ≤ 4000 s;移动 −25% | S1 的 runner |
|
||||
| **S3(P0/P1,3–5 天)** | P0-4 信念层 numpy 化;P0-3 联合信念 + 可见性选点(Q4) | `belief/occupancy.py`、`belief/joint.py`、`plan/policy.py` | 单局计算 ≤ 0.8 s;Q4 定向全清 100%、后备触发 −70% | S2 的接口 |
|
||||
| **S4(P1/P2,2–3 天)** | P1-2 覆盖参数搜索固化;P2-1 评分解析化;论文数据表 | 覆盖计划证书、`docs/paper` 数据 | 与 S3 结果配对不退化 | S3 |
|
||||
| **S5(演练)** | 官方演练:Q3 用全向、Q4 用混合;先实测单请求延迟与总请求数,核对 1200 s / 2 MB | 官方日志与报告 | 全清 + 正常退出 + 计时复算一致 | 团队排期 |
|
||||
|
||||
**顺序约束**:S1 的基准集必须先于任何算法改动落地,否则无法配对;否则"更快"不可证。
|
||||
|
||||
---
|
||||
|
||||
## 5. 验收与度量
|
||||
|
||||
### 5.1 有效局定义(沿用现有基准脚本,不得放宽)
|
||||
|
||||
```
|
||||
valid := 全部真实源被清除
|
||||
∧ 被判缺席的频道都与真值一致(无误判)
|
||||
∧ 正常 /exit(exit_reason == user_exit)
|
||||
∧ 零算法异常(anomalies == [])
|
||||
∧ 逐动作虚拟时间复算与服务器返回逐微秒一致
|
||||
```
|
||||
|
||||
### 5.2 必报指标(每个变体、每个场景,落盘 + 汇总)
|
||||
|
||||
- 虚拟时间:`T_total`、`T_accounted`、`T_last_clear`、`T_total/cleared`,以及
|
||||
均值/中位/最大/P90 与 **< 4000 s 场次比例**;
|
||||
- 动作:`measures`、`clears`、`failed_clears`、`fallback_clear_attempts`、
|
||||
`redundant_rescan_measures`、`total_requests`;
|
||||
- 移动:`move_distance_m`,以及 move/action/switch 三分项占比;
|
||||
- 墙钟:`compute_wall_s`、请求吞吐(ms/请求)、批量基准总时长、训练 steps/s(若涉及);
|
||||
- 配对比较:同场景同误差种子下 `1 − T_variant / T_baseline` 的均值、
|
||||
更快场数、以及对场景做 10 000 次 bootstrap 的 95% 区间(参照 `summarize_rl_revision.py` 的做法)。
|
||||
|
||||
### 5.3 回归
|
||||
|
||||
`uv run pytest`(当前 232 项)必须全过;新增:
|
||||
- `CoveragePlan.verify()` 的正/负例(故意删一个点必须被检出);
|
||||
- 联合信念的真源保留性质测试(随机 (p,h) × 随机观测序列,真源 (p,h) 永不被删);
|
||||
- 融合巡回的"每源至少一条站点方位线"性质测试;
|
||||
- 逐位一致性测试:numpy 后端与纯 Python 后端在同一动作序列下 summary 完全相同。
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与明确不承诺的部分
|
||||
|
||||
### 6.1 风险登记
|
||||
|
||||
| 风险 | 影响 | 缓解 |
|
||||
| --- | --- | --- |
|
||||
| 最小命中集证书依赖数值采样,官方分布未知 | 极端 (p,h) 漏检 → 漏源 | 保留解析七站证明(Q3);Q4 证书采样分辨率/裕量写入 summary;保留 10% 保险点预算与 49 点回退开关 |
|
||||
| 站点三角化在 0.72% 源位置上只有 1 站在射程内 | 这些场景仍需专用探点 | `prob_detect` 触发回退路径,保留现有 `_select_point` |
|
||||
| 朝向先验为工程假设 | 选点次优 | 先验只影响顺序;不确定时退化为全向;安全性由 20 m 证书 + 覆盖证明负责 |
|
||||
| numpy 化引入浮点路径差异 | 真源保留性质被破坏 | 两条后端同时保留并对拍;保持整数微秒复算计时 |
|
||||
| 并行基准引入进程/端口/临时目录问题 | 基准不稳定 | 每进程独立内存回环;不共用端口;`manifest` 记录随机种子 |
|
||||
|
||||
### 6.2 不承诺的部分
|
||||
|
||||
- **第四问 4000 s**:10 源场景下,仅"发现 + 缺席证明"就约需
|
||||
最小命中集巡回 4719 s + 缺席检测 ≈1900 s ≈ 6600 s,加之定位清除,4000 s 不可达。
|
||||
若必须逼近该线,需要改变停止条件(例如接受 `unresolved_channels` 而非逐频道缺席证明),
|
||||
那是**成绩口径的取舍**,必须先与题面评分方式核对,本轮不作为代码默认行为。
|
||||
- 本文所有收益数字都是**自建模拟器上的估计或本次复跑观测**,不是官方成绩;
|
||||
官方分布、r_eff 分布、误差场分布仍是测试假设。
|
||||
- 不声称 P0-1/P0-3 已在官方演练验证——需新的演练记录。
|
||||
|
||||
---
|
||||
|
||||
## 附录 A 复现命令与本次新增实验
|
||||
|
||||
```bash
|
||||
# 1) 基线配对(本次复跑:bayes 3707.3 s vs seven_batch 5581.8 s,局部 −32.1%)
|
||||
uv run python scripts/benchmark_q3_bayes.py --seeds 0,1 --out /tmp/mcmopt/q3
|
||||
|
||||
# 2) 运行历史计时分解(已入库):任意会话日志 → 移动/检测/换频分相 + 重复复扫检测
|
||||
uv run python scripts/analyze_run_timing.py session.jsonl --plan seven
|
||||
uv run python scripts/analyze_run_timing.py referee-data/sessions/BBM9-HZG2-77N5-NKKC/log.jsonl --plan seven
|
||||
uv run python scripts/analyze_run_timing.py referee-data/sessions/6X58-XQ7U-CJ24-JQQR/log.jsonl --plan 49
|
||||
# 实测输出(与服务器记录最大偏差 0.000000 s,证明复算口径正确):
|
||||
# session.jsonl 350227.692 s,移动 82.3%,检测 10323 次,重复率 96.4,
|
||||
# 末次清除后仍花 346169.9 s / 10225 次空复扫
|
||||
# BBM9 (Q3) 3134.643 s,移动 75.9%,探点 21 次 / 1138.7 s
|
||||
# 6X58 (Q4) 3251.021 s,移动 78.3%,检测 112 次
|
||||
|
||||
# 3) 第四问覆盖证书与最小命中集(已入库)
|
||||
uv run python scripts/verify_q4_cover.py --out output/q4/cover-certificate.json
|
||||
# 实测输出:目标空间 43032 个 (p,h)
|
||||
# 现役 49 点方格:缺口 0,NN 巡回 36.4 km(7280 s 移动)
|
||||
# 贪心最小命中集:31 点,缺口 0,NN 巡回 22.9 km(4587 s 移动)
|
||||
|
||||
# 4) 感知层剖面(单局 6.04 s 计算中 expected_survival 占 ~72%)
|
||||
python -m cProfile -s cumulative <单局驱动脚本>
|
||||
```
|
||||
|
||||
> 第 2、3 项已作为常驻工具入库,使"虚拟时间花在哪"与"覆盖集是否仍然合法"
|
||||
> 成为可随时重跑的回归项;第 1、4 项仍是一次性分析,建议在 S1 阶段并入
|
||||
> `bench/runner.py` 的必报指标(`compute_wall_s`、`profile_top`)。
|
||||
|
||||
## 附录 B 数字出处索引
|
||||
|
||||
| 数字 | 出处 |
|
||||
| --- | --- |
|
||||
| 移动 80.50% / 16.78% / 2.71%(Q3) | `referee-data/sessions/{BBM9,RM7Y,VQKB}/log.jsonl` 按 `verify_q3_practice.py` 模型复算 |
|
||||
| 移动 82.30%(官方 J2WH) | `session.jsonl` 求和 |
|
||||
| 346 169.9 s 空复扫 | `session.jsonl` `/exit` 时刻 350 227.691846 s 减去 12 源首轮清除的 4 057.839 s |
|
||||
| Q4 定向 2/6、漏检 4/4 全为定向 | `referee-data/sessions/{6X58,BAU8}/meta.json` + `log.jsonl` |
|
||||
| bayes 3707.3 / seven 5581.8 | 本次复跑 `benchmark_q3_bayes.py --seeds 0,1` |
|
||||
| 3535.9 s / 109 检测 / 6960 m 巡回 / 5741 m 探点 | 本次复跑计时分解 |
|
||||
| 13652.1 s / 560 检测 / 51 356 m(Q4) | 本次复跑单局 |
|
||||
| `expected_survival` 72% | 本次 `cProfile` 单局 |
|
||||
| HRL 27.0 steps/s、4007.9 s、109 776 步 | `output/hrl/ui/q3-main/q3.json` + `*.monitor.csv` |
|
||||
| bayes 比 HRL 快 4.3–5.0%、94% 差距为移动 | `output/hrl/ui/q3-main/eval/at-final/aggregate.json` + `runs/*/summary.json` |
|
||||
| 站点三角化 99.28%、交会角 5 分位 58.5° | 本次离线采样 20 000 点 |
|
||||
| 49 点 vs 30 点覆盖集 | 本次贪心击中集计算(附录 A 第 5 项) |
|
||||
Reference in New Issue
Block a user