[hbcompile][RDK S100]ONNX模型无法导出为hbm

我来帮你分析这个 ONNX 模型转换问题。先搜索一下社区中是否有类似的案例。

根据你的日志和错误信息,我定位到了两个核心问题:

问题诊断

问题 1:float division by zero 错误

根本原因:校准阶段计算 grid scale 时,input_h - 1 = 0,说明模型中存在高度为 1 的特征图,导致除零错误。

从错误堆栈看:

grid_scale_y = ha / 2 ** (interp_bit_num - 1 + s) / (input_h - 1)
ZeroDivisionError: float division by zero

问题 2:after max iteration, there are still patterns that can be matched

原因:模型中存在 BPU 无法完全优化的算子模式,通常与动态 shape 或不支持的算子组合有关。


解决方案

方案一:跳过校准(快速验证)

先不使用校准数据,用随机数据验证模型能否通过编译:

hb_compile --model ${onnx_model} --march nash-e \
    --input-shape left_image 1x3x480x640 \
    --input-shape right_image 1x3x480x640 \
    --skip-calibration

如果这样能通过,说明问题出在校准数据或校准流程上。


方案二:修改校准配置(推荐)

根据你的 YAML 配置,需要做以下调整:

1. 修改 YAML 配置文件

calibration_parameters:
  cal_data_dir: ./calibration_data_test1/left;./calibration_data_test1/right
  calibration_type: fixed  # 改用 fixed 模式,避免 KL 散度计算
  cal_data_num: 1  # 明确指定数量

compiler_parameters:
  compile_mode: latency
  optimize_level: O0  # 保持 O0,减少优化带来的问题
  core_num: 1
  jobs: 16

input_parameters:
  input_name: left_image;right_image
  input_shape: 1x3x480x640;1x3x480x640
  input_type_rt: featuremap;featuremap  # 改用 featuremap
  input_type_train: featuremap;featuremap
  input_layout_train: NCHW;NCHW
  norm_type: no_preprocess;no_preprocess  # 不做预处理
  mean_value: ''
  scale_value: ''
  std_value: ''

2. 校准数据格式要求

由于你的模型已经做过归一化(ImageNet normalization stripped),校准数据应该是已经预处理后的 float32 数据

# 生成校准数据的正确方式
import numpy as np
import os

# 左图校准数据
left_dir = './calibration_data_test1/left'
os.makedirs(left_dir, exist_ok=True)

# 读取原始图片,做与模型训练时相同的预处理
from PIL import Image
import torchvision.transforms as transforms

transform = transforms.Compose([
    transforms.Resize((480, 640)),
    transforms.ToTensor(),  # HWC->CHW, 0-255->0-1
    transforms.Normalize(mean=[0.485, 0.456, 0.406], 
                         std=[0.229, 0.224, 0.225])
])

for i, img_path in enumerate(left_image_paths[:1]):  # 先用 1 张测试
    img = Image.open(img_path).convert('RGB')
    img_tensor = transform(img)  # [3, 480, 640], float32
    img_tensor = img_tensor.unsqueeze(0)  # [1, 3, 480, 640]
    img_tensor.numpy().tofile(f'{left_dir}/{i:04d}.feature')

# 右图同理

关键点

  • 文件后缀用 .feature(float32 数据)
  • 数据已经是归一化后的 float32,不要再做 scale/mean 处理
  • YAML 中 norm_type: no_preprocess

方案三:排查模型中的 1x1 特征图

使用 Netron 查看模型结构,找到高度或宽度为 1 的节点:

# 安装 netron
pip install netron

# 启动查看
netron ffs_patch_no_scatternd_sim.onnx

在 Netron 中搜索:

  • 形状为 [1, C, 1, W][1, C, H, 1] 的节点
  • 特别是 ResizeInterpolateGlobalAveragePool 等算子

如果找到这样的节点,可能需要:

  1. 修改模型结构,避免产生 1x1 的特征图
  2. 使用 --disable-pass 跳过某些优化

方案四:多输入校准数据对齐

从社区案例 topic/34721 看,双目模型校准需要特别注意:

校准数据目录结构

calibration_data_test1/
├── left/
│   ├── 0000.feature  # float32, 1×3×480×640
│   └── 0001.feature
└── right/
    ├── 0000.feature  # float32, 1×3×480×640
    └── 0001.feature

验证脚本

import numpy as np
import os

left_dir = './calibration_data_test1/left'
right_dir = './calibration_data_test1/right'

left_files = sorted([f for f in os.listdir(left_dir) if f.endswith('.feature')])
right_files = sorted([f for f in os.listdir(right_dir) if f.endswith('.feature')])

print(f"Left: {len(left_files)} files")
print(f"Right: {len(right_files)} files")

# 验证第一个文件
if left_files:
    data = np.fromfile(f'{left_dir}/{left_files[0]}', dtype=np.float32)
    print(f"Left data shape: {data.shape}, expected: {1*3*480*640}")
    
if right_files:
    data = np.fromfile(f'{right_dir}/{right_files[0]}', dtype=np.float32)
    print(f"Right data shape: {data.shape}, expected: {1*3*480*640}")

建议执行顺序

  1. 先用方案一--skip-calibration)验证模型能否编译通过
  2. 如果成功,再用方案二准备正确的校准数据
  3. 如果失败,用方案三排查模型结构问题
  4. 参考社区案例S100 双目深度模型量化失败

额外建议

关于 ScatterND 算子

你提到已经消除了 ScatterND,但建议确认:

# 检查模型中是否还有其他不支持的算子
hb_checker --model ffs_patch_no_scatternd_sim.onnx --march nash-e

关于 --fast-perf

这个参数主要用于加速编译,不会解决算子支持问题。如果模型本身有 unsupported patterns,加这个参数也没用。


请先尝试方案一,告诉我是否能通过编译。如果还有问题,请提供:

  1. hb_checker 的输出
  2. Netron 中找到的 1x1 特征图节点截图
  3. 完整的校准数据生成脚本