赞
踩
这节课得目标是掌握基本的开发流程,以及相关的应用日志查看。
准备模型、准备数据、编译程序、运行程序
编译的主要步骤有准备环境,ACL初始化、运行管理资源申请、模型加载、模型推理、模型卸载、运行资源管理资源释放、ACL去初始化、编译代码生成可执行文件。
运行ACL时需要先初始化,初始化时需要有配置文件,json格式的。如果不关注配置文件,则该文件可以为空json文件,即只有“{}”。
准备运行环境、执行可执行文件、运行运用。
首先时头文件,需要有acl的头文件
#include "acl/acl.h"
// 此处的..表示相对路径,相对可执行文件所在的目录
//例如,编译出来的可执行文件存放在out目录下,此处的..就表示out目录的上一级目录
const char *aclConfigPath = "../src/acl.json";
aclError ret = aclInit(aclConfigPath);
需要申请的资源原有:device、context、stream等。
aclrtSetDevice这个接口,调用完毕后,除了指定了计算设备之外,还会同时创建1个默认的Context;而这个默认的Context还附赠了2个Stream,1个默认Stream和1个用于执行内部同步的Stream。这也意味着如果是编写非常简单的单线程同步推理应用,在运行资源这里我们只需要调用aclrtSetDevice就够了。
// 1 指定运算的Device
ret = aclrtSetDevice(deviceId_);
// 2 显式创建一个Context,用于管理Stream对象
ret = aclrtCreateContext(&context_, deviceId_);
// 3 显式创建一个Stream
// 用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序执行任务
ret = aclrtCreateStream(&stream_);
// 4 获取当前昇腾AI软件栈的运行模式,根据不同的运行模式,后续的接口调用方式不同
aclrtRunMode runMode;
extern bool g_isDevice;
ret = aclrtGetRunMode(&runMode);
g_isDevice = (runMode == ACL_DEVICE);
AscendCL只认识适配昇腾AI处理器的离线模型(*.om文件),因此,模型加载前,需要将第三方网络(例如,Caffe ResNet-50网络)转换为适配昇腾AI处理器的离线模型(*.om文件)
// 1 初始化变量 // 此处的..表示相对路径,相对可执行文件所在的目录 // 例如,编译出来的可执行文件存放在out目录下,此处的..就表示out目录的上一级目录 const char* omModelPath = "../model/resnet50.om" // 2 根据模型文件获取模型执行时所需的权值内存大小、工作内存大小。 aclError ret = aclmdlQuerySize(omModelPath, &modelMemSize_, &modelWeightSize_); // 3 根据工作内存大小,申请Device上模型执行的工作内存。 ret = aclrtMalloc(&modelMemPtr_, modelMemSize_, ACL_MEM_MALLOC_NORMAL_ONLY); // 4 根据权值内存的大小,申请Device上模型执行的权值内存。 ret = aclrtMalloc(&modelWeightPtr_, modelWeightSize_, ACL_MEM_MALLOC_NORMAL_ONLY); // 5 加载离线模型文件(适配昇腾AI处理器的离线模型),由用户自行管理模型运行的内存(包括权值内存、工作内存)。 // 模型加载成功,返回标识模型的ID。 ret = aclmdlLoadFromFileWithMem(modelPath, &modelId_, modelMemPtr_, modelMemSize_, modelWeightPtr_, modelWeightSize_); // 6 根据加载成功的模型的ID,获取该模型的描述信息。 // modelDesc_为aclmdlDesc类型。 modelDesc_ = aclmdlCreateDesc(); ret = aclmdlGetDesc(modelDesc_, modelId_);
准备模型推理的输入、输出数据结构,用于描述模型的输入、输出数据。
// 1 准备模型推理的输入数据结构 // 1.1申请输入内存 size_t modelInputSize; void *modelInputBuffer = nullptr; // 当前示例代码中的模型只有一个输入,所以index为0 // 如果模型有多个输入,则需要先调用aclmdlGetNumInputs接口获取模型输入的数量 modelInputSize = aclmdlGetInputSizeByIndex(modelDesc_, 0); aclRet = aclrtMalloc(&modelInputBuffer, modelInputSize, ACL_MEM_MALLOC_NORMAL_ONLY); // 1.2准备模型的输入数据结构 // 创建aclmdlDataset类型的数据,描述模型推理的输入,input_为aclmdlDataset类型 input_ = aclmdlCreateDataset(); aclDataBuffer *inputData = aclCreateDataBuffer(modelInputBuffer, modelInputSize); ret = aclmdlAddDatasetBuffer(input_, inputData); // 2 准备模型推理的输出数据结构 // 2.1创建aclmdlDataset类型的数据,描述模型推理的输出,output_为aclmdlDataset类型 output_ = aclmdlCreateDataset(); // 2.2获取模型的输出个数 size_t outputSize = aclmdlGetNumOutputs(modelDesc_); // 2.3循环为每个输出申请内存,并将每个输出添加到aclmdlDataset类型的数据中. for (size_t i = 0; i < outputSize; ++i) { size_t buffer_size = aclmdlGetOutputSizeByIndex(modelDesc_, i); void *outputBuffer = nullptr; aclError ret = aclrtMalloc(&outputBuffer, buffer_size, ACL_MEM_MALLOC_NORMAL_ONLY); aclDataBuffer* outputData = aclCreateDataBuffer(outputBuffer, buffer_size); ret = aclmdlAddDatasetBuffer(output_, outputData); }
准备模型推理的输入数据,进行推理,推理结束后,处理推理结果。
string picturePath = "../data/dog1_1024_683.bin"; // 1 自定义函数ReadBinFile,调用C++标准库std::ifstream中的函数读取图片文件 // 输出图片文件占用的内存大小inputBuffSize以及图片文件存放在内存中的地址inputBuff void *inputBuff = nullptr; uint32_t inputBuffSize = 0; auto ret = Utils::ReadBinFile(picturePath, inputBuff, inputBuffSize); // 2 准备模型推理的输入数据 // 在"申请运行管理资源"时调用aclrtGetRunMode接口获取软件栈的运行模式 // 如果运行模式为ACL_DEVICE,则g_isDevice参数值为true,无需传输图片数据或在Device内传输数据 // 如果运行模式为ACL_HOST,则g_isDevice参数值为false,需要将图片数据从Host传输到Device if (!g_isDevice) { // 内存在"输入/输出数据结构准备"时申请该内存 aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_HOST_TO_DEVICE); (void)aclrtFreeHost(inputBuff); } else { aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_DEVICE_TO_DEVICE); (void)aclrtFree(inputBuff); } // 3. 执行模型推理 // modelId_表示模型ID,在"模型加载"成功后,会返回标识模型的ID // input_、output_分别表示模型推理的输入、输出数据,在"准备模型推理的输入、输出数据结构"时已定义 aclError ret = aclmdlExecute(modelId_, input_, output_) // 4. 处理模型推理的输出数据,输出top5置信度的类别编号 for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(output_); ++i) { // 获取每个输出的内存地址和内存大小 aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(output_, i); void* data = aclGetDataBufferAddr(dataBuffer); size_t len = aclGetDataBufferSizeV2(dataBuffer); // 将内存中的数据转换为float类型 float *outData = NULL; outData = reinterpret_cast<float*>(data); // 屏显每张图片的top5置信度的类别编号 map<float, int, greater<float> > resultMap; for (int j = 0; j < len / sizeof(float); ++j) { resultMap[*outData] = j; outData++; } int cnt = 0; for (auto it = resultMap.begin(); it != resultMap.end(); ++it) { // print top 5 if (++cnt > 5) { break; } INFO_LOG("top %d: index[%d] value[%lf]", cnt, it->second, it->first); } // 5 释放模型推理的输入、输出资源 // 释放输入资源,包括数据结构和内存 for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(input_); ++i) { aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(input_, i); (void)aclDestroyDataBuffer(dataBuffer); } (void)aclmdlDestroyDataset(input_); input_ = nullptr; aclrtFree(modelInputBuffer); // 释放输出资源,包括数据结构和内存 for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(output_); ++i) { aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(output_, i); void* data = aclGetDataBufferAddr(dataBuffer); (void)aclrtFree(data); (void)aclDestroyDataBuffer(dataBuffer); } (void)aclmdlDestroyDataset(output_); output_ = nullptr;
// 1. 卸载模型 aclError ret = aclmdlUnload(modelId_); // 2. 释放模型描述信息 if (modelDesc_ != nullptr) { (void)aclmdlDestroyDesc(modelDesc_); modelDesc_ = nullptr; } // 3. 释放模型运行的工作内存 if (modelWorkPtr_ != nullptr) { (void)aclrtFree(modelWorkPtr_); modelWorkPtr_ = nullptr; modelWorkSize_ = 0; } // 4. 释放模型运行的权值内存 if (modelWeightPtr_ != nullptr) { (void)aclrtFree(modelWeightPtr_); modelWeightPtr_ = nullptr; modelWeightSize_ = 0; }
要注意,只能销毁由aclrtCreateContext接口显式创建的Context、销毁由aclrtCreateStream接口显式创建的Stream,不能销毁默认Context、默认Stream。默认Context、默认Stream会由系统自行销毁,无需调用者关注。
// 1 释放Stream
aclError ret = aclrtDestroyStream(stream_);
// 2 释放Context
ret = aclrtDestroyContext(context_);
// 3 释放Device
ret = aclrtResetDevice(deviceId_);
aclError ret = aclFinalize();
使用Caffe框架的开源ResNet-50模型作为其输入输出。
由于昇腾无法使用tensorflow、caffe等模型,所以需要使用ATC工具转换为昇腾能够使用的模型,只需要准备好开发环境,并不需要运行环境。
支持原始框架类型为Caffe和TensorFlow,
日志目录默认为:
日志中关键信息
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。