开发者
资源
[object Object]
[object Object]

本章节介绍TensorFlow框架算子适配的流程,用于将TensorFlow框架的算子映射成CANN算子(开发者基于CANN框架自定义开发的算子),从而完成从TensorFlow框架调用到CANN算子的过程。同时给出TensorFlow框架侧算子调用的示例,便于开发者了解完整流程。

下图展示了完整的开发流程,具体步骤如下:

  1. 环境准备。

    1. CANN软件安装请参考

    2. 安装框架插件包,请参考中的环境准备 > 安装框架插件包章节,获取框架插件包详细的安装步骤。

    3. [object Object][object Object]。使用msOpGen工具创建算子开发工程。TensorFlow框架算子适配场景下,需要通过framework参数指定具体的框架为tf或者tensorflow,工具会自动生成框架适配代码。以自定义CANN算子AddCustom为例,使用msOpGen工具创建算子开发工程的具体命令如下:

      [object Object]
  2. 算子实现。

    • 。通过原型定义来描述算子输入输出、属性等信息以及算子在AI处理器上相关实现信息,并关联tiling实现等函数。
    • Kernel侧算子实现和host侧tiling实现请参考;工程化算子开发,支持开发者调用Tiling API基于CANN提供的编程框架进行tiling开发,kernel侧也提供对应的接口方便开发者获取tiling参数,具体内容请参考,由此而带来的额外约束也在上述章节说明。
  3. 。算子入图场景下,需要提供shape推导等算子入图适配函数的实现。

  4. TensorFlow框架适配插件开发。详细说明见

  5. 编译部署。通过工程编译脚本完成算子的编译部署,分为两种方式。

  6. TensorFlow框架算子调用。详细说明见。完整样例请参考

[object Object]

完成后,会在算子工程目录下生成framework/tf_plugin目录,用于存放TensorFlow框架适配插件实现文件。以自定义CANN算子AddCustom为例,算子工程目录如下:

[object Object]

当TensorFlow算子与CANN算子原型定义一致时,TensorFlow框架适配插件实现代码如下:

[object Object]

当TensorFlow算子与CANN算子原型定义不一致时,TensorFlow框架适配插件实现代码如下:

[object Object]
  • 包含插件实现函数相关的头文件。

    register.h存储在CANN软件安装后文件存储路径的“include/register/”目录下,包含该头文件,可使用算子注册相关类,调用算子注册相关的接口。

  • REGISTER_CUSTOM_OP:注册自定义算子,传入算子的_OpType_,需要与算子原型注册中的_OpType_保持一致。

    • FrameworkType:TENSORFLOW代表原始框架为TensorFlow。
    • OriginOpType:算子在原始框架中的类型。对于TensorFlow自定义算子,还需要完成,这里的OriginOpType与REGISTER_OP注册算子名相同,对于TensorFlow原生算子, 即为原生算子名。
    • ParseParamsByOperatorFn:用来注册解析算子参数实现映射关系的回调函数,需要用户自定义实现回调函数ParseParamByOpFunc。原始TensorFlow算子中参数与CANN算子中参数一一对应时,可直接使用自动映射回调函数AutoMappingByOpFn自动实现映射。
    • ParseOpToGraphFn:当TensorFlow算子与CANN算子原型定义不一致(比如CANN算子原型定义原型中有可选输入,但TensorFlow原型定义中不支持可选输入,没有可选输入)的情况时,用来注册调整算子原型映射关系的回调函数。
[object Object]

以自定义算子AddCustom为例,将该算子映射到TensorFlow内置算子Add上,需要先修改AddCustom自定义算子目录framework/tf_plugin下插件代码,完成算子名映射:

[object Object]

完成算子工程的编译部署后,构造单算子的TensorFlow 1.15版本测试用例进行验证。

  1. 编写测试用例_“tf_add_.py”。

  2. 导入python库。

    [object Object]
  3. 通过config()定义AI处理器和CPU上的运行参数。

    当“execute_type“为“ai_core“时,代表在AI处理器上运行单算子网络,最终会调用到Ascend C算子。

    当“execute_type“为“cpu“时,代表在Host侧的CPU运行单算子网络,调用的是TensorFlow算子。

    [object Object]
  4. 单算子网络测试用例主函数。

    • 算子输入请根据算子实际输入个数及shape进行构造。
    • 算子输出的计算,请根据算子逻辑调用TensorFlow相关接口进行实现。
    [object Object]
  5. 运行单算子网络。

    [object Object]
[object Object]
  1. 适配插件代码开发。以自定义算子AddCustom为例,将该算子映射到TensorFlow自定义算子AddCustom上,需要先修改CANN AddCustom自定义算子工程目录framework/tf_plugin下插件代码,完成算子名映射:

    [object Object]
  2. [object Object][object Object]TensorFlow自定义算子的开发。本节仅给出示例说明,详细内容请参考TensorFlow官方文档。

    创建TensorFlow原型注册文件custom_assign_add_custom.cc,内容如下:

    [object Object]

    使用如下命令对上述代码进行编译,产物为libcustom_ops.so,后续的算子调用脚本中可通过load_op_library接口加载该so为python模块,从而调用自定义算子。

    [object Object]
  3. 测试脚本中加载上一步骤编译好的动态库,实现自定义算子的调用。

    • TensorFlow 1.15.0调用代码示例

      [object Object]
    • TensorFlow 2.6.5调用代码

      [object Object]
[object Object]

TensorFlow的原型定义中不支持可选输入,对于包含可选输入的算子,其从TensorFlow到CANN的映射关系,不满足简单的一对一映射,需要在插件适配代码中,将输入转换为可选输入,调整原型的映射关系。下文以CANN算子库中的FlashAttentionScore算子为例,介绍针对此类算子的框架适配插件如何开发。

  1. 适配插件开发

    和上文中介绍的简单的一对一映射不同,进行插件适配开发时,需要调用ParseOpToGraphFn注册回调函数,回调函数中用于调整算子原型映射关系。此时:

    • 通过ParseParamsByOperatorFn注册回调函数,回调函数中将TensorFlow原生算子映射到一个IR和TensorFlow一致的中间算子(调用AutoMappingByOpFn完成属性映射)。
    • 通过ParseOpToGraphFn注册回调函数,调整算子原型映射关系,将中间算子最终映射到CANN算子库中的算子,这里映射到Graph图的概念是指一个算子构成的单算子图。

    需要注意:在ParseParamsByOperatorFn的回调函数中,需要将TensorFlow算子名称设置到中间算子的original_type属性中,用于后续ParseOpToGraphFn回调函数的触发。示例代码如下:

    [object Object]
  2. 在TensorFlow开源框架里注册FlashAttentionScore算子的原型定义,由于TensorFlow不支持可选输入,需要将其可选输入在TensorFlow原型中表示为动态输入,并通过属性来标记动态输入的个数,这些可选输入需要放置在原型定义的最后。示例代码(FlashAttentionScore.cc)如下:

    [object Object]

    使用如下命令对上述代码进行编译,产物为libcustom_ops.so,后续的算子调用脚本中可通过load_op_library接口加载该so为python模块,从而调用自定义算子。

    [object Object]
  3. [object Object][object Object]封装一个TensorFlow的算子调用接口,在此接口中处理可选输入。在该脚本需要加载上一步骤编译好的动态库。

    [object Object]
  4. 测试脚本中实现自定义算子的调用。假设中代码文件保存为ops.py,从ops中导入npu_flash_attention函数并使用。TensorFlow 2.6.5调用代码如下:

    [object Object]
[object Object]

对于存在动态输入/输出的算子,需要在插件的回调函数ParseParamByOpFunc中使用AutoMappingByOpFnDynamic实现TensorFlow算子和CANN算子的匹配。通过DynamicInputOutputInfo结构类描述动态输入/输出的信息,将动态输入/输出的名称和描述其个数的属性名绑定,再传入AutoMappingByOpFnDynamic实现自动匹配。

以ParseSingleExample算子为例,插件适配代码如下:

[object Object]
[object Object]