本文基于开源内容进行学习整理,如有错漏,欢迎评论交流~
背景
J6M 开发板要求量化感知训练与板端推理采用同一模型,流程连贯。本文基于 plugin_basic/run_pipeline/fx_mode.py 示例完成 QAT 量化编译,再使用 ucp_tutorial/dnn/basic_samples/code/00_quick_start/resnet_rgb/src/main.cc 示例进行板端 C++ 推理,整体流程为:
针对 J6M 开发板,需对原始示例代码做以下修改。
1. QAT 量化与编译:代码修改
1.1 目标架构设为 NASH-M
get_model_fx 函数的 march 参数改为 March.NASH_M:
common.py 中 compile 函数的 march 参数指定为 "nash-m":
1.2 量化精度配置改为 qint8
QconfigSetter 的 templates 中,将 ModuleNameTemplate({"": torch.float16}) 改为 ModuleNameTemplate({"": qint8}):
1.3 插入前处理节点
hbir 导出后、convert 之前,插入 transpose 和图像前处理节点:
节点 | 作用 |
|---|---|
insert_transpose(permutes=[0, 3, 1, 2]) | 插入 transpose 节点,将输入从 NCHW 转换为 NHWC |
insert_image_preprocess() | 插入图像前处理节点,并做 mean/std 归一化,适配 RGB 输入 |
2. QAT 量化与编译:执行命令
按顺序依次执行:
完成后在 model_path 目录下生成 model.hbm,用于下一步板端推理。
3. 板端 C++ 推理
使用 resnet_rgb/src/main.cc 示例代码加载上一步生成的 model.hbm 进行板端推理。
3.1 代码修改
模型输出类别数为 10(CIFAR-10),需将 get_topk_result 中的 tensor_len 从 1000 改为 10:
其余代码不做修改。
3.2 编译
运行 build_x86.sh 编译推理程序:
3.3 运行推理
到 runtime/script_x86/00_quick_start/ 目录下,修改 run_resnet_rgb.sh,将 --model_file 指向 QAT 编译产出的 HBM,--image_file 指向准备的dog.jpg:
执行:
输出结果:
TOP 0 结果 id 为 5,对应 CIFAR-10 中的 dog 类别,推理正确。
3.4 CIFAR-10 类别索引
索引 | 类别 |
|---|---|
0 | airplane |
1 | automobile |
2 | bird |
3 | cat |
4 | deer |
5 | dog |
6 | frog |
7 | horse |
8 | ship |
9 | truck |
3.5. 为什么 HBM 输入 shape 变了、图像尺寸变了,但推理代码不需改动
与原始 resnet50 示例相比,本流程中 HBM 的输入 shape 变了(CIFAR-10 的 32x32 vs resnet50 的 224x224),dog.jpg 的尺寸也与 resnet50 期望的输入不同。但推理代码只改了 tensor_len,其余无需改动,原因如下:
推理代码所有与 shape 相关的操作都是从 HBM 模型属性中动态读取的,而非硬编码:
代码位置 | 动态读取的属性 | 作用 |
|---|---|---|
prepare_tensor | properties.alignedByteSize | 根据 HBM 模型属性分配输入输出内存大小 |
read_image_2_tensor_as_rgb | properties.validShape.dimensionSize[1] / [2] | 动态获取模型要求的 H 和 W,将任意尺寸的输入图像 resize 到该大小 |
add_padding | properties.validShape + properties.stride | 根据 HBM 的 valid shape 和 stride 做内存对齐填充 |
因此,无论 HBM 的输入 shape 是 1x32x32x3 还是 1x224x224x3,推理代码都能自动适配。唯一需要手动修改的是输出类别数 tensor_len,因为它取决于模型训练时的分类数(CIFAR-10 为 10)。

