当前位置:   article > 正文

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

【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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187

编译方法

调整后的目录结构

在这里插入图片描述

调整后的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})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

运行结果

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/407212
推荐阅读
相关标签
  

闽ICP备14008679号