更新时间:2021-03-18 GMT+08:00
分享

实现过程

本章节详细描述自定义算子部署到算子库后,如何进行单算子的网络验证。

配置自定义算子json文件

自定义算子json文件用于进行算子的描述,需要按照算子原型定义进行配置,包括算子的输入、输出及属性信息,配置示例如下所示:

[
  {
    "op": "Conv2D",
    "input_desc": [
      {
        "format": "NCHW",
        "shape": [8, 512, 7, 7],
        "type": "float16"
      },
      {
        "format": "NCHW",
        "shape": [512, 512, 3, 3],
        "type": "float16"
      }
    ],
    "output_desc": [
      {
        "format": "NCHW",
        "shape": [8, 512, 7, 7],
        "type": "float16"
      }
    ],
    "attr": [
      {
        "name": "strides",
        "type": "list_int",
        "value": [1, 1, 1, 1]
      },
      {
        "name": "pads",
        "type": "list_int",
        "value": [1, 1, 1, 1]
      },
      {
        "name": "dilations",
        "type": "list_int",
        "value": [1, 1, 1, 1]
      }
    ]
  }
]

json文件为OpDesc的数组,参数解释如下所示:

表1 OpDesc参数说明

属性名

类型

说明

是否必填

op

string

算子类型。

input_desc

TensorDesc数组

算子输入描述。

output_desc

TensorDesc数组

算子输出描述。

attr

Attr数组

算子属性。

TensorDesc数组参数说明如下所示:

表2 TensorDesc数组参数说明

属性名

类型

说明

是否必填

format

string

Tensor的排布格式,配置为算子原始框架支持的format。

支持的format如下:

  • NCHW
  • NHWC
  • ND:表示支持任意格式。
  • NC1HWC0:华为自研的5维数据格式。其中,C0与微架构强相关,该值等于cube单元的size,例如16;C1是将C维度按照C0切分:C1=C/C0, 若结果不整除,最后一份数据需要padding到C0。
  • FRACTAL_Z:卷积的权重的格式。
  • FRACTAL_NZ:华为自研的分形格式,在cube单元计算时,输出矩阵的数据格式为NW1H1H0W0。整个矩阵被分为(H1*W1)个分形,按照column major排布,形状如N字形;每个分形内部有(H0*W0)个元素,按照row major排布,形状如z字形。考虑到数据排布格式,将NW1H1H0W0数据格式称为Nz格式。其中,H0,W0表示一个分形的大小,示意图如下所示:

type

string

Tensor的数据格式,支持的type如下:

  • bool
  • int8
  • uint8
  • int16
  • uint16
  • int32
  • uint32
  • float16
  • float
  • double

shape

int数组

Tensor的shape,例如[1, 224, 224, 3]。

name

string

Tensor的名称。

如果算子的输入为动态输入,则需要设置该字段。如ConcatD算子在算子原型中,其动态输入定义为: DYNAMIC_INPUT(input_values, …),则此处需要设置为”input_values0”, “input_values1”,…。

Attr数组的参数说明如下所示:

表3 Attr数组参数说明

属性名

类型

说明

是否必填

name

string

属性名。

type

string

属性值的类型,支持的类型有:

  • bool
  • int
  • float
  • string
  • list_bool
  • list_int
  • list_float
  • list_string
  • list_list_int

value

由type的取值决定

属性值,根据type不同,属性值不同:

  • bool: true/false
  • int: 10
  • float: 1.0
  • string: “NCHW”
  • list_bool: [false, true]
  • list_int: [1, 224, 224, 3]
  • list_float: [1.0, 0.0]
  • list_string: [“str1”, “str2”]
  • list_list_int: [[1, 3, 5, 7], [2, 4, 6, 8]]

使用ATC工具生成单算子模型文件

  1. 设置环境变量。

    环境变量设置示例如下所示,此处只描述必选环境变量,环境变量的详细解释请参考ATC工具使用指导中的转换样例

    export SLOG_PRINT_TO_STDOUT=1
    export install_path=/home/HwHiAiUser/Ascend/ascend-toolkit/latest
    export PATH=${install_path}/atc/ccec_compiler/bin:${install_path}/atc/bin:$PATH
    export PYTHONPATH=${install_path}/atc/python/site-packages/te:${install_path}/atc/python/site-packages/topi:${install_path}/atc/python/site-packages/auto_tune.egg/auto_tune:${install_path}/atc/python/site-packages/schedule_search.egg
    export LD_LIBRARY_PATH=${install_path}/atc/lib64:$LD_LIBRARY_PATH
    export ASCEND_OPP_PATH=${install_path}/opp

    install_path为ATC与OPP组件的安装路径。

    export SLOG_PRINT_TO_STDOUT=1,可选,表示在当前终端窗口打印执行日志,方便问题定位。

  2. 使用ATC工具,加载单算子描述文件(json文件)生成单算子的离线模型。

    atc --singleop=test_data/config/xxx.json --soc_version=Ascend310 --output=op_models

    • singleop:算子描述的json文件,为相对于执行atc命令所在目录的相对路径。
    • soc_version:昇腾AI处理器的版本。
    • output:生成的模型文件的存储路径,为相对于执行atc命令所在目录的相对路径。

    ATC工具的其他参数解释请参考ATC工具使用指导中的转换样例

构造输入数据

进行单算子验证的ACL代码实现前,开发者需要自行构造算子测试数据的二进制文件,二进制文件以input_x.bin命名,x请从0开始命名,例如input_0.bin,input_1.bin。

说明:您可以使用numpy的random函数生成测试数据并将其输出到二进制文件。

ACL单算子网络验证代码实现

  1. 构造算子描述对象。

    ACL Lib中提供了单算子验证样例模板,开发者可直接基于样例模板进行修改。

    修改src/main.cpp文中的CreateOpDesc()函数,构造算子描述对象。

    • 不带属性的Add算子的opDesc构造示例如下:
      OperatorDesc CreateOpDesc()
      {
          std::string opType = "Add";          // 算子的类型
          // 构造算子的输入输出描述
          std::vector<int64_t> shape{8, 16};
          aclDataType dataType = ACL_INT32;
          aclFormat format = ACL_FORMAT_ND;
          OperatorDesc opDesc(opType);
          opDesc.AddInputTensorDesc(dataType, shape.size(), shape.data(), format);
          opDesc.AddInputTensorDesc(dataType, shape.size(), shape.data(), format);
          opDesc.AddOutputTensorDesc(dataType, shape.size(), shape.data(), format);
          return opDesc;
      }
    • 带属性的ConcatD算子的opDesc构造示例如下:
      OperatorDesc CreateOpDesc()
      {
          std::string opType = "ConcatD";
          std::vector<int64_t> shape1{1, 1, 4, 4};
          std::vector<int64_t> shape2{1, 2, 4, 4};
          aclDataType dataType = ACL_INT32;
          aclFormat format = ACL_FORMAT_ND;
          OperatorDesc opDesc(opType);
          opDesc.AddInputTensorDesc(dataType, shape1.size(), shape1.data(), format);
          opDesc.AddInputTensorDesc(dataType, shape1.size(), shape1.data(), format);
          opDesc.AddOutputTensorDesc(dataType, shape2.size(), shape2.data(), format);
          // 构造算子属性
          auto opAttr = opDesc.opAttr;
          aclopSetAttrInt(opAttr, "N", 2);
          aclopSetAttrInt(opAttr, "concat_dim", 1);
          return opDesc;
      }
    • Conv2D算子的opDesc构造示例如下:
      OperatorDesc CreateOpDesc()
      {
          std::vector<int64_t> shape{8, 512, 7, 7};
          std::string opType = "Conv2D";
          aclDataType dataType = ACL_FLOAT16;
          aclFormat format = ACL_FORMAT_NCHW;
          OperatorDesc opDesc(opType);
          opDesc.AddInputTensorDesc(dataType, shape.size(), shape.data(), format); // Conv2D算子的第一个输入
          std::vector<int64_t> shape1{512,512,3,3};  
          opDesc.AddInputTensorDesc(dataType, shape1.size(), shape1.data(), format);   // Conv2D算子的第二个输入
          std::vector<int64_t> shape2{8, 512, 7, 7};
          opDesc.AddOutputTensorDesc(dataType, shape2.size(), shape2.data(), format);  // Conv2D算子的输出
          int64_t intList[4]{1, 1, 1, 1};
          auto opAttr = opDesc.opAttr;
          aclopSetAttrListInt(opAttr, "strides", 4, intList);
          aclopSetAttrListInt(opAttr, "pads", 4, intList);
          aclopSetAttrListInt(opAttr, "dilations", 4, intList);
          return opDesc;
      }

    OperatorDesc为“acl_execute_add/inc/operator_desc.h”中构造的算子描述对象,其中:

    • std::string opType为算子的类型。
    • std::vector<const aclTensorDesc *> inputDesc,为算子的输入描述。
    • std::vector<const aclTensorDesc *> outputDesc,为算子的输出描述。
    • aclopAttr *opAttr,为算子的属性。

      调用aclopSetAttr**( )接口进行属性的构造,不同类型的属性调用的aclopSetAttr*的接口不同,详细的接口可参见应用软件开发指南中的“AscendCL API参考 > 数据类型及其操作接口”章节中的“aclopAttr”类型的接口。

    • OperatorDesc &AddInputTensorDesc(aclDataType dataType, int numDims, const int64_t *dims, aclFormat format)

      用于构造算子的输入,若算子有多个输入,需要使用多条opDesc.AddInputTensorDesc( )语句进行构造。

    • OperatorDesc &AddOutputTensorDesc(aclDataType dataType, int numDims, const int64_t *dims, aclFormat format)

      用于构造算子的输出,若算子有多个输出,需要使用多条opDesc.AddOutputTensorDesc( )语句进行构造。

      aclDataType与aclFormat的取值范围请参见应用软件开发指南中的“AscendCL API参考 > 数据类型及其操作接口”章节中的“aclDataType”“aclFormat”

  2. 加载单算子模型并进行算子的执行。

    主要包括以下关键接口:

    调用aclInit接口进行ACL初始化并调用aclrtSetDevice接口指定用于运算的Device。

    调用aclopSetModelDir接口,设置加载模型文件的目录,目录下存放单算子模型文件(*.om文件)。

    调用aclrtMalloc接口申请Device上的内存,存放执行算子的输入、输出数据。

    根据构造的算子描述信息(输入输出Tensor描述、算子属性等)、申请存放算子输入输出数据的内存,然后调用aclopExecute接口加载并执行算子。

    调用aclrtSynchronizeStream接口阻塞应用运行,直到指定Stream中的所有任务都完成。

    调用aclrtDestroyStream接口释放内存。

    调用aclrtResetDevice接口释放Device上的资源

    “acl_execute_add/src/main.cpp”中提供了单算子的模型加载与执行的通用流程样例代码,开发者可直接使用此部分样例代码加载自己的单算子模型文件及算子描述对象进行单算子功能验证。

分享:

    相关文档

    相关产品

close