换种方式描述昇腾 AscendCL 推理流程 - 使用 Python 与 Atlas 200I DK A2 开发者套件

换种方式描述昇腾 AscendCL 推理流程 - 使用 Python 与 Atlas 200I DK A2 开发者套件


昇腾 CANN 入门推理全流程 - 超分辨率 (PyTorch, ONNX, AscendCL)
继续上文的流程继续推进学习与开发。但前面发牢骚,不喜请跳过。

昇腾文档需要一个统合所有流程的页面

不客气的说,因为我入门遇到一些疑问,而昇腾文档跟不上我的思想使我不知道该去哪解决问题。

Dataset 概念出场太晚

我使用的是 Python 的 AscendCL 开发,昇腾文档中给了非常多的“新手教程”:概述,快速入门。但是快速入门中没有 Dataset 概念的讲解,导致我不知道模型推理需要传入什么东西进去。只有看“4. 基础推理应用开发”中的“模型推理基本场景”章节中的“模型执行”节才知道有 Dataset 概念以及其内部的结构方式。

Dataset 是昇腾 OM 模型推理时使用的输入与输出数据结构,你的所有数据都要引用成 Dataset 格式才可以被执行,后面会讲到。

Python 数据需要转换为 C 指针才可以被 AscendCL 执行

同样是一个重点,而且是对 pyACL 来说非常重要的概念。而快速入门中仅介绍的收据后处理时的方式:将 C 指针(ptr)转为 Python 变量。当时理解这一段费了很大工夫,还在想 Python 如何计算字节数从而导入 Dataset。

指针不能一直保持,当数量大于等于2个对象需要转换时就需要使用设备内存复制了(见下)。

推理总流程

已经向昇腾社区提建议,所以牢骚停止。将用自己的方式来讲述如何构建 pyACL 推理流程,首先我们拼接一遍推理的所有流程:
建议找出自己版本的文档对照看:文档 - 昇腾社区

  1. 导入依赖库 pyACL import acl
  2. 初始化 AscendCL
    • 设置 Device
    • 设置 Context
    • 设置 Stream
    • 获取设备运行模式
  3. 加载模型(从文件加载)
    • 获取模型 ID
  4. 获取及设置模型信息
    • 查询字节大小(每个输入输出)
    • 只要你有需求:动态输入,多个输入输出,模型格式,浮点精度。就在这查询或设置
  5. 打开图片并预处理
    • 预处理按照训练模型时候的操作
    • 转换为 NumPy 张量格式(Tensor 也可以)
  6. 进行昇腾预处理
    • 将张量转换为字节并计算大小
    • 使用 pyACL 转换为 C 指针
  7. 内存数据复制到昇腾设备上(可选)
    • 若在昇腾设备端执行代码,且输入节点只有一个,则不需要复制内存(见下)
  8. 构造 Dataset 用于推理
    • 构造输出数据集
      • 根据查询的输出大小申请内存
      • 创建空缓冲区(buffer)
      • 将缓冲区装入 Dataset
    • 构建输入数据集
      • 将 C 指针及大小导入新建的缓冲区
      • 将缓冲区导入 Dataset
  9. 推理模型:传入模型ID 、输入输出 Dataset
  10. 复制回设备(可选)
    • 这是一个操作对,如果上面的复制没有执行,这步也不用执行。
  11. 取出数据
    • 读取推理后的张量维度大小
    • 将 C 指针转为 Python 字节
    • 使用 NumPy 将字节数据转为数组(使用维度大小生成结构)
    • 反向预处理,生成图片

一些要点讲解

昇腾社区还是有完整的流程文档的,所以再复述一遍流程也没有意思。这里将着重补充文档中我认为强调的地方。

使用 API 查询模型的各种参数

从我的(上一篇文章)可以看出,昇腾离线模型不能够完全动态的输入,而必须要对图片进行一定的预处理。我们需要实现模型在训练时的预处理,调用 API 获取模型信息可以使代码更具泛用性。

关于模型的操作都在 acl.mdl 目录下。我们需要创建一个模型描述器并与模型绑定:

model_desc = acl.mdl.create_desc()
ret = acl.mdl.get_desc(model_desc, model_id)  # 绑定模型
CheckRet("get_desc", ret)  # 此函数用于判断是否执行成功

然后我们就可以获取模型中的输入、输出、维度、数据类型等信息了。放在下面的代码参考内。

字节转为 C 指针

AscendCL 由 C 编写,Python 对象不能直接传入,数据需要转换为 C 才能被调用。这对 pyACL 来说是一个额外的步骤。

# 图片数组转字节后转 C 指针
img_bytes = img.tobytes()
img_ptr = acl.util.bytes_to_ptr(img_bytes)

AscendCL 侧的任务都完成了就转换回来:

bytes_out = acl.util.ptr_to_bytes(temp_buffer, temp_buffer_size) 
result = np.frombuffer(bytes_out, np.float32).reshape(dim)  # 恢复 NCHW 数组

设备端,超过一个以上的输入需使用内部内存复制

数据传输 通过内存复制的方式实现数据 如果在板端环境上运行应用,则无需进行数据传输。
——昇腾文档 (下有参考图)

这里不准确,事实上我也提出过疑问“如何释放转换到 C 中的指针及那部分内存?”,而且我也尝试过将字节频繁转换为 C 指针尝试使内存溢出,但是内存完全不涨。可见这些不是持久性数据,会自动清理掉,我们需要使用内存间复制保留数据。

# 两个以上输入,准备内存复制函数
def mem_copy(src_ptr, size):
    """ src_ptr: C 指针
        size: 数据大小 len()
    """
    ACL_MEMCPY_DEVICE_TO_DEVICE = 3  # Device内或Device间的内存复制
    ACL_MEM_MALLOC_NORMAL_ONLY = 2  # 仅申请普通页
    dest_ptr, ret = acl.rt.malloc(size, ACL_MEM_MALLOC_NORMAL_ONLY)
    ret = acl.rt.memcpy(
        dest_ptr,
        size,
        src_ptr,
        size,
        ACL_MEMCPY_DEVICE_TO_DEVICE
    )
    CheckRet("memcpy", ret)
    return dest_ptr

# 用完记得释放

代码参考、返回结果范例

pyACL 获取模型信息

具体描述请看昇腾文档。

def PrintModelDesc(model_desc):
    """打印模型描述的所有信息"""
    print("""get_num_inputs: {0},
            get_num_outputs: {1},
            get_input_size_by_index: {2},
            get_input_dims_v2: {3},
            get_output_dims: {4},
            get_input_name_by_index: {5},
            get_output_name_by_index: {6},
            get_input_format: {7},
            get_output_format: {8},
            get_input_data_type: {9},
            get_output_data_type: {10},
            get_dynamic_batch: {11},
            get_dynamic_hw: {12},
            get_cur_output_dims: {13},
            get_input_dynamic_gear_count: {14},
            get_input_dynamic_dims[0]: {15},
            get_input_index_by_name(image_in): {16},
            get_output_index_by_name(image_out): {17}""".format(
        acl.mdl.get_num_inputs(model_desc),  # 输入数量
        acl.mdl.get_num_outputs(model_desc),
        acl.mdl.get_input_size_by_index(model_desc, 0),  # Index 固定为 0 (第一个输入)
        acl.mdl.get_input_dims_v2(model_desc, 0)[0],  # 含返回错误码 ret ,所以切片
        acl.mdl.get_output_dims(model_desc, 0)[0],
        acl.mdl.get_input_name_by_index(model_desc, 0),  # 输入名字
        acl.mdl.get_output_name_by_index(model_desc, 0),
        acl.mdl.get_input_format(model_desc, 0),  # 输入数据格式 NCHW
        acl.mdl.get_output_format(model_desc, 0),
        acl.mdl.get_input_data_type(model_desc, 0),  # 输入数据类型 UINT8 等
        acl.mdl.get_output_data_type(model_desc, 0),
        acl.mdl.get_dynamic_batch(model_desc),
        acl.mdl.get_dynamic_hw(model_desc, 0)[0],
        acl.mdl.get_cur_output_dims(model_desc, 0)[0],  # 模型输出时的具体形状
        acl.mdl.get_input_dynamic_gear_count(model_desc, 0)[0],
        acl.mdl.get_input_dynamic_dims(model_desc, 0, 0),
        acl.mdl.get_input_index_by_name(model_desc, "image_in"),  # 通过名称查找顺序
        acl.mdl.get_output_index_by_name(model_desc, "image_out")
        )
     )
# 不止这些,还有没用上的

环境配置

设备:昇腾 Atlas 200I DK A2 开发者套件,俗名小藤

  • 软件环境:
    • Ubuntu 20.04 LTS aarch64
    • CANN 6.2
    • Python 3.9

参考

文档 - 昇腾社区

昇腾文档部分图片

昇腾文档中的快速入门数据传输描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值