应用班第三课

应用班第三课

图片分类

这节课得目标是掌握基本的开发流程,以及相关的应用日志查看。

主要步骤

准备模型、准备数据、编译程序、运行程序

编译

编译的主要步骤有准备环境,ACL初始化、运行管理资源申请、模型加载、模型推理、模型卸载、运行资源管理资源释放、ACL去初始化、编译代码生成可执行文件。

运行ACL时需要先初始化,初始化时需要有配置文件,json格式的。如果不关注配置文件,则该文件可以为空json文件,即只有“{}”。

运行

准备运行环境、执行可执行文件、运行运用。

程序整体框架

首先时头文件,需要有acl的头文件

#include "acl/acl.h" 
ACL初始化
    // 此处的..表示相对路径,相对可执行文件所在的目录
    //例如,编译出来的可执行文件存放在out目录下,此处的..就表示out目录的上一级目录
    const char *aclConfigPath = "../src/acl.json";
    aclError ret = aclInit(aclConfigPath);
运行资源管理申请

需要申请的资源原有:device、context、stream等。

aclrtSetDevice这个接口,调用完毕后,除了指定了计算设备之外,还会同时创建1个默认的Context;而这个默认的Context还附赠了2个Stream,1个默认Stream和1个用于执行内部同步的Stream。这也意味着如果是编写非常简单的单线程同步推理应用,在运行资源这里我们只需要调用aclrtSetDevice就够了。

    // 1 指定运算的Device
    ret = aclrtSetDevice(deviceId_);

    // 2 显式创建一个Context,用于管理Stream对象
    ret = aclrtCreateContext(&context_, deviceId_);

    // 3 显式创建一个Stream
    // 用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序执行任务
    ret = aclrtCreateStream(&stream_);

    // 4 获取当前昇腾AI软件栈的运行模式,根据不同的运行模式,后续的接口调用方式不同
    aclrtRunMode runMode;
    extern bool g_isDevice;
    ret = aclrtGetRunMode(&runMode);
    g_isDevice = (runMode == ACL_DEVICE); 
模型加载获取模型新型

AscendCL只认识适配昇腾AI处理器的离线模型(*.om文件),因此,模型加载前,需要将第三方网络(例如,Caffe ResNet-50网络)转换为适配昇腾AI处理器的离线模型(*.om文件)

    // 1 初始化变量
    // 此处的..表示相对路径,相对可执行文件所在的目录
    // 例如,编译出来的可执行文件存放在out目录下,此处的..就表示out目录的上一级目录
    const char* omModelPath = "../model/resnet50.om"

    // 2 根据模型文件获取模型执行时所需的权值内存大小、工作内存大小。
    aclError ret = aclmdlQuerySize(omModelPath, &modelMemSize_, &modelWeightSize_);

    // 3 根据工作内存大小,申请Device上模型执行的工作内存。
    ret = aclrtMalloc(&modelMemPtr_, modelMemSize_, ACL_MEM_MALLOC_NORMAL_ONLY);

    // 4 根据权值内存的大小,申请Device上模型执行的权值内存。
    ret = aclrtMalloc(&modelWeightPtr_, modelWeightSize_, ACL_MEM_MALLOC_NORMAL_ONLY);

    // 5 加载离线模型文件(适配昇腾AI处理器的离线模型),由用户自行管理模型运行的内存(包括权值内存、工作内存)。
    // 模型加载成功,返回标识模型的ID。
    ret = aclmdlLoadFromFileWithMem(modelPath, &modelId_, modelMemPtr_, modelMemSize_, modelWeightPtr_, modelWeightSize_);

    // 6 根据加载成功的模型的ID,获取该模型的描述信息。
    // modelDesc_为aclmdlDesc类型。
    modelDesc_ = aclmdlCreateDesc();
    ret = aclmdlGetDesc(modelDesc_, modelId_);
执行模型推理,并且处理推理结果

准备模型推理的输入、输出数据结构,用于描述模型的输入、输出数据。

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

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

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

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

    // 2.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_NORMAL_ONLY);
        aclDataBuffer* outputData = aclCreateDataBuffer(outputBuffer, buffer_size);   
        ret = aclmdlAddDatasetBuffer(output_, outputData);
    }

准备模型推理的输入数据,进行推理,推理结束后,处理推理结果。

    string picturePath = "../data/dog1_1024_683.bin";

    // 1 自定义函数ReadBinFile,调用C++标准库std::ifstream中的函数读取图片文件
    // 输出图片文件占用的内存大小inputBuffSize以及图片文件存放在内存中的地址inputBuff    
    void *inputBuff = nullptr;
    uint32_t inputBuffSize = 0;
    auto ret = Utils::ReadBinFile(picturePath, inputBuff, inputBuffSize);

    // 2 准备模型推理的输入数据
    // 在"申请运行管理资源"时调用aclrtGetRunMode接口获取软件栈的运行模式
    // 如果运行模式为ACL_DEVICE,则g_isDevice参数值为true,无需传输图片数据或在Device内传输数据 
    // 如果运行模式为ACL_HOST,则g_isDevice参数值为false,需要将图片数据从Host传输到Device
    if (!g_isDevice) {
        // 内存在"输入/输出数据结构准备"时申请该内存
        aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_HOST_TO_DEVICE);
        (void)aclrtFreeHost(inputBuff);
       } 
    else { 
        aclError aclRet = aclrtMemcpy(modelInputBuffer, modelInputSize, inputBuff, inputBuffSize, ACL_MEMCPY_DEVICE_TO_DEVICE);
        (void)aclrtFree(inputBuff);
        }

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

    // 4. 处理模型推理的输出数据,输出top5置信度的类别编号 
    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;
卸载模型
    // 1. 卸载模型
    aclError ret = aclmdlUnload(modelId_);

    // 2. 释放模型描述信息
    if (modelDesc_ != nullptr) {
        (void)aclmdlDestroyDesc(modelDesc_);
        modelDesc_ = nullptr;
    }

    // 3. 释放模型运行的工作内存
    if (modelWorkPtr_ != nullptr) {
        (void)aclrtFree(modelWorkPtr_);
        modelWorkPtr_ = nullptr;
        modelWorkSize_ = 0;
    }

    // 4. 释放模型运行的权值内存
    if (modelWeightPtr_ != nullptr) {
        (void)aclrtFree(modelWeightPtr_);
        modelWeightPtr_ = nullptr;
        modelWeightSize_ = 0;
    }
运行管理资源释放

要注意,只能销毁由aclrtCreateContext接口显式创建的Context、销毁由aclrtCreateStream接口显式创建的Stream,不能销毁默认Context、默认Stream。默认Context、默认Stream会由系统自行销毁,无需调用者关注。

    // 1 释放Stream
    aclError ret = aclrtDestroyStream(stream_);
    // 2 释放Context
    ret = aclrtDestroyContext(context_);
    // 3 释放Device
    ret = aclrtResetDevice(deviceId_);
ACL去初始化
aclError ret = aclFinalize();

模型

使用Caffe框架的开源ResNet-50模型作为其输入输出。

模型转换

由于昇腾无法使用tensorflow、caffe等模型,所以需要使用ATC工具转换为昇腾能够使用的模型,只需要准备好开发环境,并不需要运行环境。

模型转换约束条件

支持原始框架类型为Caffe和TensorFlow,image-20220329154001598

日志

日志目录默认为:

image-20220329164107519

image-20220329164155583

日志中关键信息

image-20220329164352875

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值