1. 后量化转换 (PTQ)

1.1. 模型准备&算子支持

1. 如何准备地平线支持转换的浮点模型?

答:地平线 PTQ 浮点转换工具链支持 Caffe1.0 和 ONNX(opset_version=10/11 且 ir_version≤7)两种模型格式。 ONNX 导出方式参考如下:

训练框架

ONNX导出方式

Caffe1.0

地平线原生支持,无需导出ONNX

Pytorch

Pytorch导出ONNX及模型可视化教程

TensorFlow2

TensorFlow2导出ONNX及模型可视化教程

PaddlePaddle

PaddlePaddle导出ONNX及模型可视化教程

其他框架

ONNX Tutorials

2. 如何验证原始浮点 ONNX 模型的正确性?

答:工具链在转换模型时,会使用基于公版 ONNXRuntime 封装的接口实现模型的解析和前向,所以我们应该在使用工具链之前先检查原始浮点 ONNX 模型本身的合法性(即是否能够正常推理), 以及从训练框架导出 ONNX 的过程中是否引入了精度偏差。具体测试方式可参考: HB_ONNXRuntime 基础使用 <2.1 原始 ONNX 模型推理> 章节。

3. 如何转换 fp16 的模型?

答:工具链目前不支持 fp16 模型的直接转换,但可参考文章 如何将FP16的ONNX模型转为工具链支持的FP32的ONNX模型 将其先转换成 fp32 模型,再使用地平线工具链进行量化转换。

4. 如何修改 featuremap 模型的输入 layout?

答:通常四维 featuremap 输入模型的输入排布为 NCHW,即 input_train_layout: NCHW,但有时为了适配端侧 runtime 输入数据排布,就希望能修改编译后 bin 模型的输入 layout 为 NHWC。

如果直接配置 input_rt_layout: NHWC,则 bin 模型结构为 Transpose(CPU) + Quantize(CPU) + model(BPU);此时如果还想删除 Quantize 节点合入前处理,则必然需要先删除更前端的 Transpose 节点,这将不符合预期。

所以我们需要从模型层面修改,建议使用如下方式实现:
  1. 在 DL 框架中增加 transpose,使其输入 layout 为 NHWC,并重新导出 ONNX 模型;

  2. 使用 onnx 库直接修改模型,参考如下:

import onnx

onnx_model = onnx.load('./model.onnx')
graph = onnx_model.graph

new_node = onnx.helper.make_node(         # 创建Transpose节点
            "Transpose",
            inputs = ["data"],            # 新输入节点名称,自定义
            outputs = ["data_oringal"]    # 原输入节点名称
            perm = [0, 3, 1, 2]           # NHWC-->NCHW
)

graph.node.insert(0, new_node)            # 将Transpose插入在模型最前端

d = graph.input[0].type.tensor_type.shape.dim    # 配置新模型输入,NHWC
d[0].dim_value = 1
d[1].dim_value = 224
d[2].dim_value = 224
d[3].dim_value = 6

onnx_model.graph.input[0].name = f"data"    # 配置模型输入名称
onnx.checker.check_model(onnx_model)        # 模型检查
onnx.save(onnx_model, "new_model.onnx")     # 保存新模型

模型成功修改后,即可配置 input_rt_layout: NHWCinput_train_layout: NHWC,这样编译出的模型将自动消除前端的 transpose,直接以 NHWC 排布输入,模型前端只保留一个 CPU 运行的 Quantize 节点,可使用 hb_model_modifier 工具进一步删除。


1.2. 模型转换

1. 如何理解 shape_inference_fail.onnx?

答:该模型只会在 PTQ 模型转换失败时产出,对用户无特殊意义,可向地平线提供该模型和模型转换时的 log 文件进行 debug 分析。

2. 如何理解 log 中模型输入输出 shape 为 0?

答:模型转换日志会打印模型的输入输出节点的 name 及 shape,但有时也会出现 shape 为 0 的情况,如下图所示。 其主要发生在 batch 维度,因为某些模型的输出 shape 是动态的(使用 dim_param 来存储),转换工具前向推理模型(shape_inference)后就会使用?来占位,此时日志打印就会显示为 0。 这种情况是符合预期的,不会影响模型转换,用户无需过分关注。

../_images/shape_0.jpg

3. 如何理解某些算子符合约束,却运行在 CPU 上?

答:XJ3 平台因为没有专门的查表硬件,所以当查表算子(通常为非线性的激活函数)运行时的硬件对齐尺寸超出 8192 时就会被自动回退至 CPU 运行,此时模型转换的日志中也会有相关 warning 打印。

除此之外,XJ3 和 J5 平台的某些算子还会因为被动量化的原因运行在 CPU 上,其原理说明可参考文章: 模型转换工具链中的主动量化和被动量化逻辑


1.3. 模型性能

1. 是否有硬件支持模型输入颜色空间转换?

答:BPU 支持常见模型输入格式间的转换(例如 nv12 -> rgb/bgr)和数据归一化,可以通过 yaml 文件进行配置。 具体请见 input_type_traininput_type_rtnorm_type 等参数的说明。 配置后工具链将以 BPU 算子的形式将其集成在模型中,此时请勿在前处理中重复操作。

2. 如何理解编译器优化等级?

答:yaml 文件 optimize_level 参数可配置 O0~O3 的编译器优化等级,优化等级越高则搜索空间越大。 另外也有一些比较耗时的优化策略是在 O2O3 阶段才会启用。 优化等级并不会针对算子粒度层面进行一些确定的优化策略,大部分算子的优化与优化等级没关系(这些优化不耗时)。 优化等级主要是对全局优化起作用,是结合整个模型进行的分析和优化。

3. 如何处理模型首尾部的量化/反量化算子?

答:PTQ 工具链默认会在 featuremap 输入模型的首部插入量化算子来实现输入数据从 float32 到 int8 类型的映射,并在所有模型的尾部插入反量化算子来实现输出数据从 int8(若 BPU 以 conv 结尾则默认为 int32 输出) 到 float32 的映射。而量化/反量化算子在 CPU 上的执行效率并不高,特别是在数据 shape 比较大的时候。所以我们更建议通过以下两种路径来解决:
  1. 将量化/反量化操作融合进前后处理,此种方式最为高效,具体说明请见:反量化节点的融合实现

  2. J5 工具链自 OE-v1.1.37 版本起也提供了对应的 DSP 接口,具体说明请见:工具链用户手册 10.3.1.6.2 节

4. 如何使用 unitconv 算子优化模型性能?

答:模型中的一个算子能运行在 BPU 上,除了其本身应满足 BPU 支持条件外,还需要能在校准时找到它的量化阈值。 而部分非计算密集型算子(如 concat,reshape 等)的量化阈值会依赖于上下游算子的 featuremap Tensor。 因此,若这些算子在模型首尾处就会默认跑在 CPU 上。 此时若想追求更高效的性能,可以在该算子前/后插入 unitconv 来引入新的量化阈值统计,进而将其量化在 BPU 上。 具体说明可见:unit_conv使用说明(用于优化模型性能)

但需要注意的是,这种方法有可能会引入一定的量化损失。 以 conv+reshape+concat 结构输出的模型为例, 工具链默认 conv 会以 int32 高精度输出,反量化至 float32 后再送给 CPU 上的 reshape 和 concat。 若在 concat 之后插入 unitconv,则整个结构都将以 int8 低精度运行在 BPU 上, 此时虽然最后的 unitconv 还能以 int32 高精度输出,但前面 conv 输出的精度压缩已经引入了一定的量化损失。 所以,是否插入 unitconv 来优化性能还请综合考虑。


1.4. 模型精度

1. J5 芯片是否支持 int16/int32 计算?

答:J5 芯片在陆续开放部分算子 BPU int16 的支持能力,具体可见 算子支持列表,另外:
  1. 若模型中的 BPU 部分以 Conv 结尾,则该算子默认为 int32 高精度输出;

  2. J5 芯片上的 DSP 硬件也具备 int8/int16/float32 的计算能力。

2. 如何正确处理模型的校准数据?

答:PTQ 模型校准数据的准备请参考 工具链用户手册 6.3.3 节,另外:
  1. 对于图片输入的模型,还可以参考文章: 图片校准数据准备问题介绍与处理

  2. 对于 featuremap 输入的模型,请自行完成数据的预处理,并通过 numpy.tofile 接口保存为 float32 类型的文件。

3. 如何 dump 模型中间层输出?

答:在模型转换阶段,yaml 文件中的 layer_out_dump 参数如果配置为 True,则会为每个卷积算子增加一个反量化输出节点,它会显著的降低模型上板后的性能,一般用于配合 vec_diff 向量比较工具使用,但该工具已基本弃用,所以并不推荐。

相比之下,output_nodes 参数可以指定模型中的任意节点为输出节点,更利于我们调试调优。 此外,还可以使用 hb_verify 工具对比模型在开发机 Python 端和开发版 C++ 端的输出一致性。

在板端部署阶段,hrt_model_exec 工具也支持以 bin 或 txt 的格式保存节点输出(包括用 output_nodes 参数指定的节点),具体使用方式可参考: hrt_model_exec新增参数说明


1.5. 常见故障

1. assert len(attribute.ints) == 4

File "/usr/local/lib/python3.6/site-packages/horizon_tc_ui/hbdtort/onnx2horizonrt.py", line 403, in convert_conv
  assert len(attribute.ints) == 4
AssertionError

答:该报错主要发生在模型检查阶段,是模型中存在暂不支持的三维算子 Conv1d 导致,建议更换成 Conv2d。

2. Segmentation fault:core dumped

答:该报错可能是物理机内存不足,工具版本老旧,模型非法,或工具 bug 导致。请通过以下方式进行排查:
  1. 确认当前物理机是否存在其他任务正在占用内存(或更换至更大内存的开发机);

  2. 升级工具链版本至最新,重新转换模型;

  3. 确认原始模型本身是否合法,如 onnx 模型是否能在 onnxruntime 中正确运行;

  4. 若以上检查后仍无法解决,请联系地平线团队。

3. ERROR cannot reshape array of size xxx into shape (x, xxx, xxx)

答:该报错主要发生在模型校准阶段,是校准数据的类型或尺寸与模型不匹配导致。可以进行如下检查:
  1. 校准数据 shape 与模型输入 shape 是否正确匹配;

  2. 图片数据预处理时的读取方式是否正确,例如 opencv 读取为 0~255 的 u8 数据,颜色通道为 BGR;skimage 读取为 0~1 的 fp32 数据,颜色通道为 RGB;

  3. 非图片数据是否以正确的类型存储,目前工具链只支持 fp32 类型的数据;

  4. yaml 文件中的 cal_data_type 参数是否与保存的校准数据类型正确匹配。

4. ERROR Wrong mean_value num received

ERROR yaml file parse failed. Please double check your config file inputs
ERROR Wrong mean_value num received. input mean_value num n is not equal to input num m

答:该报错是 yaml 文件中的 mean_value 参数数量与模型输入节点数量不一致导致,请对应检查。 不同输入节点的 mean_value 需要用 “;” 号隔开; 若模型为混合多输入模型(即同时包含图像和 featuremap 输入节点), 则需要与 input_name 对应,用 “NA” 给 featuremap 节点的 mean_value 参数占位。

5. ERROR The input model is invalid

答:该报错是模型本身非法导致,即不满足公版 onnxruntime 的检查逻辑,请自行检查模型,地平线暂不提供此类问题的支持。

6. ERROR [ONNXRuntimeError]: This is an invalid model

答:该报错主要发生在模型解析阶段,请使用公版 ONNXRuntime 或地平线 HB_ONNXRuntime 检查原始模型的合法性。 可参考文章 模型精度验证及调优建议 <1.1 验证原始浮点onnx模型的正确性> 章节内容。 若模型无法推理,请自查模型;若模型能正常推理,请联系地平线进行问题分析。

7. CRITICAL can not establish ssh connection to bpu board. authentication failed!

答:旧版 hb_verifier 工具链接有密码的开发板时就会触发如下报错:

File "/usr/local/lib/python3.8/dist-packages/paramiko/auth_handler.py", line 259, in wait_for_response
raise eparamiko.ssh_exception.BadAuthenticationType: Bad authentication type; allowed types: ['publickey', 'password']
2023-07-20 11:22:18,789 CRITICAL can not establish ssh connection to bpu board. authentication failed!
此时建议:
  1. 更新版本至 J5-v1.1.63 或 X3-v2.7.2 版本;

  2. 或在当前环境中,使用 ssh-keygen 生成 ssh-key,再使用 ssh-copy-id {username}@{board_ip} 配置公钥至开发板,即可正常使用。