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

快速入门

在本节中,您可以通过一个简单的图片分类应用了解使用AscendCL接口(C语言接口)开发应用的基本过程以及开发过程中涉及的关键概念。

了解图片分类应用

“图片分类应用”,从名称上,我们也能直观地看出它的作用:标识图片所属的分类。

图1 图片分类应用

“图片分类应用”是怎么做到这一点的呢?当然得先有一个能做到图片分类的模型,我们可以直接使用一些训练好的开源模型,也可以基于开源模型的源码进行修改、重新训练,还可以自己基于算法、框架构建适合自己的模型。

鉴于当前我们是入门内容,此处我们直接获取已训练好的开源模型,这种方式相对简单。此处我们选择的是ONNX框架的ResNet-50模型。

ResNet-50模型的基本介绍如下:

  • 输入数据:BGR格式、224*224分辨率的输入图片
  • 输出数据:图片的类别标签及其对应置信度
  • 置信度是指图片所属某个类别可能性。
  • 类别标签和类别的对应关系与训练模型时使用的数据集有关,需要查阅对应数据集的标签及类别的对应关系。

了解基本概念

  • Host

    Host指与Device相连接的X86服务器、ARM服务器,会利用Device提供的NN(Neural-Network)计算能力,完成业务。

  • Device

    Device指安装了昇腾AI处理器的硬件设备,利用PCIe接口与Host侧连接,提供NN计算能力。

  • 开发环境、运行环境

    开发环境指编译开发代码的环境,运行环境指运行算子、推理或训练等程序的环境,运行环境上必须带昇腾AI处理器

    开发环境和运行环境可以合设在同一台服务器上,也可以分设:

    • 合设场景下,登录到合设的服务器上,不用切换开发环境、运行环境。
    • 分设场景下,若开发环境和运行环境上的操作系统架构不同,在开发环境中需使用交叉编译,这样编译出来的可执行文件,才可以在运行环境中执行。

    您可以登录对应的环境,执行“uname -a”命令查询其操作系统的架构。

  • 运行用户:

    运行驱动进程、推理业务或执行训练的用户。

了解开发过程

AscendCL(Ascend Computing Language)是一套用于在CANN(Compute Architecture for Neural Networks)上开发深度神经网络推理应用的C语言API库,提供模型加载与执行、媒体数据处理、算子加载与执行等API,能够实现在昇腾CANN平台上进行深度学习推理计算、图形图像预处理、单算子加速计算等能力。

图2 开发流程

了解了这些大步骤后,下面我们再展开来说明开发应用具体涉及哪些关键功能?各功能又使用哪些AscendCL接口,这些AscendCL接口怎么串联?

虽然此时您可能不理解所有细节,但这也不影响,此处先了解整体的代码逻辑,后续再通过编译及运行应用、解析样例代码了解细节。

编译及运行应用

单击Link获取快速入门样例,并按README.md中的指导下载样例、准备模型、准备测试图片、编译及运行应用。

解析样例代码

体验快速入门样例的编译运行后,若还想了解代码的实现逻辑,可查看下文的代码解析。此处按各函数的功能来介绍代码逻辑,为了便于阅读、理解代码,将各函数相关的变量与函数放在一起介绍。

打开“resnet50_firstapp/src/main.cpp”文件,先从main函数开始,了解整个样例的代码串接逻辑,再分别了解各自定义函数的实现。

  1. main函数用串联整个应用的代码逻辑。
     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
    int main()
    {	
            // 1.定义一个资源初始化的函数,用于AscendCL初始化、运行管理资源申请(指定计算设备)
    	InitResource();
    	
            // 2.定义一个模型加载的函数,加载图片分类的模型,后续推理使用,若om文件不在model目录下,请根据实际存放目录修改此处的代码
    	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();
    }
    

    了解总体的代码逻辑后,接下来开始解析各自定义函数的实现。

  2. include依赖的头文件,包括AscendCL、C或C++标准库的头文件。
    1
    2
    3
    4
    5
    6
    7
    8
    #include "acl/acl.h"
    #include <iostream>
    #include <fstream>
    #include <cstring>
    #include <map>
    #include <math.h>
    
    using namespace std;
    
  3. 资源初始化

    资源初始化包括2部分:

    • 调用aclInit接口初始化AscendCL:

      使用AscendCL接口开发应用时,必须先初始化AscendCL ,否则可能会导致后续系统内部资源初始化出错,进而导致其它业务异常。

      在初始化时,还支持跟推理相关的可配置项(例如,性能相关的采集信息配置),以json格式的配置文件传入AscendCL初始化接口。如果当前的默认配置已满足需求(例如,默认不开启性能相关的采集信息配置),无需修改,可向AscendCL初始化接口中传入nullptr。

    • 调用aclrtSetDevice接口指定计算设备。
    1
    2
    3
    4
    5
    6
    int32_t deviceId = 0;
    void InitResource()
    {
    	aclError ret = aclInit(nullptr);
    	ret = aclrtSetDevice(deviceId);
    }
    

    有初始化就有去初始化,在确定完成AscendCL所有调用之后,或者进程退出之前,需执行资源去初始化,请参见10

  4. 模型加载
    此处加载的是*.om模型文件,如果将ONNX框架的ResNet-50模型文件转换为*.om模型文件,请单击Link,查看README.md中的“准备模型”。
    1
    2
    3
    4
    5
    uint32_t modelId;
    void LoadModel(const char* modelPath)
    {
    	aclError ret = aclmdlLoadFromFile(modelPath, &modelId);
    }
    

    有加载就有卸载,模型推理结束后,需要卸载模型,请参见8

  5. 将测试图片读入内存,再传输到Device侧,供推理使用。
     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
    size_t pictureDataSize = 0;
    void *pictureHostData;
    void *pictureDeviceData;
    
    //申请内存,使用C/C++标准库的函数将测试图片读入内存
    void ReadPictureTotHost(const char *picturePath)
    {
    	string fileName = picturePath;
    	ifstream binFile(fileName, ifstream::binary);
    	binFile.seekg(0, binFile.end);
    	pictureDataSize = binFile.tellg();
    	binFile.seekg(0, binFile.beg);
    	aclError ret = aclrtMallocHost(&pictureHostData, pictureDataSize);
    	binFile.read((char*)pictureHostData, pictureDataSize);
    	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();
    }
    
  6. 执行推理

    在调用AscendCL接口进行模型推理时,模型推理有输入、输出数据,输入、输出数据需要按照AscendCL规定的数据类型存放。相关数据类型如下:

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

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

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

      调用aclDataBuffer类型下的操作接口获取内存地址、内存大小等,便于向内存中存放输入数据、获取输出数据。

    • 使用aclmdlDataset类型的数据描述模型的输入/输出数据。

      模型可能存在多个输入、多个输出,调用aclmdlDataset类型的操作接口添加多个aclDataBuffer类型的数据。

      图3 aclmdlDataset类型与aclDataBuffer类型的关系
     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
    aclmdlDataset *inputDataSet;
    aclDataBuffer *inputDataBuffer;
    aclmdlDataset *outputDataSet;
    aclDataBuffer *outputDataBuffer;
    aclmdlDesc *modelDesc;
    size_t outputDataSize = 0;
    void *outputDeviceData;
    
    // 准备模型推理的输入数据结构
    void CreateModelInput()
    {
            // 创建aclmdlDataset类型的数据,描述模型推理的输入
    	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);
    }
    
  7. 处理模型推理的结果数据,在屏幕上显示图片的top5置信度的类别编号。
     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
    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++;
            }
    
            // do data processing with softmax and print top 5 classes
            double totalValue=0.0;
            for (auto it = resultMap.begin(); it != resultMap.end(); ++it) {
                totalValue += exp(it->first);
            }
    
            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, exp(it->first) /totalValue);
            }
    }
    
  8. 卸载模型,并释放模型描述信息。
    推理结束,需及时释放模型描述信息、卸载模型。
    1
    2
    3
    4
    5
    6
    7
    void UnloadModel()
    {
            // 释放模型描述信息
    	aclmdlDestroyDesc(modelDesc);
            // 卸载模型
    	aclmdlUnload(modelId);
    }
    
  9. 释放内存、销毁推理相关的数据类型
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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;
    }
    
  10. 资源释放
    在确定完成AscendCL所有调用之后,或者进程退出之前,需调用如下接口实现计算设备释放,AscendCL去初始化
    1
    2
    3
    4
    5
    void DestroyResource()
    {
    	aclError ret = aclrtResetDevice(deviceId);
    	aclFinalize();
    }
    
搜索结果
找到“0”个结果

当前产品无相关内容

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