ACL学习总结(AscendCL)
分享一份自己学习ACL的笔记,由于内容超出了最大的字符上限(共69168),所以选择直接分享一份语雀链接,链接在图片下方,大致内容如下,欢迎各位大佬前来互相交流学习:

简单来说,ACL的作用就是用来在昇腾(Ascend)设备上做小模型推理加速,例如yolo、ResNet等等各类模型,应用场景比如视频监控实时判定等等
https://www.yuque.com/u48066541/db8sl3/evem2liq09npe599?singleDoc# 《AscendCL》
上面给出的链接里代码案例使用的是C++,这里在给一份包含详细注释的Python版本
Resnet50
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
#定义模型对象
class Net:
def __init__(self,model_path):
#构造函数
#acl初始化,device指定
self.device_id = 0
ret = acl.init()
ret = acl.rt.set_device(self.device_id)
#模型加载,返回模型的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)
#创建输入输出数据集
#创建输入数据集
self.input_dataset, self.input_data = self.prepare_dataset("input")
#创建输出数据集
self.output_dataset, self.output_data = self.prepare_dataset("output")
#实现输入输出数据集类型的创建
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 = []
#循环创建输入或输出数据类型,把每个输入或输出(buffer)装入对应的dataset中
for i in range(io_num):
#从模型描述信息中通过索引获取所需的buffer内存大小
buffer_size = acl_mdl_get_size_by_index(self.model_desc,i)
#申请buffer_size大小的buff内存,并把申请的内存地址指针给buffer
buffer, ret = acl.rt.malloc(buffer_size,ACL_MEM_MALLOC_HUGE_FIRST)
#从内存创建大小为buffer_size的buffer对象,存储到buffer指针中
data_buffer = acl.create_data_buffer(buffer,buffer_size)
#将buffer数据添加到数据集中
_, ret = acl.mdl.add_dataset_buffer(dataset,data_buffer)
datas.append({"buffer": buffer, "data": data_buffer, "size": buffer_size})
return dataset,datas
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侧,用于推理(申请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 #cp模式:host to device
)
#模型推理,传入需要推理的模型id,输入输出数据集合
ret = acl.mdl.execute(self.model_id,self.input_dataset,self.output_dataset)
#对推理的输出数据进行数据后处理,输出top5置信度的类别编号
inference_result = []
#遍历 输出集合output_data 里的所有元素,获取集合元素与其索引
for i,item in enumerate(self.output_data):
#申请host侧内存
self.buffer_host, ret = acl.rt.malloc_host(self.output_data[i]["size"])
#将数据从device侧返回host侧
ret = acl.rt.memcpy(self.buffer_host, #目标地址 host
self.output_data[i]["size"], #目标地址大小
self.output_data[i]["buffer"], #源地址 device
self.output_data[i]["size"], #源地址大小
ACL_MEMCPY_DEVICE_TO_HOST #cp模式:device to host
)
#从host侧内存地址获取(转换)bytes对象 参数1:需要转换的对象(内存地址) 参数2:数据的大小(要读取的字节数)
bytes_out = acl.util.ptr_to_bytes(self.buffer_host,self.output_data[i]["size"])
#按照float32格式将数据转为numpy数组,即将这些字节数据视为32位浮点数来解释
data = np.frombuffer(bytes_out, dtype=np.float32)
inference_result.append(data)
#将得到的data转换为numpy数组,一维
vals = np.array(inference_result).flatten()
#对结果进行softmax转换,使其变为概率分布
vals = np.exp(vals)
vals = vals / np.sum(vals)
return vals
def transfer_pic(input_path):
#数据预处理,每个模型的不同,预处理的操作也会有所不同
#将路径转换为绝对路径
input_path = os.path.abspath(input_path)
#使用 PIL的 Image.open() 方法打开图像文件,返回一个Image对象,用于图像裁剪等后续处理
with Image.open(input_path) as image_file:
#resnet50模型,图片裁剪为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模型为彩色通道在前,即HWC→CHW
img = img.transpose(2,0,1)
#返回并添加batch通道
return np.array([img])
def print_top_5(data):
#数据后处理,展示top5概率
#argsort函数:升序排序,[::-1]代表反转索引数组,即升序改为降序,[:5]代表截取前五个元素
top_5 = data.argsort()[::-1][:5]
print("======== top5 inference results: =============")
for i in top_5:
print("[%d]: %f" % (i,data[i]))
def __del__(self):
#析构函数,按照资源申请的相反顺序进行销毁,回收内存
#释放host内存
ret = acl.rt.free_host(self.buffer_host)
#销毁输入输出数据集
#先销毁dataset内的buffer
for dataset in[self.input_data,self.output_data]:
#确保dataset不为空
while dataset:
#从列表末尾移除一个元素
item = dataset.pop()
#销毁buffer数据
ret = acl.destroy_data_buffer(item["data"])
#释放buffer内存
ret = acl.rt.free(item["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()
#主调函数入口
if __name__ == "__main__":
#类对象
resnet50 = Net("/home/work/zxy/pyACL/om/cls/resnet50_310P3.om")
#数据
image_paths = ["/home/work/zxy/pyACL/data/cls/dog1_1024_683.jpg"]
#遍历数组集合
for path in image_paths:
#调用数据预处理函数,对每个数据进行预处理
image = transfer_pic(path)
#将数据按照每个输入的顺序构造list传入,调用推理函数进行推理
result = resnet50.forward([image])
#数据后处理
print_top_5(result)
del resnet50
1 个帖子 - 1 位参与者