地平线工具链中已经支持了丰富的算子,在大多数情况下,您的模型应该可以通过前文所述模型转换顺利部署到地平线计算平台上。已支持的算子情况可以参考 工具链算子支持约束列表-ONNX Operator Support List 章节。少部分算子不支持情况下,我们强烈建议您先尝试下替换算子的可能性,这样有利于将地平线计算平台能力充分发挥出来,且开发成本会更低。
自定义算子只提供CPU上算子开发能力,一个完整的自定义算子应用过程包括创建模板、算子实现、算子编译、含自定义算子模型转换和运行含自定义op模型几个阶段。具体流程如下图所示:
如图所示,定义自定义OP需要有两部分的任务:在模型转换阶段,需要提供自定义OP的python代码;在模拟器/上板运行推理阶段,需要提供自定义OP的C++代码。要确保这两部分的代码运算的一致性。
在进行含自定义算子的模型转换之前,需要先对环境进行配置,请务必保证您的环境满足本节内描述的要求。
请注意,如需在X86环境下对含自定义算子的模型进行编译或推理,需保证gcc版本大于5.0。
请参考下方,对 环境变量 进行配置:
GCC环境配置
编译器依赖配置
X86环境变量设置
Arm环境变量设置
自定义算子动态库关联
自定义算子(中间节点)是指模型中某个/某些算子注册为自定义算子的场景。
在准备好自定义算子实现后,为了将算子应用起来,您需要从原始模型文件和模型转换配置两个方面做出相应调整(下面分别以 Caffe 模型和 ONNX 模型为例)。
原始模型文件中,将自定义算子对应的算子类型标记为 Custom,并提供一组 custom_param,示例如下。
以上完整 custom_param 示例中:
kind 是自定义算子的内部实现名称,该自定义OP为恒等OP,因此命名为 CustomIdentity,该名称在后续Python及C++代码中均会体现。
shape 是算子的输出尺寸,需要完整指定。
params 是算子的传入参数指定形式为 'param_name': param_value,多个参数之间使用 \n 分隔。
在模型转换配置中,使用自定义算子需要在配置文件中加入一个新的自定义op参数组如下:
对于 Caffe 模型,以上参数组中的两个参数都是必须配置的。custom_op_method 固定使用 register; op_register_files 是自定义算子计算的实现文件,请使用相对路径。
完成这些配置后,模型转换的后续步骤与其他一般模型转换过程一致。
从pytorch等其他框架转换而来
直接生成onnx模型
参考代码:
Onnx模型中PyOp属性的注意点:
domain属性一定要设置,不然的话会被默认成onnx标准domain从而报错。不同实现的自定义算子需要设置在不同的domain下。
module需要与注册时使用的注册文件同名。若注册文件在当前目录的子文件夹中,则需要修改module内容。例如:若 sample_custom.py 在当前路径的custom_op 文件夹中,则该module应设置为 custom_op.sample_custom 。
目前仅onnx模型支持多种类型的自定义算子,如您需要在其他框架中支持多种类型的自定义算子请联系地平线技术支持人员。
与 Caffe 模型一致,需要在模型转换配置中,使用自定义算子需要在配置文件中加入一个新的自定义op参数组如下:
对于 ONNX 模型,以上参数组中的两个参数都是必须配置的。custom_op_method 固定使用 register; op_register_files 是自定义算子计算的实现文件,请使用相对路径。
完成这些配置后,模型转换的后续步骤与其他一般模型转换过程一致。
在模型转换阶段,需要提供自定义算子的Python实现,工具会利用该实现函数完成模型校准必需的推理阶段。
请注意,由于工具在PTQ转换过程中会以 working_dir 为工作目录,我们强烈建议算子实现中涉及工作目录的配置时,配置为绝对路径,如需要配置为相对路径,请以 working_dir 为工作目录进行相对路径的指定。
Python模板文件(sample_custom.py)如下:
custom_op示例中的配置文件(horizon_ops.py)如下:
该文件的名字(sample_custom.py)需要填入模型转换的yaml配置文件中 op_register_files ,否则工具无法正常import自定义算子定义, 并且修饰器 op_implement_register 注册的custom op类的名称 CustomIdentity 需要与Caffe自定义OP的属性 kind 或者Onnx自定义OP的属性 class_name 一致。
对于 Caffe 模型, init 函数中的参数(kernel_size, threshold)都是通过prototxt文件中的 params 传入的, 用于自定义op模块的初始化。 op_shape_infer_register 用于Caffe模型的算子shape注册。
对于 Onnx 模型,自定义op的shape解析有两种方式,可以通过在创建onnx模型时,将pyop输出的value_info添加到onnx模型中,或者在对应的pyop中创建output_shape属性。 还需要注意自定义算子中的 module 必须与存放自定义算子实现的文件保持一致, 如果属性设置为 custom_op.horizon_ops , 则自定义算子实现的文件名称为 horizon_ops ,且要放在 custom_op文件夹中, 保持与onnx模型的层级关系。由于同一个domain中的同名算子实现必须相同, 因此不同的自定义算子的 domain 属性需要不同。
上述操作完成后即可进行浮点转定点的操作,得到相应的hbm文件及用于含自定义算子模型部署的动态库.so文件。
自定义算子(前后处理)是指通过添加自定义算子的方式将前/后处理逻辑集成到模型中的场景。
为降低含自定义算子模型模型板端部署的难度,同时提升前后处理代码复用性,我们也提供了这种场景。该场景的使用会涉及环境变量的设置及前后处理代码的编写,您可参考下方示例。
请注意,在进行下方操作前,请确保已按照上文 环境配置 章节完成环境变量的配置。
下方示例代码,串联GoogleNet模型,将前后处理集成到HBIR模型中。
在完成上述操作后,您将得到一系列产出物文件,其中需要重点关注的文件包括:
libpipeline_xxxxxxxx_arm.so:Arm自定义算子动态库,用于模型在板端推理时使用。
*.hbm:含前/后处理的用于上板的离线模型文件。
在拿到hbm文件以及自定义算子运行相关的动态库.so文件后,还不能直接在开发板上运行,如想要上板运行,还需要配置对应动态库的加载路径,配置方法如下: