昇腾 - AscendCL C++应用开发 推理部分 模型执行

2024-08-23 22:04

本文主要是介绍昇腾 - AscendCL C++应用开发 推理部分 模型执行,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

昇腾 - AscendCL C++应用开发 推理部分 模型执行

flyfish

准备模型执行的输入/输出数据结构

 aclmdlDataset├── aclDataBuffer│   ├── buffer address│   └── buffer size└── aclDataBuffer├── buffer address└── buffer size...

使用aclmdlDataset类型的数据描述模型的输入/输出数据,模型可能存在多个输入、多个输出。

调用aclmdlDataset类型下的操作接口添加aclDataBuffer类型的数据、获取aclDataBuffer的个数等。
每个输入/输出的内存地址、内存大小用aclDataBuffer类型的数据来描述。

调用aclDataBuffer类型下的操作接口获取内存地址、内存大小等。

模型执行的输入/输出数据结构的准备流程

(编辑方法:文字前面加 - ,上下级关系按tab)

  • 创建描述模型输入/输出的数据类型

    • aclmdlCreateDataset
  • 获取模型的输入/输出的个数

    • aclmdlGetNumInputs
    • aclmdlGetNumOutputs
  • 循环:

    • 获取每个输入/输出的内存量

      • aclmdlGetInputSizeByIndex
      • aclmdlGetOutputSizeByIndex
    • 为模型的每个输入/输出申请内存

      • aclrtMalloc
    • 创建描述模型输入/输出内存的数据类型

      • aclCreateDataBuffer
    • 获取输入/输出的名称 (可选)

      • aclmdlGetInputNameByIndex
      • aclmdlGetOutputNameByIndex
    • 向aclmdlDataset中添加

      • aclDataBuffer
      • aclmdlAddDatasetBuffer

ModelProc类的主要流程

1. 初始化与资源管理

当创建一个 ModelProc 对象时,它首先会做一些基础的初始化工作,比如:

  • 初始化变量 :它会设置一些标志,表示模型是否已经加载、资源是否已释放等等。

  • 检查设备模式 :它会检查当前设备是在电脑的内存(HOST模式)上运行,还是在设备的内存(Device模式)上运行,这样在后续的操作中可以知道数据应该放在哪里。

2. 加载模型

Load 方法是用来加载模型的:

  • 检查是否已加载 :它首先会检查模型是否已经加载过,如果已经加载,就不会重复加载。

  • 加载模型文件 :如果模型没有加载过,它会从指定的路径加载模型文件,并保存模型的 ID。

  • 准备模型描述 :加载成功后,它会创建一个模型描述,获取有关模型输入输出的信息,比如模型需要多少输入,以及会产生多少输出。

  • 分配输出内存 :它还会为每个输出分配内存,这样模型运行后就知道把结果存放在哪里。

3. 准备输入数据

在模型运行之前,需要准备好输入数据。ModelProc 提供了几种不同的 CreateInput 方法:

  • 单个输入 :如果的模型只需要一个输入,可以使用一个方法将这个输入数据添加到模型的输入集里。

  • 多个输入 :如果的模型需要多个输入(比如同时处理图片和文本),它有另一种方法可以将多个输入数据添加到模型中。

  • 通用输入 :还有一种方法允许用一个列表传递多个输入数据,这样更加灵活。
    无论哪种方式,数据都会通过 AddDatasetBuffer 方法添加到模型的输入集里,准备进行推理。

4. 执行模型推理

Execute 方法就是用来让模型“干活”的:

  • 运行推理 :它会调用一个函数来执行模型推理,将输入数据进行处理。

  • 获取结果 :推理完成后,它会从模型输出中取出结果,并保存到一个结构体里,供后续使用,比如显示预测结果或进行进一步分析。

5. 资源释放与模型卸载

当用完模型之后,需要释放占用的资源:

  • 卸载模型Unload 方法负责卸载模型,释放所有分配的资源,包括输入和输出的数据集、模型描述等等。

  • 销毁数据集 :它会专门销毁输入输出的数据集,并释放缓冲区的内存,确保没有内存泄漏。
    通过 ModelProc 类,可以轻松地加载模型、准备数据、执行推理并获取结果,而无需担心资源管理的问题。这个类会帮自动处理资源的分配和释放,让更专注于模型的使用本身。

模型执行 代码的封装 实现文件

/**
* File ModelProc.cpp
* Description: handle model process
*/
#include <iostream>
#include "ModelProc.h"
using namespace std;namespace acllite {// ModelProc 构造函数,初始化成员变量。
ModelProc::ModelProc() : loadFlag_(false), isReleased_(false),modelId_(0), modelPath_(""), modelDesc_(nullptr),input_(nullptr), output_(nullptr), outputsNum_(0)
{// 获取当前设备的运行模式(CPU或Device),并将结果存储在runMode_中。aclrtGetRunMode(&runMode_);
}// 析构函数,在对象销毁时调用,确保资源被释放。
ModelProc::~ModelProc()
{DestroyResource();
}// 销毁资源,释放加载的模型和相关数据。
void ModelProc::DestroyResource()
{if (isReleased_) {return;  // 如果资源已经释放过,则直接返回。}Unload();  // 卸载模型。isReleased_ = true;  // 标记资源已释放。
}// 加载模型并初始化相关资源。
bool ModelProc::Load(const string& modelPath)
{modelPath_.assign(modelPath.c_str());  // 保存模型路径。if (loadFlag_) {LOG_PRINT("[ERROR] %s is loaded already", modelPath_.c_str());return false;  // 如果模型已经加载,则返回false。}// 从文件加载模型,加载成功返回模型ID。aclError aclRet = aclmdlLoadFromFile(modelPath_.c_str(), &modelId_);if (aclRet != ACL_SUCCESS) {LOG_PRINT("[ERROR] Load model(%s) from file return %d", modelPath_.c_str(), aclRet);return false;  // 加载失败,返回false。}loadFlag_ = true;  // 标记模型已成功加载。// 创建模型描述结构体,用于描述模型的基本信息。modelDesc_ = aclmdlCreateDesc();if (modelDesc_ == nullptr) {LOG_PRINT("[ERROR] Create model(%s) description failed", modelPath_.c_str());return false;  // 创建失败,返回false。}// 获取模型描述信息。aclRet = aclmdlGetDesc(modelDesc_, modelId_);if (aclRet != ACL_SUCCESS) {LOG_PRINT("[ERROR] Get model(%s) description failed", modelPath_.c_str());return false;  // 获取失败,返回false。}if (modelDesc_ == nullptr) {LOG_PRINT("[ERROR] Create output failed for no model(%s) description", modelPath_.c_str());return false;  // 如果模型描述为空,返回false。}// 创建用于存放模型输出数据的结构体。output_ = aclmdlCreateDataset();if (output_ == nullptr) {LOG_PRINT("[ERROR] Create output failed for create dataset error");return false;  // 创建失败,返回false。}// 获取模型输出的数量。outputsNum_ = aclmdlGetNumOutputs(modelDesc_);for (size_t i = 0; i < outputsNum_; ++i) {// 获取模型第i个输出的内存大小。size_t bufSize = aclmdlGetOutputSizeByIndex(modelDesc_, i);void *outputBuffer = nullptr;// 在Device上分配内存,用于存放模型输出数据。aclRet = aclrtMalloc(&outputBuffer, bufSize, ACL_MEM_MALLOC_NORMAL_ONLY);if (aclRet != ACL_SUCCESS) {LOG_PRINT("[ERROR] Create output failed for malloc device failed, size %d", (int)bufSize);return false;  // 内存分配失败,返回false。}// 创建aclDataBuffer,用于描述输出数据的内存信息。aclDataBuffer* dataBuf = aclCreateDataBuffer(outputBuffer, bufSize);if (dataBuf == nullptr) {LOG_PRINT("[ERROR] Create data buffer error");return false;  // 创建失败,返回false。}// 将数据缓冲区添加到输出数据集中。aclRet = aclmdlAddDatasetBuffer(output_, dataBuf);if (aclRet != ACL_SUCCESS) {LOG_PRINT("[ERROR] Add dataset buffer error %d", aclRet);aclRet = aclDestroyDataBuffer(dataBuf);if (aclRet != ACL_SUCCESS) {LOG_PRINT("[ERROR] Destroy dataset buffer error %d", aclRet);}dataBuf = nullptr;return false;  // 添加失败,返回false。}// 保存输出缓冲区信息,用于后续处理。outputBuf_.push_back(outputBuffer);outputSize_.push_back(bufSize);outputDataBuf_.push_back(dataBuf);}LOG_PRINT("[INFO] Load model %s success", modelPath_.c_str());return true;  // 加载成功,返回true。
}// 创建输入数据,单输入版本。
bool ModelProc::CreateInput(void *input, uint32_t size)
{vector<DataInfo> inputData = {{input, size}};return CreateInput(inputData);  // 调用多输入版本的CreateInput。
}// 创建输入数据,多输入版本。
bool ModelProc::CreateInput(void *input1, uint32_t input1size,void* input2, uint32_t input2size)
{vector<DataInfo> inputData = {{input1, input1size}, {input2, input2size}};return CreateInput(inputData);  // 调用多输入版本的CreateInput。
}// 创建输入数据,通用版本,支持多个输入。
bool ModelProc::CreateInput(vector<DataInfo>& inputData)
{// 获取模型的输入数量。uint32_t dataNum = aclmdlGetNumInputs(modelDesc_);if (dataNum == 0) {LOG_PRINT("[ERROR] Create input failed for no input data");return false;  // 如果模型没有输入,返回false。}if (dataNum != inputData.size()) {LOG_PRINT("[ERROR] Create input failed for wrong input nums");return false;  // 输入数量不匹配,返回false。}// 创建输入数据集。input_ = aclmdlCreateDataset();if (input_ == nullptr) {LOG_PRINT("[ERROR] Create input failed for create dataset failed");return false;  // 创建失败,返回false。}// 将输入数据缓冲区添加到数据集中。for (uint32_t i = 0; i < inputData.size(); i++) {size_t modelInputSize = aclmdlGetInputSizeByIndex(modelDesc_, i);if (modelInputSize != inputData[i].size) {LOG_PRINT("[WARNING] Input size verify failed input[%d] size: %ld, provide size : %d", i, modelInputSize, inputData[i].size);}bool ret = AddDatasetBuffer(input_, inputData[i].data, inputData[i].size);if (!ret) {LOG_PRINT("[ERROR] Create input failed for add dataset buffer error %d", ret);return false;  // 添加失败,返回false。}}return true;  // 创建输入成功,返回true。
}// 添加数据缓冲区到数据集中。
bool ModelProc::AddDatasetBuffer(aclmdlDataset *dataset, void* buffer, uint32_t bufferSize)
{// 创建aclDataBuffer,用于描述缓冲区。aclDataBuffer* dataBuf = aclCreateDataBuffer(buffer, bufferSize);if (dataBuf == nullptr) {LOG_PRINT("[ERROR] Create data buffer error");return false;  // 创建失败,返回false。}// 将数据缓冲区添加到数据集中。aclError ret = aclmdlAddDatasetBuffer(dataset, dataBuf);if (ret != ACL_SUCCESS) {LOG_PRINT("[ERROR] Add dataset buffer error %d", ret);ret = aclDestroyDataBuffer(dataBuf);if (ret != ACL_SUCCESS) {LOG_PRINT("[ERROR] Destroy dataset buffer error %d", ret);}dataBuf = nullptr;return false;  // 添加失败,返回false。}return true;  // 添加成功,返回true。
}// 执行模型推理,并获取推理结果。
bool ModelProc::Execute(vector<InferenceOutput>& inferOutputs)
{// 执行模型推理。aclError aclRet = aclmdlExecute(modelId_, input_, output_);if (aclRet != ACL_SUCCESS) {LOG_PRINT("[ERROR] Execute model(%s) error:%d", modelPath_.c_str(), aclRet);return false;  // 执行失败,返回false。}// 获取推理结果并保存到inferOutputs中。for (uint32_t i = 0; i < outputsNum_; i++) {InferenceOutput out;bool ret = GetOutputItem(out, i);if (!ret) {LOG_PRINT("[ERROR] Get the %dth interference output failed, error: %d", i, ret);return ret;  // 获取输出失败,返回false。}inferOutputs.push_back(out);}return true;  // 执行成功,返回true。
}// 获取单个输出项。
bool ModelProc::GetOutputItem(InferenceOutput& out, uint32_t idx)
{// 获取第idx个输出缓冲区的aclDataBuffer。aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(output_, idx);if (dataBuffer == nullptr) {LOG_PRINT("[ERROR] Get model output buffer failed");return false;  // 获取失败,返回false。}// 获取缓冲区内存指针。void* data = aclGetDataBufferAddr(dataBuffer);if (data == nullptr) {LOG_PRINT("[ERROR] Get model output data buffer address failed");return false;  // 获取失败,返回false。}// 获取缓冲区大小。uint32_t len = aclGetDataBufferSize(dataBuffer);out.data = data;  // 将输出数据指针保存到out中。out.size = len;  // 将输出数据大小保存到out中。return true;  // 获取成功,返回true。
}// 卸载模型并释放相关资源。
void ModelProc::Unload()
{if (!loadFlag_) {return;  // 如果模型未加载,则直接返回。}// 销毁模型输入数据集。if (input_ != nullptr) {int ret = DestroyDataset(input_);if (ret != 0) {LOG_PRINT("[ERROR] Unload model(%s) error for destroy input dataset failed", modelPath_.c_str());}}// 销毁模型输出数据集。if (output_ != nullptr) {int ret = DestroyDataset(output_);if (ret != 0) {LOG_PRINT("[ERROR] Unload model(%s) error for destroy output dataset failed", modelPath_.c_str());}}// 销毁模型描述结构体。if (modelDesc_ != nullptr) {aclError ret = aclmdlDestroyDesc(modelDesc_);if (ret != ACL_SUCCESS) {LOG_PRINT("[ERROR] Unload model(%s) error for destroy modelDesc failed, modelDesc_:%p, ret:%d", modelPath_.c_str(), modelDesc_, ret);}modelDesc_ = nullptr;}// 卸载模型。aclError ret = aclmdlUnload(modelId_);if (ret != ACL_SUCCESS) {LOG_PRINT("[ERROR] Unload model(%s) error for unload failed, modelId_:%u, ret:%d", modelPath_.c_str(), modelId_, ret);}modelId_ = 0;// 清除模型加载标记。loadFlag_ = false;
}// 销毁数据集。
int ModelProc::DestroyDataset(aclmdlDataset *dataset)
{aclError aclRet;// 遍历数据集中的每个缓冲区,并释放对应的内存。for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(dataset); ++i) {aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(dataset, i);if (dataBuffer == nullptr) {LOG_PRINT("[ERROR] Get data buffer error");continue;}void* data = aclGetDataBufferAddr(dataBuffer);if (data == nullptr) {LOG_PRINT("[ERROR] Get data buffer address error");continue;}// 释放Device上的内存。aclRet = aclrtFree(data);if (aclRet != ACL_SUCCESS) {LOG_PRINT("[ERROR] Free device memory failed");}// 销毁数据缓冲区。aclRet = aclDestroyDataBuffer(dataBuffer);if (aclRet != ACL_SUCCESS) {LOG_PRINT("[ERROR] Destroy data buffer failed");}}// 销毁数据集。aclRet = aclmdlDestroyDataset(dataset);if (aclRet != ACL_SUCCESS) {LOG_PRINT("[ERROR] Destroy dataset failed");return -1;  // 销毁失败,返回错误码。}return 0;  // 销毁成功,返回0。
}} // namespace acllite

模型执行 代码的封装 头文件

/**
* File ModelProc.h
* Description: 处理模型推理过程的头文件
*/#ifndef MODEL_PROC_H
#define MODEL_PROC_H#pragma once#include <iostream>
#include <memory>
#include <vector>
#include "acl/acl.h"
#include "acllite_common/Common.h"namespace acllite {/**
* @class ModelProc
* @brief 用于加载模型、创建输入输出、执行推理并释放资源的类
*/
class ModelProc {
public:/*** @brief 构造函数,初始化类成员变量*/ModelProc();/*** @brief 析构函数,释放资源*/~ModelProc();/*** @brief 销毁模型处理过程中的资源*/void DestroyResource();/*** @brief 加载指定路径下的模型* @param modelPath 模型文件的路径* @return 成功返回true,失败返回false*/bool Load(const std::string& modelPath);/*** @brief 创建单一输入的数据集* @param input 输入数据的指针* @param size 输入数据的大小* @return 成功返回true,失败返回false*/bool CreateInput(void* input, uint32_t size);/*** @brief 创建包含两个输入的数据集* @param input1 第一个输入数据的指针* @param input1size 第一个输入数据的大小* @param input2 第二个输入数据的指针* @param input2size 第二个输入数据的大小* @return 成功返回true,失败返回false*/bool CreateInput(void* input1, uint32_t input1size,void* input2, uint32_t input2size);/*** @brief 从多个输入数据创建输入数据集* @param inputData 包含多个输入数据的向量* @return 成功返回true,失败返回false*/bool CreateInput(std::vector<DataInfo>& inputData);/*** @brief 执行模型推理* @param inferOutputs 用于存储推理输出的向量* @return 成功返回true,失败返回false*/bool Execute(std::vector<InferenceOutput>& inferOutputs);/*** @brief 销毁输入数据集*/void DestroyInput();/*** @brief 销毁输出数据集*/void DestroyOutput();/*** @brief 卸载模型,释放相关资源*/void Unload();private:/*** @brief 向数据集添加缓冲区* @param dataset 数据集指针* @param buffer 缓冲区指针* @param bufferSize 缓冲区大小* @return 成功返回true,失败返回false*/bool AddDatasetBuffer(aclmdlDataset* dataset,void* buffer, uint32_t bufferSize);/*** @brief 获取推理输出项* @param out 存储输出项的结构* @param idx 输出项的索引* @return 成功返回true,失败返回false*/bool GetOutputItem(InferenceOutput& out, uint32_t idx);private:aclrtRunMode runMode_;                   ///< 运行模式(HOST或Device模式)bool loadFlag_;                          ///< 模型是否已加载的标志bool isReleased_;                        ///< 资源是否已释放的标志uint32_t modelId_;                       ///< 模型IDstd::string modelPath_;                  ///< 模型文件路径aclmdlDesc *modelDesc_;                  ///< 模型描述符指针aclmdlDataset *input_;                   ///< 输入数据集指针aclmdlDataset *output_;                  ///< 输出数据集指针size_t outputsNum_;                      ///< 输出数据项的数量std::vector<void*> outputBuf_;           ///< 输出缓冲区指针向量std::vector<size_t> outputSize_;         ///< 输出数据大小向量std::vector<aclDataBuffer*> outputDataBuf_; ///< 输出数据缓冲区指针向量
};}  // namespace acllite#endif  // MODEL_PROC_H

这篇关于昇腾 - AscendCL C++应用开发 推理部分 模型执行的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1100614

相关文章

C++ RabbitMq消息队列组件详解

《C++RabbitMq消息队列组件详解》:本文主要介绍C++RabbitMq消息队列组件的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. RabbitMq介绍2. 安装RabbitMQ3. 安装 RabbitMQ 的 C++客户端库4. A

SpringBoot整合Sa-Token实现RBAC权限模型的过程解析

《SpringBoot整合Sa-Token实现RBAC权限模型的过程解析》:本文主要介绍SpringBoot整合Sa-Token实现RBAC权限模型的过程解析,本文给大家介绍的非常详细,对大家的学... 目录前言一、基础概念1.1 RBAC模型核心概念1.2 Sa-Token核心功能1.3 环境准备二、表结

python web 开发之Flask中间件与请求处理钩子的最佳实践

《pythonweb开发之Flask中间件与请求处理钩子的最佳实践》Flask作为轻量级Web框架,提供了灵活的请求处理机制,中间件和请求钩子允许开发者在请求处理的不同阶段插入自定义逻辑,实现诸如... 目录Flask中间件与请求处理钩子完全指南1. 引言2. 请求处理生命周期概述3. 请求钩子详解3.1

如何基于Python开发一个微信自动化工具

《如何基于Python开发一个微信自动化工具》在当今数字化办公场景中,自动化工具已成为提升工作效率的利器,本文将深入剖析一个基于Python的微信自动化工具开发全过程,有需要的小伙伴可以了解下... 目录概述功能全景1. 核心功能模块2. 特色功能效果展示1. 主界面概览2. 定时任务配置3. 操作日志演示

Python Flask 库及应用场景

《PythonFlask库及应用场景》Flask是Python生态中​轻量级且高度灵活的Web开发框架,基于WerkzeugWSGI工具库和Jinja2模板引擎构建,下面给大家介绍PythonFl... 目录一、Flask 库简介二、核心组件与架构三、常用函数与核心操作 ​1. 基础应用搭建​2. 路由与参

Spring Boot中的YML配置列表及应用小结

《SpringBoot中的YML配置列表及应用小结》在SpringBoot中使用YAML进行列表的配置不仅简洁明了,还能提高代码的可读性和可维护性,:本文主要介绍SpringBoot中的YML配... 目录YAML列表的基础语法在Spring Boot中的应用从YAML读取列表列表中的复杂对象其他注意事项总

JavaScript实战:智能密码生成器开发指南

本文通过JavaScript实战开发智能密码生成器,详解如何运用crypto.getRandomValues实现加密级随机密码生成,包含多字符组合、安全强度可视化、易混淆字符排除等企业级功能。学习密码强度检测算法与信息熵计算原理,获取可直接嵌入项目的完整代码,提升Web应用的安全开发能力 目录

C++ HTTP框架推荐(特点及优势)

《C++HTTP框架推荐(特点及优势)》:本文主要介绍C++HTTP框架推荐的相关资料,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. Crow2. Drogon3. Pistache4. cpp-httplib5. Beast (Boos

电脑系统Hosts文件原理和应用分享

《电脑系统Hosts文件原理和应用分享》Hosts是一个没有扩展名的系统文件,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应... Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应

CSS 样式表的四种应用方式及css注释的应用小结

《CSS样式表的四种应用方式及css注释的应用小结》:本文主要介绍了CSS样式表的四种应用方式及css注释的应用小结,本文通过实例代码给大家介绍的非常详细,详细内容请阅读本文,希望能对你有所帮助... 一、外部 css(推荐方式)定义:将 CSS 代码保存为独立的 .css 文件,通过 <link> 标签