换种方式描述昇腾 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 推理流程,首先我们拼接一遍推理的所有流程:
建议找出自己版本的文档对照看:文档 - 昇腾社区
- 导入依赖库 pyACL
import acl
- 初始化 AscendCL
- 设置 Device
- 设置 Context
- 设置 Stream
- 获取设备运行模式
- 加载模型(从文件加载)
- 获取模型 ID
- 获取及设置模型信息
- 查询字节大小(每个输入输出)
- 只要你有需求:动态输入,多个输入输出,模型格式,浮点精度。就在这查询或设置
- 打开图片并预处理
- 预处理按照训练模型时候的操作
- 转换为 NumPy 张量格式(Tensor 也可以)
- 进行昇腾预处理
- 将张量转换为字节并计算大小
- 使用 pyACL 转换为 C 指针
- 内存数据复制到昇腾设备上(可选)
- 若在昇腾设备端执行代码,且输入节点只有一个,则不需要复制内存(见下)
- 构造 Dataset 用于推理
- 构造输出数据集
- 根据查询的输出大小申请内存
- 创建空缓冲区(buffer)
- 将缓冲区装入 Dataset
- 构建输入数据集
- 将 C 指针及大小导入新建的缓冲区
- 将缓冲区导入 Dataset
- 构造输出数据集
- 推理模型:传入模型ID 、输入输出 Dataset
- 复制回设备(可选)
- 这是一个操作对,如果上面的复制没有执行,这步也不用执行。
- 取出数据
- 读取推理后的张量维度大小
- 将 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