【Atlas200】使用华为ACL库实现ResNet 推理(C++版本)

ACL简介

华为ACL(Ascend Compute Library)是一款基于华为昇腾芯片的AI推理加速库,提供了一系列高性能、高效能的计算接口,支持多种常见的深度学习模型的推理加速。ACL具有高度的灵活性和可扩展性,可以轻松地与各种服务器、云平台和数据中心集成,满足不同规模、不同应用场景下的需求。ACL提供了丰富的接口,包括数据输入输出、模型加载和推理计算等核心功能,同时还支持多种精度的计算方式,包括FP32、FP16和INT8等,可以满足不同场景下的需求。此外,ACL还提供了多种优化技术,包括算子融合、tensor核心优化、算子量化和内存复用等,可进一步提高推理性能和能效比。总之,华为ACL是一款强大、灵活、高效的AI推理加速库,为用户提供了全面的AI计算加速方案.

ResNet简介

ResNet(Residual Network)是一种深度卷积神经网络,由何凯明等人在2015年提出,是当年ImageNet图像识别比赛的冠军网络。ResNet通过引入残差块(Residual Block)来解决深度神经网络训练过程中的梯度消失和梯度爆炸问题。残差块的核心思想是将前一层的特征图直接传递到后一层中,避免了信息的损失,同时通过残差的方式将新的特征图与原有的特征图相加,从而实现了更深层次的网络训练。ResNet网络结构比较简单,主要由多个残差块和全局池化层组成,其中通过步长为2的卷积和降采样操作可以逐层减小特征图大小。ResNet在图像分类、物体检测、人脸识别等领域均取得了优秀的表现,并被广泛应用于实际场景中。

程序运行效果

在这里插入图片描述

代码注释版

#include "acl/acl.h"
#include <iostream>
#include <fstream>
#include <cstring>
#include <map>
using namespace std;
size_t pictureDataSize = 0;
void *pictureHostData;
void *pictureDeviceData;
//申请内存,使用C/C++标准库的函数将测试图片读入内存
void ReadPictureTotHost(const char *picturePath)
{
    
    
// 将picturePath赋值给fileName
string fileName = picturePath;
// 以二进制模式打开fileName指定的文件,并存储在binFile对象中
ifstream binFile(fileName, ifstream::binary);
// 将文件指针移动到文件结尾处
//参数是偏移量off和偏移起始位置way。off表示要移动的字节数,可以是正数或负数;way表示偏移的起始位置
binFile.seekg(0, binFile.end);
// 获取文件大小
pictureDataSize = binFile.tellg();
// 将文件指针移动到文件开头处
binFile.seekg(0, binFile.beg);
// 使用aclrtMallocHost函数在主机内存中分配pictureDataSize字节的空间,并将指针赋值给pictureHostData
aclError ret = aclrtMallocHost(&pictureHostData, pictureDataSize);
// 读取binFile中pictureDataSize字节的数据,并存储到pictureHostData指向的内存空间中
binFile.read((char*)pictureHostData, pictureDataSize);
// 关闭binFile文件流
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();
}

int32_t deviceId = 0;
void InitResource()
{
    
    
//指定当前进程或线程中用于运算的Device,同时隐式创建默认Context。同步接口。
aclError ret = aclInit(nullptr);
ret = aclrtSetDevice(deviceId);
}
uint32_t modelId;
void LoadModel(const char* modelPath)
{
    
    
//模型ID的指针。
//系统成功加载模型后会返回的模型ID。
aclError ret = aclmdlLoadFromFile(modelPath, &modelId);
}
void DestroyResource()
{
    
    
//复位当前运算的Device,释放Device上的资源,包括默认Context、默认Stream以及默认Context下创建的所有Stream,同步接口。若默认Context或默认Stream下的任务还未完成,系统会等待任务完成后再释放。
aclError ret = aclrtResetDevice(deviceId);
aclFinalize();
}
aclmdlDataset *inputDataSet;
aclDataBuffer *inputDataBuffer;
aclmdlDataset *outputDataSet;
aclDataBuffer *outputDataBuffer;
aclmdlDesc *modelDesc;
size_t outputDataSize = 0;
void *outputDeviceData;
// 准备模型推理的输入数据结构
void CreateModelInput()
{
    
    
// 创建aclmdlDataset类型的数据,描述模型推理的输入
//使用aclmdlDesc类型的数据描述模型基本信息(例如输入/输出的个数、名称、数
//据类型、Format、维度信息等)。
//模型加载成功后,用户可根据模型的ID,调用该数据类型下的操作接口获取该模
//型的描述信息,进而从模型的描述信息中获取模型输入/输出的个数、内存大小、
//维度信息、Format、数据类型等信息。
//● 使用aclDataBuffer类型的数据来描述每个输入/输出的内存地址、内存大小。
//调用aclDataBuffer类型下的操作接口获取内存地址、内存大小等,便于向内存中存放输入数据、获取输出数据。
//● 使用aclmdlDataset类型的数据描述模型的输入/输出数据。
//模型可能存在多个输入、多个输出,调用aclmdlDataset类型的操作接口添加多个aclDataBuffer类型的数据。
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);
}
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);
}

}
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;
}
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();
}

编译方法

调整后的目录结构

在这里插入图片描述

调整后的cmakelist

# Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved.

# CMake lowest version requirement
cmake_minimum_required(VERSION 3.5.1)

# project information
project(ACL_RESNET50)

# Compile options
add_compile_options(-std=c++11)

#设置环境变量
set(NPU_HOST_LIB  "/home/HwHiAiUser/Ascend/ascend-toolkit/latest/aarch64-linux/devlib/")
set(DDK_PATH "/home/HwHiAiUser/Ascend/ascend-toolkit/latest/")

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY  "../../out")
set(CMAKE_CXX_FLAGS_DEBUG "-fPIC -O0 -g -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "-fPIC -O2 -Wall")

set(INC_PATH ${
    
    DDK_PATH})

set(LIB_PATH ${
    
    NPU_HOST_LIB})


# Header path
include_directories(
    ${
    
    INC_PATH}/acllib/include/

)

if(target STREQUAL "Simulator_Function")
    add_compile_options(-DFUNC_SIM)
endif()

# add host lib path
link_directories(
    ${
    
    LIB_PATH}
)

file(GLOB_RECURSE cpp_srcs ${
    
    PROJECT_SOURCE_DIR}/src/*.cpp)
add_executable(main ${cpp_srcs})

if (target STREQUAL "Simulator_Function")
    target_link_libraries(main funcsim)
else ()
    if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Windows")
        target_link_libraries(main
            libascendcl)
    else ()
        target_link_libraries(main
            ascendcl stdc++)
    endif ()
endif ()

install(TARGETS main DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

运行结果

省去图片及om模型转换的过程
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/hh1357102/article/details/130750267