关键词:Paddle,PaddleNLP, UIE,ATC, ACL,onnx模型, om模型,昇腾310推理卡
需求背景
有一个通用实体关系事件抽取的任务,需要使用到UIE模框架,而且需要将起部署到昇腾310服务器上,因为UIE模型底层使用的是ernie3.0,但是目前paddle官方还不支持ernie3.0模型在昇腾310上部署,所以才有了以下的操作,主要过程是,先试用paddle训练处模型,然后使用 paddle2onnx.command.c_paddle_to_onnx方法将paddle的模型转为onnx模型 ,因现在的onnx模型是动态的shape和散乱的算子形态,需要使用paddle自带的工具paddle2onnx.optimize将onnx模型先进行重塑,固定好shape的维度,将散乱的算子进行整合,命令如下:
$ python -m paddle2onnx.optimize --input_model /home/user/lijiaqi/PaddleNLP/model_zoo/uie/export_new/model.onnx --output_model /home/user/lijiaqi/model_new_uie.onnx --input_shape_dict "{'att_mask':[1,512],'pos_ids':[1,512],'token_type_ids':[1,512],'input_ids':[1,512]}"
然后将onnx模型在使用ATC工具转为acl所需要的om模型,这一步后面会讲。
另外在使用acl部署的时候,paddle框架是不能使用的,acl使用到的模型和训练过程均需要自己实现,包括from_pretrain阶段的分词,建立词表,数据处理部分,这部分我已经实现完,纯python版本的实现,有需要的可以去GitHub上看一下。
1. ACL应用案例部署
2.基础知识
2.1 ATC介绍
ATC(Ascend Tensor Compiler)是华为昇腾软件栈提供的一个编译工具,它的主要功能是将基于开源框架的网络模型(如Caffe、TensorFlow等)以及单算子Json文件,转换成昇腾AI处理器支持的离线模型Offline-Model文件(简称OM文件)。在编译过程中,可以实现算子调度的优化、权值数据重排、内存使用优化等,并且可以脱离设备完成模型的预处理。更详细的ATC介绍,可参看官方文档 。
ATC工具架构图
2.2 ACL介绍
对已训练好的权重文件,比如caffe框架下的caffemodel, tensorflow框架下得到的checkpoint或者pb文件,再经过ATC工具转换后得到的离线模型文件,ACL(Ascend Computing Language,昇腾计算语言)提供了一套用于在昇腾系列处理器上进行加速计算的API。基于这套API,您能够管理和使用昇腾软硬件计算资源,并进行机器学习相关计算。更详细的ACL介绍,可参看官方文档 。
当前ACL为您提供了C/C++和Python编程接口,能够很方便的帮助您达成包括但不限于如下这些目标:
1. 加载深度学习模型进行推理
2. 加载单算子进行计算
3. 文本、图像、视频数据的预处理
3.准备过程
本实验的最终结果是使用paddlenlp的UIE模型对文本进行实体关系事件的抽取。示例使用的是 官网的文本示例:
"texts":["北京市海淀区人民法院\n民事判决书\n(199x)建初字第xxx号\n原告:张三。\n委托代理人李四,北京市 A律师事务所律师。\n被告:B公司,法定代表人王五,开发公司总经理。\n委托代理人赵六,北京市 C律师事务所律师。", "原告赵六,2022年5月29日生\n委托代理人孙七,深圳市C律师事务所律师。\n被告周八,1990年7月28日出生\n委托代理人吴九,山东D律师事务所律师"],
"schema":["法院", {"原告": "委托代理人"}, {"被告": "委托代理人"}]
使用ATC工具,将paddle的.pdiparams模型转换成昇腾支持的om(offine-model)文件。命令如下:
$ atc --model=/root/lijiaqi/UIE/model/model_new_uie.onnx --output=/root/lijiaqi/UIE/model/model_uie_v2 --framework=5 --soc_version=Ascend310
4. ACL的调用过程
4.1 初始化
在开始调用ACL的任何接口之前,首先要做ACL的初始化。初始化的代码很简单,只有一行:
acl.init(config_path)
这个接口调用会帮您准备好ACL的运行时环境。其中调用时传入的参数是一个配置文件在磁盘上的路径,这里我们暂时不必关注。
有初始化就有去初始化,在确定完成了ACL的所有调用之后,或者进程退出之前,要做去初始化操作,接口调用也十分简单:
acl.finalize()
接口介绍:
函数示例:
def init():
ret = acl.init()
check_ret("acl.init", ret)
4.2 申请计算资源
想要使用昇腾处理器提供的加速计算能力,需要对运行管理资源申请,包括Device、Context、Stream。且需要按顺序申请,主要涉及以下三个接口:
acl.rt.set_device(device_id)
这个接口指定计算设备,告诉运行时环境我们想要用哪个设备,或者更具体一点,哪个芯片。但是要注意,芯片和我们在这里传入的编号之间并没有物理上的一一对应关系。
acl.rt.create_context(device_id)
Context作为一个容器,管理了所有对象(包括Stream、Event、设备内存等)的生命周期。不同Context的Stream、不同Context的Event是完全隔离的,无法建立同步等待关系。
acl.rt.create_stream()
Stream用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序在Device上执行。基于Stream的kernel执行和数据传输能够实现Host运算操作、Host与Device间的数据传输、Device内的运算并行。
接口介绍:
函数示例:
def init(self):
"""
Init resource
"""
print("init resource stage:")
ret = acl.init()
utils.check_ret("acl.init", ret)
#指定用于运算的Device
ret = acl.rt.set_device(self.device_id)
utils.check_ret("acl.rt.set_device", ret)
print("Set device n success.")
#显式创建一个Context
self.context, ret = acl.rt.create_context(self.device_id)
utils.check_ret("acl.rt.create_context", ret)
#创建一个Stream
self.stream, ret = acl.rt.create_stream()
utils.check_ret("acl.rt.create_stream", ret)
#获取当前昇腾AI软件栈的运行模式
#0:ACL_DEVICE,表示运行在Device的Control CPU上或开发者版上
#1:ACL_HOST,表示运行在Host CPU上
self.run_mode, ret = acl.rt.get_run_mode()
utils.check_ret("acl.rt.get_run_mode", ret)
print("Init resource success")
4.3 加载模型
既然要调用模型进行推理,首先当然是要把模型加载进来。ACL提供了多种模型加载和内存管理方式,这里我们只选取其中相对简单的一种,即从磁盘上加载离线模型,并且加载后的模型内存由ACL自动管理:
model_path = "./UIE/model/model_tiny_out.om"; #模型文件在磁盘上的路径
model_id, ret = acl.mdl.load_from_file(model_path) #加载模型
其中,model_id是系统完成模型加载后生成的模型ID对应的指针对象 加载后生成的modelId,全局唯一
记得这个“model_id”,后边我们使用模型进行推理,以及卸载模型的时候还要用到。
有加载自然就有卸载,模型卸载的接口比较简单:
acl.mdl.unload(model_id)
至此,您脑海中应该有这样一个概念,即使用了任何的资源,加载了任何的素材,都要记得用完后销毁和卸载。
接口介绍:
函数示例:
def load_model(model_path):
model_path = "./UIE/model/model_tiny_out.om"
model_id, ret = acl.mdl.load_from_file(model_path)
check_ret("acl.mdl.load_from_file", ret)
return model_id
4.4 获取模型信息
模型描述需要特殊的数据类型,使用以下函数来创建并获取该数据类型。
acl.mdl.create_desc()
acl.mdl.get_desc(model_desc, model_id)
获取到模型类型后,还需要根据该类型来获取模型的输入输出个数,调用函数如下:
acl.mdl.get_num_inputs(model_desc)
acl.mdl.get_num_outputs(model_desc)
接口介绍:
函数示例:
def get_model_data(model_id):
global model_desc
model_desc = acl.mdl.create_desc()
ret = acl.mdl.get_desc(model_desc, model_id)
check_ret("acl.mdl.get_desc", ret)
input_size = acl.mdl.get_num_inputs(model_desc)
output_size = acl.mdl.get_num_outputs(model_desc)
return input_size, output_size
4.5 申请device内存
要使用NPU进行加速计算,首先要申请能够被NPU直接访问到的专用内存。在讲解内存申请之前,首先我们要区分如下两个概念:
Host:Host指与Device相连接的X86服务器、ARM服务器,会利用Device提供的NN(Neural-Network )计算能力,完成业务。 Device:Device指安装了芯片的硬件设备,利用PCIe接口与Host侧连接,为Host提供NN计算能力。
简而言之,我们的数据需要先从Host侧进行加载,即读进Host侧内存,随后将其拷贝到Device侧内存,才能进行计算。计算后的结果还要传回Host侧才能进行使用。
申请Device侧内存:
dev_ptr, ret = acl.rt.malloc(size, policy) #申请device侧内存
dev_ptr; //device侧内存指针
size; //device侧内存大小
这里我们分配了跟Host侧同样大小的内存,准备用于在Device侧存放推理数据。本接口最后一个参数policy是内存分配规则。
ACL_MEM_MALLOC_HUGE_FIRST ----- 优先申请大页内存,如果大页内存不够,则使用普通页的内存。
ACL_MEM_MALLOC_HUGE_ONLY ----- 仅申请大页,如果大页内存不够,则返回错误。
ACL_MEM_MALLOC_NORMAL_ONLY ----- 仅申请普通页。
使用完别忘了释放申请过的内存:
`acl.rt.malloc-> acl.rt.free`
接口介绍:
函数示例:
def gen_data_buffer(size, des):
global model_desc
func = buffer_method[des]
for i in range(size):
temp_buffer_size = acl.mdl.get_output_size_by_index(model_desc, i)
temp_buffer, ret = acl.rt.malloc(temp_buffer_size,
const.ACL_MEM_MALLOC_NORMAL_ONLY)
check_ret("acl.rt.malloc", ret)
if des == "in":
input_data.append({"buffer": temp_buffer,
"size": temp_buffer_size})
elif des == "out":
output_data.append({"buffer": temp_buffer,
"size": temp_buffer_size})
def malloc_device(input_num, output_num):
gen_data_buffer(input_num, des="in")
gen_data_buffer(output_num, des="out")
4.6 输入数据的处理及从host复制到device的传输
输入数据极为文本,不用处理,只要batch_size,shape符合om模型的要求就可以。
4.6.1 host传输数据至device
把数据从Host侧拷贝至Device侧
acl.rt.memcpy(dst, dest_max, src, count, direction)
参数的顺序是:目的内存地址,目的内存最大大小,源内存地址,拷贝长度,拷贝方向。
direction拷贝方向当前支持四种:
ACL_MEMCPY_HOST_TO_HOST ----- host->host
ACL_MEMCPY_HOST_TO_DEVICE ----- host->device
ACL_MEMCPY_DEVICE_TO_HOST ----- device->host
ACL_MEMCPY_DEVICE_TO_DEVICE ----- device->device
代码示例:
model_input_ids_np_ptr = acl.util.numpy_to_ptr(model_input_ids)
model_token_type_ids_np_ptr = acl.util.numpy_to_ptr(model_token_type_ids)
model_position_ids_np_ptr = acl.util.numpy_to_ptr(model_position_ids)
model_attention_mask_np_ptr = acl.util.numpy_to_ptr(model_attention_mask)
ret = acl.rt.memcpy(temp_data_buffer[0]["buffer"], temp_data_buffer[0]["size"], model_attention_mask_np_ptr, temp_data_buffer[0]["size"], ACL_MEMCPY_HOST_TO_DEVICE)
ret1 = acl.rt.memcpy(temp_data_buffer[1]["buffer"], temp_data_buffer[1]["size"], model_position_ids_np_ptr, temp_data_buffer[1]["size"], ACL_MEMCPY_HOST_TO_DEVICE)
ret2 = acl.rt.memcpy(temp_data_buffer[2]["buffer"], temp_data_buffer[2]["size"], model_token_type_ids_np_ptr, temp_data_buffer[2]["size"], ACL_MEMCPY_HOST_TO_DEVICE)
ret3 = acl.rt.memcpy(temp_data_buffer[3]["buffer"], temp_data_buffer[3]["size"], model_input_ids_np_ptr, temp_data_buffer[3]["size"], ACL_MEMCPY_HOST_TO_DEVICE)
接口介绍:
4.6.2 准备推理所需数据结构
模型推理所需的输入输出数据,是通过一种特定的数据结构来组织的,这种数据结构叫“dataSet”,即所有的输入,组成了1个dateset,所有的输出组成了一个dataset。而对于很多模型来讲,输入其实不止一个,那么所有的输入集合叫“dataSet”,其中的每一个输入叫什么呢? 答案是“dataBuffer”。 即一个模型的多个输入,每个输入是一个“dataBuffer”,所有的dataBuffer构成了一个“dataSet”。
下面我们从构建dataBuffer开始。 dataBuffer的创建很简单,还记得前边我们申请了Device侧的内存,并且把数据传过去了吗?现在要用到了。 我们当时的device侧内存地址:data 这段内存的长度:size 使用上边两个对象来创建一个dataBuffer:
acl.create_data_buffer(data, size)
现在,这个“buffer”就是我们的第一个输入了。
接口介绍:
函数示例:
def create_buffer(dataset, type="in"):
global input_data, output_data
if type == "in":
temp_dataset = input_data
else:
temp_dataset = output_data
for i in range(len(temp_dataset)):
item = temp_dataset[i]
data = acl.create_data_buffer(item["buffer"], item["size"])
if data is None:
ret = acl.destroy_data_buffer(dataset)
check_ret("acl.destroy_data_buffer", ret)
_, ret = acl.mdl.add_dataset_buffer(dataset, data)
if ret != ACL_ERROR_NONE:
ret = acl.destroy_data_buffer(dataset)
check_ret("acl.destroy_data_buffer", ret)
我们的需求是需要有四个输入,所以要是用for循环,现在有了dataBuffer,要如何构建dataSet呢? 很简单:
acl.mdl.create_dataset()
dataset有了,下面就是向这个dataset中放置DataBuffer了:
acl.mdl.add_dataset_buffer(dataset, data)
这样,我们在dataset中添加了一个databuffer,输入准备好了。
创建输出数据结构同理,当您拿到一个模型,您应该是清楚这个模型的输出数据结构的,根据其输出个数、每个输出占用内存的大小来申请相应的device内存、dataBuffer以及dataSet即可。 现在假设我们已经创建好了输出的dataset,其变量名称叫做: outputDataSet
至此,我们准备好了推理所需的所有素材。 当然,将来用完之后别忘了销毁:
acl.create_data_buffer-> acl.destory_data_buffer; acl.mdl.create_dataset-> acl.mdl.destroy_dataset
接口介绍:
函数示例:
def _gen_dataset(type="in"):
global load_input_dataset, load_output_dataset
dataset = acl.mdl.create_dataset()
if type == "in":
load_input_dataset = dataset
else:
load_output_dataset = dataset
create_buffer(dataset, type)
4.7 推理
所有素材准备好之后,模型推理已经是顺理成章的事情了,还记得我们的几个关键素材吗?
model_id
load_input_dataset
load_output_dataset
最终的推理,其实只需要一行代码:
ret = acl.mdl.execute(model_id, input, output)
这是个同步接口,线程会阻塞在这里直到推理结束。推理结束后就可以提取load_output_dataset中的数据进行使用了。
接口介绍:
函数示例:
def inference(model_id, _input, _output):
global load_input_dataset, load_output_dataset
ret = acl.mdl.execute(model_id,
load_input_dataset,
load_output_dataset)
check_ret("acl.mdl.execute", ret)
4.8 后处理
4.8.1 释放资源
资源有申请就要有释放,调用以下接口释放之前申请的dataset和databuffer:
ret = acl.mdl.destroy_dataset(dataset)
ret = acl.destory_data_buffer(data_buffer)
接口介绍:
函数示例:
def _destroy_data_set_buffer():
global load_input_dataset, load_output_dataset
for dataset in [load_input_dataset, load_output_dataset]:
if not dataset:
continue
num = acl.mdl.get_dataset_num_buffers(dataset)
for i in range(num):
data_buf = acl.mdl.get_dataset_buffer(dataset, i)
if data_buf:
ret = acl.destroy_data_buffer(data_buf)
check_ret("acl.destroy_data_buffer", ret)
ret = acl.mdl.destroy_dataset(dataset)
check_ret("acl.mdl.destroy_dataset", ret)
4.8.2 申请host内存
建立outputDataset的时候,您应该使用了device侧的内存,将这部分内存拷贝回host侧,即可直接使用了。
申请Host侧内存:
host_ptr, ret= acl.rt.malloc_host(size) # 申请host侧内存
host_ptr; //host侧内存指针
随后即可使用host_ptr指向的内存来暂存推理输入数据。
把数据从device侧拷贝至host侧:
ret = acl.rt.memcpy(dst, dest_max, src, count, direction)
参数的顺序是:目的内存地址,目的内存最大大小,源内存地址,拷贝长度,拷贝方向。(支持host->host, host->device, device->host, device->device四种,与申请device内存相同)
使用完别忘了释放申请过的内存: acl.rt.malloc_host-> acl.rt.free_host
接口介绍:
函数示例:
def _data_interaction_out(dataset):
global output_data
temp_data_buffer = output_data
if len(dataset) == 0:
for item in output_data:
temp, ret = acl.rt.malloc_host(item["size"])
if ret != 0:
raise Exception("can't malloc_host ret={}".format(ret))
dataset.append({"size": item["size"], "buffer": temp})
for i in range(len(temp_data_buffer)):
item = temp_data_buffer[i]
ptr = dataset[i]["buffer"]
ret = acl.rt.memcpy(ptr,
item["size"],
item["buffer"],
item["size"],
ACL_MEMCPY_DEVICE_TO_HOST)
check_ret("acl.rt.memcpy", ret)
4.8.3 获取推理结果并打印
将推理后的结果数据转换为numpy类型,然后按照图片分类置信度前五的顺序打印出来。 结果示例如下:
def print_result(result):
dataset = []
for i in range(len(result)):
temp = result[i]
# size = temp["size"]/8
ptr = temp["buffer"]
data = acl.util.ptr_to_numpy(ptr, (1, 512), NPY_FLOAT32)
dataset.append(data)
start_probs = []
end_probs = []
for start_prob in dataset[0]:
start_probs.append(start_prob.tolist())
for end_prob in dataset[1]:
end_probs.append(end_prob.tolist())
return start_probs, end_probs
5. UIE模型使用acl的实战
acl的设计实现在这个文件
import argparse
import numpy as np
import struct
import acl
import os
from constant import MemcpyType, MallocType, NpyType
WORK_DIR = os.getcwd()
ACL_MEM_MALLOC_HUGE_FIRST = 0
ACL_MEMCPY_HOST_TO_DEVICE = 1
ACL_MEMCPY_DEVICE_TO_HOST = 2
ACL_ERROR_NONE = 0
NPY_FLOAT32 = 11
ret = acl.init()
# GLOBAL
load_input_dataset = None
load_output_dataset = None
input_data = []
output_data = []
_output_info = []
# images_list = []
model_desc = 0
run_mode = 0
INDEX = 0
MODEL_PATH = "/root/lijiaqi/UIE/model/model_tiny_out.om"
buffer_method = {
"in": acl.mdl.get_input_size_by_index,
"out": acl.mdl.get_output_size_by_index
}
def check_ret(message, ret):
if ret != ACL_ERROR_NONE:
raise Exception("{} failed ret={}".format(message, ret))
def init():
ret = acl.init()
check_ret("acl.init", ret)
print("init success")
def allocate_res(device_id):
ret = acl.rt.set_device(device_id)
check_ret("acl.rt.set_device", ret)
context, ret = acl.rt.create_context(device_id)
check_ret("acl.rt.create_context", ret)
print("allocate_res success")
return context
def load_model(model_path):
model_id, ret = acl.mdl.load_from_file(model_path)
check_ret("acl.mdl.load_from_file", ret)
print("load_model success")
return model_id
def get_model_data(model_id):
global model_desc
model_desc = acl.mdl.create_desc()
ret = acl.mdl.get_desc(model_desc, model_id)
check_ret("acl.mdl.get_desc", ret)
input_size = acl.mdl.get_num_inputs(model_desc)
output_size = acl.mdl.get_num_outputs(model_desc)
print("get_model_data success")
return input_size, output_size
def gen_data_buffer(num, des):
global model_desc
func = buffer_method[des]
for i in range(num):
if des == "in":
temp_buffer_size = acl.mdl.get_input_size_by_index(model_desc, i)
temp_buffer, ret = acl.rt.malloc(temp_buffer_size, MallocType.ACL_MEM_MALLOC_HUGE_FIRST.value)
check_ret("acl.rt.malloc", ret)
input_data.append({"buffer": temp_buffer,
"size": temp_buffer_size})
elif des == "out":
temp_buffer_size = acl.mdl.get_output_size_by_index(model_desc, i)
temp_buffer, ret = acl.rt.malloc(temp_buffer_size, MallocType.ACL_MEM_MALLOC_NORMAL_ONLY.value)
check_ret("acl.rt.malloc", ret)
output_data.append({"buffer": temp_buffer,
"size": temp_buffer_size})
def malloc_device(input_num, output_num):
gen_data_buffer(input_num, des="in")
gen_data_buffer(output_num, des="out")
def _data_interaction_in(model_input_ids, model_token_type_ids, model_position_ids, model_attention_mask):
global input_data
temp_data_buffer = input_data
model_input_ids_np_ptr = acl.util.numpy_to_ptr(model_input_ids)
model_token_type_ids_np_ptr = acl.util.numpy_to_ptr(model_token_type_ids)
model_position_ids_np_ptr = acl.util.numpy_to_ptr(model_position_ids)
model_attention_mask_np_ptr = acl.util.numpy_to_ptr(model_attention_mask)
ret = acl.rt.memcpy(temp_data_buffer[0]["buffer"], temp_data_buffer[0]["size"], model_attention_mask_np_ptr, temp_data_buffer[0]["size"], ACL_MEMCPY_HOST_TO_DEVICE)
ret1 = acl.rt.memcpy(temp_data_buffer[1]["buffer"], temp_data_buffer[1]["size"], model_position_ids_np_ptr, temp_data_buffer[1]["size"], ACL_MEMCPY_HOST_TO_DEVICE)
ret2 = acl.rt.memcpy(temp_data_buffer[2]["buffer"], temp_data_buffer[2]["size"], model_token_type_ids_np_ptr, temp_data_buffer[2]["size"], ACL_MEMCPY_HOST_TO_DEVICE)
ret3 = acl.rt.memcpy(temp_data_buffer[3]["buffer"], temp_data_buffer[3]["size"], model_input_ids_np_ptr, temp_data_buffer[3]["size"], ACL_MEMCPY_HOST_TO_DEVICE)
check_ret("acl.rt.memcpy", ret)
check_ret("acl.rt.memcpy", ret1)
check_ret("acl.rt.memcpy", ret2)
check_ret("acl.rt.memcpy", ret3)
print("data_interaction_in success")
def create_buffer(dataset, type="in"):
global input_data, output_data
if type == "in":
temp_dataset = input_data
else:
temp_dataset = output_data
for i in range(len(temp_dataset)):
item = temp_dataset[i]
data = acl.create_data_buffer(item["buffer"], item["size"])
if data is None:
ret = acl.destroy_data_buffer(dataset)
check_ret("acl.destroy_data_buffer", ret)
_, ret = acl.mdl.add_dataset_buffer(dataset, data)
if ret != ACL_ERROR_NONE:
ret = acl.destroy_data_buffer(dataset)
check_ret("acl.destroy_data_buffer", ret)
print("create data_buffer {} success".format(type))
def _gen_dataset(type="in"):
global load_input_dataset, load_output_dataset
dataset = acl.mdl.create_dataset()
#print("create data_set {} success".format(type))
if type == "in":
load_input_dataset = dataset
else:
load_output_dataset = dataset
create_buffer(dataset, type)
def inference(model_id, _input, _output):
global load_input_dataset, load_output_dataset
ret = acl.mdl.execute(model_id,
load_input_dataset,
load_output_dataset)
check_ret("acl.mdl.execute", ret)
def _destroy_data_set_buffer():
global load_input_dataset, load_output_dataset
for dataset in [load_input_dataset, load_output_dataset]:
if not dataset:
continue
num = acl.mdl.get_dataset_num_buffers(dataset)
for i in range(num):
data_buf = acl.mdl.get_dataset_buffer(dataset, i)
if data_buf:
ret = acl.destroy_data_buffer(data_buf)
check_ret("acl.destroy_data_buffer", ret)
ret = acl.mdl.destroy_dataset(dataset)
check_ret("acl.mdl.destroy_dataset", ret)
def _data_interaction_out(dataset):
global output_data
temp_data_buffer = output_data
if len(dataset) == 0:
for item in output_data:
temp, ret = acl.rt.malloc_host(item["size"])
if ret != 0:
raise Exception("can't malloc_host ret={}".format(ret))
dataset.append({"size": item["size"], "buffer": temp})
for i in range(len(temp_data_buffer)):
item = temp_data_buffer[i]
ptr = dataset[i]["buffer"]
ret = acl.rt.memcpy(ptr,
item["size"],
item["buffer"],
item["size"],
ACL_MEMCPY_DEVICE_TO_HOST)
check_ret("acl.rt.memcpy", ret)
print(dataset)
def print_result(result):
dataset = []
for i in range(len(result)):
temp = result[i]
# size = temp["size"]/8
ptr = temp["buffer"]
data = acl.util.ptr_to_numpy(ptr, (1, 512), NPY_FLOAT32)
dataset.append(data)
start_probs = []
end_probs = []
for start_prob in dataset[0]:
start_probs.append(start_prob.tolist())
for end_prob in dataset[1]:
end_probs.append(end_prob.tolist())
return start_probs, end_probs
def release(model_id, context):
global input_data, output_data
ret = acl.mdl.unload(model_id)
check_ret("acl.mdl.unload", ret)
while input_data:
item = input_data.pop()
ret = acl.rt.free(item["buffer"])
check_ret("acl.rt.free", ret)
while output_data:
item = output_data.pop()
ret = acl.rt.free(item["buffer"])
check_ret("acl.rt.free", ret)
if context:
ret = acl.rt.destroy_context(context)
check_ret("acl.rt.destroy_context", ret)
context = None
ret = acl.rt.reset_device(0)
check_ret("acl.rt.reset_device", ret)
print('release source success')
ACL的调用在另一个文件内,此文件包括UIE模型的整个过程,因为代码过长就不在此写了:
因为使用ACL部署UIE模型在昇腾310上有多个文件,上面只是acl的实现部分,调用部分在另一个文件里,如果感兴趣看详细代码的话可以参考我的github,稍后会更新上去。
感谢阅读!
参考文章链接:ACL 基于昇腾CANN的ACL推理应用开发原理(Python) ex_1_风尘23187的博客-CSDN博客