下载
中文
注册
我要评分
文档获取效率
文档正确性
内容完整性
文档易理解
在线提单
论坛求助
昇腾小AI

模型执行

本节结合接口调用流程、示例代码介绍模型执行前需要准备哪些数据、模型执行接口以及模型执行之后需要释放哪些资源。

基本原理

开发应用时,如果涉及整网模型推理,则应用程序中必须包含模型执行的代码逻辑,关于模型执行的接口调用流程,请先参见AscendCL接口调用流程了解整体流程,再查看本节中的流程说明。本节描述的是整网模型执行的接口调用流程,对于算子模型加载与执行的详细说明请参见单算子调用

  • 在模型加载之后,模型执行之前,需要准备输入、输出数据结构,将输入数据传输到模型输入数据结构的对应内存中。
  • 模型执行结束后,若无需使用输入数据、aclmdlDesc类型、aclmdlDataset类型、aclDataBuffer类型等相关资源,需及时释放内存、销毁对应的数据类型,防止内存异常。模型可能存在多个输入、多个输出,每个输入/输出的内存地址、内存大小用aclDataBuffer类型的数据来描述,针对每个输入/输出,需调用aclDestroyDataBuffer接口销毁相应的aclDataBuffer类型,并调用aclrtFree接口释放内存中的数据。

模型执行流程

图1 基本的模型推理流程

关键接口的说明如下:

  1. 调用aclmdlCreateDesc接口创建描述模型基本信息的数据类型。
  2. 调用aclmdlGetDesc接口根据模型加载中返回的模型ID获取模型基本信息
  3. 准备模型执行的输入、输出数据结构,具体流程,请参见准备模型执行的输入/输出数据结构

    如果模型的输入涉及动态Batch动态分辨率动态AIPP动态维度(ND格式)等特性,请参见模型动态Shape输入推理模型动态AIPP推理

  4. 执行模型推理

    对于固定的多Batch场景,需要满足batch size后,才能将输入数据发送给模型进行推理。不满足batch size时,用户需根据自己的实际场景处理。

    当前系统支持模型的同步推理和异步推理:

  5. 获取模型推理的结果,用于后续处理。
    • 对于同步推理,直接获取模型推理的输出数据即可。
    • 对于异步推理,在实现Callback功能时,在回调函数内获取模型推理的结果,供后续使用。
  6. 释放内存

    调用aclrtFree接口释放Device上的内存。

  7. 释放相关数据类型的数据

    在模型推理结束后,需依次调用aclDestroyDataBuffer接口、aclmdlDestroyDataset接口及时释放描述模型输入、输出数据类型的数据。如果存在多个输入、输出,需调用多次aclDestroyDataBuffer接口。

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

AscendCL提供了以下数据类型来描述模型、描述其输入输出以及存放数据的内存,在模型执行前,需要构造好这些数据类型,作为模型执行的输入:

  • 使用aclmdlDesc类型的数据描述模型基本信息(例如输入/输出的个数、名称、数据类型、Format、维度信息等)。

    模型加载成功后,用户可根据模型的ID,调用aclmdlGetDesc接口获取该模型的描述信息,进而从模型的描述信息中获取模型输入/输出的个数、内存大小、维度信息、Format、数据类型等信息,可参见aclmdlDesc类型下的操作接口。

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

    调用aclmdlDataset类型下的操作接口添加aclDataBuffer类型的数据、获取aclDataBuffer的个数等。

  • 每个输入/输出的内存地址、内存大小用aclDataBuffer类型的数据来描述。

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

    图2 aclmdlDataset类型与aclDataBuffer类型的关系

了解相关的数据类型后,可以使用这些数据类型的操作接口准备模型的输入、输出数据结构,如下图所示。

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

关键说明如下:

示例代码

此处的示例代码是处理图片分类模型的输出结果,屏显每张图片的top5置信度的类别编号。用户可根据实际需求,自行实现模型推理输出数据的处理逻辑。

您可以从基于ResNet-50网络实现图片分类(同步推理)中获取完整样例代码。

调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。

  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
// 1 根据模型的ID,获取该模型的描述信息。
// modelDesc_为aclmdlDesc类型。
modelDesc_ = aclmdlCreateDesc();
ret = aclmdlGetDesc(modelDesc_, modelId_);

// 2 准备模型推理的输入数据结构
// (1)申请输入内存
size_t modelInputSize;
void *modelInputBuffer = nullptr;
// 当前示例代码中的模型只有一个输入,所以index为0,如果模型有多个输入,则需要先调用aclmdlGetNumInputs接口获取模型输入的数量
modelInputSize = aclmdlGetInputSizeByIndex(modelDesc_, 0);
aclRet = aclrtMalloc(&modelInputBuffer, modelInputSize, ACL_MEM_MALLOC_HUGE_FIRST);

// (2)准备模型的输入数据结构
// 创建aclmdlDataset类型的数据,描述模型推理的输入,input_为aclmdlDataset类型
input_ = aclmdlCreateDataset();
aclDataBuffer *inputData = aclCreateDataBuffer(modelInputBuffer, modelInputSize);
ret = aclmdlAddDatasetBuffer(input_, inputData);

// 3 准备模型推理的输出数据结构
// (1)创建aclmdlDataset类型的数据,描述模型推理的输出,output_为aclmdlDataset类型
output_ = aclmdlCreateDataset();

// (2)获取模型的输出个数.
size_t outputSize = aclmdlGetNumOutputs(modelDesc_);

// (3)循环为每个输出申请内存,并将每个输出添加到aclmdlDataset类型的数据中.
for (size_t i = 0; i < outputSize; ++i) {
    size_t buffer_size = aclmdlGetOutputSizeByIndex(modelDesc_, i);
    void *outputBuffer = nullptr;
    aclError ret = aclrtMalloc(&outputBuffer, buffer_size, ACL_MEM_MALLOC_HUGE_FIRST);
    aclDataBuffer* outputData = aclCreateDataBuffer(outputBuffer, buffer_size);   
    ret = aclmdlAddDatasetBuffer(output_, outputData);
    }

// 4 模型执行
string testFile[] = {
        "../data/dog1_1024_683.bin",
        "../data/dog2_1024_683.bin"
    };

for (size_t index = 0; index < sizeof(testFile) / sizeof(testFile[0]); ++index) {
    // 4.1 自定义函数ReadBinFile,调用C++标准库std::ifstream中的函数读取图片文件,输出图片文件占用的内存大小inputBuffSize以及图片文件存放在内存中的地址inputBuff
    void *inputBuff = nullptr;
    uint32_t inputBuffSize = 0;
    auto ret = Utils::ReadBinFile(fileName, inputBuff, inputBuffSize);
    
    // 4.2 准备模型推理的输入数据
    // 在申请运行管理资源时调用aclrtGetRunMode接口获取软件栈的运行模式
    // 如果运行模式为ACL_DEVICE,则g_isDevice参数值为true,表示软件栈运行在Device侧,无需传输图片数据或在Device内传输数据 ;否则,需要调用内存复制接口将数据传输到Device
    if (!g_isDevice) {
        // if app is running in host, need copy data from host to device
        // modelInputBuffer、modelInputSize分别表示模型推理输入数据的内存地址、内存大小,在输入/输出数据结构准备时申请该内存
        aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_HOST_TO_DEVICE);
        (void)aclrtFreeHost(inputBuff);
    } else { // app is running in device
        aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_DEVICE_TO_DEVICE);
        (void)aclrtFree(inputBuff);
    }

    // 4.3 执行模型推理
    // modelId_表示模型ID,在模型加载成功后,会返回标识模型的ID
    // input_、output_分别表示模型推理的输入、输出数据,在准备模型推理的输入、输出数据结构时已定义
    aclError ret = aclmdlExecute(modelId_, input_, output_)
        

    // 处理模型推理的输出数据,输出top5置信度的类别编号 
    // output_表示模型执行的输出
    for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(output_); ++i) {
    // 获取每个输出的内存地址和内存大小
        aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(output_, i);
        void* data = aclGetDataBufferAddr(dataBuffer);

        size_t len = aclGetDataBufferSizeV2(dataBuffer);

        // 将内存中的数据转换为float类型
        float *outData = NULL;
        outData = reinterpret_cast<float*>(data);
        
        // 屏显每张图片的top5置信度的类别编号
        map<float, int, greater<float> > resultMap;
        for (int j = 0; j < len / sizeof(float); ++j) {
            resultMap[*outData] = j;
            outData++;
        }
        int cnt = 0;
        for (auto it = resultMap.begin(); it != resultMap.end(); ++it) {
            // print top 5
            if (++cnt > 5) {
                break;
            }

            INFO_LOG("top %d: index[%d] value[%lf]", cnt, it->second, it->first);
    }
}

// 5 释放模型推理的输入、输出资源
// 释放输入资源,包括数据结构和内存
for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(input_); ++i) {
        aclDataBuffer *dataBuffer = aclmdlGetDatasetBuffer(input_, i);
        (void)aclDestroyDataBuffer(dataBuffer);
}
(void)aclmdlDestroyDataset(input_);
input_ = nullptr;
aclrtFree(modelInputBuffer);

// 释放输出资源,包括数据结构和内存
for (size_t i = 0; i < aclmdlGetDatasetNumBuffers(output_); ++i) {
    aclDataBuffer* dataBuffer = aclmdlGetDatasetBuffer(output_, i);
    void* data = aclGetDataBufferAddr(dataBuffer);
    (void)aclrtFree(data);
    (void)aclDestroyDataBuffer(dataBuffer);
}

(void)aclmdlDestroyDataset(output_);
output_ = nullptr;
搜索结果
找到“0”个结果

当前产品无相关内容

未找到相关内容,请尝试其他搜索词