引言
将深度学习模型移植到嵌入式设备,需要了解模型的推理流程,以及嵌入式设备的特性。不同的嵌入式设备对模型加载推理的接口不同。此文以华为昇腾的开发套件为例,其他嵌入式设备可以参考其设备说明文档。
模型转换
本文举例的是图像处理的模型,其他模型也类似,将推理的过程分为三个部分:预处理、推理、后处理。还需要将提前训练好的模型导出,转换成嵌入式设备支持的模型格式。在昇腾设备上有模型转换工具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处理器的运行管理、资源管理等)具体流程如下:
- pyACL初始化。
- 运行管理资源申请。
- 模型推理/单算子调用/媒体数据处理。
- 模型加载:模型推理前,需要先将对应的模型加载到系统中。
- (可选)媒体数据处理:可实现JPEG图片解码、视频解码、抠图/图片缩放/格式转换、JPEG图片编码等功能。
- 模型执行:使用模型实现图片分类、目标识别等功能。
- (可选)数据后处理:处理模型推理的结果,此处根据用户的实际需求来处理推理结果,例如用户可以将获取到的推理结果写入文件、从推理结果中找到每张图片最大置信度的类别标识等。
- 模型卸载:调用acl.mdl.unload接口卸载模型。
- 运行管理资源释放。
- 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(目录根据自己的操作系统目录而定)
今天的文章深度学习模型移植到嵌入式设备(华为昇腾310 Atlas 200DK)分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/83523.html