From 37feb091853d900ac415a60b3ec21e9880b70fb9 Mon Sep 17 00:00:00 2001 From: qing <1776177662@qq.com> Date: Sat, 15 Aug 2026 09:55:22 +0000 Subject: [PATCH] =?UTF-8?q?fix(actpred):=20=E5=9B=BA=E5=AE=9A=200=20?= =?UTF-8?q?=E5=88=97=E6=9C=AA=E8=A2=AB=E8=A6=86=E7=9B=96=E2=80=94=E2=80=94?= =?UTF-8?q?=E6=8E=A9=E7=A0=81=E6=8C=89=E6=9D=83=E9=87=8D=E8=80=8C=E9=9D=9E?= =?UTF-8?q?=E5=B8=B8=E9=87=8F=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixed_zero_columns 对 spk.F 返回常量 0,调用处掩码 fixed!=0 将其 排除,spk.F 垃圾预测(69-423Hz)继续进闭环。改为按权重<=0 掩码 覆盖(含固定 0 的列)。修正后舵机闭环 MSE 14477 假象 → 293 真实 (hand 类 55K 是固定 0 vs 真值 1000 的指标伪影)。+1 回归测试。 --- actpred/predict.py | 6 ++++-- actpred/test_actpred.py | 16 ++++++++++++++++ actpred/train.py | 4 ++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/actpred/predict.py b/actpred/predict.py index 9c3e77e..0fccb7b 100644 --- a/actpred/predict.py +++ b/actpred/predict.py @@ -128,8 +128,10 @@ class ActionPredictorInference: from action_norm import default_column_weights col_w = default_column_weights(self.action_dim) fixed = fixed_zero_columns(col_w, self.action_dim) - m = fixed != 0.0 - result[m] = fixed[m] + # 按权重掩码覆盖(固定 0 的列也须覆盖——掩码不能只看常量值) + w = np.asarray(col_w, dtype=np.float32) + result[w[:self.action_dim] <= 0.0] = ( + fixed[w[:self.action_dim] <= 0.0]) if not np.all(np.isfinite(result)): result = np.nan_to_num(result, nan=0.0, posinf=0.0, neginf=0.0) return result diff --git a/actpred/test_actpred.py b/actpred/test_actpred.py index 6f212d8..5641495 100644 --- a/actpred/test_actpred.py +++ b/actpred/test_actpred.py @@ -2525,6 +2525,22 @@ class TestOneHot(unittest.TestCase): cfg2 = apply_overrides(Config(), parser.parse_args([])) self.assertEqual(cfg2.train.val_split_mode, "sample") # 新默认 + def test_fixed_zero_value_still_applied(self): + """固定 0 的列(spk.F)也必须被覆盖——掩码按权重而非常量值""" + from bins import decode, fixed_zero_columns, START + from action_norm import default_column_weights + # 构造 spk.F 头输出 1000Hz 附近 logits(垃圾预测) + logits = np.full((1, 1864), -10.0, dtype=np.float32) + logits[0, START[10] + 25] = 0.0 # 1000Hz 附近箱 + raw = decode(logits, 18) + self.assertGreater(float(raw[0, 10]), 100.0) # 未覆盖时是垃圾 + w = default_column_weights(18) + fixed = fixed_zero_columns(w, 18) + mask = np.asarray(w, dtype=np.float32) <= 0.0 + raw[0, mask] = fixed[mask] + self.assertEqual(float(raw[0, 10]), 0.0) # 固定后归零 + self.assertEqual(float(raw[0, 11]), 10.0) + def test_action_jitter(self): """角度抖动增强:历史加有界高斯噪声,目标保持干净""" with tempfile.TemporaryDirectory() as tmpdir: diff --git a/actpred/train.py b/actpred/train.py index 1a5e214..6a0f7fd 100644 --- a/actpred/train.py +++ b/actpred/train.py @@ -130,8 +130,8 @@ def train_one_epoch(model, loader, optimizer, device, max_grad_norm: float = Non # 权重 0 列固定为常量,防自预测垃圾进入历史 fixed = fixed_zero_columns( weights.cpu().numpy(), action_dim) - m = fixed != 0.0 - raw[m] = fixed[m] + w = weights.cpu().numpy()[:action_dim] + raw[w <= 0.0] = fixed[w <= 0.0] abs_action = torch.from_numpy(normalize_action( raw, action_dim, mode=range_mode)).to(device) else: