tensorRT是一个高性能深度学习推理库。tensorrt包含推理优化器和运行时。TensorRT能够以更高的吞吐量和更低的延迟运行。
本指南包含tensorrt基本的安装、转换、运行时选择以及最佳运用。
包含:容器、Debian、pip安装方式。
公有云上部署参照:NGC Certified Public Clouds Documentation
1、下载tensorRT
2、安装
os="ubuntuxx04"
tag="cudax.x-trt8.x.x.x-yyyymmdd"
sudo dpkg -i nv-tensorrt-repo-${os}-${tag}_1-1_amd64.deb
sudo apt-key add /var/nv-tensorrt-repo-${os}-${tag}/7fa2af80.pub
sudo apt-get update
sudo apt-get install tensorrt
python3-libnvinfer安装
python3 -m pip install numpy
sudo apt-get install python3-libnvinfer-dev
TensorFlow中使用tesnorrt,graphsurgeon-tf也会被一起安装
python3 -m pip install protobuf
sudo apt-get install uff-converter-tf
某些例子和python中需要ONNX graphsurgeon
python3 -m pip install numpy onnx
sudo apt-get install onnx-graphsurgeon
3、验证安装
dpkg -l | grep TensorRT
虽然pip方式安装没啥问题,但实际上还是需要依赖很多其他的项
仅支持python3.6到python3.9,以及CUDA11.x。Linux和X86_64 CPU。CentOS 7和Ubuntu 18.04以上。
开始之前
升级一下pip和setuptools
python3 -m pip install --upgrade setuptools pip
安装nvidia-pyindex。
python3 -m pip install nvidia-pyindex
如果使用requirements.txt可以在里面添加命以安装nvidia-pyindex。
--extra-index-url https://pypi.ngc.nvidia.com
步骤
1、安装tensorrt
python3 -m pip install --upgrade nvidia-tensorrt
安装的时候CUDA和CUDNN也会下载下来,因为tensorrt需要这些。
如果出现了一些错误信息,要么是nvidia-pyindex没有安装好,要么就是python版本不对。
2、验证安装是否成功
导入tensorrt包
确认安装的tensorRT版本是否正确。
创建一个builder对象
python3
>>> import tensorrt
>>> print(tensorrt.__version__)
>>> assert tensorrt.Builder(tensorrt.Logger())
tensorrt会将模型部署成一个engine。
转换一个tensorrt能够使用的模型主要有三个选择:
TF-TRT可以转换TensorFlow模型的同时也提供了一个高级runtime API,但是个别特定的算子是不支持的。
ONNX是最常见的选择,它与框架无关,可以支持TensorFlow、pytorch等。ONNX中所有的算子都必须是tensorrt支持的,那些不支持的算子需要自己手动添加。ONNX转换只会生成单一的tensorrt 引擎,比TF-TRT开销要小一些。
某些情况下,比如为了提升性能或者自定义的要求,会通过TensorRT network defintion API手动构建一个TensorRT engines。实际上的手动构建过程也是一一对应的使用TensorRT算子去构建网络,并且载入训练好的模型参数。
有三种部署的方式:
我们需要根据部署的方式选择对应的模型转换方式。
使用TF-TRT一般都是部署在TensorFlow中的,TF-TRT转换的结果是一个Tensorflow图,只不过其中的操作是TensorRT算子。也就是说TF-TRT模型可以在python中和使用其他TensorFlow模型一样使用。
TensorRT runtime API开销最小,控制最细微,但是如果有一些需要自定义的算子,需要编写成插件形式。一般都是其他AI框架导出到ONNX的。
NVIDIA Triton Inference Server是一个开源的推理服务软件,可以支持部署任意框架的模型(TensorFlow、tensorrt、pytorch、ONNX runtime),可以直接部署在本地或者其他云服务平台。可以并发执行多个推理,自带负载均衡。假如这是一个云服务,例如http形式的NVIDIA Triton Inference Server是你很好的选择。
选择转换和部署模型的两个重要因素:
两个主要的方式
下载ONNX形式的ResNet-50模型,并解压
wget https://s3.amazonaws.com/download.onnx/models/opset_8/resnet50.tar.gz
tar xzf resnet50.tar.gz
小的batch size延时会比较小,大的batch size吞吐量比较大。batch size越大,处理时间越长,但是减少了每个样本的推理时间。
TensorRT可以动态的设置batchsize的大小,也可以设置一个固定的值。比如BATCH_SIZE=64
推理的精度明显可以比训练的时候要小,精度越低,计算越快、显存消耗越低,并且不会损失太多重要的精度信息。TensorRT支持TF32、FP32、FP16和INT8。
FP32是大多数框架在训练的时候默认的训练精度
import numpy as np
PRECISION = np.float32
trtexec --onnx=resnet50/model.onnx --saveEngine=resnet_engine.trt
resnet_engine.trt就是TensorRT engine
4.5 部署模型
独立部署在python、C++中
部署在TensorFlow中。
1、创建ONNXClassifierWrapper
from onnx_helper import ONNXClassifierWrapper
N_CLASSES = 1000 # Our ResNet-50 is trained on a 1000 class ImageNet task
trt_model = ONNXClassifierWrapper("resnet_engine.trt", [BATCH_SIZE, N_CLASSES],
target_dtype = PRECISION)
2、生成模拟批量
BATCH_SIZE=32
dummy_input_batch = np.zeros((BATCH_SIZE, 224, 224, 3))
3、将batch数据喂入engine,得到预期的预测
predictions = trt_model.predict(dummy_input_batch)
直到运行的时候才开始导入和初始化engine,因此需要一些时间。
TF-TRT是一个python接口,可以直接TensorFlow中使用,可以使用TensorFlow SavedModels直接保存
TensorFlow可以使用keras2onnx和tf2onnx。
也可以使用trtexec
步骤
1、从keras.applications导入ResNet-50模型,这里面也包含了预训练的权重
from tensorflow.keras.applications import ResNet50
model = ResNet50(weights='imagenet')
2、转换ResNet-50模型到ONNX形式
import tf2onnx
model.save('my_model')
!python -m tf2onnx.convert --saved-model my_model --output temp.onnx
onnx_model = onnx.load_model('temp.onnx')
3、设置batch size
import onnx
BATCH_SIZE = 64
inputs = onnx_model.graph.input
for input in inputs:
dim1 = input.type.tensor_type.shape.dim[0]
dim1.dim_value = BATCH_SIZE
4、保存ONNX文件
model_name = "resnet50_onnx_model.onnx"
onnx.save_model(onnx_model, model_name)
步骤
1、 从torchvision中导出ResNet-50模型,并且包含预训练权重
import torchvision.models as models
resnext50_32x4d = models.resnext50_32x4d(pretrained=True)
2、保存ONNX
import torch
BATCH_SIZE = 64
dummy_input=torch.randn(BATCH_SIZE, 3, 224, 224)
这里会创建一个假的batch。
3、保存ONNX文件
import torch.onnx
torch.onnx.export(resnext50_32x4d, dummy_input, "resnet50_onnx_model.onnx",
verbose=False)
trtexec --onnx=resnet50_onnx_model.onnx --saveEngine=resnet_engine.trt
TensorRT直接运行比TensorFlow的TF-TRT的性能要好很多。C++ API的开销是最低的,但是python的API很多,比如Numpy、Scipy,比较容易做一些原型设计、debugging和测试。
步骤
1、下载源码
$ git clone https://github.com/NVIDIA/TensorRT.git
$ cd TensorRT/quickstart
2、将模型转换成ONNX
运行一个NVIDIA PyTorch容器
$ docker run --rm -it --gpus all -p 8888:8888 -v `pwd`:/workspace -w /workspace/
SemanticSegmentation nvcr.io/nvidia/pytorch:20.12-py3 bash
导出模型到ONNX
$ python export.py
3、使用trtexec将ONNX构建成一个tensorRT engine
trtexec利用tensorrt onnx解析器载入onnx模型到TensorRT网络图中,然后TensorRT Builder API生成一个被优化的engine。这个构建过程比较耗时,但是可以离线操作。
trtexec --onnx=fcn-resnet101.onnx --fp16 --workspace=64 --minShapes=input:1x3x256x256
--optShapes=input:1x3x1026x1282 --maxShapes=input:1x3x1440x2560 --buildOnly --
saveEngine=fcn-resnet101.engine
--fp16 使用FP16精度,还有一个选项是FP32
--int8 使用INT8精度,还有一个选项是FP32
--best 为每一层使用所有支持的精度,以达到最佳性能
--workspace为算法设置持久显存的大小MB。基于特定平台,这个值应该尽可能的高,TensorRT会分配所需要的量,但是不会超过最大值。
--minShapes 和 --maxShapes指定网络输入的每个维度上的范围。--optShape指定auto-tuner应该处理的尺寸。
--buildOnly 不需要度量推理性能
--saveEngine 保存的序列化引擎的路径
--safe 就是tensorRT安全模式下运行。
--minTiming 和 --avgTiming在tactic selection时的最小和平均迭代次数。
--noBuilderCache 在tensorRT中禁用层定时缓存,通过缓存层概要信息,计时缓存有助于减少构建阶段花费的时间,并且对大多数模型都适用。在遇到问题的时候才打开这个开关。
--timingCacheFile可以保存和载入全局的timing cache。
4、可选项目。使用trtexect验证随机生成的伪数据输入情况。
trtexec --shapes=input:1x3x1026x1282 --loadEngine=fcn-resnet101.engine
--shapes就是输入的形状,假设成功了。
1、构建并运行c++分割教程。
$ make
$ ./bin/segmentation_tutorial
步骤
1、从文件中反序列化TensorRT engine。该文件内容将被读取到缓冲并反序列化到内存。
td::vector engineData(fsize);
engineFile.read(engineData.data(), fsize);
util::UniquePtr
runtime{nvinfer1::createInferRuntime(sample::gLogger.getTRTLogger())};
util::UniquePtr mEngine(runtime-
>deserializeCudaEngine(engineData.data(), fsize, nullptr));
TensorRT对象通过destory()方法销毁,本示例中使用自定义删除方法的智能指针来管理生存周期。
struct InferDeleter
{
template
void operator()(T* obj) const
{
if (obj) obj->destroy();
}
};
template
using UniquePtr = std::unique_ptr
2、TensorRT执行上下文封装了执行状态,比如推理过程中产生的临时变量保存在持久化显存中。
因为分割模型的输入是动态变化的,但是推理的时候必须设置固定的形状。输出的形状可以查询网络输出形状。
auto input_idx = mEngine->getBindingIndex("input");
assert(mEngine->getBindingDataType(input_idx) == nvinfer1::DataType::kFLOAT);
auto input_dims = nvinfer1::Dims4{1, 3 /* channels */, height, width};
context->setBindingDimensions(input_idx, input_dims);
auto input_size = util::getMemorySize(input_dims, sizeof(float));
auto output_idx = mEngine->getBindingIndex("output");
assert(mEngine->getBindingDataType(output_idx) == nvinfer1::DataType::kINT32);
auto output_dims = context->getBindingDimensions(output_idx);
auto output_size = util::getMemorySize(output_dims, sizeof(int32_t));
网络输入输出索引可以通过名称查询
3、在准备推理的时,为输入输出分配CUDA显存,处理完数据后,就将数据复制到分配的显存当中,并生成引擎绑定列表。
语义分割中,输入数据会被归一化到[0,1],RGB归一化使用的均值是[0.485, 0.456, 0.406],标准差[0.229, 0.224,0.225]。此处操作有utility 类 RGBImageReader抽象。
void* input_mem{nullptr};
cudaMalloc(&input_mem, input_size);
void* output_mem{nullptr};
cudaMalloc(&output_mem, output_size);
const std::vector mean{0.485f, 0.456f, 0.406f};
const std::vector stddev{0.229f, 0.224f, 0.225f};
auto input_image{util::RGBImageReader(input_filename, input_dims, mean, stddev)};
input_image.read();
auto input_buffer = input_image.process();
cudaMemcpyAsync(input_mem, input_buffer.get(), input_size, cudaMemcpyHostToDevice, stream);
4、推理执行使用上下文executionV2或enqueueV2。在执行完成后,将预测结果复制到主机内存,并释放所有显存。
void* bindings[] = {input_mem, output_mem};
bool status = context->enqueueV2(bindings, stream, nullptr);
auto output_buffer = std::unique_ptr{new int[output_size]};
cudaMemcpyAsync(output_buffer.get(), output_mem, output_size, cudaMemcpyDeviceToHost, stream);
cudaStreamSynchronize(stream);
cudaFree(input_mem);
cudaFree(output_mem);
5、为了可视化结果,将预测结果用伪彩色显示,输出到output.ppm。有utility类ArgmaxImageWriter抽象。
const int num_classes{21};
const std::vector palette{ (0x1 << 25) - 1, (0x1 << 15) - 1, (0x1 << 21) - 1};
auto output_image{util::ArgmaxImageWriter(output_filename, output_dims, palette, num_classes)};
output_image.process(output_buffer.get());
output_image.write();
1、安装pycuda
$ pip install pycuda
2、运行jupyter,并且复制对应token到浏览器中http://
$ jupyter notebook --port=8888 --no-browser --ip=0.0.0.0 --allow-root
3、打来tutorial-runtime.ipynb作参考
表1.
资源 | 描述 |
layer构建的API文档 | layer层手动构建是很有必要的,这样我们可以自定义的构建网络模型。 |
ONNX解析插件示例文档 | 如果模型中有TensorRT不支持的层,我们就可以自己写一个解析ONNX的插件。 |
ONNX-TensorRT 的GitHub | 在python中使用ONNX-TensorRT做早期的原型设计非常有效。 |
TF-TRT产品文档 | TF-TRT产品文档 |
分析工具 | 分析工具 |
TensorRT的产品文档 | TensorRT的产品文档 |
TensorRT OSS的GitHub | 有OSS TensorRT的组件、运用、插件示例等 |
TensorRT开发者页 | 包含下载、博客、代码示例 |
B
Batch
batch是统一处理的输入集合,batch中每个实例具有相同的形状,并且经过网络的处理流程也是一样的,在推理过程中是并行处理的。
Builder
TensorRT的模型优化器,Builder接受一个网络的定义作为输入、执行一个与设备无关和指定设备的优化,并创建一个engine。
D
Dynamic batch
一种推理部署模式,batch size只有在运行的时候才知道。batch是推理时唯一可以配置的维度。
E
Engine
由TensorRT Builder优化的网络模型表达。
Explicit batch
指定Batch大小,ONNX解析中不支持。
F
Framework integration
一些如TensorFlow框架中集成了tensorRT.
N
Network definition
网络模型在TensorRT中的表示,包含tensor和operators的graph
O
ONNX
Open Neural Network eXchange,用于表示机器学习模型的独立框架标准。
ONNX parser
从ONNX模型中解析并创建一个tensorRT的网络定义。
P
Plan
序列化形式的优化后的推理引擎,为了初始化推理引擎,应用程序首先会从一个plan文件中反序列化出一个模型。典型的应用程序之构建一次引擎,并将其序列化为plan文件以供后续使用。
Precision
表示数值的精度,这个是在构建tensorRT的时候就会被指定,tensorRT支持FP32、FP16、INT8。之前大多数设备默认是FP32,现在默认为TF32,这是一种使用FP32存储用于低精度快速计算的形式。
R
Runtime
在engine推理时执行TensorRT的组件。runtime API支持引擎输入输出的同步异步执行、分析、枚举和查询。
T
TF-TRT
TensorFlow中集成的TensorRT,优化并执行兼容的子图,并允许tensorFlow执行剩余的子图。