一、学习目标
1.了解昇腾Al全栈架构及该架构中各层的作用。
2.了解异构计算架构CANN在昇腾Al全栈中的位置和作用。
3.了解应用开发编程框架、基本概念。
4.掌握基本的应用开发流程,可以按照指导编译、运行应用。
5.了解如何获取及查看应用运行日志,具备基本的问题定界、定位能力。
二、遇到问题求助
开发、编译或运行应用出错时无法解决问题或有优化建议要反馈,可以借助以下渠道:
1.参考CANN Sample仓中的样例开发时,相关问题可以在该仓上提Issue,该仓的接口人会定期处理问题。
2.模型相关的问题,可在ModelZoo仓上提lssue,该仓的接口人会定期处理问题。
3.也可在昇腾社区论坛中查阅经验贴、或者发求助帖,论坛上的其他开发人员会跟帖,论坛的接口人也会定期审视问题。
三、Ascend基础
四、图片分类应用开发
1.图像分类应用简介
使用Caffe框架的ResNet-50模型
输入数据:RGB格式、224*224分辨率的输入图片
输出数据:图片的类别标签及其对应置信度
2.开发流程
3. 开发应用
(1)创建代码目录
MyFirstApp
├── data
│ ├── dog1_1024_683.jpg // 测试图片
├── model
│ ├── resnet50.caffemodel // ResNet-50网络的预训练模型文件(*.caffemodel)
│ ├── resnet50.prototxt // ResNet-50网络的模型文件(*.prototxt)
├── script
│ ├── transferPic.py // 将测试图片预处理为符合模型要求的图片
// 包括将*.jpg转换为*.bin,同时将图片从1024*683的分辨率缩放为224*224
├── src
│ ├── CMakeLists.txt // cmake编译脚本
│ ├── main.cpp // 主函数,图片分类功能的实现文件
├── sample_build.sh // 编译代码的脚本
├── sample_run.sh // 运行应用的脚本
(2)准备模型
以运行用户将MyFirstApp目录上传至开发环境。
以运行用户登录开发环境,执行以下命令进行模型转换。
atc --model=model/resnet50.prototxt --weight=model/resnet50.caffemodel --framework=0 --output=model/resnet50 --soc_version=Ascend310
相关参数
(3)应用代码逻辑
根据过程详解,以“MyFirstApp/src/main.cpp”中main函数展示整个应用代码逻辑
int main()
{
// 1.定义一个资源初始化的函数,用于AscendCL初始化、运行管理资源申请(指定计算设备)
InitResource();
// 2.定义一个模型加载的函数,加载图片分类的模型,用于后续推理使用
const char *modelPath = "../model/resnet50.om";
LoadModel(modelPath);
// 3.定义一个读图片数据的函数,将测试图片数据读入内存,并传输到Device侧,用于后续推理使用
const char *picturePath = "../data/dog1_1024_683.bin";
LoadPicture(picturePath);
// 4.定义一个推理的函数,用于执行推理
Inference();
// 5.定义一个推理结果数据处理的函数,用于在终端上屏显测试图片的top5置信度的类别编号
PrintResult();
// 6.定义一个模型卸载的函数,卸载图片分类的模型
UnloadModel();
// 7.定义一个函数,用于释放内存、销毁推理相关的数据类型,防止内存泄露
UnloadPicture();
// 8.定义一个资源去初始化的函数,用于AscendCL去初始化、运行管理资源释放(释放计算设备)
DestroyResource();
}
(4)应用代码完善
以下代码添加在(3)中代码之前
a. include头文件
#include "acl/acl.h"
#include <iostream>
#include <fstream>
#include <cstring>
#include <map>
using namespace std;
b. 资源初始化
int32_t deviceId = 0;
void InitResource()
{
aclError ret = aclInit(nullptr);
ret = aclrtSetDevice(deviceId);
}
c. 模型加载
uint32_t modelId;
void LoadModel(const char* modelPath)
{
aclError ret = aclmdlLoadFromFile(modelPath, &modelId);
}
d. 将测试图片读入内存,在传输到Device侧,供推理使用
size_t pictureDataSize = 0;
void *pictureHostData;
void *pictureDeviceData;
//申请内存,使用C/C++标准库的函数将测试图片读入内存
void ReadPictureTotHost(const char *picturePath)
{
string fileName = picturePath;
ifstream binFile(fileName, ifstream::binary);
binFile.seekg(0, binFile.end);
pictureDataSize = binFile.tellg();
binFile.seekg(0, binFile.beg);
aclError ret = aclrtMallocHost(&pictureHostData, pictureDataSize);
binFile.read((char*)pictureHostData, pictureDataSize);
binFile.close();
}
//申请Device侧的内存,再以内存复制的方式将内存中的图片数据传输到Device
void CopyDataFromHostToDevice()
{
aclError ret = aclrtMalloc(&pictureDeviceData, pictureDataSize, ACL_MEM_MALLOC_HUGE_FIRST);
ret = aclrtMemcpy(pictureDeviceData, pictureDataSize, pictureHostData, pictureDataSize, ACL_MEMCPY_HOST_TO_DEVICE);
}
void LoadPicture(const char* picturePath)
{
ReadPictureTotHost(picturePath);
CopyDataFromHostToDevice();
}
e.执行推理
数据类型
aclmdlDataset *inputDataSet;
aclDataBuffer *inputDataBuffer;
aclmdlDataset *outputDataSet;
aclDataBuffer *outputDataBuffer;
aclmdlDesc *modelDesc;
size_t outputDataSize = 0;
void *outputDeviceData;
// 准备模型推理的输入数据结构
void CreateModelInput()
{
// 创建aclmdlDataset类型的数据,描述模型推理的输入
inputDataSet = aclmdlCreateDataset();
inputDataBuffer = aclCreateDataBuffer(pictureDeviceData, pictureDataSize);
aclError ret = aclmdlAddDatasetBuffer(inputDataSet, inputDataBuffer);
}
// 准备模型推理的输出数据结构
void CreateModelOutput()
{
// 创建模型描述信息
modelDesc = aclmdlCreateDesc();
aclError ret = aclmdlGetDesc(modelDesc, modelId);
// 创建aclmdlDataset类型的数据,描述模型推理的输出
outputDataSet = aclmdlCreateDataset();
// 获取模型输出数据需占用的内存大小,单位为Byte
outputDataSize = aclmdlGetOutputSizeByIndex(modelDesc, 0);
// 申请输出内存
ret = aclrtMalloc(&outputDeviceData, outputDataSize, ACL_MEM_MALLOC_HUGE_FIRST);
outputDataBuffer = aclCreateDataBuffer(outputDeviceData, outputDataSize);
ret = aclmdlAddDatasetBuffer(outputDataSet, outputDataBuffer);
}
// 执行模型
void Inference()
{
CreateModelInput();
CreateModelOutput();
aclError ret = aclmdlExecute(modelId, inputDataSet, outputDataSet);
}
f.推理结果数据处理并打印
void *outputHostData;
void PrintResult()
{
// 获取推理结果数据
aclError ret = aclrtMallocHost(&outputHostData, outputDataSize);
ret = aclrtMemcpy(outputHostData, outputDataSize, outputDeviceData, outputDataSize, ACL_MEMCPY_DEVICE_TO_HOST);
// 将内存中的数据转换为float类型
float* outFloatData = reinterpret_cast<float *>(outputHostData);
// 屏显测试图片的top5置信度的类别编号
map<float, unsigned int, greater<float>> resultMap;
for (unsigned int j = 0; j < outputDataSize / sizeof(float);++j)
{
resultMap[*outFloatData] = j;
outFloatData++;
}
int cnt = 0;
for (auto it = resultMap.begin();it != resultMap.end();++it)
{
if(++cnt > 5)
{
break;
}
printf("top %d: index[%d] value[%lf] \n", cnt, it->second, it->first);
}
}
g. 卸载模型,并释放模型描述信息。
void UnloadModel()
{
// 释放模型描述信息
aclmdlDestroyDesc(modelDesc);
// 卸载模型
aclmdlUnload(modelId);
}
h. 释放内存、销毁推理相关的数据类型
void UnloadPicture()
{
aclError ret = aclrtFreeHost(pictureHostData);
pictureHostData = nullptr;
ret = aclrtFree(pictureDeviceData);
pictureDeviceData = nullptr;
aclDestroyDataBuffer(inputDataBuffer);
inputDataBuffer = nullptr;
aclmdlDestroyDataset(inputDataSet);
inputDataSet = nullptr;
ret = aclrtFreeHost(outputHostData);
outputHostData = nullptr;
ret = aclrtFree(outputDeviceData);
outputDeviceData = nullptr;
aclDestroyDataBuffer(outputDataBuffer);
outputDataBuffer = nullptr;
aclmdlDestroyDataset(outputDataSet);
outputDataSet = nullptr;
}
i. 资源释放
void DestroyResource()
{
aclError ret = aclrtResetDevice(deviceId);
aclFinalize();
}
4. 编译并运行应用
(1)编译代码
以运行用户登录开发环境,切换到MyFirstApp目录下,执行以下命令。
export APP_SOURCE_PATH=${SAMPLE_DIR}/MyFirstApp
export DDK_PATH=${INSTALL_DIR}
export NPU_HOST_LIB=${INSTALL_DIR}/acllib/lib64/stub
chmod +x sample_build.sh
./sample_build.sh
${SAMPLE_DIR}表示样例所在的目录,
${INSTALL_DIR}表示CANN软件安装目录,
arch表示操作系统架构(需根据运行环境的架构选择),
{os}表示操作系统(需根据运行环境的操作系统选择)。
(2)运行应用
在MyFirstApp目录下,执行以下命令
chmod +x sample_run.sh
./sample_run.sh
5.获取运行日志
(1)日志的默认目录为CANN软件安装目录/asced/log
(2)日志文件介绍
(3)日志的默认输出方式为将日志保存在log文件中,也可以通过以下命令将日志打屏显示。
export ASCEND_SLOG_PRINT_TO_STDOUT=1