diff --git a/docs/探索日志.md b/docs/探索日志.md index 5834f5d..47ad9a6 100644 --- a/docs/探索日志.md +++ b/docs/探索日志.md @@ -103,6 +103,23 @@ - **状态**:合法口径(碰撞 0、超 VMAX 0)下的当前最好配方更新为 **K=16 + select=pre + commit_n=2 + fb=2.2 + inc_cap=1 + cert_batch=1** → 到达 56.6%、时限内 35.9%。§9.2 的缺口仍然是**到达 −41.4 pp**(时限 −64.1 pp 是另一栏)。 - **判据没被放松的证据**:四格 `collision_rate` 与 `over_vmax_rate` 全 0,`n_plans_mean` 全 195.0 ⇒ 规划次数不变;K16 的额外成本只在每颗规划的候选数(认证与打分的批量宽度),这条**必须用独占微基准量**才能进部署账(#16/#28 仍未结)。 +## §9.2 记分板(2026-10-21 现状,最好的合法口径 = `--inc_cap 1` + K=16 + `select=pre`,轮次 51) + +| 判据(§9.2) | 要求 | 当前 | 差距 | +|---|---|---|---| +| 实际执行碰撞 | = 0 | **0**(`collision_rate 0`、`collision_free_success` = `success`) | **达标** | +| 超 VMAX 的题占比 | = 0(本轮新增的硬口径) | **0**(`over_vmax_rate 0.000`,开 `--inc_cap 1` 后;不开则 1.000、峰值 1.660×VMAX) | **达标**(代价:到达 −5.1 pp) | +| held-out 可达任务到达率 | ≥ 98% | **0.5967**(可达分母,K16+`pre`)/ 0.5664(含 D 的整批)/ 0.5234(K8 控制) | 差 **38.3 pp**;且相对控制净 +11 题只到 **p=0.061 ⇒ 未确立** | +| 到达用时 ≤ 老师×1.3 | 全部到达题 | 达标比例 **0.3594**(K16+`pre`)/ 0.2969(K16)/ 0.2656(K8 控制) | **这一栏是唯一被逐题配对证明买到的**:`pre` 净 +17(s0 p=0.0015)/+11(s1 p=0.035),合并 p=9.0e−5 | +| 关节行程 ≤ 规划器×1.10 | ≤1.10 | 行程比中位 **2.414**(K16+`pre`;控制 2.616) | 超 **1.3 倍**;罚候选行程/白费比例两条都是负结论,缩短打分地平线顺带降了 0.2 | +| 冷启动 | <10 ms | 未单独测(周期 P50 9.0 ms 是稳态推理,含首段编码) | 待测 | +| 推理 + 安全认证 P99 | <20 ms | 现口径周期 P50 33.5 / P99 64.7 ms;批量认证把最坏 80.2→37.1 ms | 差 **2~3 倍** | +| 刹停 < 2% 推理周期 | <2% | `brake_rate_mean` **7.28%**(K16+`pre`,14.20 次/195 规划);p50 **0.0%**、p90 27.4% | 均值差 **3.6 倍**,但中位已达标 ⇒ 极端长尾(`stuck_share` 17.2%) | + +- 刹停率这一格现在是**手算估算**:`eval` 产物里以前没存每题规划次数。已补 `n_plans` 与 `brake_rate`(并因此踩了 `per['plans']` 键名冲突的坑),当前批次之后的臂会直接给这个数,届时替换估算。 +- 记分板脚本 `ml/runs/_scoreboard.py`(读所有产物 + 轮次 33/38 的 GPU 微基准)。**注意它的输出在 GBK 控制台下会因 `⇒` 崩**,要带 `PYTHONIOENCODING=utf-8` 跑——这是我自己的老坑第 N 次复现,脚本以后只打 ASCII。 +- 读法:判据之间是相互拉扯的(限速 −5.1 pp 到达、多段推理 +2.4 pp 时限但 −15 pp 到达、放大噪声 −8.2 pp 时限),所以**任何单格变好都要同批报其他格**,这正是记分板存在的理由。 + ## 轮次 50:门控 20 mm 的 seed=1 复现——方向对、幅度不稳:时限收益塌了,精度代价只是减小没消除 - **一条臂就够**(控制臂复用 `round47_ep_s1`,同 seed 同配方,已在盘)。 @@ -296,6 +313,23 @@ - 真正要拿到 B 类到达(34%→A 类那样的 80%),得让**被拒之前的排序**就能避开必然穿模的方向(#24:把已经算出来的沿路间隙喂进打分),而不是靠事后缩步。 - D 类几乎不停(2.8% 零步、2.2 段),是"直接开去停靠位、永远不满足判据"——再次印证它们不该在到达率分母里。 +## `ep_waste` 三臂也是负结论,但给出了原因:罚的是没被执行的那段地平线(轮次 42) + +- 同一 256 题同 seed、`--inc_cap 1`,只改 `select=ep_waste --len_w`(罚 `1 − 同轴净/总` × 当前误差): + + | 臂 | 到达 | 时限内 | 行程比 | 刹车/题 | 刹停率 mean / p50 | 换向率 | + |---|---|---|---|---|---|---| + | 对照(endpoint,1 步限幅) | 0.5234 | 0.2656 | 2.6158 | 19.55 | ≈0.099 / — | 离线 0.247 | + | waste 0.1 | 0.5156 | 0.2695 | 2.6697 | 19.39 | 0.0995 / 0.005 | 0.2527 | + | waste 0.3 | 0.5156 | 0.2656 | **2.5349** | 19.63 | 0.1007 / 0.005 | 0.2511 | + | waste 1.0 | **0.4883** | **0.2383** | 2.7083 | 20.03 | 0.1027 / 0.010 | 0.2490 | + + (`docs/experiment_round40_waste{0.1,0.3,1.0}.json`) +- **行程比没有随权重单调下降**(2.67 → 2.53 → 2.71 vs 对照 2.62):0.3 那一格看着像改善,但两侧都比它差 ⇒ 不是趋势,不能拿单点报"行程降了 0.08"。权重加到 1.0 时到达 −3.5 pp、时限 −2.7 pp,纯亏。 +- **原因(这条比结论更有用)**:打分用的浪费比例是在**候选的 16 步地平线**上算的,而每拍真正提交只有 `commit_n=2` 步。排序被"后面 14 步会不会来回抖"牵着走,执行的前 2 步基本不变 ⇒ 改了顺序改不了行为。**要影响执行,惩罚必须只算在会被提交的那段前缀上。** +- 与 `ep_len` 三臂合起来看:**选择期的"路径形状类"惩罚(总行程 / 浪费比例)在两个版本上都是负结论**,而选择期的"多给活路"(K=16 +3.9 pp)和"几何信息进打分"(#24,待测)才是对的方向。差别在于前者动的是没被执行的那段,后者动的是被拒/被选的那 2 步。 +- 内部一致性:`brake_rate_mean` 0.0995/0.1007/0.1027 与 `brakes_mean/n_plans` = 19.4/195 ≈ 0.0995 逐位对得上 ⇒ 刚补的分母口径没接错。 + ## 缺口几乎全在"直线被挡 / 贴立柱"两类(轮次 41,按任务类别分层) - 六个已跑口径(1/2 步 × cap/margin/both,同一 256 题同 seed)的逐题 dump 交叉比对: @@ -329,23 +363,6 @@ - 与失败分类完全对上:63/109"走一半被冻住"就是这批题;**它们同时也是到达缺口的全部来源**(可拿的 +25 pp 区间就在这批题里)。所以攻它们不是锦上添花,是主攻方向。 - 待办(等链跑完再改库,避免重犯 14:45 那次事故):给 `eval_cl` 补 `brake_rate_p90` 与 `stuck_share`(判据 `brake_rate > 0.2` 的题占比)。 -## `ep_waste` 三臂也是负结论,但给出了原因:罚的是没被执行的那段地平线(轮次 42) - -- 同一 256 题同 seed、`--inc_cap 1`,只改 `select=ep_waste --len_w`(罚 `1 − 同轴净/总` × 当前误差): - - | 臂 | 到达 | 时限内 | 行程比 | 刹车/题 | 刹停率 mean / p50 | 换向率 | - |---|---|---|---|---|---|---| - | 对照(endpoint,1 步限幅) | 0.5234 | 0.2656 | 2.6158 | 19.55 | ≈0.099 / — | 离线 0.247 | - | waste 0.1 | 0.5156 | 0.2695 | 2.6697 | 19.39 | 0.0995 / 0.005 | 0.2527 | - | waste 0.3 | 0.5156 | 0.2656 | **2.5349** | 19.63 | 0.1007 / 0.005 | 0.2511 | - | waste 1.0 | **0.4883** | **0.2383** | 2.7083 | 20.03 | 0.1027 / 0.010 | 0.2490 | - - (`docs/experiment_round40_waste{0.1,0.3,1.0}.json`) -- **行程比没有随权重单调下降**(2.67 → 2.53 → 2.71 vs 对照 2.62):0.3 那一格看着像改善,但两侧都比它差 ⇒ 不是趋势,不能拿单点报"行程降了 0.08"。权重加到 1.0 时到达 −3.5 pp、时限 −2.7 pp,纯亏。 -- **原因(这条比结论更有用)**:打分用的浪费比例是在**候选的 16 步地平线**上算的,而每拍真正提交只有 `commit_n=2` 步。排序被"后面 14 步会不会来回抖"牵着走,执行的前 2 步基本不变 ⇒ 改了顺序改不了行为。**要影响执行,惩罚必须只算在会被提交的那段前缀上。** -- 与 `ep_len` 三臂合起来看:**选择期的"路径形状类"惩罚(总行程 / 浪费比例)在两个版本上都是负结论**,而选择期的"多给活路"(K=16 +3.9 pp)和"几何信息进打分"(#24,待测)才是对的方向。差别在于前者动的是没被执行的那段,后者动的是被拒/被选的那 2 步。 -- 内部一致性:`brake_rate_mean` 0.0995/0.1007/0.1027 与 `brakes_mean/n_plans` = 19.4/195 ≈ 0.0995 逐位对得上 ⇒ 刚补的分母口径没接错。 - ## K=16 串行 vs 批量认证等价 + 第一次真正核算"刹停率"(轮次 41) - `--K 16 --cert_batch 1` 与 `--K 16`(串行)跑同一 256 题同 seed:`success / time_ok_1p3 / brakes_mean / travel_ratio_median / collision_*` **全部逐位相同**,差异键只有 7 个、且全是我这几轮新加/改口径的报表键(`reverse_*` 的两种口径、`n_plans`、`brake_rate`、`moving_share`)。⇒ **轮次 39 的"试候选数 1/3/8/16 曲线"同样描述批量口径**,也就是说 K=16 到达 0.5625 这个收益不依赖串行早停这个巧合。 @@ -356,22 +373,6 @@ **含义**:继续在"普遍少刹"上花力气收益有限,真正的问题是**那批卡住的题里安全层没有可行候选**——正是 K=16(+3.9 pp)与 #24(把间隙喂进打分)要打的靶子。 - 仪表互验:K=16 批量臂 `reverse_p50 = 0.2625` 与我离线同窗口重算的 K=8 对照 0.247 同量级(K=16 略高,与它到达更多、动得更多一致);`reverse_moving_p50 = 0.3194` > 全步口径 ✓ 方向正确(除掉站住的步之后更抖)。之前那批 1.x 的读数确系修分母前的旧口径。 -## #24 的设计定稿:一次几何调用同时供打分与认证(不放松判据、不加成本) - -- 表面看有循环依赖:打分要用的"沿路间隙"目前只在安全层里算,而安全层只认证**排完序**的候选。破法是把顺序倒过来——**先对全部 K 条候选算一次 `2·commit_n` 点的间隙矩阵**,然后: - - 打分:`score = −e_end − gap_w · relu(gap_ref − 该候选沿路最小间隙)`(间隙不足的候选先降序,但**不淘汰**它,判据仍是后面的 `gap > 0`); - - 认证:直接用同一份间隙矩阵算 `plen`,**不需要再算第二次**。 -- 于是代价是**零额外几何调用**,但口径上要求走批量认证(`cert_batch=1`)——串行那条靠"第一条全安全就早停"省掉后面 K−1 次调用,一旦要按间隙排序就必须先看全部候选。这跟实时性是同一个方向:批量口径本来就是为压最坏情况(轮次 34:80.2→37.1 ms)而做的,所以这条杠杆顺带把部署口径推到批量那侧。 -- 读法预设(免得事后挑数):主判据是 `brake_rate`(全拒/规划次数,刚补上的分母)与 `success`/`time_ok_1p3`;`gap_ref` 取 0.5 mm 起(宽相位截顶值以内,见轮次 33 的口径教训:余量门限高于 cap 会恒假)。 -- A/B 脚本 `ml/runs/_gapscore_sweep.sh`(`gap_w = 0.5 / 2.0 / 8.0`,三档差一个数量级,看是否有拐点)。等链跑完、库能改之后实现+单测再跑。 - -## 代价探针的第一版量错了对象(仪表缺陷,已修) - -- 第一版 `ml/runs/_kcost_probe.py` 把"整次 `rollout(..., max_sec=0.041)` 调用"当成"一次规划"来量,得到 `K8/cb0 P50 = 144.8 ms`、`K16/cb0 = 139.6 ms` 这种**比轮次 34 的 33.5 ms/规划大四倍**的数——那里面包含每次调用的固定开销(`tasks_to_tensors`、生成器、首段同步),2 步的调用只做 1 次规划,所以固定开销占了大头。绝对值完全不可比,我先差点把它当"K 翻倍不花钱"的证据。 -- 修法是**用两个长度解出固定开销**:`max_sec` 取 2 步(1 次规划)与 8 步(4 次规划),`每次规划成本 = (T8 − T2)/3`。这样才与 §9.2 的"每次推理+认证 P99 <20 ms"同一口径。 -- 顺带一条**不能混的机理**:串行认证在 B=1 有早停(第一条全安全候选就 `break`),所以它对 K **不敏感**——第一版那组 144.8 vs 139.6 其实主要是这个早停在起作用,不是"K 免费";批量认证没有早停,K 从 8 到 16 是真的多算点(第一版里 150.7 → 169.1 ms)。两条口径对 K 的响应相反,报数时必须分开写,否则会把"串行下 K 便宜"错当成"批量下也便宜"。 -- 修好的探针排在当前批次结束后单独跑(不改正在被 bash 逐行读取的 `_next_batch.sh`,只跑独立脚本)。 - ## 到达率的上限在哪(把 #21 的数读成"还剩多少可拿") - 两条界都在同一 256 题、`--inc_cap 1` 口径下量得: @@ -399,38 +400,6 @@ - **对"把一步角点训准"这条训练路线的直接警告**:一步候选的多样性,恰恰来自角点的**偏差/欠缩放**(每条 ε 被削到不同的衰减幅度)。如果训练把一步映射成两段那样的收敛解,候选可能一起塌缩 ⇒ **按当前安全层口径,到达率可能不升反降**(实测就是这个方向:steps=2 开闸 −12.9 pp)。所以任何"修角点"的训练都必须**同时**处理候选多样性(更大 K、或给候选注入受控多样性),否则就是拿开环收益去换闭环到达。 - 顺带修一处历史口径:§9.2 的"策略单独碰撞率 <3%"这一格,现在才有合法读数 **58.6%**(一步)/ **55.9%**(两段)——差距巨大,且以前从没在同一配方下报过。 -## §9.2 记分板(2026-10-21 现状,最好的合法口径 = `--inc_cap 1` + K=16 + `select=pre`,轮次 51) - -| 判据(§9.2) | 要求 | 当前 | 差距 | -|---|---|---|---| -| 实际执行碰撞 | = 0 | **0**(`collision_rate 0`、`collision_free_success` = `success`) | **达标** | -| 超 VMAX 的题占比 | = 0(本轮新增的硬口径) | **0**(`over_vmax_rate 0.000`,开 `--inc_cap 1` 后;不开则 1.000、峰值 1.660×VMAX) | **达标**(代价:到达 −5.1 pp) | -| held-out 可达任务到达率 | ≥ 98% | **0.5967**(可达分母,K16+`pre`)/ 0.5664(含 D 的整批)/ 0.5234(K8 控制) | 差 **38.3 pp**;且相对控制净 +11 题只到 **p=0.061 ⇒ 未确立** | -| 到达用时 ≤ 老师×1.3 | 全部到达题 | 达标比例 **0.3594**(K16+`pre`)/ 0.2969(K16)/ 0.2656(K8 控制) | **这一栏是唯一被逐题配对证明买到的**:`pre` 净 +17(s0 p=0.0015)/+11(s1 p=0.035),合并 p=9.0e−5 | -| 关节行程 ≤ 规划器×1.10 | ≤1.10 | 行程比中位 **2.414**(K16+`pre`;控制 2.616) | 超 **1.3 倍**;罚候选行程/白费比例两条都是负结论,缩短打分地平线顺带降了 0.2 | -| 冷启动 | <10 ms | 未单独测(周期 P50 9.0 ms 是稳态推理,含首段编码) | 待测 | -| 推理 + 安全认证 P99 | <20 ms | 现口径周期 P50 33.5 / P99 64.7 ms;批量认证把最坏 80.2→37.1 ms | 差 **2~3 倍** | -| 刹停 < 2% 推理周期 | <2% | `brake_rate_mean` **7.28%**(K16+`pre`,14.20 次/195 规划);p50 **0.0%**、p90 27.4% | 均值差 **3.6 倍**,但中位已达标 ⇒ 极端长尾(`stuck_share` 17.2%) | - -- 刹停率这一格现在是**手算估算**:`eval` 产物里以前没存每题规划次数。已补 `n_plans` 与 `brake_rate`(并因此踩了 `per['plans']` 键名冲突的坑),当前批次之后的臂会直接给这个数,届时替换估算。 -- 记分板脚本 `ml/runs/_scoreboard.py`(读所有产物 + 轮次 33/38 的 GPU 微基准)。**注意它的输出在 GBK 控制台下会因 `⇒` 崩**,要带 `PYTHONIOENCODING=utf-8` 跑——这是我自己的老坑第 N 次复现,脚本以后只打 ASCII。 -- 读法:判据之间是相互拉扯的(限速 −5.1 pp 到达、多段推理 +2.4 pp 时限但 −15 pp 到达、放大噪声 −8.2 pp 时限),所以**任何单格变好都要同批报其他格**,这正是记分板存在的理由。 - -## 多段推理的代价实测(B=1、K=8、逐轮交替、奇数轮反序) - -- `docs/experiment_round38_steps_cost.json`(24 轮 × 每轮 40 次,取每档的中位): - - | 段数 | 推理 P50 | 相对 1 段增量 | - |---|---|---| - | 1 | 9.02 ms | — | - | 2 | 14.98 ms | **+5.97 ms** | - | 4 | 26.72 ms | +17.71 ms | - | 8 | 51.07 ms | +42.05 ms | - -- 每多一段 ≈ 多一次解码器前向(编码器 `mem` 只算一次),所以增量近似线性;9.02 ms 这一档比轮次 33 报的 7.2 ms 高,是不同会话的时钟/上下文差别,**只用同批内的差值,不用绝对值跨轮比**。 -- 合到整周期上:轮次 34 的现口径 P50 ≈ 33.5 ms,两段就是 ≈39.5 ms(25 Hz),四段 ≈51 ms(19.6 Hz)——**离 20 ms 预算更远**,所以"靠多段推理把一步的偏差补回来"在部署上不成立,只能作为开环诊断口径。 -- 与 K 这条轴叠加会更糟(K=16 认证点翻倍),所以 `K=8/16 × cert_batch=0/1` 的四格周期微基准排在当前批次里(`ml/runs/_kcost_probe.py`),这是判断"候选数能不能买到部署里的到达率"的关键一列。 - ## 候选为什么会"塌":两段积分把 K 条候选吸成一条(轮次 39 探针,机理定案) - `ml/runs/_cand_div.py`(256 题特征、同一批噪声 z、K=8,只改段数): @@ -446,13 +415,6 @@ - **但"人为制造多样性"不是解法**(`z_scale=1.5`,同 K=8):刹车确实砍一半(19.55→10.51),可到到达掉到 0.4961、时限内从 0.2656 崩到 **0.1836**、行程比恶化到 3.5243、换向升到 0.32。⇒ 放大噪声换来的"能过的候选"是**分布外**的候选:安全层放行它,但它走的是绕、抖、慢的路。**K=16 的收益来自"同分布内更多候选",不是"多样性本身"**——这两个数放在一起才把这条轴的边界说清楚。 - 判据状态:#21 的两个分支现在这样分:**"动作更危险"不是主因,"候选塌缩"是主因**(spread 35 倍 + try/K 单调曲线 + z_scale 反例三条独立证据)。剩下要量的只是成本口径(K=16 在批量认证下贵多少)。 -## 事故:我在链式作业排队期间编辑了被 import 的库,两个臂直接没跑 - -- 时间线:`z15` 于 14:46:38 结束,链接着拉起 `safety=0 steps=1`(14:46:38)——而我为了让 `brake_rate` 有分母,正在 14:45:5x 用脚本改 `ml/sim/closed_loop.py`,一次替换把 `per = {...}` 的字典提前闭合,留下 `IndentationError: unexpected indent`。**新起的 python 进程 import 到的是半成品文件**,两个 safety-off 臂各 2 秒就退出(日志里 `=== safety=0 steps=1 done 14:46:40 ===` 的"done"只是 bash 循环走到下一行,不代表成功)。修好在 14:46:52,48 项测试全绿。 -- **教训(比我原先记的"别改正在跑的脚本"更进一层)**:链式作业会在**未来某个时刻**新起解释器,所以"只要不改正在跑的那一个进程"是不够的。规则改为:**链没结束就不编辑被 import 的库;要编辑,先等通知或先让链跑完**。并且 `=== done ===` 这种循环日志不能当成功凭据——**判成功要看产物文件在不在**(这次 `docs/experiment_round38_safetyoff_steps{1,2}.json` 根本没生成,一看便知)。 -- 两个 safety-off 臂与 `ep_waste`/`backoff`/K 代价一起并入下一批重跑(`ml/runs/_next_batch.sh`)。 -- 顺带补了验收判据缺的分母:`rollout` 现在记 `n_plans`(每题规划次数)并给 `brake_rate = brakes/n_plans`、`eval_cl` 报 `brake_rate_p50/mean` 与 `n_plans_mean`——§9.2 要的是"刹停 <2% **推理周期**",以前只有绝对次数,**这条判据实际上从来没被真正核算过**。命名上躲开一个坑:`per` 里原本有个 `'plans'` 键是转储用的逐规划字典,新键必须叫 `n_plans`,否则 `flat` 的过滤 `if k not in ('traj','plans')` 会把它静默吃掉。 - ## 试候选数的成本-收益曲线(轮次 39,四档齐全,#15 主结果) - 同一 256 题、同 seed、`--inc_cap 1`、其余配方(`select=endpoint, replan=empty, commit_n=2, K=8, fb=2.2`)不变,只改**安全层允许试几条候选**: @@ -696,6 +658,22 @@ - **循环内还剩的可复用点**:`replan='empty'` 且队列为空时 `qe == q`,此时目标反馈 DLS 与末态打分的 FK 和特征用的是同一姿态,可以共享一份 `cum_xf`(部署 `r=3` 时不成立,要按 r 分支)。这条待测(#19)。 - **守卫补一个**:`gaps()` / `gm_groups()` 现在拒绝行数与 `q` 不符的 `xf`(之前会静默按别的行做宽相位,实测触发 CUDA device-side assert 或给出错间隙),测试 34→35 项;`python -m ml.eval.check_parity` 仍 PASS(2000 样本,间隙差 2.52e−13 mm、碰撞布尔差 0、特征差 5.96e−08)。 +## #24 的设计定稿:一次几何调用同时供打分与认证(不放松判据、不加成本) + +- 表面看有循环依赖:打分要用的"沿路间隙"目前只在安全层里算,而安全层只认证**排完序**的候选。破法是把顺序倒过来——**先对全部 K 条候选算一次 `2·commit_n` 点的间隙矩阵**,然后: + - 打分:`score = −e_end − gap_w · relu(gap_ref − 该候选沿路最小间隙)`(间隙不足的候选先降序,但**不淘汰**它,判据仍是后面的 `gap > 0`); + - 认证:直接用同一份间隙矩阵算 `plen`,**不需要再算第二次**。 +- 于是代价是**零额外几何调用**,但口径上要求走批量认证(`cert_batch=1`)——串行那条靠"第一条全安全就早停"省掉后面 K−1 次调用,一旦要按间隙排序就必须先看全部候选。这跟实时性是同一个方向:批量口径本来就是为压最坏情况(轮次 34:80.2→37.1 ms)而做的,所以这条杠杆顺带把部署口径推到批量那侧。 +- 读法预设(免得事后挑数):主判据是 `brake_rate`(全拒/规划次数,刚补上的分母)与 `success`/`time_ok_1p3`;`gap_ref` 取 0.5 mm 起(宽相位截顶值以内,见轮次 33 的口径教训:余量门限高于 cap 会恒假)。 +- A/B 脚本 `ml/runs/_gapscore_sweep.sh`(`gap_w = 0.5 / 2.0 / 8.0`,三档差一个数量级,看是否有拐点)。等链跑完、库能改之后实现+单测再跑。 + +## 代价探针的第一版量错了对象(仪表缺陷,已修) + +- 第一版 `ml/runs/_kcost_probe.py` 把"整次 `rollout(..., max_sec=0.041)` 调用"当成"一次规划"来量,得到 `K8/cb0 P50 = 144.8 ms`、`K16/cb0 = 139.6 ms` 这种**比轮次 34 的 33.5 ms/规划大四倍**的数——那里面包含每次调用的固定开销(`tasks_to_tensors`、生成器、首段同步),2 步的调用只做 1 次规划,所以固定开销占了大头。绝对值完全不可比,我先差点把它当"K 翻倍不花钱"的证据。 +- 修法是**用两个长度解出固定开销**:`max_sec` 取 2 步(1 次规划)与 8 步(4 次规划),`每次规划成本 = (T8 − T2)/3`。这样才与 §9.2 的"每次推理+认证 P99 <20 ms"同一口径。 +- 顺带一条**不能混的机理**:串行认证在 B=1 有早停(第一条全安全候选就 `break`),所以它对 K **不敏感**——第一版那组 144.8 vs 139.6 其实主要是这个早停在起作用,不是"K 免费";批量认证没有早停,K 从 8 到 16 是真的多算点(第一版里 150.7 → 169.1 ms)。两条口径对 K 的响应相反,报数时必须分开写,否则会把"串行下 K 便宜"错当成"批量下也便宜"。 +- 修好的探针排在当前批次结束后单独跑(不改正在被 bash 逐行读取的 `_next_batch.sh`,只跑独立脚本)。 + ## 轮次 34:GPU 周期的可实现优化——复用 FK 与压认证最坏情况(exp: `exp/dagger-deploy`) - **起因与一次自我撤回**:轮次 33 记下"合并几何查询看能不能进 20 ms"后,先按 `docs/experiment_round34_fused_cycle.json` 测出"整周期 41.9→24.5 ms"。这个数**作废**,两个独立原因: @@ -721,6 +699,21 @@ - 批量认证(`--cert_batch 1`,默认 0 不变):同一 256 题重跑 **22 项指标与 base 逐项相同**(`docs/experiment_round34_cert_batch_check.json`,success 0.57421875、time_ok 0.390625、speed_ratio_max 1.660003662 等全部一致),整批墙钟 296.3 → 306.9 s(批量口径把 8 次串行调用换成 1 次 K×256 点的大调用,均值略涨符合预期,与 B=1 交错实测方向一致)。合成夹具里也断言两条路径选到同一条候选、同一前缀长度、逐位相同的轨迹与证书。 - **测试 32→34 项**:`CertBatchEquivalenceTest` 两条(候选 0 前缀不完整 → 选候选 1;所有候选第 2 步都不安全 → 两条路径都停在第 1 步)。写夹具时踩到一个自己的坑:桩里掩码写成 `q[:, :1]` 再 `[:, None]` 变成 3 维,广播出 (4,4,49) 才报"size 16",是**测试桩错**不是库错——先证形状再改代码。 +## 多段推理的代价实测(B=1、K=8、逐轮交替、奇数轮反序) + +- `docs/experiment_round38_steps_cost.json`(24 轮 × 每轮 40 次,取每档的中位): + + | 段数 | 推理 P50 | 相对 1 段增量 | + |---|---|---| + | 1 | 9.02 ms | — | + | 2 | 14.98 ms | **+5.97 ms** | + | 4 | 26.72 ms | +17.71 ms | + | 8 | 51.07 ms | +42.05 ms | + +- 每多一段 ≈ 多一次解码器前向(编码器 `mem` 只算一次),所以增量近似线性;9.02 ms 这一档比轮次 33 报的 7.2 ms 高,是不同会话的时钟/上下文差别,**只用同批内的差值,不用绝对值跨轮比**。 +- 合到整周期上:轮次 34 的现口径 P50 ≈ 33.5 ms,两段就是 ≈39.5 ms(25 Hz),四段 ≈51 ms(19.6 Hz)——**离 20 ms 预算更远**,所以"靠多段推理把一步的偏差补回来"在部署上不成立,只能作为开环诊断口径。 +- 与 K 这条轴叠加会更糟(K=16 认证点翻倍),所以 `K=8/16 × cert_batch=0/1` 的四格周期微基准排在当前批次里(`ml/runs/_kcost_probe.py`),这是判断"候选数能不能买到部署里的到达率"的关键一列。 + ## 轮次 33:把速度做成可观测的硬约束,再测限幅与余门的代价(exp: `exp/dagger-deploy`) - **仪表先行**:`rollout()` 现在逐题记录**实际执行**的速度比 `max_j|Δq_j|/STEP`(STEP=VMAX·DT,所以 1.0 = VMAX)和按控制步的速度变化比;`eval_cl` 汇总 `speed_ratio_p50/p95/max`、`over_vmax_rate`、`accel_ratio_p95/max`,并把逐题值放进 `per`(极值/分位不进按 n 加权的批聚合)。 @@ -1393,6 +1386,13 @@ - **指标**:到达 **0%**;碰撞 **99.6%**;精度 ol_fk 5.98 mm / ol_best 2.92 mm / ol_div 0.655,2 s 误差比 0.755;时延未测。 - **结论**:仿真/滚动无误,失败在策略输出。闭环对每步残差极敏感(老师动作加 σ=0.05×STEP 即 reach 1.0→0.218)。 +## 事故:我在链式作业排队期间编辑了被 import 的库,两个臂直接没跑 + +- 时间线:`z15` 于 14:46:38 结束,链接着拉起 `safety=0 steps=1`(14:46:38)——而我为了让 `brake_rate` 有分母,正在 14:45:5x 用脚本改 `ml/sim/closed_loop.py`,一次替换把 `per = {...}` 的字典提前闭合,留下 `IndentationError: unexpected indent`。**新起的 python 进程 import 到的是半成品文件**,两个 safety-off 臂各 2 秒就退出(日志里 `=== safety=0 steps=1 done 14:46:40 ===` 的"done"只是 bash 循环走到下一行,不代表成功)。修好在 14:46:52,48 项测试全绿。 +- **教训(比我原先记的"别改正在跑的脚本"更进一层)**:链式作业会在**未来某个时刻**新起解释器,所以"只要不改正在跑的那一个进程"是不够的。规则改为:**链没结束就不编辑被 import 的库;要编辑,先等通知或先让链跑完**。并且 `=== done ===` 这种循环日志不能当成功凭据——**判成功要看产物文件在不在**(这次 `docs/experiment_round38_safetyoff_steps{1,2}.json` 根本没生成,一看便知)。 +- 两个 safety-off 臂与 `ep_waste`/`backoff`/K 代价一起并入下一批重跑(`ml/runs/_next_batch.sh`)。 +- 顺带补了验收判据缺的分母:`rollout` 现在记 `n_plans`(每题规划次数)并给 `brake_rate = brakes/n_plans`、`eval_cl` 报 `brake_rate_p50/mean` 与 `n_plans_mean`——§9.2 要的是"刹停 <2% **推理周期**",以前只有绝对次数,**这条判据实际上从来没被真正核算过**。命名上躲开一个坑:`per` 里原本有个 `'plans'` 键是转储用的逐规划字典,新键必须叫 `n_plans`,否则 `flat` 的过滤 `if k not in ('traj','plans')` 会把它静默吃掉。 + ## 分支清理(2026-10-20) - `feat/fb-mix`(8e5431c):反馈混合方案的中间分支,**已验证是 `exp/dagger-deploy` 的祖先**(所有提交都在后者历史里),本地/远端分支删除,内容保留在 exp/dagger-deploy。