简单算法部署

1部署工具/技术介绍

1.1、ONNX

现在很多的深度学习框架提供的功能都是类似的,但是在 API、计算图和 runtime 方面却是独立的,这就给 AI 开发者在不同平台部署不同模型带来了很多困难和挑战,ONNX 的目的在于提供一个跨框架的模型中间表达框架,用于模型转换和部署。
无论我们使用什么样的训练框架来训练模型(TensorFlow/Pytorch/OneFlow/Paddle),我们都可以在训练后将这些框架的模型统一转为ONNX存储。 ONNX文件不仅存储了神经网络模型的权重,还存储了模型的结构信息、网络中各层的输入输出等一些信息。 然后将转换后的ONNX模型,转换成我们需要使用不同框架部署的类型,通俗来说ONNX 相当于一个翻译。

安装onnx

pip install onnx
pip install onnxruntime

调用onnx

import onnx
from onnx import helper
model = onnx.load('onnx_model.onnx’)
# 创建中间节点:层名、数据类型、维度信息
prob_info = helper.make_tensor_value_info('layer1',onnx.TensorProto.FLOAT, [1, 3, 320, 280])
# 将构造好的中间节点插入到模型中
model.graph.output.insert(0, prob_info)
#保存新模型
onnx.save(model, 'onnx_model_new.onnx’)

1.2TensorRT

训练(training)和 推理(inference)的区别
训练(training)包含了前向传播和后向传播两个阶段,针对的是训练集。训练时经过偏差反向传播来不断修改网络权值(weights)。
推理(inference)只包含前向传播一个阶段,针对的是除了训练集以外的新数据。能够是测试集,但不彻底是,更多的是整个数据集以外的数据。其实就是针对新数据进行预测,预测时,速度是一个很重要的因素。

通常的深度学习项目,训练时为了加快速度,会使用多GPU分布式训练。但在部署推理时,为了下降成本,会使用单个GPU机器甚至嵌入式平台(好比 NVIDIA Jetson)进行部署,部署端也要有与训练时相同的深度学习环境,如caffe,TensorFlow等。因为训练的网络模型可能会很大(好比,inception,resnet等),参数不少,并且部署端的机器性能存在差别,就会致使推理速度慢,延迟高。这对于那些高实时性的应用场合是致命的,好比自动驾驶要求实时目标检测,目标追踪等。因此需要提升部署推理的速度。

日常做深度学习的时候关注的更可能是训练的部分,即获得一个模型.而实际工做很大一块的工做内容集中于如何将模型部署到具体的平台或芯片上,并且自己本身写的模型效果是很难优于成熟的知名的模型的.推理引擎.model里的信息其实就是一些权重矩阵信息而已.输入数据后,进行一大堆的矩阵运算,获得最终数据的分类.推理引擎干的事情就是优化矩阵运算,缩短运算时间.

TensorRT是一个高性能的深度学习推理(Inference)优化器,能够为深度学习应用提供低延迟、高吞吐率的部署推理。当我们的网络训练完以后,能够将训练模型文件直接丢进tensorRT中,而再也不须要依赖深度学习框架(Caffe,TensorFlow等)。TensorRT可用于对超大规模数据中心、嵌入式平台或自动驾驶平台进行推理加速。TensorRT现已能支持TensorFlow、Caffe、Mxnet、Pytorch等几乎全部的深度学习框架,将TensorRT和NVIDIA的GPU结合起来,能在几乎全部的框架中进行快速和高效的部署推理。

2部署过程

在将多种不同框架下的算法部署在一个平台上时,我们可以使用ONNX作为中间格式来转换不同框架的模型。比如现在有两个NLP任务,分别是实体命名识别(基于keras实现)和情感分析(基于pytorch实现)。为了将这两种不同框架下的算法部署在一个平台上需要使用ONNX 。ONNX会将两种框架的模型都转换为ONNX模型。ONNX模型不仅存储了神经网络模型的权重,还存储了模型的结构信息、网络中各层的输入输出等一些信息。使用ONNX可以帮助我们有效地解决不同框架之间的兼容性问题,从而实现模型的跨平台部署和共享。

2.1 算法的实现

在Keras框架下实现实体命名识别任务,可以使用BERT模型结构,在BERT后面接一个CRF层以进行微调。使用已标注好的实体命名识别数据来微调该模型,以便在新数据上实现更好的性能。

在PyTorch框架下实现情感分析任务,可以使用类似的方法。使用BERT模型来提取有关情感分析的特征信息,并将这些特征输入到一个LSTM网络中进行分类。这样,我们就可以在PyTorch中使用BERT模型的预训练权重,同时添加自定义的LSTM层来处理情感分析任务。

2.2 ONNX模型转换

在完成了模型训练后,需要使用ONNX将两个模型转换为ONNX模型,并保存到本地。

import onnx
# Save Keras model to ONNX
model_1 = keras_model()  # replace with your Keras model
onnx.save(model_1, 'onnx_model_keras.onnx')
# Save PyTorch model to ONNX
model_2 = pytorch_model()  # replace with your PyTorch model
onnx.save(model_2, 'onnx_model_pytorch.onnx')

接下来,我们可以使用ONNX库提供的onnx.load()函数重新加载保存在本地的ONNX模型,并使用该模型对新数据进行预测。在预测之前,我们需要先对输入数据进行预处理,使其符合模型的输入要求。处理后的数据可以直接传递给ONNX模型,然后通过forward()函数进行推理。

import onnxruntime as ort

# Load Keras ONNX model
model_keras = onnx.load('onnx_model_keras.onnx')
sess_keras = ort.InferenceSession(model_keras.SerializeToString())

# Load PyTorch ONNX model
model_pytorch = onnx.load('onnx_model_pytorch.onnx')
sess_pytorch = ort.InferenceSession(model_pytorch.SerializeToString())

# Preprocess input data, replace with your own data preprocessing function
input_data = preprocess_data(data)

# Run inference with Keras model
outputs_keras = sess_keras.run(None, {'input': input_data})

# Run inference with PyTorch model
outputs_pytorch = sess_pytorch.run(None, {'input': input_data})

以上代码将分别加载保存在本地的Keras和PyTorch ONNX模型,然后使用ONNXRuntime包调用模型的run()函数进行推理。预处理的数据将通过run()函数传递,并返回模型的输出结果。这样,我们就可以使用已训练好的ONNX模型对新数据进行预测,从而实现深度学习模型的跨平台部署和使用。

2.3 ONNX模型转为TensorRT模型后进行推理

2.3.1 方案一

将ONNX模型转换为TensorRT可用的格式有两种方法:
1、使用TensorRT自带的工具
TensorRT 生成器(trtexec)是 TensorRT 软件包的一部分提供了可以将 ONNX 模型转换为 TensorRT 可用格式的功能。
在Ubuntu上,安装TensorRT时会自动安装 TensorRT生成器,可以在 /usr/src/tensorrt/bin/ 目录下找到该工具。使用下面的命令将ONNX模型转换为TensorRT:

trtexec --onnx=<path to onnx model file> --saveEngine=<path to save the trt engine file>

其中, 指向你的ONNX模型文件的路径, 是要保存的TensorRT引擎文件的完整路径和名称。

2、使用TensorRT Python API
另一种方法是使用TensorRT Python API将ONNX模型转换为TensorRT可用的格式。在转换模型之前,你需要先安装TensorRT Python包。可以使用以下命令在pip上安装:
pip install tensorrt
然后,使用以下Python代码将ONNX模型转换为TensorRT引擎:

import tensorrt as trt
import onnx
import os
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
def create_engine(onnx_file_path, engine_file_path):
    onnx_model = onnx.load(onnx_file_path)
    engine = None
    with trt.Builder(TRT_LOGGER) as builder, builder.create_network(explicit_batch=True) as network, trt.OnnxParser(network, TRT_LOGGER) as parser:
        builder.max_batch_size = 1
        builder.max_workspace_size = 1 << 30
        builder.fp16_mode = builder.platform_has_fast_fp16
        builder.strict_type_constraints = True
        parser.parse(onnx_model.SerializeToString())
        engine = builder.build_cuda_engine(network)
        serialized_engine = engine.serialize()
        with open(engine_file_path, 'wb') as f:
            f.write(serialized_engine)
    return engine

if __name__ == '__main__':
    onnx_file_path = '/path/to/onnx/model'
    engine_file_path = '/path/to/tensorrt/engine'
    create_engine(onnx_file_path=onnx_file_path, engine_file_path=engine_file_path)

其中, 指向你的ONNX模型文件的路径, 是要保存的TensorRT引擎文件的完整路径和名称。调用该函数将会返回一个TensorRT引擎。

TensorRT引擎文件推理

import tensorrt as trt
import numpy as np
import pycuda.driver as cuda
import pycuda.autoinit


def inference(engine, input_data):
    input_dtype = np.float32
    output_dtype = np.float32

    batch_size, sequence_length, embedding_dim = input_data.shape
    input_shape = (batch_size, sequence_length, embedding_dim)
    output_shape = (batch_size, 1)
    input_size = np.prod(input_shape) * np.dtype(input_dtype).itemsize
    output_size = np.prod(output_shape) * np.dtype(output_dtype).itemsize

    input_ptr = cuda.mem_alloc(input_size)
    output_ptr = cuda.mem_alloc(output_size)

    stream = cuda.Stream()
    context = engine.create_execution_context()
    bindings = [int(input_ptr), int(output_ptr)]
    context.set_binding_shape(0, input_shape)
    context.set_binding_shape(1, output_shape)

    cuda.memcpy_htod_async(input_ptr, input_data.flatten(), stream)

    context.execute_async_v2(bindings=bindings, stream_handle=stream.handle)
    output_data = np.empty(output_shape, dtype=output_dtype)
    cuda.memcpy_dtoh_async(output_data, output_ptr, stream)
    stream.synchronize()

    output_data = 1 / (1 + np.exp(-output_data))
    threshold = 0.5 
    binary_output_data = (output_data > threshold).astype(int) 

    return output_data, binary_output_data


if __name__ == '__main__':
    engine_file_path = '/path/to/tensorrt/engine'
    input_data = np.random.rand(64, 256, 512).astype(np.float32)
    with open(engine_file_path, 'rb') as f, trt.Runtime(trt.Logger(trt.Logger.WARNING)) as runtime:
        engine_data = f.read()
        engine = runtime.deserialize_cuda_engine(engine_data)
        output_data, binary_output_data = inference(engine, input_data)
        print("Output data (probabilities):\n", output_data)
        print("Binary output data:\n", binary_output_data)

2.3.2 方案二

#将onnx模型转为TensorRT 模型,新建onnx2trt.py
import tensorrt as trt

def build_engine(onnx_file_path,engine_file_path,half=False):
    """Takes an ONNX file and creates a TensorRT engine to run inference with"""
    logger = trt.Logger(trt.Logger.INFO)
    builder = trt.Builder(logger)
    config = builder.create_builder_config()
    config.max_workspace_size = 4 * 1 << 30
    flag = (1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
    network = builder.create_network(flag)
    parser = trt.OnnxParser(network, logger)
    if not parser.parse_from_file(str(onnx_file_path)):
        raise RuntimeError(f'failed to load ONNX file: {onnx_file_path}')
    half &= builder.platform_has_fast_fp16
    if half:
        config.set_flag(trt.BuilderFlag.FP16)
    with builder.build_engine(network, config) as engine, open(engine_file_path, 'wb') as t:
        t.write(engine.serialize())
    return engine_file_path
if __name__ =="__main__":
    onnx_path1 = 'model_onnx.onnx'
    engine_path = 'model_trt.engine'
    build_engine(onnx_path1,engine_path,True) 
    作者:深度之眼官方账号 https://www.bilibili.com/read/cv16042515 出处:bilibili

build_engine函数共有三个参数:
onnx_file_path: onnx模型的路径。
engine_file_path:TensorRT模型的路径。
half:是否使用单精度。
单精度的模型速度更快,深度之眼选择使用单精度。
通过上面的代码就可以完成模型的转化,下面开始实现推理部分,推理分为动态推理和静态推理。
作者:深度之眼官方账号 https://www.bilibili.com/read/cv16042515 出处:bilibili

静态推理

import time
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import numpy as np
import cv2
import time
import torchvision.transforms as transforms
from PIL import Image
def load_engine(engine_path):
    # TRT_LOGGER = trt.Logger(trt.Logger.WARNING)  # INFO
    TRT_LOGGER = trt.Logger(trt.Logger.ERROR)
    with open(engine_path, 'rb') as f, trt.Runtime(TRT_LOGGER) as runtime:
        return runtime.deserialize_cuda_engine(f.read())

path = 'model_trt.engine'
engine = load_engine(path)
context = engine.create_execution_context()
outshape = context.get_binding_shape(1)

# 2. 读取数据,数据处理为可以和网络结构输入对应起来的的shape,数据可增加预处理
def get_test_transform():
    return transforms.Compose([
        transforms.Resize([224, 224]),
        transforms.ToTensor(),
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
    ])

image = Image.open('11.jpg')
image = get_test_transform()(image)
image = image.unsqueeze_(0) # -> NCHW, 1,3,224,224
print("input img mean {} and std {}".format(image.mean(), image.std()))

image =  np.array(image)
# image = np.expand_dims(image, axis=1)
image = image.astype(np.float32)

image = image.ravel()  # 数据平铺

output = np.empty((outshape), dtype=np.float32)
d_input = cuda.mem_alloc(1 * image.size * image.dtype.itemsize)
d_output = cuda.mem_alloc(1 * output.size * output.dtype.itemsize)
bindings = [int(d_input), int(d_output)]
stream = cuda.Stream()


cuda.memcpy_htod(d_input, image)
context.execute_v2(bindings)
cuda.memcpy_dtoh(output, d_output)
output = output.reshape(outshape)
y_pred_binary = np.argmax(output, axis=1)
print(y_pred_binary[0]) 
作者:深度之眼官方账号 https://www.bilibili.com/read/cv16042515 出处:bilibili

你可能感兴趣的:(#,算法部署,算法,深度学习,人工智能)