执行自定义运算符寄存前,可使用最新 Netron 程序来检查编译的模型。在以下计算图中,PPScatter 分配给 CPU。您必须实现并寄存 PPScatter OP。
图 1. CPU 子计算图中的 PPScatter OP
步骤
- 使用 Netron 打开编译的模型,并利用运算符信息查找 CPU 子计算图中的自定义 OP。图 2. PPScatter OP 的输入和输出
从先前模型结构图中可以观察发现,运算类型 (op type) 为 PPScatterV2。PPScatterV2 表示要创建的自定义运算 (op) 的名称。
要获取有关自定义 OP 的详细信息,可使用 xdputil 工具。运行以下命令检查 custom_layer OP:xdputil xmodel pointpillars_custom_op.xmodel --op VoxelNet__VoxelNet_input_4
- 自行编写此 op 的实现。
自定义 OP 寄存支持 C++ 和 Python。以下步骤显示了如何在 C++ 中实现 OP。如需了解有关 OP Python 实现的信息,请参阅 Vitis-AI/examples/custom_operator/pytorch_example/op_registration/python/
注释: 在 Vitis-AI/examples/custom_operator/op_add 中可以找到“README.md”文件,此文件全面描述了自定义 OP 的实现过程。如需获取有关实现和寄存自定义 OP 的指导信息和说明,请参阅此“README.md”文件。- 创建 my_PPScatter_op.cpp 源文件,并将其置于新的 op_PPScatter 文件夹内。
您也可以复制现有 OP,并将其重命名为自己 OP,然后将 my_tanh_op.cpp 重命名为 my_PPScatter_op.cpp。
cp - r Vitis-AI/src/vai_library/cpu_task/examples/op_tanh/ op_PPScatter
- 创建 Makefile。
OUTPUT_DIR = $(PWD) all: $(OUTPUT_DIR) $(OUTPUT_DIR)/libvart_op_imp_PPScatterV2.so $(OUTPUT_DIR): mkdir -p $@ $(OUTPUT_DIR)/my_PPScatter_op.o: my_PPScatter_op.cpp $(CXX) -std=c++17 -fPIC -c -o $@ -I. -I=/install/Debug/include -Wall -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 $< $(OUTPUT_DIR)/libvart_op_imp_PPScatterV2.so: $(OUTPUT_DIR)/my_PPScatter_op.o $(CXX) -Wl,--no-undefined -shared -o $@ $+ -L=/install/Debug/lib -lglog -lvitis_ai_library-runner_helper -lvart-runner -lxir
- 编写 op 的实现。
在 my_PPScatter_op.cpp 文件中,使用构造函数初始化任何必需的变量。在本例中,无需初始化任何变量。
在“calculate()”函数中实现定制逻辑。此逻辑的主要目标是从 inputs 变量检索输入数据、执行必要的计算,并将输出数据写入 output 变量。
在 calculate() 函数中,实现您自己的逻辑。该逻辑主要用于从“输入”变量获取输入数据,计算逻辑,并将输出数据写入“输出”变量。
my_PPScatter_op.cpp 代码如下#include <vart/op_imp.h> class MyPPScatterOp { public: MyPPScatterOp(const xir::Op* op1, xir::Attrs* attrs) : op{op1} { // op and attrs is not in use. } int calculate(vart::simple_tensor_buffer_t output, std::vector<vart::simple_tensor_buffer_t<float>> inputs) { CHECK_EQ(inputs.size(), 2); auto input_data_shape = inputs[0].tensor->get_shape(); auto input_coord_shape = inputs[1].tensor->get_shape(); auto output_shape = output.tensor->get_shape(); CHECK_EQ(input_data_shape.size(), 4); // 1 12000 1 64 --> 1 64 12000 1 CHECK_EQ(input_coord_shape.size(), 3); // 1 12000 4 CHECK_EQ(output_shape.size(), 4); // 1 496 432 64 ---> 1 64 496 432 auto coord_numbers = input_coord_shape[1]; auto coord_channel = input_coord_shape[2]; CHECK_EQ(coord_numbers, input_data_shape[2]); auto batch = output_shape[0]; auto height = output_shape[2]; auto width = output_shape[3]; auto channel = output_shape[1]; CHECK_EQ(input_data_shape[0], batch); CHECK_EQ(channel, input_data_shape[1]); auto output_idx = 0; auto input_idx = 0; auto x_idx = 0; memset(output.data, 0, output_shape[0]*output_shape[1]*output_shape[2]*output_shape[3]*sizeof(float)); for (auto n = 0; n < coord_numbers; n++) { auto x = (int)inputs[1].data[x_idx + 3]; auto y = (int)inputs[1].data[x_idx + 2]; if (x < 0) break; // stop copy data when coord x == -1 . for(int i=0; i < channel; i++) { output_idx =i*height*width + y*width+x; input_idx = n+i*coord_numbers; output.data[output_idx] = inputs[0].data[ input_idx ]; } x_idx += coord_channel; } return 0; } public: const xir::Op* const op; }; DEF_XIR_op_IMP(MyPPScatterOp)
- 构建库。目标目录为 $(HOME)/build/custom_op/。您可以修改 Makefile 中的路径。
使用您提供的 Makefile 执行 make 命令时,自定义 OP 库将在以下目录中生成:$(HOME)/build/custom_op/。
文件名为“libvart_op_imp_PPScatterV2.so”。
- 将 libvart_op_imp_PPScatterV2.so 复制到目标上的 /usr/lib。
- 创建 my_PPScatter_op.cpp 源文件,并将其置于新的 op_PPScatter 文件夹内。
- 在目标上验证 Op。
- 在 xdputil 中使用 run_op 命令来测试此 OP:
xdputil run_op pointpillars_op.xmodel VoxelNet__VoxelNet_input_4 -r ref -d dump
运行此命令前,请准备 OP 的参考输入。运行此命令后,就会生成 VoxelNet__VoxelNet_input_4.bin 文件。
- 将输出与黄金文件进行对比:
xdputil comp_float ref/VoxelNet__VoxelNet_input_4.bin dump/VoxelNet__VoxelNet_input_4.bin
如果 OP 实现成功,您将看到如下结果:
- 在 xdputil 中使用 run_op 命令来测试此 OP: