BPU加速:是指模型在板端推理时,该算子可以通过BPU硬件进行量化加速。其中,大部分算子(如conv等)是硬件直接支持的。有些会被替换成其他算子实现加速(如gemm会被替换成conv);还有一些则依赖特定的上下文(如Reshape、Transpose需要前后均为BPU算子)才能被动量化。
CPU计算:对于模型中BPU硬件无法直接或间接加速的算子,工具链会将其放在CPU上计算,runtime预测库也会在模型推理时自动完成执行硬件的异构调度。
当模型在BPU算子中间存在不能加速的CPU算子时,在BPU算子和CPU算子之间切换计算时会引入一定的性能损耗,具体包括两方面:
CPU算子性能远低于BPU算子。
CPU和BPU之间的异构调度还会引入量化、反量化算子(运行在CPU上),且因为内部计算需要遍历数据,所以其耗时会与shape大小成正比。
地平线建议您尽量选择BPU算子搭建模型,以获取更好的性能表现。
首先,我们需要理解以下两个概念:
目前只在模型尾部支持Conv算子以int32高精度输出,其他算子都只能以int8低精度输出。
通常情况下,模型转换会在optimization阶段将Conv与其后的BN和ReLU/ReLU6融合在一起进行计算。但由于BPU硬件本身限制,在模型尾部以int32高精度输出的Conv却并不支持算子融合。
所以如果模型以Conv+ReLU/ReLU6结尾,那么为了保证量化模型的整体精度,Conv会默认以int32高精度输出,ReLU/ReLU6则会跑在CPU上。同理,其他尾部可加速算子运行在CPU上也都是默认精度优先的选择。不过,地平线支持在yaml文件通过配置 node_info 将这些算子运行在BPU上,从而获取更好的性能表现,但会引入一定的精度损失。
为了减轻用户调试校准方案的工作量,地平线提供了default自动搜索策略,当前其内部逻辑如下:
Step1:尝试Max、Max-Percentile 0.99995和KL三种校准方法,计算得到分别的余弦相似度。如果三种方法中的最高余弦相似度小于0.995,进入Step2;反之,返回最高相似度对应的阈值组合。
Step2:尝试Max-Percentile 0.99995和perchannel量化的组合方法,如果余弦相似度仍小于0.995,进入Step3。反之,返回最高相似度对应的阈值组合。
Step3:选取Step2中最高余弦相似度对应的方法,应用非对称量化作为第5种方法,根据余弦相似度选取5种方案中的最佳方案,返回对应的阈值组合。
为了集成不同校准方法的优势,地平线提供了mix搜索策略,当前其内部逻辑如下:
Step1:采用KL校准方法,计算当前模型中节点的量化敏感度(使用余弦相似度来衡量),将值小于特定阈值的节点定义为量化敏感节点(对模型量化精度影响较大的节点)。
Step2:遍历所有量化敏感节点,在每一个节点上尝试Max、Max-Percentile 0.99995和KL三种校准方法,并为该节点选出最佳的校准方法,最终得到Mix校准模型。
Step3:评估Mix、Max、Max-Percentile 0.99995和KL校准模型的累积误差情况,输出最优模型。
在模型转换的yaml配置文件中,编译参数组提供了optimize_level参数来选择模型编译的优化等级,可选范围为 O0~O2。其中:
O0 不做任何优化,编译速度最快,适合在模型转换功能验证、调试不同校准方式时使用。
在 O1~O2 范围内,优化等级越高,编译优化时的搜索空间就会越大。
编译器的优化策略并不是算子粒度层面的,而是针对整个模型的全局优化。
根据原模型种类,我们将分为动态输入模型和非动态输入模型来讨论这个问题。
input_batch 参数仅在单输入且 input_shape 第一维为1的时候可以使用,且此参数仅在原始onnx模型本身支持多batch推理时才能生效。
每份校准数据shape大小,应和 input_shape 的大小保持一致。
动态输入模型:如果原模型为动态输入模型时,比如,?x3x224x224(动态输入模型必须使用input_shape参数指定模型输入信息)。
当配置input_shape为1x3x224x224时,如果您想编译得到多batch的模型,可以使用 input_batch 参数,此时每份校准数据shape大小为1x3x224x224。
当配置input_shape的第一维为大于1的整数时,原模型本身将会认定为多batch模型,将无法使用 input_batch 参数,且需要注意每份校准数据shape大小。例如配置input_shape为4x3x224x224时,此时每份校准数据shape大小需要为4x3x224x224。
非动态输入模型:
当输入的input shape[0]为1,且是单输入模型时,可以使用 input_batch 参数。每份校准数据shape大小与原模型shape保持一致。
当输入的input shape[0]不为1,不支持使用 input_batch 参数。
此种情况是正常现象,多输入模型在转换过程中,模型输入顺序是有可能发生变化的。 可能发生的情况如下例所示:
原始浮点模型输入顺序:input1、input2、input3。
original.onnx模型输入顺序:input1、input2、input3。
quanti.bc模型输入顺序:input2、input1、input3。
hbm模型输入顺序:input3、input2、input1。
当您做精度一致性对齐时,请确保输入顺序是正确的,不然有可能会影响精度结果。
如果您想查看hbm模型输入的顺序,可以使用 hb_model_info 指令来查看,input_parametersinfo分组中列出的输入顺序,即为hbm模型的输入顺序。
在模型成功转换成hbm模型后,可能会出现发现仍然有个别op运行在CPU上的情况,但回头仔细对照工具链算子约束列表,明明该op是符合算子约束条件的,也就是理论上该算子应该成功运行在BPU上,为什么仍然是CPU计算呢?
针对此问题,您可参考 地平线官方社区文章,该文章对模型转换工具链中内部的量化原理与背后的逻辑进行了介绍,并针对问题提供了几种解决方法。