TensorRT详细入门指南

2023-11-16

前言

大名鼎鼎的TensorRT有多牛逼就不多说了,因为确实很好用。

作为在英伟达自家GPU上的推理库,这些年来一直被大力推广,更新也非常频繁,issue反馈也挺及时,社区的负责人员也很积极,简直不要太NICE。

只是TensorRT的入门门槛略微高一点点,劝退了一部分玩家。一部分原因是官方文档也不够详细(其实也挺细了,只不过看起来有些杂乱)、资料不够;另一部分可能是因为TensorRT比较底层,需要一点点C++和硬件方面的知识,相较学习Python难度更大一点。

不过吐槽归吐槽,TensorRT官方文档依旧是最权威最实用的查阅手册,另外TensorRT也是全面支持Python的,不习惯用C++的小伙伴,用Python调用TensorRT是没有任何问题的。

一直也想写TensorRT的系列教程,也顺便整理一下自己之前做的笔记。拖了好久的TensorRT入门指北终于写完了。

本教程基于目前(2021-4-26)最新版TensorRT-7.2.3.4,TensorRT更新频繁,TensorRT-8可能不久也会发布,不过TensorRT对于向下兼容的API做的还是比较好的,不必担心太多的迁移问题。

今天简单一刷(2021-5-5),TensorRT终于发布8版本了,仍然是尝鲜的Early Access (EA),官方release。不过TensorRT8-EA版一些功能不是很稳定,我们先不着急使用。尝鲜的小伙伴们可以先试试,生产环境版本建议先不要动。

什么是TensorRT

TensorRT是可以在NVIDIA各种GPU硬件平台下运行的一个C++推理框架。我们利用Pytorch、TF或者其他框架训练好的模型,可以转化为TensorRT的格式,然后利用TensorRT推理引擎去运行我们这个模型,从而提升这个模型在英伟达GPU上运行的速度。速度提升的比例是比较可观的。
TensorRT支持的平台如下:
在这里插入图片描述
支持计算能力在5.0及以上的显卡(当然,这里的显卡可以是桌面级显卡也可以是嵌入版式显卡),我们常见的RTX30系列计算能力是8.6、RTX20系列是7.5、RTX10系列是6.1,如果我们想要使用TensorRT,首先要确认下我们的显卡是否支持。

至于什么是计算能力(Compute Capability),咋说嘞,计算能力并不是描述GPU设备计算能力强弱的绝对指标,准确的说,这是一个架构的版本号。一般来说越新的架构版本号更高,计算能力的第一个数值也就最高(例如3080计算能力8.6),而后面的6代表在该架构前提下的一些优化特性。
说回TensorRT本身,TensorRT是由C++、CUDA、python三种语言编写成的一个库,其中核心代码为C++和CUDA,Python端作为前端与用户交互。当然,TensorRT也是支持C++前端的,如果我们追求高性能,C++前端调用TensorRT是必不可少的。
在这里插入图片描述

使用TensorRT的场景

TensorRT的使用场景很多。服务端、嵌入式端、家用电脑端都是我们的使用场景。

  • 服务端对应的显卡型号为A100、T4、V100等
  • 嵌入式端对应的显卡为AGX Xavier、TX2、Nano等
  • 家用电脑端对应的显卡为3080、2080TI、1080TI等
    当然这不是固定的,只要我们显卡满足TensorRT的先决条件,用就对了。

TensorRT的加速效果怎么样

加速效果取决于模型的类型和大小,也取决于我们所使用的显卡类型。

对于GPU来说,因为底层的硬件设计,更适合并行计算也更喜欢密集型计算。TensorRT所做的优化也是基于GPU进行优化,当然也是更喜欢那种一大块一大块的矩阵运算,尽量直通到底。因此对于通道数比较多的卷积层和反卷积层,优化力度是比较大的;如果是比较繁多复杂的各种细小op操作(例如reshape、gather、split等),那么TensorRT的优化力度就没有那么夸张了。

为了更充分利用GPU的优势,我们在设计模型的时候,可以更加偏向于模型的并行性,因为同样的计算量,“大而整”的GPU运算效率远超“小而碎”的运算。

工业界更喜欢简单直接的模型和backbone。2020年的RepVGG(RepVGG:极简架构,SOTA性能,让VGG式模型再次伟大(CVPR-2021)),就是为GPU和专用硬件设计的高效模型,追求高速度、省内存,较少关注参数量和理论计算量。相比resnet系列,更加适合充当一些检测模型或者识别模型的backbone。

在实际应用中,也简单总结了下TensorRT的加速效果:

  • SSD检测模型,加速3倍(Caffe)
  • CenterNet检测模型,加速3-5倍(Pytorch)
  • LSTM、Transformer(细op),加速0.5倍-1倍(TensorFlow)
  • resnet系列的分类模型,加速3倍左右(Keras)
  • GAN、分割模型系列比较大的模型,加速7-20倍左右(Pytorch)

TensorRT有哪些黑科技

为什么TensorRT能够提升我们模型在英伟达GPU上运行的速度,当然是做了很多对提速有增益的优化:
在这里插入图片描述

  • 算子融合(层与张量融合):简单来说就是通过融合一些计算op或者去掉一些多余op来减少数据流通次数以及显存的频繁使用来提速
  • 量化:量化即IN8量化或者FP16以及TF32等不同于常规FP32精度的使用,这些精度可以显著提升模型执行速度并且不会保持原先模型的精度
  • 内核自动调整:根据不同的显卡构架、SM数量、内核频率等(例如1080TI和2080TI),选择不同的优化策略以及计算方式,寻找最合适当前构架的计算方式
  • 动态张量显存:我们都知道,显存的开辟和释放是比较耗时的,通过调整一些策略可以减少模型中这些操作的次数,从而可以减少模型运行的时间
  • 多流执行:使用CUDA中的stream技术,最大化实现并行操作

TensorRT的这些优化策略代码虽然是闭源的,但是大部分的优化策略我们或许也可以猜到一些,也包括TensorRT官方公布出来的一些优化策略:
在这里插入图片描述
左上角是原始网络(googlenet),右上角相对原始层进行了垂直优化,将conv+bias(BN)+relu进行了融合优化;而右下角进行了水平优化,将所有1x1的CBR融合成一个大的CBR;左下角则将concat层直接去掉,将contact层的输入直接送入下面的操作中,不用单独进行concat后在输入计算,相当于减少了一次传输吞吐;

等等等等,还有很多例子。

上述的这些算子融合、动态显存分配、精度校准、多steam流、自动调优等操作,TensorRT都帮你做了。这样通过TensorRT帮你调优模型后,自然模型的速度就上来了。

当然也有其他在NVIDIA-GPU平台上的推理优化库,例如TVM,某些情况下TVM比TensorRT要好用些,但毕竟是英伟达自家产品,TensorRT在自家GPU上还是有不小的优势,做到了开箱即用,上手程度不是很难。

安装TensorRT!

安装TensorRT的方式有很多,官方提供了多种方式:

You can choose between the following installation options when installing TensorRT; Debian or RPM packages, a pip wheel file, a tar file, or a zip file.

这些安装包都可以从官方直接下载,从 https://developer.nvidia.com/… 进入下载即可,需要注意这里我们要注册会员并且登录才可以下载。一直使用的方式是下载tar包,下载好后解压即可,只要我们的环境符合要求就可以直接运行,类似于绿色免安装。

例如下载TensorRT-7.2.3.4.Ubuntu-18.04.x86_64-gnu.cuda-11.1.cudnn8.1.tar.gz,下载好后,tar -zxvf解压即可。

解压之后我们需要添加环境变量,以便让我们的程序能够找到TensorRT的libs。

vim ~/.bashrc
# 添加以下内容
export LD_LIBRARY_PATH=/path/to/TensorRT-7.2.3.4/lib:$LD_LIBRARY_PATH
export LIBRARY_PATH=/path/to/TensorRT-7.2.3.4/lib::$LIBRARY_PATH

这样TensorRT就安装好了,很快吧!

TensorRT常见FAQ

对于使用TensorRT,还是有一些需要注意的地方,在这里总结了一些大家可能感兴趣或者之后可能遇到的一些问题。

TensorRT版本相关

TensorRT的版本与CUDA还有CUDNN版本是密切相关的,我们从官网下载TensorRT的时候应该就可以注意到:
在这里插入图片描述
不匹配版本的cuda以及cudnn是无法和TensorRT一起使用的。

所以下载的时候要注意,不要搞错版本了哦。

关于如何选择合适自己的TensorRT版本,首先看驱动,其次看CUDA版本。

驱动怎么看,使用nvidia-smi命令即可:
在这里插入图片描述
驱动可以通过root权限去换,而CUDA版本只要驱动满足要求就可以随便换(不需要root权限),这点注意下就好。
耍个小聪明,其实版本也不是严格限制的,只要是你需要的功能函数在这个低版本中存在,那也是可以使用的。

举个例子。

我们从官方下载的TensorRT-7.0.0.11.Ubuntu-16.04.x86_64-gnu.cuda-10.2.cudnn7.6.tar依赖libcudnn.so.7.6.0。但我们使用libcudnn.so.7.3.0去跑这个TensorRT去做一些事情时,因为版本不一致就会报错:

TensorRT-7.0.0.11/lib/libmyelin.so.1: undefined reference to `cudnnGetBatchNormalizationBackwardExWorkspaceSize@libcudnn.so.7'
TensorRT-7.0.0.11/lib/libmyelin.so.1: undefined reference to `cudnnGetBatchNormalizationForwardTrainingExWorkspaceSize@libcudnn.so.7'
TensorRT-7.0.0.11/lib/libmyelin.so.1: undefined reference to `cudnnGetBatchNormalizationTrainingExReserveSpaceSize@libcudnn.so.7'
TensorRT-7.0.0.11/lib/libmyelin.so.1: undefined reference to `cudnnBatchNormalizationBackwardEx@libcudnn.so.7'
TensorRT-7.0.0.11/lib/libmyelin.so.1: undefined reference to `cudnnBatchNormalizationForwardTrainingEx@libcudnn.so.7'

显然在链接TensorRT的时候,libmyelin.so.1这个东西需要的符号表在libcudnn.so.7.3.0这里找不到,因此也无法编译成功。

但是如果我们使用libcudnn.so.7.6.0将其编译好得到可执行文件,但跑这个程序时只给它提供libcudnn.so.7.3.0的运行环境。那么运行的时候会有
TensorRT was linked against cuDNN 7.6.3 but loaded cuDNN 7.3.0的提示,不过程序可以正常运行!

原因很简单,libcudnn.so.7.6.0所拥有的cudnnGetBatchNormalizationBackwardExWorkspaceSize虽然libcudnn.so.7.3.0没有,但是我们也不用它,所以程序可以正常运行而不会报错,但如果你需要这个函数调用这个函数那么就没有办法了。

通过strings命令观察可以看到libcudnn.so.7.3.0确实没有cudnnGetBatchNormalizationBackwardExWorkspaceSize这个函数实现:

strings /usr/local/cuda/lib64/libcudnn.so.7.3.0 | grep cudnnGetBatchNormalizationBackwardExWorkspaceSize
而7.6.5中有
strings /usr/local/cuda/lib64/libcudnn.so.7.6.5 | grep cudnnGetBatchNormalizationBackwardExWorkspaceSize
cudnnGetBatchNormalizationBackwardExWorkspaceSize
cudnnGetBatchNormalizationBackwardExWorkspaceSize

什么模型可以转换为TensorRT

TensorRT官方支持Caffe、Tensorflow、Pytorch、ONNX等模型的转换(不过Caffe和Tensorflow的转换器Caffe-Parser和UFF-Parser已经有些落后了),也提供了三种转换模型的方式:

  • 使用TF-TRT,将TensorRT集成在TensorFlow中
  • 使用ONNX2TensorRT,即ONNX转换trt的工具
  • 手动构造模型结构,然后手动将权重信息挪过去,非常灵活但是时间成本略高,有大佬已经尝试过了:tensorrtx

不过目前TensorRT对ONNX的支持最好,TensorRT-8最新版ONNX转换器又支持了更多的op操作。而深度学习框架中,TensorRT对Pytorch的支持更为友好,除了Pytorch->ONNX->TensorRT这条路,还有:

  • torch2trt
  • torch2trt_dynamic
  • TRTorch

总而言之,理论上95%的模型都可以转换为TensorRT,条条大路通罗马嘛。只不过有些模型可能转换的难度比较大。如果遇到一个无法转换的模型,先不要绝望,再想想,再想想,看看能不能通过其他方式绕过去。

TensorRT是否支持动态尺寸(dynamic shape)吗

支持,而且用起来还很方便,如果某些OP不支持,也可以自己写动态尺度的Plugin。

动态尺度支持NCHW中的N、H以及W,也就是batch、高以及宽。

对于动态模型,我们在转换模型的时候需要额外指定三个维度信息即可(最小、最优、最大)。

举个转换动态模型的命令:

./trtexec --explicitBatch --onnx=demo.onnx --minShapes=input:1x1x256x256 --optShapes=input:1x1x2048x2048 --maxShapes=input:1x1x2560x2560 --shapes=input:1x1x2048x2048 --saveEngine=demo.trt --workspace=6000

很简单吧~

TensorRT是硬件相关的

这个很好明白,因为不同显卡(不同GPU),其核心数量、频率、架构、设计(还有价格…)都是不一样的,TensorRT需要对特定的硬件进行优化,不同硬件之间的优化是不能共享的。

The generated plan files are not portable across platforms or TensorRT versions. Plans are specific to the exact GPU model they were built on (in addition to platforms and the TensorRT version) and must be re-targeted to the specific GPU in case you want to run them on a different GPU

TensorRT是否开源

TensorRT是半开源的,除了核心部分其余的基本都开源了。TensorRT最核心的部分是什么,当然是官方展示的一些特性了。如下:
在这里插入图片描述
以上核心优势,也就是TensorRT内部的黑科技,可以帮助我们优化模型,加速模型推理,这部分当然是不开源的啦。

Python中可以使用TensorRT吗

当然是可以的,官方有python的安装包(下文有说),安装后就可以import tensorrt使用了。

用ldd命令看一下tensorrt.so中都引用了什么。

ldd tensorrt.so
        linux-vdso.so.1 =>  (0x00007ffe477d4000)
        libnvinfer.so.7 => /TensorRT-7.0.0.11/lib/libnvinfer.so.7 (0x00007f2a76f6b000)
        libnvonnxparser.so.7 => /TensorRT-7.0.0.11/lib/libnvonnxparser.so.7 (0x00007f2a76ca5000)
        libnvparsers.so.7 => /TensorRT-7.0.0.11/lib/libnvparsers.so.7 (0x00007f2a76776000)
        libnvinfer_plugin.so.7 => /TensorRT-7.0.0.11/lib/libnvinfer_plugin.so.7 (0x00007f2a758e2000)
        libstdc++.so.6 => /TensorRT-7.0.0.11/lib/libstdc++.so.6 (0x00007f2a75555000)
        libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f2a7532f000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f2a74f61000)
        libcudnn.so.7 => /usr/local/cuda/lib64/libcudnn.so.7 (0x00007f2a60768000)
        libcublas.so.10.0 => /usr/local/cuda/lib64/libcublas.so.10.0 (0x00007f2a5c1d2000)
        libcudart.so.10.0 => /usr/local/cuda/lib64/libcudart.so.10.0 (0x00007f2a5bf57000)
        libmyelin.so.1 => /TensorRT-7.0.0.11/lib/libmyelin.so.1 (0x00007f2a5b746000)
        libnvrtc.so.10.0 => /usr/local/cuda/lib64/libnvrtc.so.10.0 (0x00007f2a5a12a000)
        libdl.so.2 => /lib64/libdl.so.2 (0x00007f2a59f25000)
        libm.so.6 => /lib64/libm.so.6 (0x00007f2a59c23000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f2a84f5c000)
        libprotobuf.so.16 => /onnx-tensorrt/protobuf-3.6.0/lib/libprotobuf.so.16 (0x00007f2a597ad000)
        libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f2a59591000)
        librt.so.1 => /lib64/librt.so.1 (0x00007f2a59389000)
        libz.so.1 => /lib64/libz.so.1 (0x00007f2a59172000)

TensorRT部署相关

部署TensorRT的方式,官方提供了三种:

  • 集成在Tensorflow中使用,比例TF-TRT,这种操作起来比较便捷,但是加速效果并不是很好;
  • 在TensorRT Runtime环境中运行模型,就是直接使用TensorRT;
  • 搭配服务框架使用,最配的就是官方的triton-server,完美支持TensorRT,用在生产环境杠杠的!

TensorRT支持哪几种权重精度

支持FP32、FP16、INT8、TF32等,这几种类型都比较常用。

  • FP32:单精度浮点型,没什么好说的,深度学习中最常见的数据格式,训练推理都会用到;
  • FP16:半精度浮点型,相比FP32占用内存减少一半,有相应的指令值,速度比FP32要快很多;
  • TF32:第三代Tensor Core支持的一种数据类型,是一种截短的 Float32 数据格式,将FP32中23个尾数位截短为10bits,而指数位仍为8bits,总长度为19(=1+8 +10)。保持了与FP16同样的精度(尾数位都是 10 位),同时还保持了FP32的动态范围指数位都是8位);
  • INT8:整型,相比FP16占用内存减小一半,有相应的指令集,模型量化后可以利用INT8进行加速。

简单展示下各种精度的区别:
在这里插入图片描述

简单举一个例子吧!

说了那么多理论知识,不来个栗子太说不过去了。

这个例子的目的很简单,就是简单展示一下使用TensorRT的一种场景以及基本流程。假设有一个onnx模型想要在3070卡上运行,并且要快,这时候就要祭出TensorRT了。

关于什么是ONNX(ONNX是一个模型结构格式,方便不同框架之间的模型转化例如Pytorch->ONNX->TRT)可以看这个,这里先不细说了~

手头没有现成训练好的模型,直接从开源项目中白嫖一个吧。找到之前一个比较有趣的项目,可以通过图片识别三维人体关键点,俗称人体姿态检测,项目地址在此:

https://link.segmentfault.com/?enc=ifq7h7mDt6RPxLKipBCtfA%3D%3D.gJY0l4kkuej4Bj0JdedrqKkRlPODvNvvSOmpYoMVcMgycvwelUEkRy0gCEOod97KyNo1RcJ9Ic7hVXWAbiUpLw%3D%3D

实现的效果如下,该模型的精度还是可以的,但是画面中只能出现一个目标人物。速度方面的话,主页有介绍:

  • GeForce RTX2070 SUPER ⇒ About 30 FPS
  • GeForce GTX1070 ⇒ About 20 FPS
    在这里插入图片描述
    让我们来试试TensorRT能够在3070卡上为其加速多少吧。

看一下模型结构

用到的核心模型在Github主页有提供,即ONNX模型Resnet34_3inputs_448x448_20200609.onnx。作者演示使用的是Unity与Barracuda,其中利用Barracuda去加载onnx模型然后去推理。

我们先用Netron去观察一下这个模型结构,3个输入4个输出,为什么是3个输入呢?其实这三个输入在模型的不同阶段,作者训练的时候的输入数据可能是从视频中截出来的连续3帧的图像,这样训练可以提升模型的精度(之后模型推理的时候也需要一点时间的预热,毕竟3帧的输入也是有是时间连续性的):
在这里插入图片描述

使用onnxruntime验证一下模型

一般来说,我们在通过不同框架(Pytorch、TF)转换ONNX模型之后,需要验证一下ONNX模型的准确性,否则错误的onnx模型转成的TensorRT模型也100%是错误的。

但显然上述作者提供的Resnet34_3inputs_448x448_20200609.onnx是验证过没问题的,但这边我们也走一下流程,以下代码使用onnxruntime去运行:

import onnx
import numpy as np
import onnxruntime as rt
import cv2

model_path = '/home/oldpan/code/models/Resnet34_3inputs_448x448_20200609.onnx'

# 验证模型合法性
onnx_model = onnx.load(model_path)
onnx.checker.check_model(onnx_model)

# 读入图像并调整为输入维度
image = cv2.imread("data/images/person.png")
image = cv2.resize(image, (448,448))
image = image.transpose(2,0,1)
image = np.array(image)[np.newaxis, :, :, :].astype(np.float32)

# 设置模型session以及输入信息
sess = rt.InferenceSession(model_path)
input_name1 = sess.get_inputs()[0].name
input_name2 = sess.get_inputs()[1].name
input_name3 = sess.get_inputs()[2].name

output = sess.run(None, {input_name1: image, input_name2: image, input_name3: image})
print(output)

打印一下结果看看是什么样子吧~其实输出信息还是很多的,全粘过来太多了,毕竟这个模型的输出确实是多…这里只截取了部分内容意思一哈:

2021-05-05 10:44:08.696562083 [W:onnxruntime:, graph.cc:3106 CleanUnusedInitializers] Removing initializer 'offset.1.num_batches_tracked'. It is not used by any node and should be removed from the model.
...
[array([[[[ 0.16470502,  0.9578098 , -0.82495296, ..., -0.59656703,
           0.26985374,  0.5808018 ],
         [-0.6096473 ,  0.9780458 , -0.9723106 , ..., -0.90165156,
          -0.8959699 ,  0.91829604],
         [-0.03562748,  0.3730615 , -0.9816262 , ..., -0.9905239 ,
          -0.4543069 ,  0.5840921 ],
         ...,
         ...,
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ],
         [0.        , 0.        , 0.        , ..., 0.        ,
          0.        , 0.        ]]]], dtype=float32)]

看到Removing initializer ‘offset.1.num_batches_tracked’. It is not used by any node and should be removed from the model这个提示我亲切地笑了,这不就是用Pytorch训练的么,看来作者这个模型也是通过Pytorch训练然后导出来的。

怎么对比下ONNX和Pytorch输出结果是否一致呢?可以直接看到输出的数值是多少,但是这个模型的输出还是比较多的,直接通过肉眼对比转换前后的结果是不理智的。我们可以通过代码简单对比一下:

y = model(x)
y_onnx = model_onnx(x)

# check the output against PyTorch
print(torch.max(torch.abs(y - y_trt)))

ONNX转换为TensorRT模型

ONNX模型转换TensorRT模型还是比较容易的,目前TensorRT官方对ONNX模型的支持最好,而且后续也会将精力重点放到ONNX上面(相比ONNX,UFF、Caffe这类转换工具可能不会再更新了)。

目前官方的转换工具TensorRT Backend For ONNX(简称ONNX-TensorRT)已经比较成熟了,开发者也在积极开发,提issue官方回复的也比较快。我们就用上述工具来转一下这个模型。

我们不需要克隆TensorRT Backend For ONNX,之前下载好的TensorRT包中已经有这个工具的可执行文件了,官方已经替我们编译好了,只要我们的环境符合要求,是直接可以用的。

到TensorRT-7.2.3.4/bin中直接使用trtexec这个工具,这个工具可以比较快速地转换ONNX模型以及测试转换后的trt模型有多快:

https://link.segmentfault.com/?enc=I2bcEKoUmrMMYowUKER79Q%3D%3D.7Jtw4svseZdbzzSkkAv9bZRx1X19o4o31cJ9PXE8mP7bWjY%2B0gLNpoZXQmp5%2FEfa3UU2GBInRMYkhvEcxRaJdaPwsya49IIdsFS6JV86mLC8xycHDL5YkpaNjjMC%2BIh4

我们使用命令转换,可以看到输出信息:

&&&& RUNNING TensorRT.trtexec # ./trtexec --onnx=Resnet34_3inputs_448x448_20200609.onnx --saveEngine=Resnet34_3inputs_448x448_20200609.trt --workspace=6000
[05/09/2021-17:00:50] [I] === Model Options ===
[05/09/2021-17:00:50] [I] Format: ONNX
[05/09/2021-17:00:50] [I] Model: Resnet34_3inputs_448x448_20200609.onnx
[05/09/2021-17:00:50] [I] Output:
[05/09/2021-17:00:50] [I] === Build Options ===
[05/09/2021-17:00:50] [I] Max batch: explicit
[05/09/2021-17:00:50] [I] Workspace: 6000 MiB
[05/09/2021-17:00:50] [I] minTiming: 1
[05/09/2021-17:00:50] [I] avgTiming: 8
[05/09/2021-17:00:50] [I] Precision: FP32
[05/09/2021-17:00:50] [I] Calibration: 
[05/09/2021-17:00:50] [I] Refit: Disabled
[05/09/2021-17:00:50] [I] Safe mode: Disabled
[05/09/2021-17:00:50] [I] Save engine: Resnet34_3inputs_448x448_20200609.trt
[05/09/2021-17:00:50] [I] Load engine: 
[05/09/2021-17:00:50] [I] Builder Cache: Enabled
[05/09/2021-17:00:50] [I] NVTX verbosity: 0
[05/09/2021-17:00:50] [I] Tactic sources: Using default tactic sources
[05/09/2021-17:00:50] [I] Input(s)s format: fp32:CHW
[05/09/2021-17:00:50] [I] Output(s)s format: fp32:CHW
...
[05/09/2021-17:02:32] [I] Timing trace has 0 queries over 3.16903 s
[05/09/2021-17:02:32] [I] Trace averages of 10 runs:
[05/09/2021-17:02:32] [I] Average on 10 runs - GPU latency: 4.5795 ms  
[05/09/2021-17:02:32] [I] Average on 10 runs - GPU latency: 4.6697 ms 
[05/09/2021-17:02:32] [I] Average on 10 runs - GPU latency: 4.6537 ms 
[05/09/2021-17:02:32] [I] Average on 10 runs - GPU latency: 4.5953 ms 
[05/09/2021-17:02:32] [I] Average on 10 runs - GPU latency: 4.6333 ms 
[05/09/2021-17:02:32] [I] Host Latency
[05/09/2021-17:02:32] [I] min: 4.9716 ms (end to end 108.17 ms)
[05/09/2021-17:02:32] [I] max: 4.4915 ms (end to end 110.732 ms)
[05/09/2021-17:02:32] [I] mean: 4.0049 ms (end to end 109.226 ms)
[05/09/2021-17:02:32] [I] median: 4.9646 ms (end to end 109.241 ms)
[05/09/2021-17:02:32] [I] percentile: 4.4915 ms at 99% (end to end 110.732 ms at 99%)
[05/09/2021-17:02:32] [I] throughput: 0 qps
[05/09/2021-17:02:32] [I] walltime: 3.16903 s
[05/09/2021-17:02:32] [I] Enqueue Time
[05/09/2021-17:02:32] [I] min: 0.776001 ms
[05/09/2021-17:02:32] [I] max: 1.37109 ms
[05/09/2021-17:02:32] [I] median: 0.811768 ms
[05/09/2021-17:02:32] [I] GPU Compute
[05/09/2021-17:02:32] [I] min: 4.5983 ms
[05/09/2021-17:02:32] [I] max: 4.1133 ms
[05/09/2021-17:02:32] [I] mean: 4.6307 ms
[05/09/2021-17:02:32] [I] median: 4.5915 ms
[05/09/2021-17:02:32] [I] percentile: 4.1133 ms at 99%

其中FP32推理速度是4-5ms左右,而FP16只需要1.6ms。

PS:关于ONNX-TensorRT这个工具,本身是由C++写的,整体结构设计的比较紧凑,值得一读。

运行TensorRT模型

这里我们使用TensorRT的Python端加载转换好的resnet34_3dpose.trt模型。使用Python端时首先需要安装TensorRT-tar包下的pyhton目录下的tensorrt-7.2.3.4-cp37-none-linux_x86_64.whl安装包,目前7.0支持最新的python版本为3.8,而TensorRT-8-EA则开始支持python-3.9了。

安装Python-TensorRT后,首先import tensorrt as trt。

然后加载Trt模型:

logger = trt.Logger(trt.Logger.INFO)
  with open("resnet34_3dpose.trt", "rb") as f, trt.Runtime(logger) as runtime:
    engine=runtime.deserialize_cuda_engine(f.read())

加载好之后,我们打印下这个模型的输入输出信息,观察是否与ONNX模型一致:

for idx in range(engine.num_bindings):
    is_input = engine.binding_is_input(idx)
    name = engine.get_binding_name(idx)
    op_type = engine.get_binding_dtype(idx)
    model_all_names.append(name)
    shape = engine.get_binding_shape(idx)

    print('input id:',idx,'   is input: ', is_input,'  binding name:', name, '  shape:', shape, 'type: ', op_type)

可以看到:

engine bindings message: 
input id: 0    is input:  True   binding name: input.1   shape: (1, 3, 448, 448) type:  DataType.FLOAT
input id: 1    is input:  True   binding name: input.4   shape: (1, 3, 448, 448) type:  DataType.FLOAT
input id: 2    is input:  True   binding name: input.7   shape: (1, 3, 448, 448) type:  DataType.FLOAT
input id: 3    is input:  False   binding name: 499   shape: (1, 24, 28, 28) type:  DataType.FLOAT
input id: 4    is input:  False   binding name: 504   shape: (1, 48, 28, 28) type:  DataType.FLOAT
input id: 5    is input:  False   binding name: 516   shape: (1, 672, 28, 28) type:  DataType.FLOAT
input id: 6    is input:  False   binding name: 530   shape: (1, 2016, 28, 28) type:  DataType.FLOAT

3个输入4个输出,完全一致没有问题!

然后载入图像,运行模型:

image = cv2.imread(image_path)
image = cv2.resize(image, (200,64))
image = image.transpose(2,0,1)
img_input = image.astype(np.float32)
img_input = torch.from_numpy(img_input)
img_input = img_input.unsqueeze(0)
img_input = img_input.to(device)
# 运行模型
result_trt = trt_model(img_input)

咦?这么简单么,trt的engine在哪儿?

当然不是,其实这个trt_model是一个类,我们这样使用它:

# engine即上述加载的engine
trt_model = TRTModule(engine, ["input.1", "input.4", "input.7"])

而这个TRTModule是什么呢,为了方便地加载TRT模型以及创建runtime,我们借鉴torch2trt这个库中的一个实现类,比较好地结合了Pytorch与TensorRT,具体实现如下:

class TRTModule(torch.nn.Module):
    def __init__(self, engine=None, input_names=None, output_names=None):
        super(TRTModule, self).__init__()
        self.engine = engine
        if self.engine is not None:
            # engine创建执行context
            self.context = self.engine.create_execution_context()

        self.input_names = input_names
        self.output_names = output_names

    def forward(self, *inputs):
        batch_size = inputs[0].shape[0]
        bindings = [None] * (len(self.input_names) + len(self.output_names))

        for i, input_name in enumerate(self.input_names):
            idx = self.engine.get_binding_index(input_name)
            # 设定shape 
            self.context.set_binding_shape(idx, tuple(inputs[i].shape))
            bindings[idx] = inputs[i].contiguous().data_ptr()

        # create output tensors
        outputs = [None] * len(self.output_names)
        for i, output_name in enumerate(self.output_names):
            idx = self.engine.get_binding_index(output_name)
            dtype = torch_dtype_from_trt(self.engine.get_binding_dtype(idx))
            shape = tuple(self.context.get_binding_shape(idx))
            device = torch_device_from_trt(self.engine.get_location(idx))
            output = torch.empty(size=shape, dtype=dtype, device=device)
            outputs[i] = output
            bindings[idx] = output.data_ptr()

        self.context.execute_async_v2(bindings,
                                      torch.cuda.current_stream().cuda_stream)

        outputs = tuple(outputs)
        if len(outputs) == 1:
            outputs = outputs[0]

        return outputs

我们重点看__init__()和forward这两个成员方法,即engine创建context,然后确定输入输出,执行execute_async_v2即可获取结果。

就这样,使用TensorRT调用已经序列化好的trt模型就成功啦。

利用Polygraphy查看ONNX与TRT模型的输出差异

Polygraphy是TensorRT官方提供的一系列小工具合集,通过这个工具我们看一下这个Resnet34_3inputs_448x448_20200609.onnx模型在转换为trt之后是否会有精度折损:

首先看一下FP32精度:
在这里插入图片描述
再看下FP16精度:
在这里插入图片描述
这里的绝对误差和相对误差容忍度设置的均为1e-3,精确到小数点后3位,可以看到上述onnx模型在转化为FP32的trt是没有大问题的,而FP16则有比较多的精度折损。至于会不会严重地影响结果需要我们通过具体或者一批case分析。限于篇幅放到后续说。

例子就这样~

TensorRT的缺点

TensorRT不是没有“缺点”的,有一些小小的缺点需要吐槽一下:

  • 经过infer优化后的模型与特定GPU绑定,例如在1080TI上生成的模型在2080TI上无法使用;
  • 高版本的TensorRT依赖于高版本的CUDA版本,而高版本的CUDA版本依赖于高版本的驱动,如果想要使用新版本的TensorRT,更换环境是不可避免的;
  • TensorRT尽管好用,但推理优化infer还是闭源的,像深度学习炼丹一样,也像个黑盒子,使用起来会有些畏手畏脚,不能够完全掌控。所幸TensorRT提供了较为多的工具帮助我们调试。

正所谓爱之深恨之切,老潘也知道,这些”缺点“也是没有办法的缺点,既然无法避免,就轻轻吐槽一下吧。

TensorRT配套周边

TensorRT毕竟发展这么多年了,官方也深知大家使用TensorRT的一些痛点,所以开发了一些比较实用的小工具来供大家使用,工具目前在TensorRT的开源主页,也就是说这些工具也是开源的:

在这里插入图片描述
这三个工具的基本功能大概介绍下:

  • ONNX GraphSurgeon 可以修改我们导出的ONNX模型,增加或者剪掉某些节点,修改名字或者维度等等
  • Polygraphy 各种小工具的集合,例如比较ONNX和trt模型的精度,观察trt模型每层的输出等等,主要用来debug一些模型的信息,还是比较有用的
  • PyTorch-Quantization 可以在Pytorch训练或者推理的时候加入模拟量化操作,从而提升量化模型的精度和速度,并且支持量化训练后的模型导出ONNX和TRT

用过ONNX GraphSurgeon以及Polygraphy,两者都是在实际部署转换中比较实用的工具,也确实解决了一些问题。唯一不足的是这些工具的教程都不是很详细,较难上手,之后也会详细介绍一哈这些工具的使用方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TensorRT详细入门指南 的相关文章

  • 如何编写嵌套的 __init__.py 文件

    我正在努力解决嵌套问题 init py在我正在编写的Python包中 该包具有以下架构 module init py submodule1 init py source py submodule2 init py source py sub
  • 使用 django-rest-framework 设置对象级权限

    尝试使用 django rest framework 最干净 最规范地管理 django guardian 对象级权限 我想将对象的读取权限 module view object 分配给在执行 POST 时发出请求的用户 我的基于阶级的观点
  • 多处理中的动态池大小?

    有没有办法动态调整multiprocessing Pool尺寸 我正在编写一个简单的服务器进程 它会产生工作人员来处理新任务 使用multiprocessing Process对于这种情况可能更适合 因为工作人员的数量不应该是固定的 但我需
  • Mypy 无法从文字列表推断项目的类型

    我有一个变量x和一个文字列表 例如 0 1 2 我想转换x这些文字之一 如果x在列表中 我将其退回 否则我返回一个后备值 from typing import Literal Set Foo Literal 0 1 2 foos Set F
  • 是否可以从 Julia 调用 Python 函数并返回其结果?

    我正在使用 Python 从网络上抓取数据 我想使用这些数据在 Julia 中运行计算 是否可以在 Julia 中调用该函数并返回其结果 或者我最好直接导出到 CSV 并以这种方式加载数据 绝对地 看PyCall jl https gith
  • Matplotlib:如何有效地将大量线段着色为独立渐变

    Python 绘图库 如何有效地将大量线段着色为独立渐变 已经 阅读this https stackoverflow com questions 8500700 how to plot a gradient color line in ma
  • 协程从未被等待

    我正在使用一个简单的上下文管理器 其中包含一个异步循环 class Runner def init self self loop asyncio get event loop def enter self return self def e
  • Python Tkinter 模块不显示输出

    我正在尝试学习 Python 并尝试使用 Python 中的 GUI 并遇到了这个 Tkinter 模块 我的代码运行 但运行时窗口没有出现 我的代码如下 from Tkinter import to create a root windo
  • Python Anaconda:如何测试更新的库是否与我现有的代码兼容?

    我在 Windows 7 机器上使用 Python 2 7 Anaconda 安装进行数据分析和科学计算 当新的库发布时 例如新版本的 pandas patsy 等 您建议我如何测试新版本与现有代码的兼容性 是否可以在同一台机器上安装两个
  • Python将文本文件解析为嵌套字典

    考虑以下数据结构 HEADER1 key value key value HEADER2 key value key value HEADER3 key value HEADER4 key value key value 原始数据中没有缩进
  • 从 Flask 运行 NPM 构建

    我有一个 React 前端 我想在与我的 python 后端 API 相同的源上提供服务 我正在尝试使用 Flask 来实现此目的 但我遇到了 Flask 找不到我的静态文件的问题 我的前端构建是用生成的npm run build in s
  • Airflow 1.9 - 无法将日志写入 s3

    我在 aws 的 kubernetes 中运行气流 1 9 我希望将日志发送到 s3 因为气流容器本身的寿命并不长 我已经阅读了描述该过程的各种线程和文档 但我仍然无法让它工作 首先是一个测试 向我证明 s3 配置和权限是有效的 这是在我们
  • 使用 Python 将连续日期分组在一起

    Given dates datetime 2014 10 11 datetime 2014 10 1 datetime 2014 10 2 datetime 2014 10 3 datetime 2014 10 5 datetime 201
  • 在Raspberry pi上升级skimage版本

    我已经使用 Raspberry Pi 2 上的 synaptic 包管理器安装了 python 包 然而 skimage 模块版本 0 6 是 synaptic 中最新的可用版本 有人可以指导我如何将其升级到0 11 因为旧版本中缺少某些功
  • XPath:通过当前节点属性选择当前和下一个节点的文本

    首先 这是从我之前的问题 https stackoverflow com questions 5202187 xpath select current and next nodes text by current node attribut
  • 可以使用哪些技术来衡量 pandas/numpy 解决方案的性能

    Question 如何简洁全面地衡量下面各个功能的性能 Example 考虑数据框df df pd DataFrame Group list QLCKPXNLNTIXAWYMWACA Value 29 52 71 51 45 76 68 6
  • 无法通过 Python 子进程进行 SSH

    我需要通过堡垒 ssh 进入机器 因此 该命令相当长 ssh i
  • AWS Lambda 不读取环境变量

    我正在编写一个 python 脚本来查询 Qualys API 中的漏洞元数据 我在 AWS 中将其作为 lambda 函数执行 我已经在控制台中设置了环境变量 但是当我执行函数时 出现以下错误 module initialization
  • 如何使用 python 定位和读取 Data Matrix 代码

    我正在尝试读取微管底部的数据矩阵条形码 我试过libdmtx http libdmtx sourceforge net 它有 python 绑定 当矩阵的点是方形时工作得相当好 但当矩阵的点是圆形时工作得更糟 如下所示 另一个复杂问题是在某
  • IndexError - 具有匀称形状的笛卡尔 PolygonPatch

    我曾经使用 shapely 制作一个圆圈并将其绘制在之前填充的图上 这曾经工作得很好 最近 我收到索引错误 我将代码分解为最简单的操作 但它甚至无法执行最简单的循环 import descartes import shapely geome

随机推荐

  • 并网逆变器PI控制(并网模式)

    并网逆变器PI控制 1 逆变器拓扑结构与数学模型 2 常用变换 2 1 abc alpha beta 变换及其逆变换 2 2
  • coreldraw软件完整最新版2022矢量图形设计软件

    CorelDRAW2022是一款矢量图平面设计软件 发展历史也很长 是平面设计师必学软件 和 illustrator 比起来操作性相对差一点 但在排版上比 AI 好用 还有一个很明显的优点就是在设计大型文件的时候不会很卡 矢量图形设计软件C
  • 设计模式C++学习笔记之二(Proxy代理模式)

    http www cnblogs com wanggary archive 2011 04 08 2009965 html 代理 一看名字就知道这只是个中介而已 真实的执行者在代理的后面呢 cbf4life在他的书里提的例子也很有趣 更详细
  • 基于Three.js的WebXR渲染入门

    1 Three js 渲染管线快速概览 我不会花太多时间讨论 Three JS 渲染管道的工作原理 因为它在互联网上有详细记录 例如 此链接 我将在下图中列出基础知识 以便更容易理解各个部分的去向 2 WebXR 设备 API 入门 在我们
  • AI绘画网站都有哪些比较好用?

    人工智能绘画网站是一种利用人工智能技术进行图像处理和创作的网站 这些绘画网站通常可以帮助艺术家以人工智能绘画的形式快速生成有趣 美丽和独特的绘画作品 无论你是专业的艺术家还是对人工智能绘画感兴趣的普通人 人工智能绘画网站都可以为你提供新的创
  • 【图像去噪】【TGV 正则器的快速计算方法】通过FFT的总(广义)变化进行图像去噪(Matlab代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 图像去噪 TGV 正则化器的快速计算方法
  • tomcat部署war包出错解决方案

    tomcat部署war包出错解决方案 最最简单直接明了的方法 卸载重新再装一遍 笔者重装了5 6遍 算是整好了 写篇博客 希望你萌 少走弯路 这是我走的弯路 https 下载 安装 配置及部署war包出错解决方案 1 jdk的安装及配置 2
  • git、gitee创建第一个项目、导入项目到线上仓库

    1 新建一个空文件夹 2 在文件夹中运行git bash 3 克隆线上的空仓库到文件夹中 git clone 线上仓库的地址 eg git clone aaa git 4 将项目中的代码复制到文件夹中 5 提交项目代码 运行命令 1 git
  • Shell脚本相关命令

    文章目录 1 编写简单的hello world脚本 查看脚本内容 并运行 2 练习加减乘除 比较等基本的算术运算 3 使用test命令对两个数值比较 等于 小于 大于等于 注释说明含义 4 使用test命令对两个字符串比较 等于 非空 小于
  • 大数据学习线路图

    大数据学习线路图 Java教程 Java 教程 Java 简介 Java 开发环境配置 Java 基础语法 Java 对象和类 Java 基本数据类型 Java变量类型 Java修饰符 Java运算符 Java循环结构 Java分支结构 J
  • C++循环经典练习题

    目录 前言 一 方阵 二 乘法口诀表 1 正向打印 2 反向打印 三 打印三角形 1 正三角 2 倒三角 四 杨辉三角 五 水仙花数 六 斐波那契数列 七 进制转换 1 十进制转二进制 2 二进制转十进制 总结 前言 在C 的海洋中 循环是
  • Qt-4.7.2在dm365下的移植

    此篇为我的上一篇博客 Qt 4 7 2及QtEmbedded 4 7 2在ubuntu11 10下的安装 后续 主要参考 博客http blog chinaunix net uid 25652733 id 291521 html 同样 参照
  • 如何成为一个牛逼的程序员

    一个快乐的人 不是因为他拥有的多 而是因为他计较的少 心态比勤劳更重要 用心做好事 作为程序员 身边总是有牛逼的前辈 后辈或者同辈 牛逼的人总是让人羡慕 比如自己苦搞一天的 BUG 头发快抓掉完了 人家扫上两眼 改一行代码 问题就解决了 比
  • Building Mongodb with Visual Studio 2008

    Building Mongodb with Visual Studio 2008 标签 mongodbpythonjavascriptbuild数据库c 2011 08 27 14 43 938人阅读 评论 0 收藏 举报 分类 VS200
  • C++基础二:模板

    模板 模板的概念 模板就是建立通用的模具 大大提高复用性 例如生活中的模板 模板的特点 模板不可以直接使用 它只是一个框架 模板的通用并不是万能的 函数模板 C 另一种编程思想称为 泛型编程 主要利用的技术就是模板 C 提供两种模板机制 函
  • base64的原理及优缺点

    优点可以加密 减少了HTTTP请求 缺点是需要消耗CPU进行编解码
  • 机器学习总结(一)

    机器学习总结 一 一 基本概念 1 有监督学习 输入数据是带有标签的 称为有监督学习 2 无监督学习 输入数据是不带标签的 称为无监督学习 3 局部最优 函数值空间的一个有限区域内寻找最小值 这个最小值 是小于或等于附近点的函数值 但是有可
  • tinycore的探索

    tinycore的探索 缘起 tinycore的安装 tinycore的持久化 tinycore的初始设置 结语 参考博文 这篇文章会写一些我在学习tinycore的过程中的体会 感兴趣的同学可以了解一下 由于我也是刚开始学习 所以可能会不
  • 【Android】Android工具函数整理

    import android app Activity import android app ActivityManager import android app KeyguardManager import android content
  • TensorRT详细入门指南

    前言 大名鼎鼎的TensorRT有多牛逼就不多说了 因为确实很好用 作为在英伟达自家GPU上的推理库 这些年来一直被大力推广 更新也非常频繁 issue反馈也挺及时 社区的负责人员也很积极 简直不要太NICE 只是TensorRT的入门门槛