Files
blackbird/actpred/_npu_test.py
T
qing 14964b9574 fix(actpred): 修复 Hailo HEF 编译链路的输入断连与封装问题
- hailo_infer: 帧输入按 input_layerN 排序绑定(vstream 报告顺序实测
  为 1,5,4,3,2,按报告顺序会 frame1/frame2 错位),新增 --pred-token
- gen_calibration: pred_token 校准改为学习值 ±30% 抖动,修复常量
  校准量化退化(scale=18571.7,任何输入都被量化成 0)
- export_deploy: 新增 --pred-token-out 保存学习值供推理端喂入
- dfc_compile: parser/optimize 后自动检查输入层连通性,断开即报错
- 新增 _npu_test.py(NPU 4 角敏感性复验)与 _onnx_validate.py
  (编译前 ONNX 数值门槛),test_actpred 补乱序帧序用例
2026-08-13 12:22:26 +08:00

109 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""actpred HEF NPU 输入敏感性复验脚本(重编译后的验收门槛)
用法: venv/bin/python _npu_test.py
4 角测试(真实/全零帧 × 真实/全零历史)+ 与 PyTorch 参考对比。
HEF 尺寸/布局从 vstream info 自动推导,checkpoint 用
checkpoints/pretrain_best_inference.pt。
"""
import sys
import time
sys.path.insert(0, "/opt/blackbird/actpred")
import numpy as np
import torch
import torch.nn as nn
from PIL import Image
from hailo_platform import HEF
from config import parse_checkpoint_config
from model import ActionPredictor
from hailo_infer import HailoPredictor
HEF_PATH = "checkpoints/actpred_pretrain.hef"
CKPT = "checkpoints/pretrain_best_inference.pt"
IMG_DIR = "data/episode_0000/images"
hef = HEF(HEF_PATH)
print("=== HEF vstream infos ===")
for i in hef.get_input_vstream_infos():
q = i.quant_info
print(f"IN {i.name:16s} {tuple(i.shape)} {i.format.type} "
f"zp={q.qp_zp} scale={q.qp_scale:.6g}")
for o in hef.get_output_vstream_infos():
q = o.quant_info
print(f"OUT {o.name:16s} {tuple(o.shape)} {o.format.type} "
f"zp={q.qp_zp} scale={q.qp_scale:.6g}")
imgs_real = [np.array(Image.open(f"{IMG_DIR}/000{i}.jpg").convert("RGB"))
for i in range(3)]
imgs_zero = [np.zeros_like(imgs_real[0]) for _ in range(3)]
hist_real = np.array([
[45, 0, 45, 0, 45, 0, 45, 0, 90, 90, 10, 200, 0, 0, 0, 0, 0, 0],
[45, 0, 45, 0, 45, 0, 45, 0, 90, 90, 0, 20, 0, 0, 0, 0, 0, 0],
[45, 0, 45, 0, 45, 0, 45, 0, 90, 90, 0, 0, 0, 0, 0, 0, 0, 0],
[45, 0, 45, 0, 45, 0, 45, 0, 90, 90, 0, 0, 0, 0, 0, 0, 0, 0],
[45, 0, 45, 0, 45, 0, 45, 0, 90, 90, 0, 0, 0, 0, 0, 0, 0, 0],
], dtype=np.float32)
hist_zero = np.zeros((5, 18), dtype=np.float32)
# PyTorch 参考(checkpoint 自带 config)
ck = torch.load(CKPT, map_location="cpu", weights_only=True)
cfg = parse_checkpoint_config(ck["config"])
model = ActionPredictor(cfg.model, cfg.data, pretrained=False)
model.load_state_dict(ck["model_state_dict"])
model.eval()
pred_learned = model.pred_token.data.unsqueeze(1).numpy() # (1,1,1,d)
from torchvision import transforms
tf = transforms.Compose([
transforms.Resize((cfg.data.image_height, cfg.data.image_width)),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
])
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 use_learned_pred:
with torch.no_grad():
return model(x, h).squeeze(0).numpy()
saved = model.pred_token.data.clone()
model.pred_token.data.zero_()
with torch.no_grad():
out = model(x, h).squeeze(0).numpy()
model.pred_token.data.copy_(saved)
return out
with HailoPredictor(HEF_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()
a = p.predict(imgs_real, hist_real, pred_token=pred_learned)
b = p.predict(imgs_zero, hist_real, pred_token=pred_learned)
c = p.predict(imgs_real, hist_zero, pred_token=pred_learned)
d = p.predict(imgs_zero, hist_zero, pred_token=pred_learned)
t1 = time.perf_counter()
print(f"4x predict: {(t1 - t0) / 4 * 1000:.1f} ms/次")
for name, r in [("real_img+real_hist", a), ("zero_img+real_hist", b),
("real_img+zero_hist", c), ("zero_img+zero_hist", d)]:
print(f"{name:20s} max_abs={np.abs(r).max():.4f} out={np.round(r[:6], 3)}...")
img_sens = np.abs(a - b).max()
hist_sens = np.abs(a - c).max()
print(f"\nimg sensitivity = {img_sens:.4f}(>0.1 视为输入连通)")
print(f"hist sensitivity = {hist_sens:.4f}(>0.1 视为输入连通)")
ref = torch_pred(imgs_real, hist_real, True)
ref_zero_pred = torch_pred(imgs_real, hist_real, False)
print(f"torch learned_pred : {np.round(ref[:6], 3)}...")
print(f"torch zero_pred : {np.round(ref_zero_pred[:6], 3)}...")
print(f"HEF vs torch(learned) max_diff={np.abs(a - ref).max():.4f}")
ok = img_sens > 0.1 and hist_sens > 0.1
print("\nPASS: HEF 输入连通,可继续与 torch 对比量化误差"
if ok else "\nFAIL: HEF 输出对输入不敏感,编译仍有问题")