深度学习模型移植到嵌入式设备(华为昇腾310 Atlas 200DK)

引言

       将深度学习模型移植到嵌入式设备,需要了解模型的推理流程,以及嵌入式设备的特性。不同的嵌入式设备对模型加载推理的接口不同。此文以华为昇腾的开发套件为例,其他嵌入式设备可以参考其设备说明文档。

模型转换

       本文举例的是图像处理的模型,其他模型也类似,将推理的过程分为三个部分:预处理、推理、后处理。还需要将提前训练好的模型导出,转换成嵌入式设备支持的模型格式。在昇腾设备上有模型转换工具ATC,可以将caffe、tensorflow、ONNX等开源框架的模型,转换成适配昇腾AI处理器的离线模型。ATC的功能架构如下图所示:

       模型转换过程中,ATC会进行算子调度优化、权重数据重排、内存使用优化等具体操作,对原始的深度学习模型进行进一步的调优,从而满足部署场景下的高性能需求,使其能够高效执行在昇腾AI处理器上。我们使用atc命令就可以运行ATC工具,其中有很多参数,以下参数是必须的:

--model:需要转换的模型的路径名称

--output:转换结果模型的存放路径及名称

--framework:原始框架类型 0(Caffe) 1(MindSpore) 3(TensorFlow) 5(ONNX)

--soc_version:模型转换时指定芯片版本

如果模型有输入,则需要增加参数--input_shape:设置输入数据的尺寸,分别为:N,C,H,W。还有参数--input_format:设置输入数据格式,Caffe默认为NCHW,TensorFlow默认为NHWC。

运行结果如下图所示,表示模型转换成功,在指定目录下会出现.om文件。

推理代码

       在昇腾芯片上有自己的模型推理API,需要根据说明文档合理调用API。首先,需要确定用什么语言来编写推理代码,目前昇腾310支持C&C++和python语言。确定好语言之后,就要了解在昇腾芯片上推理的流程是什么样的。以python语言为例,(pyACL就是在AscendCL的基础上使用CPython封装得到的Python API库,使用户可以通过Python进行昇腾AI处理器的运行管理、资源管理等)具体流程如下:

  1. pyACL初始化。

    调用acl.init接口实现初始化pyACL。

  2. 运行管理资源申请。

    依次申请运行管理资源:Device、Context、Stream。

  3. 模型推理/单算子调用/媒体数据处理。
    • 模型加载:模型推理前,需要先将对应的模型加载到系统中。
    • (可选)媒体数据处理:可实现JPEG图片解码、视频解码、抠图/图片缩放/格式转换、JPEG图片编码等功能。
    • 模型执行:使用模型实现图片分类、目标识别等功能。
    • (可选)数据后处理:处理模型推理的结果,此处根据用户的实际需求来处理推理结果,例如用户可以将获取到的推理结果写入文件、从推理结果中找到每张图片最大置信度的类别标识等。
    • 模型卸载:调用acl.mdl.unload接口卸载模型。
  4. 运行管理资源释放。

    所有数据处理都结束后,需要依次释放运行管理资源:Stream、Context、Device。

  5. pyACL去初始化。

    调用acl.finalize接口实现pyACL去初始化。

      按照上述步骤编写推理代码,具体用到的一些特定的接口函数可参考说明文档。这里提供一个简单的推理代码样例:

import os
import cv2
import acl

NPY_FLOAT32 = 11
ACL_MEMCPY_HOST_TO_HOST = 0
ACL_MEMCPY_HOST_TO_DEVICE = 1
ACL_MEMCPY_DEVICE_TO_HOST = 2
ACL_MEMCPY_DEVICE_TO_DEVICE = 3
ACL_MEM_MALLOC_HUGE_FIRST = 0
ACL_DEVICE, ACL_HOST = 0, 1
ACL_SUCCESS = 0


class UNet(object):
    def __init__(self, device_id, model_path, model_width, model_height):
        self.device_id = device_id      # int
        self.context = None             # pointer
        self.stream = None
        self.model_width = model_width
        self.model_height = model_height
        self.model_id = None            # pointer
        self.model_path = model_path    # string
        self.model_desc = None          # pointer when using
        self.input_dataset = None
        self.output_dataset = None
        self.input_buffer = None
        self.output_buffer = None
        self.input_buffer_size = None
        self.image_bytes = None
        self.image_name = None
        self.dir = None
        self.image = None


    def init_resource(self):  #pyACL初始化
        ret = acl.init()
        if ret != ACL_SUCCESS:
            print('acl init failed, errorCode is', ret)
        ret = acl.rt.set_device(self.device_id)
        if ret != ACL_SUCCESS:
            print('set device failed, errorCode is', ret)
        self.context, ret = acl.rt.create_context(self.device_id)
        if ret != ACL_SUCCESS:
            print('create context failed, errorCode is', ret)
        self.stream, ret = acl.rt.create_stream()
        if ret != ACL_SUCCESS:
            print('create stream failed, errorCode is', ret)

        self.model_id, ret = acl.mdl.load_from_file(self.model_path)
        if ret != ACL_SUCCESS:
            print('load model failed, errorCode is', ret)

        self.model_desc = acl.mdl.create_desc()
        ret = acl.mdl.get_desc(self.model_desc, self.model_id)
        if ret != ACL_SUCCESS:
            print('get desc failed, errorCode is', ret)

        self.input_dataset = acl.mdl.create_dataset()
        input_index = 0
        self.input_buffer_size = acl.mdl.get_input_size_by_index(self.model_desc, input_index)
        self.input_buffer, ret = acl.rt.malloc(self.input_buffer_size, ACL_MEM_MALLOC_HUGE_FIRST)
        input_data = acl.create_data_buffer(self.input_buffer, self.input_buffer_size)
        self.input_dataset, ret = acl.mdl.add_dataset_buffer(self.input_dataset, input_data)
        if ret != ACL_SUCCESS:
            print('acl.mdl.add_dataset_buffer failed, errorCode is', ret)

        self.output_dataset = acl.mdl.create_dataset()
        output_index = 0
        output_buffer_size = acl.mdl.get_output_size_by_index(self.model_desc, output_index)
        self.output_buffer, ret = acl.rt.malloc(output_buffer_size, ACL_MEM_MALLOC_HUGE_FIRST)
        output_data = acl.create_data_buffer(self.output_buffer, output_buffer_size)
        self.output_dataset, ret = acl.mdl.add_dataset_buffer(self.output_dataset, output_data)
        if ret != ACL_SUCCESS:
            print('acl.mdl.add_dataset_buffer failed, errorCode is', ret)


    def process_input(self, input_path):
        
        ####图像预处理代码####

        image_data = image_data.copy()
        self.image_bytes = np.frombuffer(image_data.tobytes())
        

    def inference(self): #推理
        if self.runMode_ == ACL_DEVICE:
            kind = ACL_MEMCPY_DEVICE_TO_DEVICE
        else:
            kind = ACL_MEMCPY_HOST_TO_DEVICE
        if "bytes_to_ptr" in dir(acl.util):
            bytes_data = self.image_bytes.tobytes()
            ptr = acl.util.bytes_to_ptr(bytes_data)
        else:
            ptr = acl.util.numpy_to_ptr(self.image_bytes)
        ret = acl.rt.memcpy(self.input_buffer,
                            self.input_buffer_size,
                            ptr,
                            self.input_buffer_size,
                            kind)
        if ret != ACL_SUCCESS:
            print('memcpy failed, errorCode is', ret)

        ret = acl.mdl.execute(self.model_id,
                              self.input_dataset,
                              self.output_dataset)
        if ret != ACL_SUCCESS:
            print('execute failed, errorCode is', ret)

    def get_result(self): #图像后处理

        output_index = 0
        output_data_buffer = acl.mdl.get_dataset_buffer(self.output_dataset, output_index)
        output_data_buffer_addr = acl.get_data_buffer_addr(output_data_buffer)
        output_data_size = acl.get_data_buffer_size(output_data_buffer)
        ptr, ret = acl.rt.malloc_host(output_data_size)

        ret = acl.rt.memcpy(ptr,
                            output_data_size,
                            output_data_buffer_addr,
                            output_data_size,
                            ACL_MEMCPY_DEVICE_TO_HOST)
        if ret != ACL_SUCCESS:
            print('memcpy failed, errorCode is', ret)

        index = 0
        dims, ret = acl.mdl.get_cur_output_dims(self.model_desc, index)
        if ret != ACL_SUCCESS:
            print('get output dims failed, errorCode is', ret)
        out_dim = dims['dims']

        if "ptr_to_bytes" in dir(acl.util):
            bytes_data = acl.util.ptr_to_bytes(ptr, output_data_size)
            data = np.frombuffer(bytes_data, dtype=np.float32).reshape(out_dim)
        else:
            data = acl.util.ptr_to_numpy(ptr, out_dim, NPY_FLOAT32)
        
        ####图像后处理代码####


    def release_resource(self):  #pyACL去初始化
        acl.rt.free(self.input_buffer)
        acl.mdl.destroy_dataset(self.input_dataset)
        acl.rt.free(self.output_buffer)
        acl.mdl.destroy_dataset(self.output_dataset)

        ret = acl.mdl.unload(self.model_id)
        if ret != ACL_SUCCESS:
            print('unload model failed, errorCode is', ret)
        if self.model_desc:
            acl.mdl.destroy_desc(self.model_desc)
            self.model_desc = None

        if self.stream:
            ret = acl.rt.destroy_stream(self.stream)
            if ret != ACL_SUCCESS:
                print('destroy stream failed, errorCode is', ret)
            self.stream = None
        if self.context:
            ret = acl.rt.destroy_context(self.context)
            if ret != ACL_SUCCESS:
                print('destroy context failed, errorCode is', ret)
            self.context = None
        
        ret = acl.rt.reset_device(self.device_id)
        if ret != ACL_SUCCESS:
            print('reset device failed, errorCode is', ret)
                
        ret = acl.finalize()
        if ret != ACL_SUCCESS:
            print('finalize failed, errorCode is', ret)

if __name__ == '__main__':
    device = 0
    model_width = 512
    model_height = 512
    current_dir = os.path.dirname(os.path.abspath(__file__))
    model_path = os.path.join(current_dir, "../model/unet.om")
    if not os.path.exists(model_path):
        raise Exception("the model is not exist")

    images_path = os.path.join(os.path.dirname(current_dir), "data")
    if not os.path.exists(images_path):
        raise Exception("the directory is not exist")
    all_path = []
    for path in os.listdir(images_path):
        if path != '.keep':
            total_path = os.path.join(images_path, path)
            all_path.append(total_path)
    if len(all_path) == 0:
        raise Exception("the directory is empty, please download image")

    net = UNet(device, model_path, model_width, model_height)
    net.init_resource()
    for path in all_path:
        net.process_input(path)
        net.inference()
        net.get_result()
    print("*****run finish******")
    net.release_resource()

代码测试

       在运行代码前,需要先构建好代码目录,存放代码文件、编译脚本、测试图片数据、模型文件等。可以构建以下目录:

      在主目录下运行python脚本即可。我使用的CANN版本是6.0.3,运行示例如下:

代码运行问题与解决方案

      在运行代码过程中会遇到各种各样的问题,下面举例几个我遇到的问题,以供参考。

问题1 

在将onnx文件转换为om文件时,出现以下报错:

解决方法:因为原先使用的CANN版本是5.0.2.alpha002,版本比较旧,有些算子不支持。类似的问题一般更新CANN版本后就能解决。

问题2

      运行代码报错如下:

解决方法:在命令行输入export LD_PRELOAD=$LD_PRELOAD:/home/HwHiAiUser/.local/lib/python3.9/site-packages/torch/lib/../../torch.libs/libgomp-d22c30c5.so.1.0.0(目录根据自己的操作系统目录而定)

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值