6.6.1. 训练后量化(PTQ)常见问题
6.6.1.1. 如何理解算子约束中提及的BPU加速和CPU计算两种形式?¶
BPU加速:是指模型在板端推理时,该算子可以通过BPU硬件进行量化加速。其中,大部分算子(如conv等)是硬件直接支持的。 有些会被替换成其他算子实现加速(如gemm会被替换成conv);还有一些则依赖特定的上下文(如Reshape、Transpose需要前后均为BPU算子)才能被动量化。
CPU计算:对于模型中BPU硬件无法直接或间接加速的算子,工具链会将其放在CPU上计算,runtime预测库也会在模型推理时自动完成执行硬件的异构调度。
6.6.1.2. 如何理解模型分段的性能影响?
当模型在BPU算子中间存在不能加速的CPU算子时,就会被切分成不同的Subgraph(模型转换日志中会通过不同的id号进行区分),从而引入一定的性能损耗,具体包括两方面:
CPU算子性能远低于BPU算子。
CPU和BPU之间的异构调度还会引入量化、反量化算子(运行在CPU上),且因为内部计算需要遍历数据,所以其耗时会与shape大小成正比。
以上CPU算子和量化、反量化算子均可通过板端工具hrt_model_exec传入 profile_path 参数实测得到。地平线建议您尽量选择BPU算子搭建模型,以获取更好的性能表现。
6.6.1.3. 如何理解模型尾部部分BPU可加速算子运行在CPU上?
首先,我们需要理解以下两个概念:
J5算法工具链目前只在模型尾部支持Conv算子以int32高精度输出,其他算子都只能以int8低精度输出。
通常情况下,模型转换会在optimization阶段将Conv与其后的BN和ReLU/ReLU6融合在一起进行计算。 但由于BPU硬件本身限制,在模型尾部以int32高精度输出的Conv却并不支持算子融合。
所以如果模型以Conv+ReLU/ReLU6结尾,那么为了保证量化模型的整体精度,Conv会默认以int32高精度输出,ReLU/ReLU6则会跑在CPU上。 同理,其他尾部可加速算子运行在CPU上也都是默认精度优先的选择。不过,地平线支持在yaml文件将这些算子配置 run_on_bpu ,从而获取更好的性能表现,但会引入一定的精度损失。
6.6.1.4. 如何理解地平线的default校准方式?
为了减轻用户调试校准方案的工作量,地平线提供了default自动搜索策略,当前其内部逻辑如下:

Step1:尝试Max、Max-Percentile 0.99995和KL三种校准方法,计算得到分别的余弦相似度。 如果三种方法中的最高余弦相似度小于0.995,进入Step2;反之,返回最高相似度对应的阈值组合。
Step2:尝试Max-Percentile 0.99995和perchannel量化的组合方法,如果余弦相似度仍小于0.995,进入Step3。 反之,返回最高相似度对应的阈值组合。
Step3:选取Step2中最高余弦相似度对应的方法,应用非对称量化作为第5种方法,根据余弦相似度选取5种方案中的最佳方案, 返回对应的阈值组合。
6.6.1.5. 如何理解地平线的mix校准方式?
为了集成不同校准方法的优势,地平线提供了mix搜索策略,当前其内部逻辑如下:

Step1:采用KL校准方法,计算当前模型中节点的量化敏感度(使用余弦相似度来衡量), 将值小于特定阈值的节点定义为量化敏感节点(对模型量化精度影响较大的节点)。
Step2:遍历所有量化敏感节点,在每一个节点上尝试Max、Max-Percentile 0.99995和KL三种校准方法, 并为该节点选出最佳的校准方法,最终得到Mix校准模型。
Step3:评估Mix、Max、Max-Percentile 0.99995和KL校准模型的累积误差情况,输出最优模型。
6.6.1.6. 如何理解yaml文件中的编译器优化等级参数?
在模型转换的yaml配置文件中,编译参数组提供了optimize_level参数来选择模型编译的优化等级,可选范围为 O0~O3。其中:
O0 不做任何优化,编译速度最快,适合在模型转换功能验证、调试不同校准方式时使用。
O3 优化等级最高,可以获得模型最佳性能,但编译时间也相对较长。
在 O1~O3 范围内,优化等级越高,编译优化时的搜索空间就会越大。同时,一些比较耗时的优化策略也仅会在 O2/O3 等级才会启用。
编译器的优化策略并不是算子粒度层面的,而是针对整个模型的全局优化。
6.6.1.7. 为何nv12模型hb_perf得出的输入大小和预测库不一致?
NV12图像格式属于YUV颜色空间中的YUV420SP格式,是摄像头可直接输出的视频格式。由于一般在实际训练时不会使用这种格式, 因此底层硬件拿到摄像头输出的数据后,会先将其转换为yuv444的格式再进行后续推理(该转换过程用户不感知,板端推理时只需依据模型转换时配置的input_type_rt准备对应类型的数据即可)。 hb_perf工具显示的是nv12数据的大小(即NxHxWx3/2),而板端hrt_model_exec model_info获取到的数据大小则是模型真实输入的数据大小(即NxHxWx3)。 且由于nv12数据已经不具有channel的概念了,板端部署时用户只需依据输入节点的H和W信息,准备对应大小的nv12数据即可。
6.6.1.8. 量化模型和上板bin模型的输入的数据排布是否一定一致?
onnx模型和上板bin模型的输入数据排布 不一定完全一致 。上板bin的输入数据排布与yaml配置文件中的input_layout_rt对齐,但quanti.onnx的数据排布则会因为多种因素发生改变,例如:
当模型的input type rt设置为 nv12 时,input_source默认为 pyramid , 当输入为pyramid时,量化模型quanti.onnx的输入均为NHWC。
当input type rt为 yuv444/rgb/bgr 等类型时,input_source默认为 ddr , 此时量化模型quanti.onnx的输入与原始浮点模型一致。
此外其他场景也有可能会出现***.onnx与***.bin输入数据排布不一致的情况。
因此,当您在PC端推理***.onnx时,建议使用可视化工具查看一下onnx模型的输入shape,并准备对应的数据。 当在板端推理.bin模型时,则依据转换时配置的input_layout_rt或使用工具hrt_model_exec model_info以及BPU SDK相关API(hbDNNGetInputTensorProperties())获取.bin模型的输入shape。 当您使用同一张图片推理quanti.onnx以及.bin发现输出结果差异很大时,建议您先排查输入数据排布是否正确。
6.6.1.9. 如何编译得到多batch模型?
根据原模型种类,我们将分为动态输入模型和非动态输入模型来讨论这个问题。
注解
input_batch 参数仅在 input_shape 第一维为1的时候可以使用(模型为多输入时,需要所有输入的 input_shape 第一维均为1),且此参数仅在原始onnx模型本身支持多batch推理时才能生效,该参数仅支持配置一个数值,模型为多输入时,配置后该值将作用于模型的所有输入。
每份校准数据shape大小,应和 input_shape 的大小保持一致。
动态输入模型:如果原模型为动态输入模型时,比如,? x3x224x224(动态输入模型必须使用input_shape参数指定模型输入信息)。
1.当配置input_shape为1x3x224x224时, 如果您想编译得到多batch的模型,可以使用 input_batch 参数,此时每份校准数据shape大小为1x3x224x224。
2.当配置input_shape的第一维为大于1的整数时,原模型本身将会认定为多batch模型,将无法使用 input_batch 参数,且需要注意每份校准数据shape大小。例如配置input_shape为4x3x224x224时, 此时每份校准数据shape大小需要为4x3x224x224。
非动态输入模型:
1.当输入的input shape[0]为1时,可以使用 input_batch 参数,每份校准数据shape大小与原模型shape保持一致。
2.当输入的input shape[0]不为1时,不支持使用 input_batch 参数。
6.6.1.10. 多输入模型在转换过程中,模型输入顺序发生变化,此种情况正常么?
此种情况是正常现象,多输入模型在转换过程中,模型输入顺序是有可能发生变化的。 可能发生的情况如下例所示:
原始浮点模型输入顺序:input1、input2、input3。
original.onnx模型输入顺序:input1、input2、input3。
quanti.onnx模型输入顺序:input2、input1、input3。
bin模型输入顺序:input3、input2、input1。
注意
当您做精度一致性对齐时,请确保输入顺序是正确的,不然有可能会影响精度结果。
如果您想查看bin模型输入的顺序,可以使用 hb_model_info 指令来查看,input_parameters info分组中列出的输入顺序,即为bin模型的输入顺序。
6.6.1.11. 如何理解PTQ模型转换过程中的主动量化和被动量化?
在模型成功转换成bin模型后,可能会出现发现仍然有个别op运行在CPU上的情况,但回头仔细对照工具链算子约束列表,明明该op是符合算子约束条件的,也就是理论上该算子应该成功运行在BPU上,为什么仍然是CPU计算呢?
针对此问题,您可参考 地平线官方社区文章 ,该文章对模型转换工具链中内部的量化原理与背后的逻辑进行了介绍,并针对问题提供了几种解决方法。
6.6.2. 常见故障处理
本章节为您介绍在使用地平线J5工具链产品时,您可能遇到的一些异常故障现象。针对这些故障,我们为您提供对应故障的可能原因及通用解决建议,方便您快速定位问题及解决故障。
6.6.2.1. hb_mapper checker 常见故障
背景信息:模型检查命令( hb_mapper checker )
在实际工程中,由于并非所有浮点模型均能够转为量化模型,因此在转换之前需要进行一次检查,这个check过程,会完成一遍模型转换的过程, 但是对于比较耗时的步骤,进行了简化处理。该命令在完成模型的检查后,会输出检查结果和OP在设备上的部署情况。
故障场景:以下为在使用 hb_mapper checker 时常见的故障场景:
1.故障现象一:
故障可能原因:发生此故障的原因可能是模型输入为动态shape。
解决建议:针对此故障,您可使用参数 --input-shape "input_name input_shape" 来指定输入节点的shape信息。
2.故障现象二:
故障可能原因:发生此故障的原因可能是使用的CPU算子为地平线不支持的CPU算子。
解决建议:针对此故障,您可以根据我们提供的算子支持列表中的内容对算子进行替换;若不被支持的CPU算子为模型核心算子,请您联系地平线对此进行开发评估。
3.故障现象三:
故障可能原因:发生此故障的原因可能是使用的BPU算子为地平线不支持的BPU算子。
解决建议:针对此故障,若模型整体性能可满足需要,您可以忽略该日志;若模型整体性能不能达到您的预期,您可以根据我们提供的算子支持列表中的内容对算子进行替换。
4.故障现象四:
故障可能原因:发生此故障的原因可能是使用的自定义算子为地平线不支持的自定义算子。
解决建议:针对此故障,您可以根据我们提供的算子支持列表中的内容对算子进行替换或参考 地平线开发者论坛-自定义算子开发 中的内容完成自定义CPU算子注册。
6.6.2.2. hb_mapper makertbin常见故障¶
背景信息:模型编译命令( hb_mapper makertbin )
该命令根据配置文件和模型的种类,会生成ONNX量化模型以及仿真上板情况的runtime模型。
故障场景:以下为在使用 hb_mapper makertbin 时常见的故障场景:
1.故障现象一:
故障可能原因:发生此故障的原因可能是,{op_name}算子超过支持限制被回退到CPU计算。
解决建议:针对此故障,若CPU算子带来的性能损耗您可接受,则无需关注该信息;若性能不能达到您的要求,您可以根据我们提供的算子支持列表中的内容将该op修改至BPU可支持的范围。
2.故障现象二:
故障可能原因:发生此故障的原因可能是,{op_name}算子优化失败。
解决建议:针对此故障,请您将模型以及.log文件收集好后提供给地平线技术人员进行分析处理。
3.故障现象三:
故障可能原因:发生此故障的原因可能是该算子onnxruntime暂未支持。
解决建议:针对此故障,您可以根据我们提供的算子支持列表中的内容对算子进行替换,如不被支持的算子为核心算子,请您联系地平线对此进行开发评估。
4.故障现象四:
故障可能原因:发生此故障的原因可能是模型解析失败(可能是导出模型时只为一个output/input节点指定了name)。
解决建议:针对此故障,建议您重新导出onnx并确认其有效性(导出onnx模型时不指定output/input name,或者依次为每个output/input节点指定名称)。
5.故障现象五:
故障可能原因:发生此故障的原因可能是模型量化/编译失败。
解决建议:针对此故障,请您将模型以及.log文件收集好后提供给地平线技术人员进行分析处理。
6.故障现象六:
故障可能原因:发生此故障的原因可能是onnx模型的输入shape非法,或者是工具优化pass有误。
解决建议:针对此故障,请您确保onnx模型的有效性,若onnx模型可正常推理,请将模型提供给地平线技术人员进行分析处理。
7.故障现象七:
故障可能原因:发生此故障的原因可能是数据预处理有误,或该节点weight值太小/太大。
解决建议:针对此故障,请您检查数据预处理是否有误;我们建议您使用BN算子优化数据分布。
8.故障现象八:
故障可能原因:发生此故障的原因可能是模型编译失败。
解决建议:针对此故障,请您将模型以及.log文件收集好后提供给地平线技术人员进行分析处理。
9.故障现象九:
故障可能原因:发生此故障的原因可能是工具链暂不支持该op输入维度为非四维。
解决建议:针对此故障,我们建议您将该op输入维度调整为四维输入。
10.故障现象十:
故障可能原因:发生此故障的原因可能是工具链暂不支持op的该属性。
解决建议:针对此故障,您可以根据我们提供的算子支持列表中的内容进行替换或联系地平线对此进行开发评估。
11.故障现象十一:
故障可能原因:发生此故障的原因可能是模型中没有可量化的BPU节点。
解决建议:针对此故障,请您确保onnx模型的有效性,且模型中至少使用了一个conv;若前述条件均已满足,请您将模型以及.log文件收集好后提供给地平线技术人员进行分析处理。
12.故障现象十二:
故障可能原因:发生此故障的原因可能是模型opset版本超出工具链支持限制。
解决建议:针对此故障,请您重新导出模型,确保opset_version=10或者11。
13.故障现象十三:
在使用run_on_bpu后转换报错。
故障可能原因:发生此故障的原因可能是目前暂不支持将该算子run_on_bpu。
解决建议:run_on_bpu暂仅支持指定模型中Relu/Softmax/Reshape/pooling(maxpool、avgpool等)算子以及CPU*+Transpose组合(可通过声明Transpose节点名称,将CPU*+Transpose都运行在BPU上,CPU*特指BPU支持的op), 若满足前述条件但仍run_on_bpu失败,请您联系地平线技术人员对此进行分析处理;若不满足前述条件,可联系地平线技术人员对此进行开发评估。
14.故障现象十四:
故障可能原因:发生此故障的原因是J5目前暂不支持编译双核模型。
解决建议:针对此故障,建议您将yaml配置文件中的core_num设置为1。
15.故障现象十五:
故障可能原因:发生此故障的原因可能是模型非法或工具解析失败。
解决建议:请您将 .log 文件及生成的 shape_inference_fail.onnx 提供给地平线技术人员进行原因分析。
6.6.2.3. hb_model_modifier常见故障
背景信息: hb_model_modifier 工具用于对指定的runtime模型中输入端的Transpose、Quantize节点和输出端的Transpose、Dequantize、DequantizeFilter、Cast、Reshape、Softmax节点进行删除操作, 并将删除节点的信息存放在BIN模型中,可以通过 hb_model_info 进行查看。
故障场景:以下为在使用 hb_model_modifier 时常见的故障场景:
故障现象:
故障可能原因:该问题为地平线已知问题,于OE1.1.14版本进行了修复。
解决建议:针对此故障,请您完整更新OE开发包或将horizon-tc-ui升级至1.7.8。
6.6.2.4. hb_verifier常见故障
背景信息: hb_verifier 工具是用于对指定的定点模型和runtime模型进行结果验证的工具。
若您使用工具前指定了图片,则 hb_verifier 工具会使用指定图片进行定点模型推理、runtime模型板端和X86端模拟器上的推理,并对结果进行两两比较, 给出是否通过的结论(此过程支持自选,您可以根据需要选择进行对比的内容)。
若您在使用工具前未指定图片,则 hb_verifier 工具会默认使用随机生成的tensor数据进行推理。
故障场景:以下为在使用 hb_verifier 时常见的故障场景:
故障现象:
故障可能原因:发生此故障的原因可能是模型一致性比对失败。
解决建议:针对此故障,请您将模型提供给地平线技术人员进行分析处理。
6.6.2.5. hb_onnxruntime常见故障
背景信息: hb_onnxruntime 主要是用于onnx模型推理的类。
故障场景:以下为在使用 hb_onnxruntime 时常见的故障场景:
1.故障现象一:
故障可能原因:发生此故障的原因可能是输入数据格式与模型不匹配。
解决建议:针对此故障,一般来说浮点onnx模型的输入格式为float32,量化后模型输入格式为int8。可使用可视化工具查看onnx模型input节点的属性。
2.故障现象二:
故障可能原因:发生此故障的原因可能是torch使用的protobuf版本和horizon使用的protobuf版本有冲突,需要在torch前import。
解决建议:针对此故障,您可以将from horizon_tc_ui import HB_ONNXRuntime放在第一行import。import其他API出现相同报错也同等适用。
6.6.2.6. libDNN常见故障
背景信息: libDNN 主要是用于地平线模型的推理库。
故障场景:以下为在使用 libDNN 时常见的故障场景:
1.故障现象一:
故障可能原因:发生此故障的原因可能是libDNN暂不支持该op的某个属性(后续我们将逐步把算子约束前移至模型转换阶段提醒)。
解决建议:针对此故障,您可以根据我们提供的算子支持列表中的内容进行替换或联系地平线对此进行开发评估。
2.故障现象二:
故障可能原因:发生此故障的原因可能是libDNN暂不支持该输入类型(后续我们将逐步把算子约束前移至模型转换阶段提醒)。
解决建议:针对此故障,您可以根据我们提供的算子支持列表中的内容进行替换或联系地平线对此进行开发评估。
3.故障现象三:
故障可能原因:发生此故障的原因可能是输入数据申请内存不足。
解决建议:针对此故障,由于使用hrt_model_exec model_info查看模型input节点的aligned shape,是按aligned shape*size_of(tensor type)来申请内存空间的。 此处我们建议:若您的libDNN版本高于1.5.4b,建议直接使用hbDNNTensorProperties.alignedByteSize来申请内存空间,若您的libDNN版本低于1.5.4b版本则直接使用aligned shape*size_of(tensor type)来申请内存空间。
4.故障现象四:
故障可能原因:该问题为地平线已知问题,属于hb_model_modifer工具已知问题,已于OE1.1.14版本进行了修复。
解决建议:针对此故障,请您完整更新OE开发包或将horizon-tc-ui升级至1.7.8版本。