手順
-
Netron を使用してコンパイル済みモデルを開き、op 情報を使用して 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 には、カスタム op を実装する手順を包括的に説明した「README.md」ファイルがあります。カスタム op の実装と登録に関するガイダンスと手順については、この「README.md」ファイルを参照してください。-
my_PPScatter_op.cpp ソース ファイルを作成し、新規フォルダー op_PPScatter に置きます。
次に示すように、既存の 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() 関数に、独自のロジックを実装します。このロジックは、主に「inputs」変数から入力データを取得し、ロジックを計算し、「output」変数に出力データを書き込みます。
次に、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 をテストできます。