前面的文章已经完成 YOLOv5x 的 HBM 编译、板端推理,以及 hb_analyzer 性能分析。这次换一个问题:如果部署后的检测结果与浮点模型不同,差异从哪一个模型阶段开始出现?
OpenExplorer 的 hb_compile 不会只留下一个 HBM。它还会保存原始浮点模型、优化后的浮点模型、校准模型、PTQ 模型和量化 BC。hb_verifier 可以在这些阶段之间比较节点输出;对 BC 与 HBM,则比较模型最终输出。
官方 PTQ 部署一致性指南给出了各阶段产物的含义,
hb_verifier 工具手册列出了支持的模型组合与输入要求。
本文沿用同一份 YOLOv5x 编译产物,不重新量化或编译。实验依次核对浮点图优化、伪量化、定点 BC,以及 J6E 板上的 HBM。本文中的余弦相似度和一致性数字都来自实际运行日志。
1. 先分清两种比较
hb_verifier 的输出分成两类,不能混用:
模型组合 | 工具给出的内容 | 用来回答的问题 |
|---|
ONNX ↔ ONNX、ONNX ↔ BC | 逐节点余弦相似度,包括最终输出节点 | 数值变化主要出现在哪个模型阶段、哪些节点附近? |
定点 BC ↔ HBM | 最终输出的一致性、不一致元素数量、最大绝对误差和最大相对误差 | 已量化模型编译并在目标环境运行后,最终输出是否与 BC 对齐? |
余弦相似度接近 1 表示对应张量的方向接近,
不表示所有元素相等。HBM 只支持最终输出一致性比较,不能靠这项工具直接得到 HBM 的逐层余弦表。
工具手册:输出内容说明本文的对照路径是:
这条路径省略了中间的 ptq_model.onnx。
2. 实验环境和模型接口
项目 | 本次实验 |
|---|
OpenExplorer 发布包 | horizon_j6_open_explorer_v3.8.1-py310_20260326 |
hb_verifier | 3.5.11 |
HBM 运行时日志中的 HBRT4 | 4.9.7 |
目标板 | Horizon Robotics J6E MatrixB Development Board |
编译架构 | nash-e |
原始图片 | COCO_val2014_000000181007.jpg,原图尺寸 446×640 |
HBM | yolov5x_640x640_nv12.hbm |
工程位于容器内:
本次比较使用同一条 hb_compile PTQ 链路中的模型文件。编译配置里,训练输入为 rgb、NCHW、1×3×640×640,运行时输入为 nv12,separate_batch: false,HBM 的 max_l2m_size 为 0。
工具手册:支持范围模型接口也有明显差别:
模型 | 输入名称 | 形状与数据类型 |
|---|
优化后的浮点 ONNX | images | (1, 3, 640, 640),FP32 |
最终 HBM | images_y | (1, 640, 640, 1),U8 |
最终 HBM | images_uv | (1, 320, 320, 2),U8 |
HBM 的三个原始检测头分别为 output、729、741,形状依次是 (1,3,80,80,85)、(1,3,40,40,85) 和 (1,3,20,20,85)。
3. 输入数据:浮点 .npy 与 NV12 双平面不能混传
ONNX 对照使用已有校准样本:
该样本由原图先做 letterbox,再做 BGR→RGB、HWC→CHW 和 /255。在本次环境中,用工程的 preprocess_one() 重新处理原图,得到的数组与现有 .npy 逐元素相等。
HBM 的运行时输入必须是 NV12 的两个 U8 平面。工程原有的 y_data.npy、uv_data.npy 虽然来自同一张图片,却使用直接拉伸到 640×640,与校准样本的 letterbox 几何处理不同。为了让本文各段实验尽量沿用同一张 letterbox 图,重新生成一组 NV12 输入,保存到独立实验目录,不覆盖工程原文件:
生成结果分别是 (1,640,640,1) uint8 和 (1,320,320,2) uint8。同源图片和同样的 letterbox 几何处理,不代表浮点 RGB 数组与 NV12 数组在数值上相同:色彩空间、色度采样和量化范围不同。本文的余弦值只比较各自命令指定的两个模型;BC↔HBM 的一致性也只针对传给它们的同一组 NV12 平面。
4. 依次运行四组分段比较
以下命令在 OpenExplorer 容器内执行。每组使用独立目录,避免 hb_verifier.log 相互覆盖。运行脚本会把标准输出保存为 run.log,并把真实进程退出码保存为 exit_code.txt。run_case 函数本身会继续执行下一组;判断工具是否正常退出时,须看各目录的 exit_code.txt。
最后一组 OPTIMIZED ↔ QBC 是跨阶段对照,不用相邻阶段的余弦相似度相乘来推算。以上命令采用同一份 FP32 .npy;这是 hb_compile 产出的 ONNX 与量化 BC 组合在官方手册中的用法。
工具手册:ONNX 与 HBIR 比较示例接着在 J6E 板上运行 HBM,与定点 BC 比较最终输出:
这里 None 对应本地运行的 BC,板卡 IP 对应 HBM;--remote-root 的两个值与模型位置一一对应。
5. 实测结果:变化从哪一段开始?
下表只摘取三个最终检测头的余弦相似度。原始日志还保存了逐节点结果,可以按节点名称继续追踪。
比较阶段 | output:80×80 | 729:40×40 | 741:20×20 | 工具退出码 |
|---|
原始浮点 ONNX → 优化浮点 ONNX | 1.000000 | 1.000000 | 1.000000 | 0 |
优化浮点 ONNX → 校准 ONNX | 0.996401 | 0.995874 | 0.995633 | 0 |
校准 ONNX → 量化 BC | 0.997702 | 0.997328 | 0.997706 | 139 |
优化浮点 ONNX → 量化 BC | 0.995399 | 0.994687 | 0.994519 | 139 |
第一组的三个 1.000000 是工具按六位小数打印的结果。官方指南把“原始浮点 → 优化浮点”视为等价图优化,给出的最终输出预期是余弦相似度不低于 99.9%;本次样本满足这个预期。
官方 PTQ 部署一致性指南第二组从优化浮点模型进入校准模型,三个输出头降到约 0.996。官方文档将校准模型描述为引入伪量化行为的模型;因此,本次样本中可观察的输出差异首先出现在这一阶段。第三组进一步到定点 BC,三个头为 0.997328~0.997706。余弦相似度不是检测框召回率,也不存在“所有节点必须高于某个统一阈值”的结论。
逐节点表还提供了继续排查的入口。例如在“优化浮点 → 校准 ONNX”中,/model.20/m/m.3/cv1/act/LeakyRelu 的余弦相似度为 0.831278;在“优化浮点 → 量化 BC”中,同名节点为 0.797768。最终三个头的相似度明显高于该节点,不能只凭一个局部最小值判定模型精度不合格。
6. 定点 BC 与 J6E HBM:三个检测头均一致
使用第 3 节生成的 letterbox 后 NV12 双平面,hb_verifier 在 J6E 板上完成 HBM 推理,并打印:
输出名 | 工具显示的一致性 | 不一致元素 | 最大绝对误差 | 最大相对误差 |
|---|
output | True | 0/1,632,000 | 0.0 | 0.0 |
729 | True | 0/408,000 | 0.0 | 0.0 |
741 | True | 0/102,000 | 0.0 | 0.0 |
日志原文还包含 The model output consistency comparison was successful.。这里的判断采用 hb_verifier 默认比较精度;官方文档说明默认按小数点后五位比较。表中为 0 个不一致元素,表示
这一次输入在工具的比较规则下,BC 与板端 HBM 的三个最终输出一致。
工具手册:--compare_digits 与一致性表这组结果与前面的余弦表回答的问题不同:浮点到定点 BC 的输出已经产生数值差异;对本次 NV12 输入,BC 到 J6E HBM 没有在最终输出比较中观察到新的差异。因此,如果业务图片上的检测框数量或类别异常,下一步还要检查图片前处理、输出解码与 NMS,并用多图标注集验证检测指标。

7. 这次验证能够说明什么
在同一份 YOLOv5x PTQ 模型链、同一张源图片下,浮点图优化阶段的三个检测头相似度均打印为 1.000000;引入校准模型后,三个检测头出现可观察的数值变化;量化 BC 与 J6E 板端 HBM 在一次 NV12 输入上得到三个最终输出一致的比较表。hb_verifier 让“结果不同”能够按模型阶段拆开观察,而不只停留在 HBM 能否运行。
本次仅使用一张图片。本文没有计算精度、召回率或 mAP,也没有证明整个业务预处理和 YOLOv5 后处理已经对齐。需要判断量化是否满足产品指标,应继续用真实评估集跑完整检测流程;若发现掉点,再按本篇方法选取异常图片做分段、逐节点比较。