由于项目需要,最近捣鼓了一波如何让用tensorrt部署训练好的模型来达到更快的推理速度,期间花费了大量的时间在知乎和各种网页上去搜索别人的方案,但始终没有找到我想要的条理相对清晰的记录贴(也许只是我没找到),因此作为记录,也希望能帮到别人,弯路嘛,少走一条是一条。

由于之前项目一直是用tensorflow和keras,因此本文所讲述的是如何将模型从keras上迁移到tensorrt中,采用的方案是keras保存的hdf5模型先转为onnx模型,再将onnx模型转为tensorrt模型。采取这种方案主要有两点考虑,一方面是在tensorrt的官网只看到tensorflow转为tensorrt的教程,没有将keras模型直接部署的教程。另一方面是考虑到以后可能转去pytorch阵营(嘴上说着转已经说了半年,tf的api实在看的我眼花缭乱),到时候只需要再熟悉一下pytorch转onnx即可。废话不多说,下面是我的部署记录。

首先tensorrt是老黄为自家英伟达GPU所专门打造的推理引擎,TPU、CPU、FPGA等等请去看看TVM,本文是基于tensorrt已经安装好的基础上进行模型部署,没有安装的小伙伴请去百度,官网上的英文教程很详细,配合网友的中文安装记录很快就能配好。

Keras To Onnx

这一步主要是将keras保存的hdf5模型转为onnx后缀的模型,主要代码如下:

import keras2onnx
import onnx
from keras.models import load_model
model = load_model('testonnx.hdf5')
onnx_model = keras2onnx.convert_keras(model, model.name)
temp_model_file = 'result.onnx'
onnx.save_model(onnx_model, temp_model_file)

代码很简单,分为三步,分别是加载keras模型,keras模型中转为onnx模型,保存onnx模型。需要注意的是加载的hdf5文件中需要包含网络结构和参数,仅仅包含参数是不行的。

此外,在我部署过程中,报了一些错误,主要是一些自定义的layer和自定义的loss无法被识别,如何转换自定义的函数我暂时还没有深入研究,后续会补上,采取了一些“曲线救国”的法子,把自定义的layer尽量用keras自带的代替,效果差点就差点吧。在训练时采用自定义的loss,最后用keras自带的loss重新编译加载再保存就不会再报错了。

如果不出错的话,生成onnx结尾的文件表示转换完成,接下来是如何将onnx模型转换为tensorrt模型。

Onnx To Tensorrt

在安装tensorrt时,会下载一些官方示例,以tensorrt6为例,在samples/python/introductoryparsersamples中,有名为onnx_resnet50.py的文件,此文件是将resnet50.onnx转为tensorrt模型的示例代码,接下来以这个文件为基础,讲述如何将我们自己的onnx模型转为tensorrt模型。

先上代码。

from PIL import Image
import numpy as np
import pycuda.driver as cuda
import time
import tensorrt as trt
import sys, os
sys.path.insert(1, os.path.join(sys.path[0], ".."))
import commonclass ModelData(object):MODEL_PATH = "result.onnx"INPUT_SHAPE = (1, 512, 512)# We can convert TensorRT data types to numpy types with trt.nptype()DTYPE = trt.float32# You can set the logger severity higher to suppress messages (or lower to display more messages).
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)# Allocate host and device buffers, and create a stream.
def allocate_buffers(engine):# Determine dimensions and create page-locked memory buffers (i.e. won't be swapped to disk) to hold host inputs/outputs.h_input = cuda.pagelocked_empty(trt.volume(engine.get_binding_shape(0)), dtype=trt.nptype(ModelData.DTYPE))h_output = cuda.pagelocked_empty(trt.volume(engine.get_binding_shape(1)), dtype=trt.nptype(ModelData.DTYPE))# Allocate device memory for inputs and outputs.d_input = cuda.mem_alloc(h_input.nbytes)d_output = cuda.mem_alloc(h_output.nbytes)# Create a stream in which to copy inputs/outputs and run inference.stream = cuda.Stream()return h_input, d_input, h_output, d_output, streamdef do_inference(context, h_input, d_input, h_output, d_output, stream):# Transfer input data to the GPU.cuda.memcpy_htod_async(d_input, h_input, stream)# Run inference.context.execute_async(bindings=[int(d_input), int(d_output)], stream_handle=stream.handle)# Transfer predictions back from the GPU.cuda.memcpy_dtoh_async(h_output, d_output, stream)# Synchronize the streamstream.synchronize()# The Onnx path is used for Onnx models.
def build_engine_onnx(model_file):with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, trt.OnnxParser(network, TRT_LOGGER) as parser:builder.max_workspace_size = common.GiB(1)# Load the Onnx model and parse it in order to populate the TensorRT network.with open(model_file, 'rb') as model:parser.parse(model.read())last_layer = network.get_layer(network.num_layers - 1)network.mark_output(last_layer.get_output(0))returnresult=builder.build_cuda_engine(network)return returnresultdef load_normalized_test_case(test_image, pagelocked_buffer):# Converts the input image to a CHW Numpy arraydef normalize_image(image):# Resize, antialias and transpose the image to CHW.c, h, w = ModelData.INPUT_SHAPEimage_arr = np.asarray(image.resize((w, h), Image.ANTIALIAS))image_arr = np.reshape(image_arr, image_arr.shape + (1,))image_arr=image_arr.transpose([2, 0, 1])image_arr=image_arr.astype(trt.nptype(ModelData.DTYPE))image_arr=image_arr.ravel()# This particular ResNet50 model requires some preprocessing, specifically, mean normalization.return (image_arr / 255.0 - 0.45) / 0.225# Normalize the image and copy to pagelocked memory.np.copyto(pagelocked_buffer, normalize_image(Image.open(test_image)))return test_imagedef main():  onnx_model_file='result.onnx'# Build a TensorRT engine.with build_engine_onnx(onnx_model_file) as engine:# Inference is the same regardless of which parser is used to build the engine, since the model architecture is the same.# Allocate buffers and create a CUDA stream.h_input, d_input, h_output, d_output, stream = allocate_buffers(engine)with engine.create_execution_context() as context:# Load a normalized test case into the host input page-locked buffer.starttime=time.time()for i in range(100):test_image ='test.jpg'test_case = load_normalized_test_case(test_image, h_input)# Run the engine. The output will be a 1D tensor of length 1000, where each value represents the# probability that the image corresponds to that labeldo_inference(context, h_input, d_input, h_output, d_output, stream)#print('ok')endtime=time.time()pertime=(endtime-starttime)/100print('perimg cost'+str(pertime))

以上代码的大部分都是源自于onnx_resnet50中,各位可以参照着比对,主要的改动是:

(1)初始化部分,模型的输入大小,即经过各种预处理之后喂入到网络的图像大小。

(2)build_engineonnx函数部分,下面是onnx_resnet50中的原函数和我修改后的函数对比。

#修改后
def build_engine_onnx(model_file):with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, trt.OnnxParser(network, TRT_LOGGER) as parser:builder.max_workspace_size = common.GiB(1)# Load the Onnx model and parse it in order to populate the TensorRT network.with open(model_file, 'rb') as model:parser.parse(model.read())last_layer = network.get_layer(network.num_layers - 1)network.mark_output(last_layer.get_output(0))returnresult=builder.build_cuda_engine(network)return returnresult#原函数
def build_engine_onnx(model_file):with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, trt.OnnxParser(network, TRT_LOGGER) as parser:builder.max_workspace_size = common.GiB(1)# Load the Onnx model and parse it in order to populate the TensorRT network.with open(model_file, 'rb') as model:parser.parse(model.read())returnresult=builder.build_cuda_engine(network)return returnresult

主要的改动是在with循环之外增加了两句代码:

last_layer = network.get_layer(network.num_layers - 1)
network.mark_output(last_layer.get_output(0))

如果不做修改的话,会报如下错误:

[TensorRT] ERROR: Network must have at least one output

上述增加代码就是明确了模型结构的输出。

以上代码就是onnx+tensorrt部署keras模型的全记录,希望能让弯路少一点。关于速度提升,我只是简单的转换了一下模型,没有做任何的优化,大约能够50%的速度提升,后面会继续优化,看情况补上前后的速度对比。

keras保存模型_onnx+tensorrt部署keras模型相关推荐

  1. tensorRT 部署 YOLOV5模型详解

    tensorRT 部署 YOLOV5模型详解 第一步: 下载tensorRT库 https://developer.nvidia.com/nvidia-tensorrt-8x-download 欢迎使 ...

  2. 2022-10-31如何将自己训练的yolo模型转为tensorrt相关的模型(.trt or .engine)

    如何将自己训练的yolo模型转为tensorrt相关的模型 [1].pt模型转.wts模型 [2].wts模型转.trt/.engine模型 本文主要是以自己训练的yolov3-tiny模型作为例子进 ...

  3. Keras保存和载入训练好的模型和参数

    1.保存模型 my_model = create_model_function( ...... )my_model.compile( ...... )my_model.fit( ...... )mod ...

  4. tf.keras 05: 使用Keras保存和加载不同格式的模型

    本文是 tf.keras 系列文章的第五篇.通过手写数字识别数据集介绍了 Keras 模型训练中的 检查点(checkpoint) 文件,.weights 文件,.pb 文件以及 .h5 文件等四种格 ...

  5. 解锁新姿势-使用TensorRT部署pytorch模型

    一.整体流程概览 使用pytorch训练模型,生成*.pth文件 将*.pth转换成onnx模型 在tensorrt中加载onnx模型,并转换成trt的object 在trt中使用第三步转换的obje ...

  6. tensorrt部署YOLOv5模型记录【附代码,支持视频检测】

    训练出来的模型最终都需要进行工业部署,现今部署方案有很多,tensorflow和pytorch官方也都有发布,比如现在pytorch有自己的Libtorch进行部署[可以看我另一篇文章有讲利用Libt ...

  7. Jetson NX和Nano上使用TensorRT部署YOLOv4模型速度测试

    Jetson板卡算力对比 以及NX和Nano板卡上TensorRT加速测试 前言 Jetson CUDA 算力表: Xavier NX 上TensorRT测试: Nano上TensorRT推理测试: ...

  8. yolov5模型部署:Nvidia使用TensorRT部署yolov5s模型

    点上方计算机视觉联盟获取更多干货 仅作学术分享,不代表本公众号立场,侵权联系删除 转载于:作者丨梁彦哲@知乎(已授权) 来源丨https://zhuanlan.zhihu.com/p/38688102 ...

  9. 20211221:Tensorrt部署解析模型uff模型

    void createngine() //生成 engine {//创建builder,第一步创建buildIBuilder* builder = createInferBuilder(gLogger ...

最新文章

  1. SmartRoute之大规模消息转发集群实现
  2. 模型神器组合,yyds!
  3. 第十三周项目三-形状类族中的纯虚函数
  4. 题目1171:C翻转
  5. ASP.NET MVC 5 - 将数据从控制器传递给视图
  6. 字节跳动 EB 级 HDFS 实践
  7. 入职体检——项目列表(12项)
  8. 【算法设计与分析】流水作业调度问题 动态规划算法与代码实现 超详细
  9. 2020运动相机推荐_2020年最好的全景运动相机推荐
  10. Scikit-Learn与回归树
  11. RocketMQ使用mmap - TODO
  12. 深耕小而美的领域,也能过好一生
  13. nginx添加nginx_mod_h264_streaming-2.2.7模块编译报错
  14. iPhone屏幕尺寸
  15. 基于simulink的有源功率因数校正(APFC) 系统的性能仿真
  16. 装配作业指导书是什么?装配作业指导书主要包括哪些内容?
  17. 首批Android Q版本真机上线,Testin云测助力尝鲜
  18. 变频器的工作原理和功能应用
  19. matlab dms2degrees,Convert degrees-minutes to degrees
  20. SQL 根据汉字获取全拼

热门文章

  1. SAP 销售与分销模块定价完全实施手册(SAP SD Pricing Handbook)
  2. Excel 2013:如何实现数据分列
  3. 把SAP里的负号放到前面来的函数
  4. ALV_GRID介绍
  5. 第一次接触AgilePoint业务流程及任务管理(BPM)
  6. 数据解密2020年高考,志愿填报可以选这些
  7. att格式汇编指令_ARM汇编伪指令介绍.
  8. 用jk触发器构成二分频电路_深入了解数字电路之时序电路
  9. android设计ip输入框,Android下自定义IP控件
  10. c语言题目及答案1037,PAT Basic 1037. 在霍格沃茨找零钱(20)(C语言实现)