您可以参考本章节进行算子适配插件的开发,将第三方框架的算子映射成适配昇腾AI处理器的算子。基于TensorFlow 框架的网络运行时,首先会加载并调用GE中的插件信息,将原始框架网络中的算子进行解析并映射成适配昇腾AI处理器算子。
下文我们将适配昇腾AI处理器的算子称为CANN算子。
算子插件的实现包含CANN算子类型的注册、原始框架中算子类型的注册以及原始框架中算子属性到CANN算子属性的映射,算子的映射通过Parser模块完成。插件在整网络运行场景下的实现流程如图1所示。
GE提供REGISTER_CUSTOM_OP宏,按照指定的算子名称完成算子的注册。
#include "register/register.h"
#include "graph/operator.h"
namespace domi
{
REGISTER_CUSTOM_OP("OpType")
.FrameworkType(TENSORFLOW)
.OriginOpType("OriginOpType")
.ParseParamsByOperatorFn(ParseParamByOpFunc)
.ImplyType(ImplyType::TVM);
}
register.h存储在CANN软件安装后文件存储路径的“include/register/”目录下,包含该头文件,可使用算子注册相关类,调用算子注册相关的接口。
operator.h(可选),存储在CANN软件安装后文件存储路径的“include/graph/”目录下,包含该头文件,可以使用Operator类相关接口,获取算子输入输出及属性等算子信息。
下面详细介绍解析函数ParseParamsByOperatorFn,此函数的实现有以下几种场景:
场景 |
实现方法 |
|---|---|
原始TensorFlow算子中属性与CANN算子中属性一一对应,即属性的个数、属性名称与属性含义一致。 |
可直接使用自动映射回调函数AutoMappingByOpFn自动实现映射。 .ParseParamsByOperatorFn(AutoMappingByOpFn) AutoMappingByOpFn函数会将TensorFlow算子中比CANN算子多的属性追加到CANN算子的属性中去。 |
原始TensorFlow算子中属性与CANN算子中属性无法一一对应,则需要重新计算为CANN算子中对应属性赋值。 |
需要在回调函数ParseParamByOpFunc中进行属性解析的实现,详细实现方法可参见•算子属性无法一一对应。 |
对于TensorFlow原始图中不带数据排布格式信息的张量,GE在接收后会将其数据排布格式设置为ND,但对于某些格式敏感算子,ND为不正确的格式,需要在插件的解析函数中进行数据排布格式的设置。 |
需要在回调函数ParseParamByOpFunc中进行数据排布格式的设置,详细的设置方法可参见•格式敏感算子。 |
REGISTER_OP("TopKV2")
.Input("input: T")
.Input("k: int32")
.Output("values: T")
.Output("indices: int32")
.Attr("sorted: bool = true")
.Attr("T: realnumbertype")
.SetShapeFn(TopKShapeFn); 其映射的CANN算子TopK的定义如下所示:
REG_OP(TopK)
.INPUT(x, TensorType::RealNumberType())
.INPUT(k, TensorType({DT_INT32}))
.OUTPUT(values, TensorType::RealNumberType())
.OUTPUT(indices, TensorType({DT_INT32}))
.ATTR(sorted, Bool, true)
.ATTR(largest, Bool, true)
.ATTR(dim, Int, -1)
.OP_END_FACTORY_REG(TopK) 从以上定义中可以看出,CANN算子TopK的属性largest与dim在TensorFlow的TopKV2算子中未定义,所以我们需要在回调函数ParseParamByOpFunc中实现对这两个属性的赋值,实现如下所示。
Status TopKMappingFn(const ge::Operator &op_src, ge::Operator& op) {
// 使用AutoMappingFn函数实现可以对应的属性的映射
AutoMappingFn(op_src, op) != SUCCESS) {
return FAILED;
}
// 为CANN算子TopK的dim属性赋初始值
int32_t dim = -1;
op.SetAttr("dim", dim);
// 为CANN算子TopK的largest属性赋初始值
bool largest = true;
op.SetAttr("largest", largest);
return SUCCESS;
}const int kInputFilter = 1
Status ParseParamsConv2D(const ge::Operator &op_src, ge::Operator& op) {
AutoMappingByOpFn(op_src, op);
TensorDesc org_tensor_w = op.GetInputDesc(kInputFilter);
org_tensor_w.SetOriginFormat(ge::FORMAT_HWCN);
org_tensor_w.SetFormat(ge::FORMAT_HWCN);
auto ret = op.UpdateInputDesc(kInputFilter, org_tensor_w);
if (ret != ge::GRAPH_SUCCESS) {
return FAILED;
}
return SUCCESS;
}