命令行场景下,开发者可以直接基于Ascend开源社区中提供的算子测试样例代码进行修改,适配自己的算子测试代码。
样例获取:单击Gitee或Github,进入Ascend samples开源仓,参见README中的“版本说明”下载配套版本的sample包,从“cplusplus/level1_single_api/4_op_dev/2_verify_op”目录中获取样例。
ST测试用例的实现思路为:进行算子测试用例json文件的定义,将自定义算子转换成单个算子的离线模型文件(*.om),然后使用AscendCL(Ascend Computing Language)提供的单算子模型加载接口加载离线模型,并传入算子输入数据,进行算子执行,通过查看输出结果验证算子功能是否正确,如下图所示。
下面详细讲解基于样例代码实现自定义算子ST测试用例的步骤。
自定义算子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文件的参数说明及更多的配置样例可参见《ATC工具使用指南》的“高级功能>单算子模型转换”。
使用ATC工具进行模型转换的过程中,会自动将CANN软件安装后文件存储路径“python/site-packages”路径下的算子编译依赖的TBE Python库加入PYTHONPATH的环境变量中。
若算子实现时用户引入了TBE模块外的其他Python依赖,请自行添加PYTHONPATH的环境变量,配置引入的Python依赖所在路径,如下所示:
export PYTHONPATH=xxxx:$PYTHONPATH
atc --singleop=test_data/config/xxx.json --soc_version=${soc_version} --output=op_models
ATC工具的其他参数解释请参考《ATC工具使用指南》中的转换样例。
进行单算子验证的AscendCL代码实现前,开发者需要自行构造算子测试数据的二进制文件,二进制文件以input_x.bin命名,x请从0开始命名,例如input_0.bin,input_1.bin。
说明:您可以使用numpy的random函数生成测试数据并将其输出到二进制文件。
样例模板获取:单击Gitee或Github,进入Ascend samples开源仓,参见README中的“版本说明”下载配套版本的sample包,从“cplusplus/level1_single_api/4_op_dev/2_verify_op/acl_execute_add”目录中获取样例。
开发者可直接基于样例模板进行修改。
修改src/main.cpp文中的CreateOpDesc()函数,构造算子描述对象。
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;
}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;
}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”中构造的算子描述对象,其中:
调用aclopSetAttr**( )接口进行属性的构造,不同类型的属性调用的aclopSetAttr*的接口不同,详细的接口可参见《应用软件开发指南(C&C++)》中的“AscendCL API参考 > 数据类型及其操作接口”章节中的“aclopAttr”类型的接口。
用于构造算子的输入,若算子有多个输入,需要使用多条opDesc.AddInputTensorDesc( )语句进行构造。
用于构造算子的输出,若算子有多个输出,需要使用多条opDesc.AddOutputTensorDesc( )语句进行构造。
aclDataType与aclFormat的取值范围请参见《应用软件开发指南(C&C++)》中的“AscendCL API参考 > 数据类型及其操作接口”章节中的“aclDataType”与“aclFormat”。
主要包括以下关键接口:
调用aclInit接口进行AscendCL初始化并调用aclrtSetDevice接口指定用于运算的Device。
调用aclopSetModelDir接口,设置加载模型文件的目录,目录下存放单算子模型文件(*.om文件)。
调用AscendCL提供的内存管理接口,申请存放执行算子的输入、输出数据内存。
根据构造的算子描述信息(输入输出Tensor描述、算子属性等)、申请存放算子输入输出数据的内存,然后调用aclopExecuteV2接口加载并执行算子。
调用aclrtSynchronizeStream接口阻塞应用运行,直到指定Stream中的所有任务都完成。
调用aclrtDestroyStream接口释放stream。
调用aclrtResetDevice接口释放Device上的资源。
调用aclFinalize接口实现AscendCL去初始化。
“acl_execute_add/src/main.cpp”中提供了单算子的模型加载与执行的通用流程样例代码,开发者可直接使用此部分样例代码加载自己的单算子模型文件及算子描述对象进行单算子功能验证。
编译测试用例代码,并将生成的动态库文件、单算子模型文件以及测试数据在运行环境执行,详细操作步骤可参见样例参考。