在模型部署过程中,我们经常会遇到这样的问题:
浮点模型结果正常,但经过模型优化、量化或者编译之后,模型输出开始出现差异,这个差异到底是在哪个阶段引入的?
如果只对比原始 ONNX 和最终 HBM,虽然可以知道最终结果"不一致",但很难进一步判断问题来自模型优化、PTQ 量化,还是 BC 到 HBM 的编译过程。
hb_verifier 就是用来解决这类问题的。
一、hb_verifier 是什么
hb_verifier 是地平线算法工具链提供的 模型一致性验证工具,主要用于比较模型在不同转换阶段的计算结果。
征程6 PTQ 模型转换过程中会产生多个阶段的模型,例如:

当我们怀疑某两个阶段之间产生了额外误差时,就可以使用 hb_verifier 对两个模型进行比较。
目前主要支持两类比较方式:
1. 余弦相似度比较
主要用于:
- ONNX vs ONNX
- ONNX vs HBIR(BC)
- HBIR vs HBIR
工具会比较两个模型对应节点的输出,并计算 Cosine Similarity(余弦相似度)。
余弦相似度越接近 1,说明两个 Tensor 的结果越接近。
例如:
| NodeName | TensorName | CosineSimilarity |
|---|---|---|
| Conv_0 | 365 | 0.999978 |
| Relu_2 | 367 | 0.999976 |
| MaxPool_3 | 368 | 0.999901 |
| Conv_4 | 369 | 0.999112 |
因此,它很适合用于观察:
模型从哪个阶段、哪个节点开始产生比较明显的数值变化。
征程6 官方文档同样推荐在出现模型精度下降或怀疑不同阶段存在一致性问题时,使用 hb_verifier 对各阶段模型进行逐节点余弦相似度分析。
2. 输出一致性比较
对于:
- Quantized BC vs HBM
- HBM vs HBM
主要关注的是模型最终输出是否一致。
输出类似:
| OutputName | Consistency | Mismatched Elements | Max Abs Diff | Max Rel Diff |
|---|---|---|---|---|
| output | True | 0/1000 (0.00%) | 0.0 | 0.0 |
其中:
- Consistency:输出是否一致;
- Mismatched Elements:不一致元素数量;
- Max Abs Diff:最大绝对误差;
- Max Rel Diff:最大相对误差。
需要注意的是,HBM 目前只支持最终输出一致性比较,不支持像 ONNX、BC 那样进行逐节点余弦相似度分析。
因此,可以简单理解为:

二、hb_verifier 怎么用
hb_verifier 的基本命令格式比较简单:
bash
hb_verifier \
-m model_a,model_b \
-i input.npy
其中 -m 指定需要比较的两个模型,-i 指定模型推理使用的数据。
当前工具支持 ONNX、BC 和 HBM 等模型格式。
常用参数
| 参数 | 作用 | 说明 |
|---|---|---|
| -m, --model | 指定待比较模型 | 支持 .onnx、.bc、.hbm,两个模型使用英文逗号分隔 |
| -i, --input | 指定输入数据 | 输入为 .npy;多输入模型可使用逗号分隔多个输入 |
| -c, --compare_digits | 设置比较精度 | 指定比较到小数点后多少位,默认 5 位 |
| --ip | 指定开发板 IP | HBM 可以指定在真实开发板运行;None 表示使用本地模拟环境 |
| -u, --username | 开发板用户名 | 默认 root |
| -p, --password | 开发板密码 | 无密码时不需要配置 |
| --port | SSH 端口 | 默认 22 |
| --version | 查看版本 | 显示当前 hb_verifier 版本 |
| -h, --help | 查看帮助 | 建议实际使用时结合当前 OE 版本查看 |
参数定义及模型输入方式可参考工具文档。
输入数据怎么传
单输入模型最简单:
bash
-i input.npy
对于多输入模型,可以按照模型输入顺序:
bash
-i image.npy,intrinsic.npy,extrinsic.npy
也可以显式指定输入名称:
bash
-i image:image.npy,intrinsic:intrinsic.npy,extrinsic:extrinsic.npy
如果两个被比较模型对输入数据的要求不同,还可以分别传入两组 -i:
bash
-i input.npy \
-i runtime_input.npy
例如原始 ONNX 使用的是模型训练侧输入,而 Quantized BC 已经插入了预处理或者 Layout 转换,这时候两边所需要的数据可能并不相同。
需要特别注意:
hb_verifier 使用的输入数据必须和对应模型实际输入要求一致,包括 input name、shape、dtype、layout 以及前处理方式。
尤其是 征程6 PTQ 编译过程中可能改变 BC/HBM 的输入 Layout 或插入预处理节点,因此不要看到两个模型"都是同一个网络",就直接认为它们一定可以使用完全相同的 .npy 数据。
下面看几种最常见的用法。
1. ONNX vs ONNX
ONNX 之间主要用于比较模型转换过程中不同 ONNX 阶段的一致性。
例如比较:

命令:
bash
hb_verifier \
-m googlenet_optimized_float_model.onnx,googlenet_calibrated_model.onnx \
-i input.npy
工具会对两个模型对应节点的输出计算余弦相似度。
这种方式比较适合观察:
Calibration / Fake Quantization 引入之后,各层数值发生了多大变化。
2. ONNX vs BC
这也是 PTQ 精度分析中非常常用的一种方式。
例如比较优化后的浮点模型和最终定点 BC:
bash
hb_verifier \
-m googlenet_optimized_float_model.onnx,googlenet_quantized_model.bc \
-i input.npy
如果这两个模型来自同一次 hb_compile 转换,工具可以根据转换链路对应模型节点进行一致性分析。
如果不是通过 hb_compile 同一次转换产生,并且两个模型的输入要求不同,则分别传入原始输入和 Runtime 输入:
bash
hb_verifier \
-m googlenet_original_float_model.onnx,googlenet_quantized_model.bc \
-i input.npy \
-i runtime_input.npy
这种比较非常适合用来观察:
从浮点模型进入最终定点计算以后,量化带来了多大的数值变化。
3. BC vs BC
如果模型转换是通过 PTQ API 或者其它方式进行的,也可能需要直接比较两个 HBIR 模型。
例如:

可以执行:
bash
hb_verifier \
-m googlenet_float_model.bc,googlenet_quantized_model.bc \
-i input.npy \
-i runtime_input.npy
这种方式比较适合 PTQ API 或者模型修改场景。
例如修改了:
- 输入预处理;
- Quantization Config;
- 部分节点计算精度;
- HBIR 图结构;
都可以通过 BC vs BC 检查修改前后的模型是否出现了非预期的数值变化。
4. BC vs HBM
这是我认为实际部署中非常值得使用的一种方式。
PTQ 编译完成之后通常会得到:
text
xxx_quantized_model.bc
xxx.hbm
其中:

这一步理论上不应该引入额外的数值不一致,因此可以使用:
bash
hb_verifier \
-m googlenet_quantized_model.bc,googlenet.hbm \
-i runtime_input.npy
检查两者最终输出。
官方 PTQ 快速上手中也直接给出了 Quantized BC 与 HBM 的 hb_verifier 一致性验证方式。
如果希望 HBM 在真实 征程6 板端执行,可以指定开发板 IP,例如:
bash
hb_verifier \
-m googlenet_quantized_model.bc,googlenet.hbm \
-i runtime_input.npy \
--ip None,192.168.1.10
其中:
text
None
表示本地模型不使用开发板,
text
192.168.1.10
指定 HBM 在对应 征程6 开发板运行。官方工具示例同样使用 None,board_ip 的方式区分本地与板端模型。
使用提醒
如果 HBM 推理时不指定开发板 IP,工具会在本地环境通过 BPU 仿真环境执行 HBM 推理。
本地仿真主要适合快速功能验证,在实际使用中部分环境或版本下可能出现结果波动。因此在做最终模型一致性确认时,更建议指定真实 征程6 开发板 IP,让 HBM 在真实板端执行推理,再与 Quantized BC 进行比较。
5. HBM vs HBM
hb_verifier 也支持直接比较两个 HBM。
一个比较实用的用法就是:
比较同一个 HBM 在本地模拟环境和真实开发板上的输出。
例如:
bash
hb_verifier \
-m googlenet.hbm,googlenet.hbm \
-i runtime_input.npy \
--ip None,192.168.1.10
此时:

然后比较两种执行环境下的最终模型输出。
这种方式非常适合排查:
当前异常究竟来自模型本身,还是 Runtime / 实际执行环境。
三、一个完整的使用例子
最后看一个实际项目中比较常见的场景。
假设我们已经使用 hb_compile 完成一个模型的 PTQ 转换,并得到:
text
resnet18_original_float_model.onnx
resnet18_optimized_float_model.onnx
resnet18_calibrated_model.onnx
resnet18_ptq_model.onnx
resnet18_quantized_model.bc
resnet18.hbm
模型部署到 征程6 后,发现最终结果和预期存在差异。
这时没必要一开始就直接怀疑 HBM。
可以使用 hb_verifier 按模型转换阶段逐步确认。
首先检查优化前后:
bash
hb_verifier \
-m resnet18_original_float_model.onnx,resnet18_optimized_float_model.onnx \
-i input.npy
这一阶段主要是等价图优化,正常情况下模型输出应该保持很高的一致性。征程6 官方文档给出的预期是该阶段输出余弦相似度应达到 99.9% 以上。
然后检查 Calibration 引入后的变化:
bash
hb_verifier \
-m resnet18_optimized_float_model.onnx,resnet18_calibrated_model.onnx \
-i input.npy
这一阶段已经开始模拟量化行为,因此允许产生一定的量化误差。
如果这里已经出现明显的相似度下降,那么问题更可能集中在:
text
Calibration
Quantization Config
量化敏感节点
而不是最终 HBM 编译。
接下来还可以比较:
bash
hb_verifier \
-m resnet18_optimized_float_model.onnx,resnet18_quantized_model.bc \
-i input.npy
进一步观察浮点模型到定点模型之间的逐节点数值变化。
最后再验证 Quantized BC 与最终 HBM:
bash
hb_verifier \
-m resnet18_quantized_model.bc,resnet18.hbm \
-i runtime_input.npy \
--ip None,192.168.1.10
如果结果显示:
text
Consistency = True
Mismatched Elements = 0
Max Abs Diff = 0
那么可以认为:
Quantized BC 编译为 HBM 的过程中没有引入额外的输出一致性问题。
这样原本一个比较模糊的问题:
text
"为什么 征程6 模型结果不对?"
就可以逐步拆成:

通过 hb_verifier 分阶段比较,很快判断误差究竟从哪个阶段开始出现。
所以 hb_verifier 的使用其实并不复杂,它最核心的价值可以概括成一句话:
给两个模型、准备好对应输入,然后通过余弦相似度或最终输出一致性比较,验证 征程6 模型转换链路中不同阶段的计算结果是否符合预期。
对于 PTQ 精度问题来说,与其一开始就盲目调整 Calibration 或 quant_config,先用 hb_verifier 确认问题发生在哪个阶段,通常会让后续排查更加直接。
参考资料
本文部分内容参考地平线官方文档: