【TensorRT】使用记录 —— TensorRTv6

删除回忆录丶 提交于 2020-02-01 02:46:55

TensorRT使用记录

版本号 TensorRT 6
万恶之源——TensorRT Release Notes,有不同版本的文档供你阅读

TensorRT Developer Guide
官方手册:Python user guide
官方手册:C++ TensorRT Documentation

不想看英文版的可以看看中文博客的介绍:高性能深度学习支持引擎实战——TensorRT

安装教程:推荐

框架介绍

TensorRT的流程:输入是一个预先训练好的FP32的模型和网络,将模型通过parser等方式输入到TensorRT中,TensorRT可以生成一个Serialization,也就是说将输入串流到内存或文件中,形成一个优化好的engine,执行的时候可以调取它来执行推断(Inference)。只要理解框架的运作方式,就很容易利用官方给的samples和手册进行代码的魔改了。
在这里插入图片描述

插件支持Plugin: 首先TensorRT是支持插件(Plugin)的,或者前面提到的Customer layer的形式,也就是说我们在某些层TensorRT不支持的情况下,最主要是做一些检测的操作的时候,很多层是该网络专门定义的,TensorRT没有支持,需要通过Plugin的形式自己去实现。

推荐阅读:TensorRT优化原理和TensorRT Plguin总结

Python API

pycuda: tensorRT并没有像pytorch一样把cuda的也集成在里面,所以在使用tensorRT的时候,还要结合cuda的API进行显存的管理和分配,两者往往一起使用。
cuda的python的API叫pycuda,看完下面的官方手册之后,就可以对整个框架有更加深入的理解。
pycuda英文官方手册
pycuda中文手册

下面是tensorRT框架的一些关键步骤

import tensorflow as tf
import tensorrt as trt
import pycuda.autoinit
import pycuda.driver as cuda

#tensorRT框架使用流程,在导入不同类型的深度学习框架模型的时候,某些步骤会稍有不用,但是主要是集中在builder这一步
#更多的详细情况以官方sample/python/里面的例子为准,楼主也是引用别人的博客并进行了稍微版本的修改
iGpu = 0
cuda.Device(iGpu).make_context()                # 设备上下文
logger = trt.Logger(trt.Logger.WARNING)         # 创建 logger

trtFilePath = "./densenetEngine.engine"    # 读取现成的 engine 序列文件,否则现场生成一个 engine 并序列化保存为文件,现在6的版本模型后缀为engine。
with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, trt.CaffeParser() as parser:
        builder.max_batch_size = batch_size #1/2 parament for builder
        builder.max_workspace_size = common.GiB(1) #2/2 parament for builder
        model_tensors = parser.parse(deploy=deploy_file, model=model_file, network=network, dtype=ModelData.DTYPE)
        #in caffe version, wo need to set the mark for output-end
        network.mark_output(model_tensors.find(ModelData.OUTPUT_NAME_1))
        network.mark_output(model_tensors.find(ModelData.OUTPUT_NAME_2))
        network.mark_output(model_tensors.find(ModelData.OUTPUT_NAME_3))
        engine = builder.build_cuda_engine(network)
        print('We had builded an engine...')
        return engine
    
def get_engine(deploy_file, model_file, engine_path): 
	# 利用运行时环境读取序列化的 engine(现场创建 engine 的可以跳过这步)
    #build an engine and save the serializing model
    try:
        with open(engine_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime:
            #If we had builder an engine ,we can deserialize an engine built.(Here you can add an IPlugin or IPluginExt).
            print('We had read an engine.')
            return runtime.deserialize_cuda_engine(f.read())
    except:
        # Fallback to building an engine if the engine cannot be loaded.
        engine = build_engine(deploy_file, model_file)
        with open(engine_path, "wb") as f:
            f.write(engine.serialize())
        return engine
#以上大致步骤已经完成


context = engine.create_execution_context()                                 # 创建内核上下文(区别于设备上下文)
stream  = cuda.Stream()                                                     # 创建流(可选)

hIn = cuda.pagelocked_empty(engine.get_binding_shape(0), dtype=np.float32)  # 使用无初始化的页锁定内存,指定尺寸(隐式转换为 trt.volume)和数据类型,也可用 np.empty 等来申请一般内存
hOut = cuda.pagelocked_empty(engine.get_binding_shape(1), dtype=np.float32) # engine.get_binding_shape 的 (0) 和 (1) 分别等于network 的输入和输出节点尺寸
dIn = cuda.mem_alloc(h_input.nbytes)                                        # 申请设备内存,使用主机内存的大小
dOut = cuda.mem_alloc(h_output.nbytes)

cuda.memcpy_htod_async(d_input, h_input, stream)                            # 异步数据拷贝
#cuda.memcpy_htod(d_input, data)                                            # 非异步数据拷贝
context.execute_async(batchSize, bindings=[int(dIn), int(dOut)], stream_handle=stream.handle)  # 异步执行内核
context.execute(batchSize, bindings=[int(dIn), int(dOut)])                  # 非异步执行内核
cuda.memcpy_dtoh_async(hOut, dOut, stream)

stream.synchronize()                                                        # 同步

context = None                                                              # 清空内核上下文和 engine
engine  = None
cuda.Context.pop()                                                          # 关闭设备上下文

Python API 报错一览:

  • [TensorRT] ERROR: Network must have at least one output: 这个报错最常见的原因就是通过不同方式转换过来的模型中掺杂了奇奇怪怪的非神经网络操作,就会导致tensorrt解析不了这个模型。正确的做法是先检查tensorrt要加载的模型及其参数和没转换过来之前的模型是不是完全一致,然后再针对出错的地方进行修改。官方论坛中关于对这一报错的建议 也是如此,不过也有可能是其他原因的而导致的问题,如果实在找不到问题的出处,可以先用network.mark_output(network.get_layer(network.num_layers-1).get_output(0))这条语句先强行输出结果查看,与原来的网络结果进行对比(建议先打印出network.num_layers你的engine里面的网络层数正不正确,有的模型连层都检测不到,那肯定就是你在转换模型的时候出了问题)
  • tensorrt不支持分支型输出?: 这一点在官方的论坛得到了确定,当你的模型文件不完整或者是输出没有定义好,的确会出现这个。那么应该怎么办,官方在yolo3转换的sample里面给出了答案,可以把结果extend或者concat起来,先把结果输出出来再进行分开提出所要的结果。楼主亲自检验过,效果ok。PS:有的如果tensorrt可以检测到你的网络有两个输出的话,他会把结果变成一个一维的向量来输出,然后你就分不清哪一个是哪一个了,还是先自己concat一下比较好~
  • Caffe Parser: Invalid reshape param. TensorRT does not support reshape in N (batch) dimension 将所有Reshape层的第一个维度参数dim由1变成0就可以啦

C++ API

推荐阅读:
C++ API:利用TensorRT实现神经网络提速(读取ONNX模型并运行)

使用心得

  • pytorch: 对于pytorch的模型,使用onnx当做转换中介,你会方便很多

  • onnx(中介): 对于通过转换为onnx的模型,因为不同框架下onnx神经层的支持性和兼容性也是不一样的,要以对应框架下onnx的api为准。 同时要注意的是!!! 如果转换过来的模型中夹杂着着太多的非神经网络的操作,tensorrt就有可能解析不了,因为tensorrt对于某些python的操作是不支持,比如求和,平均,这样即使你的onnx转换没有报错,最后得到的tensorrt的engine就会检测不到输出(当前tensorrt版本为6)。

  • 解析模型: 很多人会卡在tensorrt的解析模型这一步,确实楼主也栽跟头了,确实tensorrt对模型的要求比较太严格,原因是他可以进行加速的层的种类是有限的(支持的层在官网的开发者手册中可以查询得到,不然你只能通过tensorrt提供的plugin来自定义你想要的层了,其中包括cuda,tensorrt的结合编程,有点麻烦)

  • 出现报错多去tensorrt的官网论坛看看 : )

参考资料
https://blog.csdn.net/haima1998/article/details/83245155
https://www.cnblogs.com/cuancuancuanhao/p/11725715.html

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!