【2024第一期CANN训练营】1、AscendCL应用开发快速入门

【2024第一期CANN训练营】1、AscendCL应用开发快速入门

本教程将介绍如何使用AscendCL接口(C语言接口)开发一个简单的ResNet-50图片分类应用。并通过昇腾AI处理器和CANN平台来完成这个任务。

前提条件

已在环境上部署昇腾AI软件栈。

安装环境,请参见准备开发和运行环境

1. 创建代码目录

首先,下载并解压样例包MyFirstApp_ONNX.zip,样例包中包含以下文件和目录结构:

MyFirstApp_ONNX
├── data
│   ├── dog1_1024_683.jpg            // 测试图片
├── model                            // 用于存放ResNet-50模型文件
├── script
│   ├── transferPic.py               // 预处理脚本
├── src
│   ├── CMakeLists.txt              // cmake编译脚本
│   ├── main.cpp                    // 主函数实现文件
├── sample_build.sh                  // 编译脚本
├── sample_run.sh                    // 运行脚本

2. 准备模型

使用ATC工具将ONNX模型转换为昇腾AI处理器可识别的.om格式,这里以resnet50.onnx模型为例:

各参数的解释如下,详细约束说明请参见《ATC工具使用指南》。

  • –model:ResNet-50网络的模型文件的路径。
  • –framework:原始框架类型。5表示ONNX。
  • –output:resnet50.om模型文件的路径。请注意,记录保存该om模型文件的路径,后续开发应用时需要使用。
  • –input_shape:模型输入数据的shape。
  • –soc_version:昇腾AI处理器的版本,执行npu-smi info命令进行查询,在查询到的Name前增加Ascend,如Ascend310
# 下载resnet50.onnx
wget https://cann-info.obs.cn-east-2.myhuaweicloud.com/samples/MyfirstAPP/MyFirstApp_ONNX.zip
unzip MyFirstApp_ONNX.zip
cd MyFirstApp_ONNX/model
wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/003_Atc_Models/resnet50/resnet50.onnx

# 使用ATC工具将ONNX模型转换为昇腾AI处理器可识别的.om格式
# 看到ATC run success, welcome to the next use.则转换成功
atc --model=resnet50.onnx --framework=5 --output=resnet50 --input_shape="actual_input_1:1,3,224,224"  --soc_version=Ascend310

image-20240317130103950

3. 开发应用

MyFirstApp_ONNX/src/main.cpp文件中,添加以下代码:

#include "acl/acl.h"
#include <iostream>
#include <fstream>
#include <cstring>
#include <map>
#include <cmath>

using namespace std;
const char *modelPath = "../model/resnet50.om";
const char *picturePath = "../data/dog1_1024_683.bin";

// 主函数
int main()
{
    // 初始化资源并加载模型
    InitResource();
    LoadModel(modelPath);
    
    // 加载图片
    LoadPicture(picturePath);
    
    // 执行推理
    Inference();
    
    // 打印结果
    PrintResult();
    
    // 卸载模型并释放资源
    UnloadModel();
    DestroyResource();
    
    return 0;
}

接下来,实现每个函数的具体操作。

3.1 初始化资源和加载模型

  • 调用aclInit接口初始化AscendCL
  • 调用aclrtSetDevice接口指定计算设备
  • 完成了AscendCL的所有调用之后,或者进程退出之前,需执行资源去初始化
// 资源初始化
int32_t deviceId = 0;
void InitResource()
{
    aclError ret = aclInit(nullptr);
    ret = aclrtSetDevice(deviceId);
}

// 模型加载
uint32_t modelId;
void LoadModel(const char* modelPath)
{
    aclError ret = aclmdlLoadFromFile(modelPath, &modelId);
}

3.2 加载图片

size_t pictureDataSize = 0;
void *pictureHostData;
void *pictureDeviceData;

void ReadPictureTotHost(const char *picturePath);
void CopyDataFromHostToDevice();
    
// 图片加载到Host内存并传输到Device
void LoadPicture(const char* picturePath)
{
    ReadPictureTotHost(picturePath);		// 读取图片到Host内存
    CopyDataFromHostToDevice();				// 传输到Device
}

//申请内存,使用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);
}

3.3 执行推理

  • 使用aclmdlDesc类型的数据描述模型基本信息
  • 使用aclDataBuffer类型的数据来描述每个输入/输出的内存地址、内存大小。
  • 使用aclmdlDataset类型的数据描述模型的输入/输出数据。
  • aclmdlDatasetaclDataBuffer的关系类似于列表与元素的集合关系:aclmdlDataset[aclDataBuffer1aclDataBuffer2,…]
aclmdlDataset *inputDataSet;
aclDataBuffer *inputDataBuffer;
aclmdlDataset *outputDataSet;
aclDataBuffer *outputDataBuffer;
aclmdlDesc *modelDesc;
size_t outputDataSize = 0;
void *outputDeviceData;

void CreateModelInput();
void CreateModelOutput();

// 执行推理
void Inference()
{
    // 创建输入输出数据结构
    CreateModelInput();
    CreateModelOutput();
    
    // 执行模型推理
    aclError ret = aclmdlExecute(modelId, inputDataSet, outputDataSet);
}

// 准备模型推理的输入数据结构
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);
}

3.4 打印结果

  • 在屏幕上显示图片的top5置信度的类别编号。
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);
        map<float, unsigned int, greater<float>> resultMap;
        for (unsigned int j = 0; j < outputDataSize / sizeof(float);++j)
        {
                resultMap[*outFloatData] = j;
                outFloatData++;
        }

        // 使用softmax进行数据处理并打印前5类
        double totalValue=0.0;
        for (auto it = resultMap.begin(); it != resultMap.end(); ++it) {
            totalValue += exp(it->first);
        }

        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, exp(it->first) /totalValue);
        }
}

3.5 卸载模型并释放资源

// 卸载模型
void UnloadModel()
{
    // 释放模型描述信息
	aclmdlDestroyDesc(modelDesc);
    // 卸载模型
	aclmdlUnload(modelId);
}

// 销毁推理数据类型
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;
}

// 资源释放
void DestroyResource()
{
	aclError ret = aclrtResetDevice(deviceId);
	aclFinalize();
}

4. 编译及运行应用

编译代码:

  • <SAMPLE_DIR>表示样例所在的目录
  • $HOME/Ascend/ascend-toolkit表示CANN软件包的安装路径,
  • <arch-os>表示操作系统架构(Arch64或者X86_64)
  • 如果执行脚本报错“ModuleNotFoundError: No module named ‘PIL’”,是由于开发环境中缺少Pillow库,需使用pip3 install Pillow --user命令安装Pillow库后,再执行脚本。
  • 如果执行脚本报错“bash: ./sample_build.sh: /bin/bash^M: bad interpreter: No such file or directory”,是由于开发环境中没有安装dos2unix包,需使用sudo apt-get install dos2unix命令安装dos2unix包后,执行dos2unix sample_build.sh,然后再执行脚本。

修改sample_build.sh内的内容如下

model_name="MyFirstApp_build"

cd ${APP_SOURCE_PATH}/data

python3 ${APP_SOURCE_PATH}/script/transferPic.py

if [ -d ${APP_SOURCE_PATH}/build/intermediates/host ];then
	rm -rf ${APP_SOURCE_PATH}/build/intermediates/host
fi

mkdir -p ${APP_SOURCE_PATH}/build/intermediates/host
cd ${APP_SOURCE_PATH}/build/intermediates/host

cmake ${APP_SOURCE_PATH}/src -DCMAKE_CXX_COMPILER=g++ -DCMAKE_SKIP_RPATH=TRUE

make

if [ $? -eq 0 ];then
	echo "make for app ${model_name} Successfully"
	exit 0
else
	echo "make for app ${model_name} failed"
	exit 1
fi
export APP_SOURCE_PATH=<SAMPLE_DIR>/MyFirstApp_ONNX
export DDK_PATH=$HOME/Ascend/ascend-toolkit/latest
export NPU_HOST_LIB=$HOME/Ascend/ascend-toolkit/latest/<arch-os>/devlib

# 在MyFirstApp_ONNX文件夹下执行
pip3 install Pillow
chmod +x sample_build.sh
./sample_build.sh

运行应用:

# 运行应用后,终端将显示测试图片的Top 5类别编号及其置信度
chmod +x sample_run.sh
./sample_run.sh

image-20240317150832944

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绿洲213

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值