【2023 · CANN训练营第一季】- 昇腾AI入门课(TensorFlow)第三章 AI应用开发 学习笔记

举报
Heartt 发表于 2023/05/16 08:03:40 2023/05/16
【摘要】 一、学习目标1.了解昇腾Al全栈架构及该架构中各层的作用。2.了解异构计算架构CANN在昇腾Al全栈中的位置和作用。3.了解应用开发编程框架、基本概念。4.掌握基本的应用开发流程,可以按照指导编译、运行应用。5.了解如何获取及查看应用运行日志,具备基本的问题定界、定位能力。二、遇到问题求助开发、编译或运行应用出错时无法解决问题或有优化建议要反馈,可以借助以下渠道:1.参考CANN Sampl...

一、学习目标

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



【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。