YOLOv5x 在 Horizon J6 上的端到端部署实践(上):工具链部署
本文记录 YOLOv5x 从 PyTorch 权重到 J6 可执行 HBM 模型的部署流程。上篇聚焦工具链侧:环境准备、ONNX 导出、校准数据生成和 HBM 编译;下篇再展开板端 C++ 推理部署与精度评估。
平台 :Horizon J6 (nash-e)
SDK :horizon_j6_open_explorer_v3.8.1-py310
模型 :YOLOv5x,输入分辨率 640x640
板端输入:NV12;PC 端校准输入:RGB float32
1. 环境准备
首先安装 SDK 中的 host 端工具链和 board 端运行时:
bash
cd horizon_j6_open_explorer_v3.8.1-py310_20260326/package/host
bash install.sh
cd ../board
bash install.sh
Python 侧需要准备模型导出、ONNX 处理、校准与评估相关依赖:
bash
pip install torch onnx onnxsim onnxruntime opencv-python-headless pycocotools
Horizon 工具链建议使用 SDK 自带 whl 安装,重点包括:
horizon_tc_uihmcthbdk4_compiler
后续操作均在示例工程目录中进行:
bash
cd horizon_j6_open_explorer_v3.8.1-py310_20260326/nano_toolchain/yolov5x_End-to-End_Pipeline/
2. Stage 1:PyTorch 导出 ONNX
YOLOv5 的 .pt 权重是包含 Python 对象引用的序列化文件,因此导出前需要确保 YOLOv5 源码在 Python 路径中。SDK 中已经包含对应源码,可直接复用:
bash
nano_toolchain/model_convert/detection/yolov5x_scratch/yolov5_repo/
导出流程可以概括为:
- 加载
yolov5x.pt - 设置模型为
eval和export状态 - 构造
(1, 3, 640, 640)的 dummy input - 使用
torch.onnx.export导出 ONNX - 使用
onnxsim简化模型
执行命令:
bash
python3 stage1_model_export.py \
--weights ./model/yolov5x.pt \
--img-size 640 \
--batch-size 1 \
--include onnx \
--opset 12 \
--simplify
导出后得到:
| 文件 | 说明 |
|---|---|
model/yolov5x.pt |
原始 PyTorch 权重 |
model/yolov5x.onnx |
简化后的 ONNX 模型 |
模型输出包含三个检测头:
scss
P3/8 (1, 3, 80, 80, 85)
P4/16 (1, 3, 40, 40, 85)
P5/32 (1, 3, 20, 20, 85)
其中 85 = 4(bbox) + 1(obj) + 80(classes)。
3. Stage 2:生成校准数据
PTQ 量化需要少量代表性样本来统计激活分布。一般选择 50 到 500 张图片即可,关键是预处理必须和后续推理保持一致。
本流程中的校准预处理为:
rust
原始图片
-> Letterbox Resize 到 640x640,padding=114
-> BGR 转 RGB
-> HWC 转 CHW
-> /255 归一化
-> 保存为 .npy
执行:
bash
mkdir -p calib_images
# 从 COCO 验证集拷贝若干图片到 calib_images/
python3 stage2_calibration_set.py
输出目录:
erlang
calibration_data_yolov5/
├── xxx.npy
├── yyy.npy
└── ...
每个 .npy 文件的格式为:
makefile
shape: (1, 3, 640, 640)
dtype: float32
range: [0, 1]
4. Stage 3:ONNX 编译为 HBM
Horizon 工具链的大致链路为:
rust
ONNX -> hmct 量化校准 -> .bc 量化模型 -> hbdk4 编译 -> .hbm
核心编译配置如下:
yaml
model_parameters:
onnx_model: model/yolov5x.onnx
march: nash-e
working_dir: ./model_output
output_model_file_prefix: yolov5x_640x640_nv12
remove_node_type: 'Dequantize'
input_parameters:
input_name: images
input_type_rt: nv12
input_type_train: rgb
input_layout_train: NCHW
input_shape: 1x3x640x640
norm_type: 'data_scale'
scale_value: 0.003921568627451
calibration_parameters:
cal_data_dir: ./calibration_data_yolov5
calibration_type: 'default'
compiler_parameters:
compile_mode: latency
core_num: 1
optimize_level: O2
jobs: 16
这里最需要注意的是:
march: nash-e对应 J6 BPU 架构。input_type_rt: nv12表示板端运行时输入为 NV12。input_type_train: rgb与校准数据保持一致。scale_value: 1/255表示板端运行时由模型完成归一化。
编译命令:
bash
hb_compile -c full_compile_config.yaml
主要产物:
model_output/
├── yolov5x_640x640_nv12.hbm
├── yolov5x_640x640_nv12_quantized_model.bc
├── yolov5x_640x640_nv12_original_float_model.onnx
├── yolov5x_640x640_nv12_quant_info.json
└── yolov5x_640x640_nv12.html
其中 .hbm 是后续板端部署使用的模型文件。
5. 查看模型输入输出
将 HBM 放到板端后,可以用 hrt_model_exec 查看模型信息:
bash
hrt_model_exec model_info --model_file yolov5x_640x640_nv12.hbm
输入一般会被拆成两个 NV12 tensor:
| Tensor | Shape | 说明 |
|---|---|---|
images_y |
(1, 640, 640, 1) |
Y 平面 |
images_uv |
(1, 320, 320, 2) |
UV 交错平面 |
输出仍然是 YOLOv5 的三个检测头,但要特别关注 stride:
ini
output: shape=(1,3,80,80,85), stride=(7372800,2457600,30720,384,4)
729: shape=(1,3,40,40,85), stride=(1843200,614400,15360,384,4)
741: shape=(1,3,20,20,85), stride=(460800,153600,7680,384,4)
最后一维虽然只有 85 个 float,理论大小是 340 bytes,但实际 col stride 是 384 bytes,说明中间存在 padding。这个细节会直接影响下篇的 C++ 后处理实现。
小结
上篇完成了从 yolov5x.pt 到 yolov5x_640x640_nv12.hbm 的工具链流程。关键点主要有三个:
- 导出 ONNX 时要正确加载 YOLOv5 源码和模型结构。
- 校准数据预处理要与部署推理保持一致。
- 编译配置中要明确 J6 架构、NV12 输入和运行时归一化方式。
下篇将继续介绍如何在 J6 板端用 C++ 加载 HBM、构造 NV12 输入、调用 UCP/DNN API、解析带 padding 的输出,并完成 YOLOv5 后处理和 mAP 评估。