地平線工具鏈中已經支持了豐富的算子,在大多數情況下,您的模型應該可以通過使用hb_mapper工具完成轉換并順利部署到地平線芯片上。 少部分算子不支持情況下,我們建議您先嘗試下替換算子的可能性,這樣有利于將地平線芯片能力充分發揮出來。
自定義算子目前只提供CPU算子開發能力,可自定義onnx算子以及caffe算子。一個完整的自定義算子應用過程包括注冊算子、算子實現、含自定義算子模型轉換和運行含自定義op模型四個階段。
1 自定義onnx算子
1.1 將含有自定義算子的pytorch模型導出ONNX
使用torch.onnx.register_custom_op_symbolic注冊自定義算子,再導出onnx模型。有以下幾處配置參數需要注意:
1. register_custom_op_symbolic函數的第一個參數'::adaptive_avg_pool2d'為pytorch對應操作符名稱,若填寫錯誤,則會導致自定義算子注冊失敗
2. 操作域必須設置為horizon.custom,算子類型為PyOp
3. class_name_s需要與算子實現文件中的類名相對應
4. module_s與算子實現文件名相同,若算子實現文件在當前目錄的子目錄(custom_op)中,要將相對路徑包含進去:"custom_op/sample_custom"
5. 必須指定input_types_i、output_types_i、output_shape_s三個參數
6. 注意指定opset_version為10或11
參考代碼:
import torch
from horizon_nn.horizon_onnx.onnx_pb import TensorProto
from torch.onnx.utils import register_custom_op_symbolic
#prepare your model and input_data
def horizon_pool(g, input, output_size):
return g.op(
'horizon.custom::PyOp', #required, ! must be 'horizon.custom' domain !
input,
class_name_s="GlobalAveragePool", #required ! must match the class def name in sample_custom python file !
compute_s="compute", #optional, 'compute' by default
module_s="sample_custom",#required ! must match the file name of the "op_register_files" !
input_types_i=[TensorProto.FLOAT], #required
output_types_i=[TensorProto.FLOAT],#required
output_shape_s=["1, 1024, 1, 1"]) #required
register_custom_op_symbolic('::adaptive_avg_pool2d',
horizon_pool,
opset_version=11)
torch.onnx.export(model, input_data, "custom_op.onnx", opset_version=11)
1.2 算子實現
對應上一節注冊自定義算子時配置的算子實現文件(class_name需要保持一致)。
#sample_custom.py
import numpy as np
from horizon_nn.custom import op_implement_register
@op_implement_register("CustomIdentity")
class CustomIdentity(object):
def __init__(self, kernel_size, threshold):
self._kernel_size = kernel_size
self._default_threshold = threshold
def compute(self, X):
return X
@op_implement_register("GlobalAveragePool")
class GlobalAveragePool(object):
def __init__(self):
pass
def compute(self, X):
return np.nanmean(X, axis=(2, 3)).reshape(-1, 1024, 1, 1)
2 自定義caffe算子
2.1 修改prototxt
在原始模型文件中,將自定義算子對應的類型標記為"Custom" ,并設置custom_param。params 是算子的傳入參數,指定方式為‘param_name’:param_value, 多個參數之間使用 \n 分隔。
layer {
name: "hr_op"
type: "Custom"
bottom: "res3d_in"
top: "res3d"
custom_param {
kind: "CustomIdentity"
shape {
dim: 1
dim: 512
dim: 28
dim: 28
}
params: "'kernel_size': 10 \n'threshold': 0.5"
}
}
2.2 算子實現
相比于onnx模型,caffe模型的自定義算子實現還需要提供該算子的輸出尺寸。
#sample_custom.py
from horizon_nn.custom.op_registration import op_implement_register, op_shape_infer_register
@op_implement_register("CustomIdentity")
class CustomIdentity(object):
def __init__(self, kernel_size, threshold):
self._kernel_size = kernel_size
self._default_threshold = threshold
def compute(self, X):
return X
@op_shape_infer_register("CustomIdentity")
def infer_shape(inputs_shape):
"""Infer the output shapes of the custom operator.
Arguments:
input_shapes: A list of input shapes.
Returns:
Return a list of custom operator's output shapes.
"""
outputs_shape = inputs_shape
return outputs_shape
3 含自定義算子的模型轉換
在模型轉換配置文件中,添加自定義算子相關參數,示例如下:

custom_op_method
:
固定使用 register
;
op_register_files
:
自定義算子計算的實現文件,如果有多份實現,使用 ‘;’
將各個文件分開即可。
4 含自定義算子的模型推理
想將包含自定算子的.bin模型順利部署到開發板上,還需要提供自定義算子的C++代碼實現。 您可以使用下文提供的模板進行修改:
頭文件:
// custom_identity.h
#ifndef ADVANCED_SAMPLES_CUSTOM_IDENTITY_H_
#define ADVANCED_SAMPLES_CUSTOM_IDENTITY_H_
#include
#include
#include "dnn/hb_dnn.h"
#include "dnn/plugin/hb_dnn_layer.h"
#include "dnn/plugin/hb_dnn_ndarray.h"
namespace hobot {
namespace dnn {
Layer *CustomIdentity_layer_creator();
class CustomIdentity : public Layer {
public:
CustomIdentity() = default;
~CustomIdentity() override = default;
public:
int32_t Init(const Attribute &attributes) override;
int32_t Forward(const std::vector &bottomBlobs,
std::vector &topBlobs,
const hbDNNInferCtrlParam *inferCtrlParam) override;
std::string GetType() const override { return "CustomIdentity"; }
private:
std::string module_;
};
} // namespace dnn
} // namespace hobot
#endif
cpp文件:
// custom_identity.cpp
#include "custom_identity.h"
namespace hobot {
namespace dnn {
Layer *CustomIdentity_layer_creator() { return new CustomIdentity; }
int32_t CustomIdentity::Init(const Attribute &attributes) {
// unused attribute, just demonstrating
attributes.GetAttributeValue(&module_, "module");
return 0;
}
int32_t CustomIdentity::Forward(const std::vector &bottomBlobs,
std::vector &topBlobs,
const hbDNNInferCtrlParam *inferCtrlParam) {
const NDArray *input = bottomBlobs[0];
NDArray *out = topBlobs[0];
const auto *input_data = input->Dptr();
auto *out_data = out->Dptr();
uint32_t size = input->Size();
for (uint32_t i = 0U; i < size; i++) {?
out_data[i] = input_data[i];
}
return 0;
}
} // namespace dnn
} // namespace hobot
將以上兩個文件放在當前工程目錄下之后,編寫infer代碼時僅需要在加載模型之前增加對算子的注冊即可,注冊可參考以下代碼:
//infer.cpp
#include "custom_identity.h"
// register custom layer
hbDNNRegisterLayerCreator("CustomIdentity",
hobot::dnn::CustomIdentity_layer_creator)
本文轉載自地平線開發者社區:https://developer.horizon.ai
原作者:顏值即正義
原文鏈接:https://developer.horizon.ai/forumDetail/71036525692881018
-
算子
+關注
關注
0文章
16瀏覽量
7320 -
模型轉換
+關注
關注
0文章
4瀏覽量
5263
發布評論請先 登錄
相關推薦
如何添加自定義單板
如何快速創建用戶自定義Board和App工程

think-cell:自定義think-cell(四)

think-cell;自定義think-cell(一)

智能工業主板:ROC-RK3576-PC

低功耗大模型主板:ROC-RK3576-PC

AWTK 開源串口屏開發(18) - 用 C 語言自定義命令

TSMaster 自定義 LIN 調度表編程指導

HarmonyOS開發案例:【UIAbility和自定義組件生命周期】

評論