您可以参考本章节进行算子适配插件的开发,将基于第三方框架的算子映射成适配昇腾AI处理器的算子,将算子信息注册到GE中。基于ONNX框架的网络运行时,首先会加载并调用GE中的插件信息,将原始框架网络中的算子进行解析并映射成适配昇腾AI处理器中的算子。
下文我们将适配昇腾AI处理器的算子称为CANN算子。
算子插件的实现包含CANN算子类型的注册、原始框架中算子类型的注册以及原始框架中算子属性到CANN算子属性的映射,算子的映射通过Parser模块完成。插件在整网络运行场景下的实现流程如图1所示。
GE提供REGISTER_CUSTOM_OP宏,按照指定的算子名称完成算子的注册。
原始框架为ONNX的自定义算子注册代码:
#include "register/register.h"
#include "graph/operator.h"
#include "json.hpp"
namespace domi
{
REGISTER_CUSTOM_OP("OpType")
.FrameworkType(ONNX)
.OriginOpType("OriginOpType")
.ParseParamsByOperatorFn(ParseParamByOpFunc) //用来注册解析算子属性的函数
.ImplyType(ImplyType::TVM);
}
若样例工程中未提供“json.hpp”文件,用户可以自行下载,并将“json.hpp”放在工程可以找到的任意路径下,然后包含此头文件即可,下载路径可参见json.hpp。
回调函数ParseParamByOpFunc的声明如下所示:
Status ParseParamByOpFunc(const ge::Operator& op_src, ge::Operator& op_dest)
ONNX原始模型中,属性为repeated message类型,如下所示:
message NodeProto {
repeated string input = 1; // namespace Value
repeated string output = 2; // namespace Value
string name = 3; // namespace Node
string op_type = 4; // namespace Operator
string domain = 7; // namespace Domain
// Additional named attributes.
repeated AttributeProto attribute = 5;
} GE对属性进行解析时,对于repeated message类型的参数,可使用GetAttr(const char *name, ge::AscendString &attr_value)接口获取其属性值,然后将AscendString类型的属性值转换为String类型,再将其转换为json格式进行属性字段的解析。
实现如下所示:
using namespace ge;
using json = nlohmann::json;
namespace domi {
namespace {
const int kTypeFloat = 1;
}
Status ParseOnnxParamsLeakyRelu(const ge::Operator& op_src, ge::Operator& op_dest) {
// trans op_src to op_dest
// if op_src get required attr failed, need to return Failed
// if op_src get optional attr failed, need to return Failed or set a default value
float negative_slope = 0.01f;
string negative_slope_str;
AscendString attrs_string;
// 使用固定属性名称“attribute”获取ONNX算子中的属性,并赋值给AscendString类型对象
if (ge::GRAPH_SUCCESS == op_src.GetAttr("attribute", attrs_string)) {
// 转换为json格式
json attrs = json::parse(attrs_string.GetString());
for (json attr : attrs["attribute"]) {
if (attr["name"] == "alpha" && attr["type"] == kTypeFloat) {
negative_slope_str = attr["f"]; // float type in json has accuracy loss, so we use string type to store it
negative_slope = atof(negative_slope_str.c_str());
}
}
}
op_dest.SetAttr("negative_slope", negative_slope);
return SUCCESS;
}