docs(actpred): 模型 B zscore 等权稳定基线文档 + spk 两列固定常量
B 是六模型闭环评估中唯一稳定配置(2445/0.082),固化完整方案作为 部署基线与实验对照基准,并总结归一化系列失败机理(反馈回路需误差 有界且收缩)。配套实现:spk.F/spk.delay 权重归零、推理/反馈固定 常量(bins.FIXED_OUTPUT),拔除闭环发散源,为 B2(zscore+v2)与 F2(onehot+固定)铺路。
This commit is contained in:
1 parent
7f21b65428
commit
9000bb4b04
6 files changed
+149
-15
No files matched your search
@@ -85,15 +85,13 @@ def denormalize_action(norm_action, action_dim: int = None,
|
||||
|
||||
|
||||
def default_column_weights(action_dim: int = 18) -> np.ndarray:
|
||||
"""逐列损失权重默认表:舵机 1.0(安全关键)、spk.F 0.2(保留梯度)、
|
||||
spk.delay 0.0(步级数据恒 10ms——微动作分解的隐式节拍,时长信息
|
||||
已编码在步数中)、保留位 0.0(恒 0 纯噪声,参与只会稀释有效梯度)。
|
||||
action_dim>18 的扩展列默认 1.0:超出当前 18 列语义布局的通道视为
|
||||
新执行器,静默给 0 会让其完全不参与训练(P2-a1)。"""
|
||||
"""逐列损失权重默认表:舵机 1.0(安全关键);spk.F / spk.delay 0.0
|
||||
(只影响声音/节奏不影响运动,推理端固定常量,见 bins.FIXED_OUTPUT);
|
||||
保留位 0.0(恒 0 纯噪声)。action_dim>18 的扩展列默认 1.0:超出
|
||||
当前 18 列语义布局的通道视为新执行器,静默给 0 会让其完全不参与
|
||||
训练(P2-a1)。"""
|
||||
w = np.zeros(action_dim, dtype=np.float32)
|
||||
w[:min(10, action_dim)] = 1.0
|
||||
if action_dim > 10:
|
||||
w[10] = 0.2
|
||||
if action_dim > 18:
|
||||
w[18:] = 1.0
|
||||
return w
|
||||
@@ -29,6 +29,26 @@ TOTAL_BINS = int(START[-1]) # 1864
|
||||
LOG_COLS = (10, 11) # 对数分箱列
|
||||
LOG_HI = {10: 5000.0, 11: 2000.0}
|
||||
|
||||
# 权重 0 列的输出固定值(原始量纲):spk.F 固定 0(只影响声音不影响
|
||||
# 运动,闭环中持续发声无意义);spk.delay 在步级数据中恒 10ms(微动作
|
||||
# 分解的隐式节拍)。无监督头在闭环中会输出中段垃圾污染历史,
|
||||
# 推理/反馈时直接固定为常量。其余权重 0 列(保留位)固定 0。
|
||||
FIXED_OUTPUT = {10: 0.0, 11: 10.0}
|
||||
|
||||
|
||||
def fixed_zero_columns(weights, action_dim: int = 18) -> np.ndarray:
|
||||
"""按列权重表返回权重<=0 列的固定输出向量(其余列 0)
|
||||
|
||||
与 default_column_weights 联动:列权重 0 → 推理输出固定为
|
||||
FIXED_OUTPUT 常量(缺省 0),避免无监督头在闭环反馈中输出垃圾。
|
||||
"""
|
||||
w = np.asarray(weights, dtype=np.float32)
|
||||
fixed = np.zeros(action_dim, dtype=np.float32)
|
||||
for i in range(min(action_dim, len(w))):
|
||||
if w[i] <= 0.0:
|
||||
fixed[i] = FIXED_OUTPUT.get(i, 0.0)
|
||||
return fixed
|
||||
|
||||
|
||||
def bin_edges(col: int) -> np.ndarray:
|
||||
"""列 col 的箱边界数组(长度 bins+1),原始量纲递增"""
|
||||
|
||||
+10
-2
@@ -10,7 +10,7 @@ from config import parse_checkpoint_config
|
||||
from imu_norm import normalize_imu
|
||||
from action_norm import (normalize_action, denormalize_action,
|
||||
is_range_mode)
|
||||
from bins import decode as bin_decode
|
||||
from bins import decode as bin_decode, fixed_zero_columns
|
||||
from model import ActionPredictor
|
||||
from utils import parse_action_history
|
||||
|
||||
@@ -120,8 +120,16 @@ class ActionPredictorInference:
|
||||
).unsqueeze(0).to(self._device)
|
||||
pred = self._model(img_tensor, history, imu_tensor).squeeze(0) # 归一化空间
|
||||
if self._onehot:
|
||||
# 多头 logits → 期望值解码 → 原始量纲(箱生活在原始空间)
|
||||
# 多头 logits → 期望值解码 → 原始量纲(箱生活在原始空间);
|
||||
# 权重 0 列固定为常量(delay=10ms 等),防闭环垃圾回写
|
||||
result = bin_decode(pred.cpu().numpy(), self.action_dim)
|
||||
col_w = self._data_cfg.column_weights
|
||||
if col_w is None:
|
||||
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]
|
||||
if not np.all(np.isfinite(result)):
|
||||
result = np.nan_to_num(result, nan=0.0, posinf=0.0, neginf=0.0)
|
||||
return result
|
||||
|
||||
+16
-5
@@ -2004,14 +2004,12 @@ class TestFixedRangeNorm(unittest.TestCase):
|
||||
from action_norm import default_column_weights
|
||||
w18 = default_column_weights(18)
|
||||
np.testing.assert_allclose(w18[:10], 1.0)
|
||||
self.assertAlmostEqual(float(w18[10]), 0.2, places=6) # spk.F 保留梯度
|
||||
np.testing.assert_allclose(w18[11:], 0.0) # spk.delay 步级恒值 + 保留位
|
||||
np.testing.assert_allclose(w18[10:], 0.0) # spk 两列 + 保留位全 0
|
||||
# 小 action_dim:全部落在舵机段 → 全 1.0
|
||||
np.testing.assert_allclose(default_column_weights(4), 1.0)
|
||||
# 12 列:舵机 1.0 + spk.F 0.2,delay 0
|
||||
# 12 列:舵机 1.0,spk 两列 0
|
||||
w12 = default_column_weights(12)
|
||||
self.assertAlmostEqual(float(w12[10]), 0.2, places=6)
|
||||
self.assertEqual(float(w12[11]), 0.0)
|
||||
np.testing.assert_allclose(w12[10:], 0.0)
|
||||
# 20 列:超出 18 列语义布局的扩展列默认 1.0(新执行器须参与训练)
|
||||
w20 = default_column_weights(20)
|
||||
np.testing.assert_allclose(w20[12:18], 0.0)
|
||||
@@ -2502,6 +2500,19 @@ class TestOneHot(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
validate_config_values(cfg.data, cfg.model, cfg.train)
|
||||
|
||||
def test_fixed_zero_columns(self):
|
||||
"""权重 0 列固定常量:spk.F→0、spk.delay→10ms、保留位→0"""
|
||||
from bins import fixed_zero_columns
|
||||
from action_norm import default_column_weights
|
||||
fixed = fixed_zero_columns(default_column_weights(18), 18)
|
||||
np.testing.assert_allclose(fixed[:10], 0.0)
|
||||
self.assertEqual(float(fixed[10]), 0.0)
|
||||
self.assertEqual(float(fixed[11]), 10.0)
|
||||
np.testing.assert_allclose(fixed[12:], 0.0)
|
||||
# 全 1 权重 → 无固定列
|
||||
np.testing.assert_allclose(
|
||||
fixed_zero_columns(np.ones(4), 4), 0.0)
|
||||
|
||||
def test_onehot_model_output_shape(self):
|
||||
"""onehot 模型前向输出 TOTAL_BINS logits"""
|
||||
cfg = Config()
|
||||
|
||||
+7
-1
@@ -9,7 +9,8 @@ from tqdm import tqdm
|
||||
from config import (Config, build_argparser, apply_overrides,
|
||||
validate_config_values, warn_init_from_mismatch)
|
||||
from action_norm import normalize_action, is_range_mode
|
||||
from bins import encode as bin_encode, decode as bin_decode, START, HEAD_COLS
|
||||
from bins import (encode as bin_encode, decode as bin_decode,
|
||||
fixed_zero_columns, START, HEAD_COLS)
|
||||
from model import ActionPredictor
|
||||
from dataset import ActionDataset
|
||||
from action_norm import default_column_weights
|
||||
@@ -126,6 +127,11 @@ def train_one_epoch(model, loader, optimizer, device, max_grad_norm: float = Non
|
||||
if onehot:
|
||||
raw = bin_decode(pred.detach().cpu().numpy(),
|
||||
action_dim)
|
||||
# 权重 0 列固定为常量,防自预测垃圾进入历史
|
||||
fixed = fixed_zero_columns(
|
||||
weights.cpu().numpy(), action_dim)
|
||||
m = fixed != 0.0
|
||||
raw[m] = fixed[m]
|
||||
abs_action = torch.from_numpy(normalize_action(
|
||||
raw, action_dim, mode=range_mode)).to(device)
|
||||
else:
|
||||
|
||||
Reference in new issue
Block a user