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: