如果模型输入Shape是动态的,在模型执行之前调aclmdlSetDatasetTensorDesc设置该输入的tensor描述信息(主要是设置Shape信息),在模型执行之后,调用aclmdlGetDatasetTensorDesc接口获取模型动态输出的Tensor描述信息,再进一步调用aclTensorDesc下的操作接口获取输出Tensor数据占用的内存大小、Tensor的Format信息、Tensor的维度信息等)。
关键原理说明如下:
模型推理场景下,对于动态Shape的输入数据,使用ATC工具转换模型时,通过input_shape_range参数设置输入Shape范围。
ATC工具的参数说明请参见《ATC工具使用指南》。
模型加载的详细流程,请参见接口调用流程,模型加载成功后,返回标识模型的ID。
详细调用流程请参见准备模型执行的输入/输出数据结构。
注意点如下:
在调用aclCreateTensorDesc接口,创建Tensor描述信息时,设置Shape信息,包括维度个数、维度大小,此处设置的维度个数、维度大小必须在模型构建时设置的输入Shape范围内,模型构建的详细说明请参见模型构建。
例如,调用aclmdlExecute接口(同步接口)执行模型。
调用aclmdlGetDatasetTensorDesc接口获取动态Shape输出的Tensor描述信息,再利用aclTensorDesc数据类型的操作接口(请参见aclTensorDesc)获取Tensor描述信息的属性,此处以获取size(表示Tensor数据占用的空间大小)为例,然后从内存中读取对应size的数据。
调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
// 此例中假设模型的第一个输入为动态输入,其index为0;模型的第一个输出为动态输出,其index为0。 // 1.模型加载,加载成功后,再设置动态输入的Tensor描述信息,主要设置动态输入的Shape信息 // ...... // 2.准备模型描述信息modelDesc_,准备模型的输入数据input_和模型的输出数据output_ // 此处需注意: // 当利用aclmdlGetInputSizeByIndex获取到的size大小为0时,表示该输入的Shape是动态的,用户可根据实际情况预估一块较大的输入内存 // 当利用aclmdlGetOutputSizeByIndex获取到的size大小为0时,表示该输出的Shape是动态的,用户可根据实际情况预估一块较大的输出内存 // ...... // 3.自定义函数,设置动态输入的Tensor描述信息 void SetTensorDesc() { // ...... // 创建Tensor描述信息,其中dataType和format不需要设置,AscendCL直接从模型中获取,此处使用的默认值 shape需要和给定的输入数据的shape一致 int64_t shapes = {1,3,224,224}; aclTensorDesc *inputDesc = aclCreateTensorDesc(ACL_FLOAT, 4, shapes, ACL_FORMAT_NCHW); // 设置index为0的动态输入的Tensor描述信息 aclError ret = aclmdlSetDatasetTensorDesc(input_, inputDesc, 0); // ...... } // 4.自定义函数,执行模型,并获取动态输出的Tensor描述信息 void ModelExecute() { aclError ret; // 调用自定义接口,设置动态输入的Tensor描述信息 SetTensorDesc(); // 执行模型 ret = aclmdlExecute(modelId, input_, output_); // 获取index为0的动态输出的Tensor描述信息 aclTensorDesc *outputDesc = aclmdlGetDatasetTensorDesc(output_, 0); // 利用aclTensorDesc数据类型的操作接口获取outputDesc的属性,此处需要获取size(表示Tensor数据占用的空间大小),然后从内存中读取对应size的数据 string outputFileName = ss.str(); FILE *outputFile = fopen(outputFileName.c_str(), "wb"); size_t outputDesc_size = aclGetTensorDescSize(outputDesc); aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(output_, 0); void *data = aclGetDataBufferAddr(dataBuffer); void *outHostData = nullptr; // 调用aclrtGetRunMode接口获取软件栈的运行模式,并根据运行模式判断是否进行数据传输 aclrtRunMode runMode; ret = aclrtGetRunMode(&runMode); if (runMode == ACL_HOST) { ret = aclrtMallocHost(&outHostData, outputDesc_size); // 由于动态shape申请的内存比较大,而真实数据的大小是outputDesc_size,所以此处用真实数据大小去拷贝内存 ret = aclrtMemcpy(outHostData, outputDesc_size, data, outputDesc_size, ACL_MEMCPY_DEVICE_TO_HOST); fwrite(outHostData, outputDesc_size, sizeof(char), outputFile); ret = aclrtFreeHost(outHostData); } else { // if app is running in host, write model output data into result file fwrite(data, outputDesc_size, sizeof(char), outputFile); } fclose(outputFile); // ...... } // 5.处理模型推理结果 // TODO