From ee7fe829ac1f832c6e57e7759ff778c992deb964 Mon Sep 17 00:00:00 2001 From: cbb <2625274797@qq.com> Date: Tue, 20 Oct 2026 21:06:35 +0800 Subject: [PATCH] =?UTF-8?q?fix+exp:=20=E8=BD=AE=E6=AC=A135(=E8=A1=A5)?= =?UTF-8?q?=E2=80=94=E2=80=94gaps/gm=5Fgroups=20=E6=8B=92=E7=BB=9D?= =?UTF-8?q?=E8=A1=8C=E6=95=B0=E4=B8=8D=E7=AC=A6=E7=9A=84=20xf=EF=BC=9B?= =?UTF-8?q?=E6=92=A4=E5=9B=9E=E4=B8=A4=E6=AC=A1=E4=B8=8D=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E7=9A=84"=E5=90=88=E5=B9=B6=E8=B0=83=E7=94=A8"=E6=94=B6?= =?UTF-8?q?=E7=9B=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 探测脚本把认证点当成与特征无关的已知量,据此测出的 共享cum_xf −4.3ms / 合并一次调用 −8.1ms 都不成立:真实顺序是 特征(q)→推理→候选→认证点,同一次规划内既不能共享 xf 也不能合并 gaps 只有轮次34 已落地的 gm_groups 复用同姿态 xf(−3.4ms,22项指标逐位不变)是真的 - 守卫:xf 行数与 q 不符时抛错(之前会静默按别的行做宽相位,实测触发 CUDA device-side assert) - 测试 34→35 项;check_parity 仍 PASS(2000样本,间隙差 2.52e-13 mm、碰撞布尔差 0、特征差 5.96e-08) --- docs/探索日志.md | 4 ++++ ml/common/kin_torch.py | 13 +++++++++++-- tests/test_eval_cl.py | 13 +++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/探索日志.md b/docs/探索日志.md index 96d9c07..daf4fd2 100644 --- a/docs/探索日志.md +++ b/docs/探索日志.md @@ -734,6 +734,10 @@ - **流水线复用特征间隙——否掉**(`docs/experiment_round35_pipeline_gaps.json`):想法是"下一次规划的 q 正好是本次认证已算过的末点"。第一版给出 42.1→31.8 ms,但**等价性检查不通过**(缓存行与新算差 3.19 mm)——是我取行索引用错(`full[CN-1]` 取到第 2 条候选的第 1 步,而非第 1 条候选走完 commit 步)。修好后收益只剩 **34.8→31.7 ms(−3.1 ms)**:复用要求认证调用改用 cap=40 的宽相位(特征所需),省下的被吃回大半;且缓存行与新算**不是逐位相同**(差 6.3e−5 mm,源于不同 batch 下矩阵乘的结合序),会扰动特征输入。⇒ 不为这 3 ms 改口径,放弃。 - **剩下的量级判断**:一个周期 ≈ 推理 7.2 + 特征 13.1(宽相位 8.2 + FK 4.0)+ 末态打分 4.3 + DLS 6.5 + 认证 7.4 ≈ 33 ms,其中 **4–5 次调用各自重做同一个宽相位筛选**(67,256 个簇对)。下一步是把一次筛选的半径按位移界(`cert_lever`)扩到整个 commit 地平线,供同周期所有点共用——这才是有量级的候选,而不是再抠精度。 +- **再一次自我撤回(方法论)**:为了找"合并几何调用"的收益,我写了三个变体的探测:①两次独立调用 17.62 ms、②共享一次 `cum_xf` 13.32 ms、③合成一次 cap=40 调用 9.51 ms(交错 90 轮,特征行逐位一致)。**②③ 都不成立**:探测里认证点直接取自轨迹、与特征无关,而真实循环的顺序是 特征(q) → 推理 → 候选 → 认证点(q+Δ),认证点在特征算完之前不存在 ⇒ 同一次规划内既不能共享 `cum_xf`,也不能把两次 `gaps` 合并。只有 ①→轮次 34 已落地的 `gm_groups` 复用 `xf`(同一姿态内部,−3.4 ms)是真的。教训:**效率探测必须按真实数据依赖排布,否则测的是不存在的调用图**。 +- **循环内还剩的可复用点**:`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)。 + ## 分支清理(2026-10-20) - `feat/fb-mix`(8e5431c):反馈混合方案的中间分支,**已验证是 `exp/dagger-deploy` 的祖先**(所有提交都在后者历史里),本地/远端分支删除,内容保留在 exp/dagger-deploy。 diff --git a/ml/common/kin_torch.py b/ml/common/kin_torch.py index da8f414..54a7598 100644 --- a/ml/common/kin_torch.py +++ b/ml/common/kin_torch.py @@ -107,6 +107,15 @@ class Kin: for a in range(7) for b in range(a + 2, 7)]) # ---------------- 运动学 ---------------- + def _checked_xf(self, q, xf): + """xf 是按行对应 q 的位姿数组,行数不匹配会静默广播出错(宽相位筛到别的行 ⇒ device assert 或错间隙)。""" + if xf is None: + return self.cum_xf(q) + Rs, Ts = xf + if Rs.shape[0] != q.shape[0] or Ts.shape[0] != q.shape[0]: + raise ValueError('xf 的行数 %d 与 q 的行数 %d 不符' % (Rs.shape[0], q.shape[0])) + return xf + def cum_xf(self, q): """q: [B,6] 度 → Rs [B,7,3,3], Ts [B,7,3]""" B = q.shape[0] @@ -176,7 +185,7 @@ class Kin: 2026-10-19(用户实测 4096 点 cap 40 峰值 4.46 GiB 全在拼接 8.6e7 对的 b/i/j/gap): 默认只返回 gm,每块算完就丢掉胶囊对,峰值只剩单块。 """ - xf = xf if xf is not None else self.cum_xf(q) + xf = self._checked_xf(q, xf) Rs, Ts = xf B = q.shape[0] step = max(1, self.COARSE_ENTRIES // int(self.cp_a.shape[0])) @@ -248,7 +257,7 @@ class Kin: sel = torch.isin(cg, torch.tensor([min(a, b) * 7 + max(a, b) for a, b in gp], device=q.device)) self._gp = (m, sel, sel.nonzero(as_tuple=True)[0]); self._gp_key = key self._gp49, sel, idx = self._gp - xf = xf if xf is not None else self.cum_xf(q); Rs, Ts = xf; B = q.shape[0] + xf = self._checked_xf(q, xf); Rs, Ts = xf; B = q.shape[0] with torch.no_grad(): cc = torch.einsum('bkij,kj->bki', Rs[:, self.cl_g], self.cl_c.to(q.dtype)) + Ts[:, self.cl_g] d2 = (cc[:, self.cp_a[idx]] - cc[:, self.cp_b[idx]]).pow(2).sum(-1) diff --git a/tests/test_eval_cl.py b/tests/test_eval_cl.py index 2ac5c8c..8ab17cd 100644 --- a/tests/test_eval_cl.py +++ b/tests/test_eval_cl.py @@ -700,5 +700,18 @@ class CertBatchEquivalenceTest(unittest.TestCase): self.assertAlmostEqual(seq['traj'][0, 2, 0].item(), 0.24, places=5) # 第 2 步被拒后停在原地 + def test_mismatched_xf_row_count_is_rejected(self): + kin = Kin() + q1 = torch.zeros(1, 6); q5 = torch.zeros(5, 6) + xf5 = kin.cum_xf(q5) + for name, fn in (('gaps', lambda: kin.gaps(q1, 40.0, xf5)), + ('gm_groups', lambda: kin.gm_groups(q1, [(0, 2)], 40.0, xf=xf5))): + with self.subTest(fn=name): + with self.assertRaisesRegex(ValueError, '行数'): + fn() + self.assertEqual(kin.gaps(q5, 40.0, xf5)['gm'].shape, (5, 49)) + self.assertEqual(kin.gm_groups(q5, [(0, 2)], 40.0, xf=xf5).shape, (5, 1)) + + if __name__ == '__main__': unittest.main()