跳到主要内容

模型构建

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包。
    . ${HOME}/Ascend/ascend-toolkit/set_env.sh
  • 若环境中安装了cann-nnrt软件包:

    # 以root用户安装nnrt包。
    . /usr/local/Ascend/nnrt/set_env.sh
    # 以非root用户安装nnrt包。
    . ${HOME}/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)》。

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

pyACL初始化与去初始化

关于pyACL初始化与去初始化的接口调用流程,请参见pyACL接口调用流程

基本原理

您必须调用acl.init接口初始化pyACL,配置文件内容为JSON格式。

如果当前的默认配置已满足需求,无需修改,acl.init接口中可不传入参数,或者可将配置文件配置为空JSON串(即配置文件中只有{})。在acl.init接口中不传入参数,示例如下:

ret = acl.init()

有初始化就有去初始化,在确定完成了pyACL的所有调用之后,或者进程退出之前,需调用acl.finalize接口实现pyACL去初始化

示例代码

您可以从样例介绍中获取完整样例代码。

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

import acl
## ......
## 初始化基本配置。
## 此处的“..”表示相对路径,相对可执行文件所在的目录。
acl_config_path = "../src/acl.json"
ret = acl.init(acl_config_path)
## ......

## 去初始化。
ret = acl.finalize()
## ......

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

运行管理资源申请与释放

开发应用时,应用程序中必须包含运行管理资源申请的代码逻辑,关于运行管理资源申请的接口调用流程,请先参见pyACL接口调用流程了解整体流程,再查看本节中的资源申请、释放流程说明。

基本原理

您需要按顺序依次申请如下运行管理资源:Device、Context、Stream,确保可以使用这些资源执行运算、管理任务。所有数据处理都结束后,需要按顺序依次释放运行管理资源:Stream、Context、Device。

您需要按照Device、Context、Stream的顺序依次申请。其中,创建Context、Stream的方式分为隐式创建和显式创建,其适用场景有所不同:

  • 隐式创建Context和Stream:适合简单、无复杂交互逻辑的应用,但缺点在于,在多线程编程中,每个线程都使用默认Context或默认Stream,默认Stream中任务的执行顺序取决于操作系统线程调度的顺序。

  • 显式创建Context和Stream:推荐显式,适合大型、复杂交互逻辑的应用,且便于提高程序的可读性、可维护性。

  • 关于单进程、单线程、单Stream场景如下所示:

    • 单进程:一个应用程序对应一个进程。

    • 单线程:不创建多个线程时,默认只有一个线程。

    • 单Stream:整个开发的过程中使用同一个Stream。

      对于同一个Stream中的异步任务,pyACL会按照应用程序中任务的顺序执行任务,确保异步任务执行的顺序。

  • 关于多线程、多Stream的场景请参见Stream管理

运行管理资源申请流程

图1 运行管理资源申请流程

关键接口的说明如下:

  1. 申请运行管理资源时,需按顺序依次申请:Device、Context、Stream。

  2. (可选)调用acl.rt.get_run_mode接口获取软件栈的运行模式,根据运行模式来判断后续的内存申请接口调用逻辑。

    如果查询结果为ACL_HOST,则数据传输时涉及申请Host上的内存。

    如果查询结果为ACL_DEVICE,则数据传输时仅需申请Device上的内存。

    数据传输的详细介绍请参见数据传输

运行管理资源释放流程

图2 运行管理资源释放流程

关键接口的说明如下:

示例代码

您可以从样例介绍中获取完整样例代码。

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

<br> 1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>python<br>import acl<br>#......<br># ======运行管理资源申请======<br># 1.指定运算的Device。<br>ret = acl.rt.set_device(device_id)<br># 2.显式创建一个Context,用于管理Stream对象。<br>context, ret = acl.rt.create_context(device_id)<br># 3.显式创建一个Stream。<br>#用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序执行任务。<br>stream, ret = acl.rt.create_stream()<br># ======运行管理资源申请======<br>#......<br># ======运行管理资源释放======<br>ret = acl.rt.destroy_stream(stream)<br>ret = acl.rt.destroy_context(context)<br>ret = acl.rt.reset_device(device_id)<br># ======运行管理资源释放======<br>#......<br>

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

接口调用流程

数据传输的关键接口调用流程如下:

  1. 申请内存

  2. 将数据读入内存

    由用户自行管理数据读入内存的实现逻辑。

  3. 通过内存复制实现数据传输

    数据传输可以通过内存复制的方式实现,分为同步内存复制异步内存复制

    :::note 说明 Ascend RC场景下,不涉及Host上的内存申请、Host内的数据传输、Host与Device之间的数据传输。

    如果当前版本支持多种运行形态,在这种场景下,若想实现相同的应用程序可支持在多种形态下运行,申请内存的方式不同,会影响数据传输时调用的接口:

    • 若应用程序中区分申请Host内存或Device内存的接口,例如使用acl.rt.malloc_host接口申请Host内存、使用acl.rt.malloc接口申请Device内存时:

      需先调用acl.rt.get_run_mode接口获取软件栈的运行模式。

      • 当查询结果为ACL_HOST = 1,则数据传输时涉及申请Host上的内存。
      • 当查询结果为ACL_DEVICE = 0 ,则数据传输时不涉及申请Host上的内存,仅需申请Device上的内存。

      该种方式多一些代码逻辑的判断,不需要由用户处理Device上的内存对齐。在Device上运行应用的场景,该种方式少一些内存复制的步骤,性能较好。

    • 若应用程序中不区分申请Host内存或Device内存的接口,统一使用acl.rt.malloc_host接口(该接口支持申请Host或Device内存),pyACL内部会根据软件栈的运行模式自行判断运行时申请的是Host内存还是Device内存:

      无需调用acl.rt.get_run_mode接口获取软件栈的运行模式。该种方式代码逻辑相比前一种简单,但需由用户处理Device上的内存对齐。 :::

父主题: 数据传输

Device内的数据传输

同步内存复制

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

import acl
## ......

## 1.申请内存。
size = 1 * 1024 * 1024
dev_ptr_a, ret = acl.rt.malloc(size, ACL_MEM_MALLOC_NORMAL_ONLY)
dev_ptr_b, ret = acl.rt.malloc(size, ACL_MEM_MALLOC_NORMAL_ONLY)

## 2.申请内存后,可向内存中读入数据,该自定义函数read_file由用户实现。
read_file(fileName, dev_ptr_a, size)

## 3.同步内存复制。
## 同步内存复制,dev_ptr_a表示Device上源内存地址的指针地址,dev_ptr_b表示Device上目的内存地址的指针地址,size表示内存大小。
## ACL_MEMCPY_DEVICE_TO_DEVICE = 3。
ret = acl.rt.memcpy(dev_ptr_b, size, dev_ptr_a, size, ACL_MEMCPY_DEVICE_TO_DEVICE)

## 4.使用完内存中的数据后,需及时释放资源。
ret = acl.rt.free(dev_ptr_a)
ret = acl.rt.free(dev_ptr_b)

## ......

异步内存复制

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

import acl
## ......

## 1.申请内存。
size = 1 * 1024 * 1024
dev_ptr_a, ret = acl.rt.malloc(size, ACL_MEM_MALLOC_NORMAL_ONLY)
dev_ptr_b, ret = acl.rt.malloc(size, ACL_MEM_MALLOC_NORMAL_ONLY)

## 2.申请内存后,可向内存中读入数据,该自定义函数read_file由用户实现。
read_file(fileName, dev_ptr_a, size)

## 3.异步内存复制。
## 异步内存复制,dev_ptr_a表示Device上源内存地址的指针地址,dev_ptr_b表示Device上目的内存地址的指针地址,size表示内存大小。
## ACL_MEMCPY_DEVICE_TO_DEVICE = 3。
stream = acl.rt_create_stream()
ret = acl.rt.memcpy_async(dev_ptr_b, size, dev_ptr_a, size, ACL_MEMCPY_DEVICE_TO_DEVICE, stream)
ret = acl.rt.synchronize_stream(stream)

## 4.使用完内存中的数据后,需及时释放资源。
ret = acl.rt.destroy_stream(stream)
ret = acl.rt.free(dev_ptr_a)
ret = acl.rt.free(dev_ptr_b)

## ......

父主题: 数据传输

在线提单