跳到主要内容

pyACL接口调用流程

快速入门

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

什么是图片分类应用?

“图片分类应用”顾名思义标识图片所属的分类。

图1 图片分类应用

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

本次快速入门样例,我们直接获取已训练好的开源模型,这种方式相对简单,此处我们选择的是ONNX框架的ResNet-50模型。

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

  • 输入数据:RGB格式、224*224分辨率的输入图片。
  • 输出数据:图片的类别标签及其对应置信度。

:::note 说明

  • 置信度是指图片所属某个类别可能性。
  • 类别标签和类别的对应关系与训练模型时使用的数据集有关,需要查阅对应数据集的标签及类别的对应关系。 :::

前提条件

  • 已在环境上部署昇腾AI软件栈。

  • 安装环境,请参见应用开发环境准备

  • 安装必要的Python软件依赖(Pillow、numpy)。

    pip3 install pillow numpy

了解基本概念

  • Host

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

  • Device

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

  • 开发环境、运行环境

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

    :::note 说明 您可以登录对应的环境,执行“arch”命令查询其操作系统的架构。 :::

  • 运行用户

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

了解开发过程

pyACL(Python Ascend Computing Language)是一套在AscendCL的基础上使用CPython封装得到的Python API库,使用户可以通过Python进行昇腾AI处理器的运行管理、资源管理等,实现在昇腾CANN平台上进行深度学习推理计算、图形图像预处理、单算子加速计算等能力。

图2 开发流程

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

虽然此时您可能不理解所有细节,但这也不影响,通过快速入门旨在先了解整体的代码逻辑,后续再深入学习,了解其它细节。

创建代码目录

请参考以下目录结构,在开发环境中下创建“first_app”代码目录(例如“$HOME”目录)。

first_app
├── data
│ ├── dog1_1024_683.jpg //测试图片1
│ └── dog2_1024_683.jpg //测试图片2
└── model //用于存放ONNX ResNet-50模型文件
└── resnet50.onnx

其中,需准备以下数据模型

  • 准备测试数据,本次样例需要使用两张动物图片,请从以下链接获取,将下载好的图片上传至“first_app/data”目录。

    • 测试图片1

      cd $HOME/first_app/data
      wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/models/aclsample/dog1_1024_683.jpg
    • 测试图片2

      cd $HOME/first_app/data
      wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/models/aclsample/dog2_1024_683.jpg
  • 准备模型数据,参考以下命令,将ONNX模型下载至“model”目录下或通过模型获取链接下载到本地后上传到运行环境。

    cd $HOME/first_app/model
    wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/003_Atc_Models/resnet50/resnet50.onnx
  • 模型转换,对于开源框架的模型,不能直接在昇腾AI处理器上进行推理,需要使用ATC(Ascend Tensor Compiler)工具将开源框架的网络模型转换为适配昇腾AI处理器的离线模型(*.om文件)。

    执行以下命令,将原始模型转换为昇腾AI处理器能识别的*.om模型文件。请注意,执行命令的用户需具有命令中相关路径的可读、可写权限。

    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处理器的版本。

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

开发应用

在“first_app”目录下创建“first_app.py”文件并依次写入以下内容。

  1. 引入pyACL必要的模块,定义pyACL常量。

    import os

    import acl
    import numpy as np
    from PIL import Image

    ACL_MEM_MALLOC_HUGE_FIRST = 0
    ACL_MEMCPY_HOST_TO_DEVICE = 1
    ACL_MEMCPY_DEVICE_TO_HOST = 2
  2. 定义模型对象。

    网络模型对象中应当包含以下函数。

    • 初始化函数
    • 执行推理任务函数
    • 析构函数

    对于后续的使用,用户只需要调用网络模型中的forward函数,传入对应的输入数据即可获得相应的输出。

    class net:

    # def __init__(self, model_path):
    # 初始化函数,需要在后续步骤中实现。

    # def forward(self, inputs):
    # 执行推理任务,需要在后续步骤中实现。

    # def __del__(self):
    # 析构函数,按照初始化资源的相反顺序释放资源,需要在后续步骤中实现。
  3. 实现初始化方法,具体涉及以下步骤(请在net类中实现)。

    1. 调用acl.init接口进行初始化,在使用pyACL开发应用时,需要先初始化pyACL(在完成所有pyACL接口调用后,还需进行去初始化)。初始化时,也可通过JSON配置文件,向初始化接口传入配置参数(例如,传入性能相关的采集信息配置)。
    2. 通过ID,调用acl.rt.set_device接口指定具体的计算设备(Device)。
    3. 加载模型。
      1. 在此处样例选择调用acl.mdl.load_from_file接口加载om模型文件。
      2. 调用acl.mdl.create_desc接口创建模型描述信息。
      3. 根据加载成功的模型ID,调用acl.mdl.get_desc接口获取该模型的描述信息。
    4. 创建输入数据集与输出数据集,对应方法在4中实现。
    def __init__(self, model_path):
    # 初始化函数
    self.device_id = 0

    # step1: 初始化
    ret = acl.init()
    # 指定运算的Device
    ret = acl.rt.set_device(self.device_id)

    # step2: 加载模型,本示例为ResNet-50模型
    # 加载离线模型文件,返回标识模型的ID
    self.model_id, ret = acl.mdl.load_from_file(model_path)
    # 创建空白模型描述信息,获取模型描述信息的指针地址
    self.model_desc = acl.mdl.create_desc()
    # 通过模型的ID,将模型的描述信息填充到model_desc
    ret = acl.mdl.get_desc(self.model_desc, self.model_id)

    # step3:创建输入输出数据集
    # 创建输入数据集
    self.input_dataset, self.input_data = self.prepare_dataset('input')
    # 创建输出数据集
    self.output_dataset, self.output_data = self.prepare_dataset('output')
  4. 实现数据集创建方法(请在net类中实现)。

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

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

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

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

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

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

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

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

    def prepare_dataset(self, io_type):
    # 准备数据集
    if io_type == "input":
    # 获得模型输入的个数
    io_num = acl.mdl.get_num_inputs(self.model_desc)
    acl_mdl_get_size_by_index = acl.mdl.get_input_size_by_index
    else:
    # 获得模型输出的个数
    io_num = acl.mdl.get_num_outputs(self.model_desc)
    acl_mdl_get_size_by_index = acl.mdl.get_output_size_by_index
    # 创建aclmdlDataset类型的数据,描述模型推理的输入。
    dataset = acl.mdl.create_dataset()
    datas = []
    for i in range(io_num):
    # 获取所需的buffer内存大小
    buffer_size = acl_mdl_get_size_by_index(self.model_desc, i)
    # 申请buffer内存
    buffer, ret = acl.rt.malloc(buffer_size, ACL_MEM_MALLOC_HUGE_FIRST)
    # 从内存创建buffer数据
    data_buffer = acl.create_data_buffer(buffer, buffer_size)
    # 将buffer数据添加到数据集
    _, ret = acl.mdl.add_dataset_buffer(dataset, data_buffer)
    datas.append(&#123;"buffer": buffer, "data": data_buffer, "size": buffer_size&#125;)
    return dataset, datas
  5. 实现同步推理方法(请在net类中实现)。

    def forward(self, inputs):
    # 执行推理任务
    # 遍历所有输入,拷贝到对应的buffer内存中
    input_num = len(inputs)
    for i in range(input_num):
    bytes_data = inputs[i].tobytes()
    bytes_ptr = acl.util.bytes_to_ptr(bytes_data)
    # 将图片数据从Host传输到Device。
    ret = acl.rt.memcpy(self.input_data[i]["buffer"], # 目标地址 device
    self.input_data[i]["size"], # 目标地址大小
    bytes_ptr, # 源地址 host
    len(bytes_data), # 源地址大小
    ACL_MEMCPY_HOST_TO_DEVICE) # 模式:从host到device
    # 执行模型推理。
    ret = acl.mdl.execute(self.model_id, self.input_dataset, self.output_dataset)
    # 处理模型推理的输出数据,输出top5置信度的类别编号。
    inference_result = []
    for i, item in enumerate(self.output_data):
    buffer_host, ret = acl.rt.malloc_host(self.output_data[i]["size"])
    # 将推理输出数据从Device传输到Host。
    ret = acl.rt.memcpy(buffer_host, # 目标地址 host
    self.output_data[i]["size"], # 目标地址大小
    self.output_data[i]["buffer"], # 源地址 device
    self.output_data[i]["size"], # 源地址大小
    ACL_MEMCPY_DEVICE_TO_HOST) # 模式:从device到host
    # 从内存地址获取bytes对象
    bytes_out = acl.util.ptr_to_bytes(buffer_host, self.output_data[i]["size"])
    # 按照float32格式将数据转为numpy数组
    data = np.frombuffer(bytes_out, dtype=np.float32)
    inference_result.append(data)
    vals = np.array(inference_result).flatten()
    # 对结果进行softmax转换
    vals = np.exp(vals)
    vals = vals / np.sum(vals)

    return vals
  6. 实现析构方法(请在net类中实现)。

    1. 销毁数据集资源(buffer数据、buffer内存、输入数据集、输出数据集)。
    2. 销毁模型描述、卸载模型。
    3. 释放计算资源。
    4. 所有pyACL接口调用结束后(或在进程退出前),调用acl.finalize接口进行pyACL进行去初始化

    在推理过程中可能会抛出异常,请将资源释放步骤实现在析构方法中确保资源能够得到正确释放。以下内容仅供参考,实际情况下需要考虑更多情况下的资源释放问题。

    def __del__(self):
    # 析构函数 按照初始化资源的相反顺序释放资源。
    # 销毁输入输出数据集
    for dataset in [self.input_data, self.output_data]:
    while dataset:
    item = dataset.pop()
    ret = acl.destroy_data_buffer(item["data"]) # 销毁buffer数据
    ret = acl.rt.free(item["buffer"]) # 释放buffer内存
    ret = acl.mdl.destroy_dataset(self.input_dataset) # 销毁输入数据集
    ret = acl.mdl.destroy_dataset(self.output_dataset) # 销毁输出数据集
    # 销毁模型描述
    ret = acl.mdl.destroy_desc(self.model_desc)
    # 卸载模型
    ret = acl.mdl.unload(self.model_id)
    # 释放device
    ret = acl.rt.reset_device(self.device_id)
    # acl去初始化
    ret = acl.finalize()
  7. 实现图像预处理函数。

    def transfer_pic(input_path):
    # 图像预处理
    input_path = os.path.abspath(input_path)
    with Image.open(input_path) as image_file:
    # 缩放为224*224
    img = image_file.resize((224, 224))
    # 转换为float32类型ndarray
    img = np.array(img).astype(np.float32)
    # 根据imageNet图片的均值和方差对图片像素进行归一化
    img -= [123.675, 116.28, 103.53]
    img /= [58.395, 57.12, 57.375]
    # RGB通道交换顺序为BGR
    img = img[:, :, ::-1]
    # resnet50为色彩通道在前
    img = img.transpose((2, 0, 1))
    # 返回并添加batch通道
    return np.array([img])
  8. 调用forward函数(具体实现请参见5),执行同步推理并在屏幕中打印top5类别编号置信度

    def print_top_5(data):
    top_5 = data.argsort()[::-1][:5]
    print("======== top5 inference results: =============")
    for j in top_5:
    print("[%d]: %f" % (j, data[j]))

    if __name__ == "__main__":
    resnet50 = net('./model/resnet50.om')
    image_paths = ["./data/dog1_1024_683.jpg", "./data/dog2_1024_683.jpg"]
    for path in image_paths:
    # 图像预处理,此处仅供参考,用户按照自己需求进行预处理
    image = transfer_pic(path)
    # 将数据按照每个输入的顺序构造list传入,当前示例的ResNet-50模型只有一个输入
    result = resnet50.forward([image])
    # 输出top_5
    print_top_5(result)

    del resnet50

运行应用

将编写好的“first_app”文件夹及内容上传到运行环境,进入到代码目录下,检查环境变量配置是否正确,然后执行以下命令。

python3 first_app.py

可以得到如下输出,分别为两张测试图片的top5分类信息。

其中**[161]: 0.809159**表示的是类别标识索引“161”的置信度为“0.809159”。

======== top5 inference results: =============
[161]: 0.809159
[162]: 0.103680
[178]: 0.017600
[166]: 0.013922
[212]: 0.009644
======== top5 inference results: =============
[267]: 0.728299
[266]: 0.101693
[265]: 0.100117
[151]: 0.004214
[160]: 0.002721

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

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

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

"162": ["beagle"]

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

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

"167": ["English foxhound"] :::

基于AscendCL的pyACL

AscendCL(Ascend Computing Language)是一套用于在昇腾平台上开发深度神经网络应用的C语言API库,提供运行资源管理、内存管理、模型加载与执行、算子加载与执行、媒体数据处理等API,能够实现利用昇腾硬件计算资源、在昇腾CANN平台上进行深度学习推理计算图形图像预处理单算子加速计算等能力。简单来说,就是统一的API框架,实现对所有资源的调用。

计算资源层是昇腾AI处理器的硬件算力基础,主要完成神经网络的矩阵相关计算、完成控制算子/标量/向量等通用计算和执行控制功能、完成图像和视频数据的预处理,为深度神经网络计算提供了执行上的保障。

**pyACL(Python Ascend Computing Language)**就是在AscendCL的基础上使用CPython封装得到的Python API库,使用户可以通过Python进行昇腾AI处理器的运行管理、资源管理等。

图1 逻辑架构图

父主题: 概述

基本概念

表1 概念介绍

概念描述
同步/异步本文中提及的同步、异步是站在调用者和执行者的角度,在当前场景下,若在Host调用接口后不等待Device执行完成再返回,则表示Host的调度是异步的;若在Host调用接口后需等待Device执行完成再返回,则表示Host的调度是同步的。
进程/线程本文中提及的进程、线程,若无特别注明,则表示Host上的进程、线程。
HostHost指与Device相连接的X86服务器、ARM服务器,会利用Device提供的NN(Neural-Network )计算能力,完成业务。
DeviceDevice指安装了昇腾AI处理器的硬件设备,利用PCIe接口与Host侧连接,为Host提供NN计算能力。若存在多个Device,多个Device之间的内存资源不能共享。
ContextContext作为一个容器,管理了所有对象(包括Stream、Event、设备内存等)的生命周期。不同Context的Stream、不同Context的Event是完全隔离的,无法建立同步等待关系。 Context分为两种:
- 默认Context:调用acl.rt.set_device接口指定用于运算的Device时,系统会自动隐式创建一个默认Context,一个Device对应一个默认Context,默认Context不能通过acl.rt.destroy_context接口来释放。
- 显式创建的Context:推荐,在进程或线程中调用acl.rt.create_context接口显式创建一个Context。
StreamStream用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序在Device上执行。 基于Stream的kernel执行和数据传输能够实现Host运算操作、Host与Device间的数据传输、Device内的运算并行。 Stream分两种:
- 默认Stream:调用acl.rt.set_device接口指定用于运算的Device时,系统会自动隐式创建一个默认Stream,一个Device对应一个默认Stream,默认Stream不能通过acl.rt.destroy_stream接口来释放。
- 显式创建的Stream:推荐,在进程或线程中调用acl.rt.create_stream接口显式创建一个Stream。
Event支持调用pyACL接口同步Stream之间的任务,包括同步Host与Device之间的任务、同一个Device上的多个任务。 例如,若stream2的任务依赖stream1的任务,想保证stream1中的任务先完成,这时可创建一个Event,并将Event插入到stream1,在执行stream2的任务前,先同步等待Event完成。
AIPPAIPP(Artificial Intelligence Pre-Processing)用于在AI Core上完成图像预处理,包括色域转换(转换图像格式)、图像归一化(减均值/乘系数)和抠图(指定抠图起始点,抠出神经网络需要大小的图片)等。 AIPP区分为静态AIPP和动态AIPP。您只能选择静态AIPP或动态AIPP其中一种方式来处理图片,不能同时配置静态AIPP和动态AIPP两种方式。
- 静态AIPP:模型转换时设置AIPP模式为静态,同时设置AIPP参数,模型生成后,AIPP参数值被保存在离线模型(*.om)中,每次模型推理过程采用固定的AIPP预处理参数(无法修改)。 如果使用静态AIPP方式,多Batch情况下共用同一份AIPP参数。
- 动态AIPP:模型转换时仅设置AIPP模式为动态,每次模型推理前,根据需求,在执行模型前设置动态AIPP参数值,然后在模型执行时可使用不同的AIPP参数。 如果使用动态AIPP方式,多Batch可使用不同的AIPP参数。
动态batch/动态分辨率在某些场景下,模型每次输入的batch size或分辨率是不固定的,如检测出目标后再执行目标识别网络,由于目标个数不固定导致目标识别网络输入batch size不固定。
- 动态batch:用户执行推理时,其batch size是动态可变的。
- 动态分辨率:用户执行推理时,每张图片的分辨率H*W是动态可变的。
动态维度(ND格式)为了支持Transformer等网络在输入格式的维度不确定的场景,需要支持ND格式下任意维度的动态设置。 ND表示支持任意格式,当前N ≤ 4。
通道在RGB色彩模式下,图像通道就是指单独的红色R、绿色G、蓝色B部分。也就是说,一幅完整的图像,是由红色、绿色、蓝色三个通道组成的,它们共同作用产生了完整的图像。同样在HSV色系中指的是色调H、饱和度S、亮度V三个通道。
RC模式以昇腾 AI 处理器的PCIe的工作模式进行区分,如果PCIe工作在主模式,可以扩展外设,则称为RC模式。

Device、Context、Stream之间的关系

图1 Device、Context、Stream之间的关系

  • Device,用于指定计算设备。
    • Device的生命周期源于首次调用acl.rt.set_device接口。
    • 每次调用acl.rt.set_device接口,系统会进行引用计数加1;调用acl.rt.reset_device接口,系统会进行引用计数减1
    • 当引用计数减为时,在本进程中Device上的资源不可用。
  • Context,在Device下,一个Context一定属于一个唯一的Device。
    • Context分隐式创建显式创建

    • 隐式创建的Context(即默认Context),生命周期始于调用acl.rt.set_device接口,终结于调用acl.rt.reset_device接口使引用计数为零时。

      隐式Context只会被创建一次,调用acl.rt.set_device接口重复指定同一个Device,只增加隐式创建的Context的引用计数。

    • 显式创建的Context,生命周期始于调用acl.rt.create_context接口,终结于调用acl.rt.destroy_context接口。

    • 若在某一进程内创建多个Context(Context的数量与Stream相关,Stream数量有限制,请参见acl.rt.create_stream),当前线程在同一时刻内只能使用其中一个Context,建议通过acl.rt.set_context接口明确指定当前线程的Context,增加程序的可维护性**。**

    • 进程内的Context是共享的,可以通过acl.rt.set_context进行切换。

  • Stream,是Device上的执行流,在同一个Stream中的任务执行严格保序。
    • Stream分隐式创建显式创建
    • 每个Context都会包含一个默认Stream,属于隐式创建,隐式创建的Stream生命周期同归属的Context。
    • 用户可以显式创建Stream,显式创建的Stream生命周期始于调用acl.rt.create_stream,终结于调用acl.rt.destroy_stream接口。显式创建的Stream归属的Context被销毁或生命周期结束后,会影响该Stream的使用,虽然此时Stream没有被销毁,但不可再用。
  • Task/Kernel,是Device上真正的任务执行体。

线程、Context、Stream之间的关系

  • 一个用户线程一定会绑定一个Context,所有Device的资源使用或调度,都必须基于Context。

  • 一个线程中当前会有一个唯一的Context在用,Context中已经关联了本线程要使用的Device。

  • 可以通过acl.rt.set_context进行Device的快速切换。示例代码如下,仅供参考,不可以直接拷贝运行:


    ctx1, ret = acl.rt.create_context(0) #使用acl.rt.create_context接口通过传入Device Id创建Context。
    stream, ret = acl.rt.create_stream()
    ret = acl.op.execute_v2(op_type, input_desc, inputs, output_desc, outputs, attr, stream)
    ctx2, ret = acl.rt.create_context(1)

    # 在当前线程中,创建ctx2后,当前线程对应的Context切换为ctx2,对应在Device 1进行后续的计算任务,本例中将在Device 1上进行op2的执行调用。
    stream2, ret = acl.rt.create_stream()
    ret = acl.op.execute_v2(op_type2, input_desc, inputs, output_desc, outputs, attr, stream2)
    ret = acl.rt.set_context(ctx1);

    # 在当前线程中,通过Context切换,使后续计算任务在对应的Device 0上进行。
    ret = acl.op.execute_v2(op3,...,s1)

  • 一个线程中可以创建多个Stream,不同的Stream上计算任务是可以并行执行,多线程场景下,也可以每个线程创建一个Stream,线程之间的Stream在Device上相互独立,每个Stream内部的任务是按照Stream下发的顺序执行。

  • 多线程的调度依赖于运行应用的操作系统调度,多Stream调度Device侧,由Device上调度组件进行调度。

一个进程内多个线程间的Context迁移

  • 一个进程中可以创建多个Context,但一个线程同一时刻只能使用一个Context。

  • 线程中创建的多个Context,线程缺省使用最后一次创建的Context。

  • 进程内创建的多个Context,可以通过acl.rt.set_context设置当前需要使用的Context。

    图2 接口调用流程

默认Context和默认Stream的使用场景

  • Device上执行操作下发前,必须有Context和Stream,这个Context、Stream可以显式创建,也可以隐式创建。隐式创建的Context、Stream就是默认Context、默认Stream。

    默认Stream作为接口入参时,直接传0。

  • 默认Context不允许用户执行acl.rt.get_contextacl.rt.set_context操作,也不允许执行acl.rt.destroy_context操作。

  • 默认Context默认Stream一般适用于简单应用,用户仅仅需要一个Device的计算场景下。多线程应用程序建议全部使用显式创建的Context和Stream。

示例代码如下,仅供参考,不可以直接拷贝运行:

## …
ret = acl.init(config_path)
ret = acl.rt.set_device(device_id)

## 已经创建了一个default ctx,在default ctx中创建了一个default stream,并且在当前线程可用。
## …
ret = acl.op.execute_v2(op1, input_desc, inputs, output_desc, outputs, attr, 0) # 最后一个0表示在default stream上执行算子op1。
ret = acl.op.execute_v2(op2, input_desc, inputs, output_desc, outputs, attr, 0) # 最后一个0表示在default stream上执行算子op2。
ret = acl.rt.synchronize_stream(0)

## 等待计算任务全部完成(op1、op2执行结束),用户根据需要获取计算任务的输出结果。
## …
ret = acl.rt.reset_device(device_id) # 释放计算设备0,对应的default ctx及default stream生命周期也终止。

多线程、多stream的性能说明

  • 线程调度依赖运行的操作系统,Stream上下发了任务后,Stream的调度由Device的调度单元调度,但如果一个进程内的多Stream上的任务在Device存在资源争抢的时候,性能可能会比单Stream低。
  • 当前昇腾AI处理器有不同的执行部件,如AI Core、AI CPU、Vector Core等,对应使用不同执行部件的任务,建议多Stream的创建按照算子执行引擎划分。
  • 单线程多Stream与多线程多Stream(一个进程中可以包含多个线程,每个线程中一个Stream)性能上哪个更优,具体取决于应用本身的逻辑实现,一般来说前者性能略好,原因是相对后者,应用层少了线程调度开销。

父主题: 概述

pyACL接口调用流程

调用pyACL接口,可开发包含模型推理、媒体数据处理、单算子调用等功能的应用,这些功能可以独立存在,也可以组合存在。下图给出了使用pyACL接口开发AI应用的整体接口调用流程。

图1 接口调用流程图

上图根据应用开发中的典型功能抽象出主要的接口调用流程,具体场景参考如下。

  • 如果模型对输入图片的宽高要求与用户提供的源图不一致,则需要媒体数据处理,将源图裁剪成符合模型的要求。
  • 如果需要实现模型推理的功能,则需要先加载模型,模型推理结束后,则需要卸载模型。
  • 如果模型推理后,需要从推理结果中查找最大置信度的类别标识对图片分类,则需要数据后处理。

接口调用流程各步骤操作参见如下。

  1. pyACL初始化。

    调用acl.init接口实现初始化pyACL。

  2. 运行管理资源申请。

    依次申请运行管理资源:DeviceContextStream

    具体流程,请参见运行管理资源申请流程

  3. 模型推理/单算子调用/媒体数据处理。

    • 模型推理

      1. 生成模型om文件:模型推理场景下,必须要有适配昇腾AI处理器的离线模型,需提前构建模型,请参见模型构建

      2. 模型加载:模型推理前,需要先将对应的模型加载到系统中。

        接口调用流程,请参见模型加载

      3. (可选)媒体数据处理:可实现JPEG图片解码、视频解码、抠图/图片缩放/格式转换、JPEG图片编码等功能。

        接口调用流程,请参见媒体数据处理

      4. 模型执行:使用模型实现图片分类、目标识别等功能。

        接口调用流程,请参见模型执行

      5. (可选)数据后处理:处理模型推理的结果,此处根据用户的实际需求来处理推理结果,例如用户可以将获取到的推理结果写入文件、从推理结果中找到每张图片最大置信度的类别标识等。

      6. 模型卸载:调用acl.mdl.unload接口卸载模型。

    • 算子调用

      如果AI应用中不仅仅包括模型推理,还有数学运算(例如BLAS基础线性代数运算)、数据类型转换等功能,也想使用昇腾的算力,直接通过pyACL接口加载并执行单个算子,省去模型构建、训练的过程,相对轻量级,又可以使用昇腾的算力。另外,自定义的算子,也可以通过单算子调用的方式来验证算子的功能。接口调用流程,请参见接口调用流程

  4. 运行管理资源释放。

    所有数据处理都结束后,需要依次释放运行管理资源:StreamContextDevice

    具体流程,请参见运行管理资源释放流程

  5. pyACL去初始化。

    调用acl.finalize接口实现pyACL去初始化。

:::note 说明

  • 在应用开发过程中,各环节都涉及内存的申请与释放、数据传输(通过内存复制实现)、数据类型的创建与销毁,因此未在图中一一标识,关于内存申请与释放、内存复制的接口请参见内存管理。数据类型的创建与销毁的接口请参见数据类型及其操作接口
  • 关于日志的处理机制和日志级别设置等功能,请参见《日志参考》。
  • pyACL部分功能可能会涉及到ATC工具来进行模型转换,请参见《ATC工具使用指南》。
  • 生成pyACL中的“acl.so”依赖的Python版本范围为3.7.5~3.9.2。 :::

父主题: 概述

应用开发环境准备

部署开发环境和运行环境,请参见对应Atlas产品的描述。

  • 部署开发环境后,才能获取调用接口所需的头文件、编译运行接口所需的库文件。

    对于昇腾设备,已安装驱动、固件场景下,该环境可直接作为运行环境,执行编译生成的应用可执行文件。

  • 部署运行环境后,才能在运行环境上执行编译生成的应用可执行文件。

    :::note 说明

    • pyACL库文件路径:CANN软件安装后文件存储路径/lib64

      需要根据运行环境的安装包,确定引用的组件目录,否则会导致运行报错。安装方案请参见。

      安装CANN软件后,需要以CANN运行用户登录环境,执行source ${install_path}/set_env.sh命令设置环境变量,其中${install_path}为CANN软件的安装目录。

    • 本文中的操作步骤需以运行用户登录开发环境或运行环境后再执行,请务必获取各组件的运行用户,以便后续操作时使用。 :::

  • (可选)通过环境变量“ASCEND_CACHE_PATH”、“ASCEND_WORK_PATH”设置pyACL应用运行过程中产生的文件的落盘路径,涉及ATC模型转换、AOE模型智能调优、性能数据采集、日志采集等功能,落盘文件包括知识库文件、调优结果文件、性能数据文件、日志文件等。

    配置示例如下,详细配置说明请参见《环境变量参考》:

    export ASCEND_CACHE_PATH=/repo/task001/cache
    export ASCEND_WORK_PATH=/repo/task001/172.16.1.12_01_03

pyACL的依赖

pyACL没有安装依赖,但是有运行依赖。详情请参见《CANN 软件安装指南》完成对开发环境和运行环境的部署。

安装后的环境变量设置

在安装完CANN软件包之后,请务必自行配置以下环境变量,否则,将无法正常使用“import acl”。

  • 若环境中安装了cann-toolkit软件包:

    # 以root用户安装toolkit包。
    . /usr/local/Ascend/ascend-toolkit/set_env.sh
    # 以非root用户安装toolkit包。
    . $&#123;HOME&#125;/Ascend/ascend-toolkit/set_env.sh
  • 若环境中安装了cann-nnrt软件包:

    # 以root用户安装nnrt包。
    . /usr/local/Ascend/nnrt/set_env.sh
    # 以非root用户安装nnrt包。
    . $&#123;HOME&#125;/Ascend/nnrt/set_env.sh

设置完环境变量后,在Python脚本中加入“import acl”,就可以使用pyACL中的函数了。

其它

  • 关于日志的处理机制和日志级别设置等功能,请参见《日志参考》。
  • pyACL部分功能可能会涉及到ATC工具来进行模型转换,请参见《ATC工具使用指南》。
  • 生成pyACL中的“acl.so”依赖的Python版本范围为3.7.5~3.9.2。

父主题: 概述

开发流程

图1 开发流程

  1. 准备环境,包括开发环境和运行环境。

  2. 创建代码目录。

    在开发应用前,您需要先创建目录,存放代码文件、编译脚本、测试图片数据、模型文件等。如下仅是示例,可参考:

    ├App名称
    ├── caffe_model # 该目录下存放模型转换相关的配置文件、模型文件。
    │ ├── xxx.cfg
    │ ├── xxx.prototxt
    ├── data
    │ ├── xxx.jpg # 测试数据。

    ├── model
    │ ├── xxx.om # 转换后的模型文件。

    ├── xxx.py # python脚本。
    ├── xxx.py
  3. 开发应用。

    1. pyACL初始化,请参见pyACL初始化与去初始化

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

    2. 运行管理资源申请,请参见运行管理资源申请与释放

    3. 数据传输,请参见数据传输

    4. 执行模型推理。请参见模型推理基本场景

      模型推理结束后,需及时释放相关资源。

      若需要处理模型推理的结果,还需要进行数据后处理,例如对于图片分类应用,通过数据后处理从推理结果中查找最大置信度的类别标识。

    5. 所有数据处理结束后,需及时释放运行管理资源,请参见运行管理资源申请与释放

    6. 执行pyACL去初始化,请参见pyACL初始化与去初始化

  4. 运行应用,包括模型转换、运行应用,请参见应用调试

父主题: 开发基础推理应用

模型构建

对于开源框架的网络模型(如Caffe、TensorFlow等),不能直接在昇腾AI处理器上运行推理,需要先使用ATC(Ascend Tensor Compiler)工具将开源框架的网络模型转换为适配昇腾AI处理器的离线模型(*.om文件),模型转换的方法请参见《[转换模型](https://www.hiascend.com/document/detail/zh/Atlas200IDKA2DeveloperKit/23.0.RC2/Application Development Guide/tmuacop/tmuacop_0001.html)》。

父主题: 开发基础推理应用

在线提单