YOLOv5x 在 Horizon J6 上的端到端部署实践(下):板端推理与精度评估
上篇完成了模型从 PyTorch 到 HBM 的转换。本篇继续介绍板端 C++ 推理部署,包括 NV12 输入构造、UCP/DNN API 调用、YOLOv5 输出解析、交叉编译、板端运行以及 mAP 精度评估。
前置产物:yolov5x_640x640_nv12.hbm
板端输入:NV12 双平面
输出:YOLOv5 三个检测头,注意 tensor stride 与 padding
1. 板端推理整体流程
板端 C++ 推理可以拆成以下几步:
部署时最容易出问题的地方主要有两个:
输入必须是模型期望的 NV12 双平面格式。
输出不能按紧密内存布局读取,必须按 stride 访问。
2. NV12 输入格式
HBM 的输入通常会被拆成两个 tensor:
Tensor | Shape | 数据类型 | 说明 |
|---|---|---|---|
images_y | (1, 640, 640, 1) | U8 | Y 亮度平面 |
images_uv | (1, 320, 320, 2) | U8 | UV 交错色度平面 |
从 OpenCV 读取的 BGR 图片需要先做 letterbox,再转换为 NV12:
3. UCP/DNN API 调用流程
板端推理主流程可以简化为:
实际工程中还需要处理错误码、动态 stride、内存大小计算和多输入多输出遍历。
4. 输出解析:必须按 stride 访问
YOLOv5 的输出 tensor 中存在 padding,不能用普通的连续数组方式读取。例如:
正确访问方式是使用 byte stride 计算偏移:
不要这样做:
这是板端 YOLO 后处理最常见的坑之一。
5. YOLOv5 解码与 NMS
YOLOv5 每个检测头对应一个 stride 和一组 anchors:
解码公式:
后处理一般包含:
遍历三个检测头。
对 obj 和 class 做 sigmoid。
按置信度阈值过滤候选框。
将 letterbox 坐标映射回原图。
执行 NMS。
绘制检测框和类别标签。
6. 交叉编译
使用 SDK 自带的 aarch64 交叉编译器:
CMake 中需要链接 DNN、UCP、OpenCV 等运行依赖:
如果共享库中的部分符号由板端系统运行时提供,可以加入:
7. 部署到板端运行
将可执行文件、HBM 模型和测试图片拷贝到板端:
板端执行:
如果结果框明显异常,优先检查三件事:
输入 NV12 是否正确。
- 是否重复做了 /255。
输出是否按 tensor stride 读取。
8. mAP 精度评估
部署完成后,可以对比浮点模型和量化模型在 COCO val2017 上的 mAP:
示例结果:
Metric | Float ONNX | Quantized BC | Diff |
|---|---|---|---|
mAP | 0.4913 | 0.4826 | -0.0087 |
mAP_50 | 0.6424 | 0.6509 | +0.0085 |
mAP_75 | 0.5241 | 0.5054 | -0.0187 |
mAP_small | 0.3575 | 0.3196 | -0.0379 |
mAP_med | 0.5280 | 0.5066 | -0.0214 |
mAP_large | 0.6710 | 0.6771 | +0.0061 |
量化后 mAP 有轻微下降属于正常现象。若下降过大,优先排查校准数据覆盖度、预处理一致性、输入归一化和后处理 decode 是否一致。
9. 关键点速查
环节 | 要点 |
|---|---|
输入格式 | 板端 HBM 输入为 NV12 双平面 |
归一化 | 由编译配置中的 scale_value 完成 |
校准数据 | 使用 RGB、NCHW、float32、[0,1] |
预处理 | Stage 2、Stage 4、Stage 5 的 letterbox 逻辑必须一致 |
输出解析 | 必须按 tensor byte stride 访问 |
后处理 | YOLOv5 decode、坐标映射、NMS 要和浮点侧保持一致 |
小结
下篇完成了从 HBM 模型到板端 C++ 推理的部署闭环。相比工具链编译,板端实现更容易踩到数据格式和内存布局问题。只要保证 NV12 输入正确、运行时归一化不重复、输出按 stride 解析,YOLOv5x 在 J6 上的端到端部署就能稳定跑通。
