Relay TensorRT 集成#

Author: Trevor Morris

简介#

NVIDIA TensorRT 是用于优化深度学习推理的库。此集成将尽可能多地将算子从 Relay 卸载到 TensorRT,从而在 NVIDIA GPU 上提供性能提升,而无需调整调度。

本指南将演示如何安装 TensorRT 并构建启用 TensorRT BYOC 和运行时的 TVM。它还将提供使用 TensorRT 编译和运行 ResNet-18 模型的示例代码,以及如何配置编译和运行时设置。最后,将记录支持的算子以及如何扩展集成以支持其他算子。

安装 TensorRT#

要下载 TensorRT,您需要创建 NVIDIA 开发者计划账户。请参阅 NVIDIA 的文档以获取更多信息:https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html。如果您拥有 Jetson 设备(例如 TX1、TX2、Xavier 或 Nano),TensorRT 将通过 JetPack SDK 预装在设备上。

有两种安装 TensorRT 的方法:

  • 通过 deb 或 rpm 包进行系统安装。

  • 通过 tar 文件安装。

使用 tar 文件安装方法时,您必须提供解压后的 tar 存档路径,例如 USE_TENSORRT_RUNTIME=/path/to/TensorRT。使用系统安装方法时,USE_TENSORRT_RUNTIME=ON 将自动定位您的安装。

构建支持 TensorRT 的 TVM#

TVM 中的 TensorRT 集成有两个独立的构建标志。这些标志还支持交叉编译:USE_TENSORRT_CODEGEN=ON 允许您在主机上构建支持 TensorRT 的模块,而 USE_TENSORRT_RUNTIME=ON 将启用边缘设备上的 TVM 运行时以执行 TensorRT 模块。如果您想使用相同的 TVM 构建编译并执行模型,则应同时启用这两个标志。

  • USE_TENSORRT_CODEGEN=ON/OFF - 此标志将启用编译 TensorRT 模块,该模块不需要任何 TensorRT 库。

  • USE_TENSORRT_RUNTIME=ON/OFF/path-to-TensorRT - 此标志将启用 TensorRT 运行时模块。这将根据已安装的 TensorRT 库构建 TVM。

config.cmake 文件中的示例设置:

set(USE_TENSORRT_CODEGEN ON)
set(USE_TENSORRT_RUNTIME /home/ubuntu/TensorRT-7.0.0.11)

使用 TensorRT 构建和部署 ResNet-18#

从 MXNet ResNet-18 模型创建 Relay 图。

import tvm
from tvm import relay
import mxnet
from mxnet.gluon.model_zoo.vision import get_model

dtype = "float32"
input_shape = (1, 3, 224, 224)
block = get_model('resnet18_v1', pretrained=True)
mod, params = relay.frontend.from_mxnet(block, shape={'data': input_shape}, dtype=dtype)

为 TensorRT 注解和分区图。TensorRT 集成支持的所有算子将被标记并卸载到 TensorRT。其余算子将通过常规的 TVM CUDA 编译和代码生成。

from tvm.relay.op.contrib.tensorrt import partition_for_tensorrt
mod = partition_for_tensorrt(mod, params)

使用 partition_for_tensorrt 返回的新模块和配置构建 Relay 图。目标必须始终是 cuda 目标。partition_for_tensorrt 将自动填充配置中的所需值,因此无需修改它——只需将其传递给 PassContext,以便在编译期间读取这些值。

target = "cuda"
with tvm.transform.PassContext(opt_level=3):
    lib = relay.build(mod, target=target, params=params)

导出模块。

lib.export_library('compiled.so')

在目标机器上加载模块并运行推理,目标机器必须启用 USE_TENSORRT_RUNTIME 构建。第一次运行会花费较长时间,因为需要构建 TensorRT 引擎。

dev = tvm.cuda(0)
loaded_lib = tvm.runtime.load_module('compiled.so')
gen_module = tvm.contrib.graph_executor.GraphModule(loaded_lib['default'](dev))
input_data = np.random.uniform(0, 1, input_shape).astype(dtype)
gen_module.run(data=input_data)

分区和编译设置#

可以在 partition_for_tensorrt 中配置一些选项。

  • version - 目标 TensorRT 版本,格式为 (major, minor, patch) 元组。如果 TVM 使用 USE_TENSORRT_RUNTIME=ON 编译,则将使用链接的 TensorRT 版本。版本将影响哪些算子可以分区到 TensorRT。

  • use_implicit_batch - 使用 TensorRT 隐式批处理模式(默认为 true)。设置为 false 将启用显式批处理模式,这将扩展支持的算子以包括修改批处理维度的算子,但可能会降低某些模型的性能。

  • remove_no_mac_subgraphs - 一种提高性能的启发式方法。如果子图没有乘加操作,则删除为 TensorRT 分区的子图。删除的子图将通过 TVM 的标准编译进行处理。

  • max_workspace_size - 允许每个子图用于 TensorRT 引擎创建的工作空间大小(以字节为单位)。有关更多信息,请参阅 TensorRT 文档。可以在运行时覆盖。

运行时设置#

可以使用环境变量在运行时配置一些其他选项。

  • 自动 FP16 转换 - 可以设置环境变量 TVM_TENSORRT_USE_FP16=1 以自动将模型的 TensorRT 组件转换为 16 位浮点精度。这可以大大提高性能,但可能会导致模型精度略有下降。

  • 缓存 TensorRT 引擎 - 在第一次推理期间,运行时将调用 TensorRT API 来构建引擎。这可能很耗时,因此您可以设置 TVM_TENSORRT_CACHE_DIR 指向一个目录,以将这些构建的引擎保存到磁盘上。下次加载模型并提供相同的目录时,运行时将加载已构建的引擎以避免长时间预热。每个模型都需要一个唯一的目录。

  • TensorRT 有一个参数用于配置模型中每层可以使用的最大暂存空间量。通常最好使用不会导致内存不足的最大值。您可以使用 TVM_TENSORRT_MAX_WORKSPACE_SIZE 通过指定要使用的工作空间大小(以字节为单位)来覆盖此设置。

  • 对于包含动态批处理维度的模型,可以使用变量 TVM_TENSORRT_MULTI_ENGINE 来确定如何在运行时创建 TensorRT 引擎。默认模式 TVM_TENSORRT_MULTI_ENGINE=0 将一次仅在内存中维护一个引擎。如果遇到具有更高批处理大小的输入,引擎将使用新的 max_batch_size 设置重建。该引擎将兼容从 1 到 max_batch_size 的所有批处理大小。此模式减少了运行时使用的内存量。第二种模式 TVM_TENSORRT_MULTI_ENGINE=1 将为遇到的每个批处理大小构建一个唯一的 TensorRT 引擎。这将提供更高的性能,但会消耗更多内存。

支持的算子#

Relay 节点

标记

nn.relu

sigmoid

tanh

nn.batch_norm

nn.layer_norm

nn.softmax

nn.conv1d

nn.conv2d

nn.dense

nn.bias_add

add

subtract

multiply

divide

power

maximum

minimum

nn.max_pool2d

nn.avg_pool2d

nn.global_max_pool2d

nn.global_avg_pool2d

exp

log

sqrt

abs

negative

nn.batch_flatten

expand_dims

squeeze

concatenate

nn.conv2d_transpose

transpose

layout_transform

reshape

nn.pad

sum

prod

max

min

mean

nn.adaptive_max_pool2d

nn.adaptive_avg_pool2d

nn.batch_matmul

clip

Requires TensorRT 5.1.5 or greater

nn.leaky_relu

Requires TensorRT 5.1.5 or greater

sin

Requires TensorRT 5.1.5 or greater

cos

Requires TensorRT 5.1.5 or greater

atan

Requires TensorRT 5.1.5 or greater

ceil

Requires TensorRT 5.1.5 or greater

floor

Requires TensorRT 5.1.5 or greater

split

Requires TensorRT 5.1.5 or greater

strided_slice

Requires TensorRT 5.1.5 or greater

nn.conv3d

Requires TensorRT 6.0.1 or greater

nn.max_pool3d

Requires TensorRT 6.0.1 or greater

nn.avg_pool3d

Requires TensorRT 6.0.1 or greater

nn.conv3d_transpose

Requires TensorRT 6.0.1 or greater

erf

Requires TensorRT 7.0.0 or greater

Adding a new operator#

要添加对新算子的支持,需要修改一系列文件:

  • src/runtime/contrib/tensorrt/tensorrt_ops.cc 创建实现 TensorRTOpConverter 接口的新算子转换器类。您必须实现构造函数以指定有多少输入以及它们是张量还是权重。您还必须实现 Convert 方法以执行转换。这是通过使用来自 params 的输入、属性和网络来添加新的 TensorRT 层并推送层输出来完成的。您可以使用现有的转换器作为示例。最后,在 GetOpConverters() 映射中注册您的新算子转换器。

  • python/relay/op/contrib/tensorrt.py 此文件包含 TensorRT 的注释规则。这些规则决定了支持的算子及其属性。您必须为 relay 算子注册注解函数,并通过检查属性返回 true 或 false 来指定您的转换器支持哪些属性。

  • tests/python/contrib/test_tensorrt.py 为给定的算子添加单元测试。