文档
注册
评分
提单
论坛
小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,获取基础的样例包MyFirstApp_ONNX.zip,以运行用户将该样例包上传至开发环境(例如,存放在/root/demo目录下),先执行“chmod +x MyFirstApp_ONNX.zip”命令增加执行权限,再执行“unzip MyFirstApp_ONNX.zip”命令解压样例包。

该样例包中包含测试图片、测试图片预处理脚本、编译脚本等,如下所示。

MyFirstApp_ONNX
├── data
│   ├── dog1_1024_683.jpg            // 测试图片

├── model                            // 用于存放ResNet-50模型文件                  

├── script
│   ├── transferPic.py               // 将测试图片预处理为符合模型要求的图片
                                         // 包括将*.jpg转换为*.bin,同时将图片从1024*683的分辨率缩放为224*224
├── src
│   ├── CMakeLists.txt              // cmake编译脚本
│   ├── main.cpp                    // 主函数,图片分类功能的实现文件

├── sample_build.sh                  // 编译代码的脚本

├── sample_run.sh                    // 运行应用的脚本

准备模型

对于开源框架的网络模型,不能直接在昇腾AI处理器上做推理,需要先使用ATC(Ascend Tensor Compiler)工具将开源框架的网络模型转换为适配昇腾AI处理器的离线模型(*.om文件)。

  1. 以运行用户登录开发环境。
  2. 执行模型转换。

    执行以下命令,将原始模型转换为昇腾AI处理器能识别的*.om模型文件。请注意,执行命令的用户需具有命令中相关路径的可读、可写权限。以下命令中的“<SAMPLE_DIR>”请根据实际样例包的存放目录替换、“<soc_version>”请根据实际昇腾AI处理器版本替换。

    cd <SAMPLE_DIR>/MyFirstApp_ONNX/model
    wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/003_Atc_Models/resnet50/resnet50.onnx
    atc --model=resnet50.onnx --framework=5 --output=resnet50 --input_shape="actual_input_1:1,3,224,224"  --soc_version=<soc_version>

    各参数的解释如下,详细约束说明请参见ATC工具使用指南

    • --model:ResNet-50网络的模型文件的路径。
    • --framework:原始框架类型。5表示ONNX。
    • --output:resnet50.om模型文件的路径。请注意,记录保存该om模型文件的路径,后续开发应用时需要使用。
    • --input_shape:模型输入数据的shape。
    • --soc_version:昇腾AI处理器的版本。

      如果无法确定当前设备的soc_version,则在安装NPU驱动包的服务器执行npu-smi info命令进行查询,在查询到的“Name”前增加Ascend信息,例如“Name”对应取值为xxxyy,实际配置的soc_version值为Ascendxxxyy

开发应用

有了模型,接下来可以使用AscendCL接口加载、执行模型,开发AI应用了。

在开发环境中,打开“MyFirstApp_ONNX/src/main.cpp“文件,在该cpp中添加如下步骤中灰色底纹的代码。

  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();
    }
    

    了解总体的代码逻辑后,接下来开始写各自定义函数的实现,以下函数的实现请按顺序添加在main函数之前

  2. include依赖的头文件,包括AscendCL、C或C++标准库的头文件。
    在“MyFirstApp_ONNX/src/main.cpp”文件的开头增加如下代码。
    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模型文件来实现模型加载,如何获取om模型文件请参见准备模型
    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();
    }
    

编译及运行应用

  1. 编译代码

    以运行用户登录开发环境,切换到MyFirstApp_ONNX目录下,执行以下命令。

    如下为设置环境变量的示例,<SAMPLE_DIR>表示样例所在的目录,$HOME/Ascend/ascend-toolkit表示CANN软件包的安装路径,<arch-os>中arch表示操作系统架构(需根据运行环境的架构选择),os表示操作系统(需根据运行环境的操作系统选择)。

    export APP_SOURCE_PATH=<SAMPLE_DIR>/MyFirstApp_ONNX
    export DDK_PATH=$HOME/Ascend/ascend-toolkit/latest
    export NPU_HOST_LIB=$HOME/Ascend/ascend-toolkit/latest/<arch-os>/devlib
    chmod +x sample_build.sh
    ./sample_build.sh
    • 如果执行脚本报错“ModuleNotFoundError: No module named 'PIL'”,是由于开发环境中缺少Pillow库,需使用pip3 install Pillow --user命令安装Pillow库后,再执行脚本。
    • 如果执行脚本报错“bash: ./sample_build.sh: /bin/bash^M: bad interpreter: No such file or directory”,是由于开发环境中没有安装dos2unix包,需使用sudo apt-get install dos2unix命令安装dos2unix包后,执行dos2unix sample_build.sh,然后再执行脚本。
  2. 运行应用

    以运行用户将MyFirstApp_ONNX目录上传至运行环境,以运行用户登录运行环境,切换到MyFirstApp_ONNX目录下,执行以下命令。

    chmod +x sample_run.sh
    ./sample_run.sh

    终端上屏显的结果如下,index表示类别标识、value表示该分类的最大置信度:

    top 1: index[162] value[0.954676]
    top 2: index[161] value[0.033442]
    top 3: index[166] value[0.006534]
    top 4: index[167] value[0.004561]
    top 5: index[163] value[0.000315]

    类别标签和类别的对应关系与训练模型时使用的数据集有关,本样例使用的模型是基于imagenet数据集进行训练的,您可以在互联网上查阅对应数据集的标签及类别的对应关系。

    当前屏显信息中的类别标识与类别的对应关系如下:

    "162": ["beagle"]

    "161": ["basset", "basset hound"]

    "166": ["Walker hound", "Walker foxhound"]

    "167": ["English foxhound"]

    "163": ["bloodhound", "sleuthhound"]

搜索结果
找到“0”个结果

当前产品无相关内容

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