fix(actpred): 定位 HEF 输入不敏感根因,补齐归一化预训练链路
根因:预训练全零图像占位使 BN running_var 停在初始小值,推理时 特征放大至 ±1e6,DFC 在 token merge 点按最大幅度分配共享量化 scale,pred_token/act_tokens 全部被压成 0,HEF 输出对任何输入 恒定(详见 docs/reports/actpred_hef_insensitivity_rootcause_20260813.md) - export_deploy.py:新增 check_token_ranges 导出前 token 量级检查 (|img|>100 中止导出并提示微调;act/img 比例过大警告;归一化 模型在 z 空间采样,避免零方差列爆炸) - pretrain_mt.py:新增 --random-images(ImageNet 归一化噪声占位, 恢复 BN 统计)、--init-from(微调起点,含动作空间一致性警告)、 --normalize-actions 完整支持(训练集 stats 计算与保存、验证集复用) - gen_calibration.py:归一化模型的校准集自动 z-score 动作历史 - _onnx_validate.py / _npu_test.py:归一化模型适配(torch 参考 z-score、HailoPredictor 传 stats_path) - README:预训练章节补充新参数与 NPU 部署警告 - 单测 76/76 通过;冒烟验证:1 epoch 噪声微调后 |img| 2.46e5→8, 归一化后 |act| 694→2.9
This commit is contained in:
1 parent
8c58770280
commit
52c0ade3c4
8 files changed
+394
-27
No files matched your search
+18
-1
@@ -56,6 +56,20 @@ model.load_state_dict(ck["model_state_dict"])
|
||||
model.eval()
|
||||
pred_learned = model.pred_token.data.unsqueeze(1).numpy() # (1,1,1,d)
|
||||
|
||||
# 归一化模型:torch 参考在喂入前 z-score;HailoPredictor 传 stats_path
|
||||
# 由封装内部归一化(hist_real/hist_zero 保持原始值,避免双重归一化)
|
||||
import os
|
||||
stats_path = None
|
||||
if cfg.data.normalize_actions:
|
||||
stats_path = cfg.data.action_stats_path or os.path.join(
|
||||
os.path.dirname(os.path.abspath(CKPT)), "action_stats.npy")
|
||||
if not os.path.isabs(stats_path):
|
||||
stats_path = os.path.join(os.path.dirname(os.path.abspath(CKPT)),
|
||||
stats_path)
|
||||
if not os.path.exists(stats_path):
|
||||
raise FileNotFoundError(f"统计文件不存在: {stats_path}")
|
||||
print(f"HailoPredictor stats: {stats_path}")
|
||||
|
||||
from torchvision import transforms
|
||||
tf = transforms.Compose([
|
||||
transforms.Resize((cfg.data.image_height, cfg.data.image_width)),
|
||||
@@ -67,6 +81,9 @@ tf = transforms.Compose([
|
||||
def torch_pred(imgs, hist, use_learned_pred=True):
|
||||
x = torch.stack([tf(Image.fromarray(im)) for im in imgs]).unsqueeze(0)
|
||||
h = torch.tensor(hist).unsqueeze(0)
|
||||
if stats_path is not None:
|
||||
stats = np.load(stats_path)
|
||||
h = (h - torch.tensor(stats[0])) / torch.tensor(stats[1])
|
||||
if use_learned_pred:
|
||||
with torch.no_grad():
|
||||
return model(x, h).squeeze(0).numpy()
|
||||
@@ -78,7 +95,7 @@ def torch_pred(imgs, hist, use_learned_pred=True):
|
||||
return out
|
||||
|
||||
|
||||
with HailoPredictor(HEF_PATH) as p:
|
||||
with HailoPredictor(HEF_PATH, stats_path=stats_path) as p:
|
||||
print(f"\ndims: frame_len={p.frame_len} img={p.image_width}x{p.image_height} "
|
||||
f"history={p.history_len} action_dim={p.action_dim}")
|
||||
t0 = time.perf_counter()
|
||||
|
||||
Reference in new issue
Block a user