ACL小模型推理(AscendCL)

ACL小模型推理(AscendCL)
ACL小模型推理(AscendCL)

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

image
简单来说,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 位参与者

阅读完整话题

来源: LinuxDo 最新话题查看原文