使用NVIDIA TensorRT加速深度学习推理(更新)
本文于2021年7月20日更新,以反映NVIDIA TensorRT 8.0的更新。
TensorRT8都出来了,记得2021年春,我还用5版 做加速,各种版本,模型不支持很痛苦。相信8会支持更多模型和 更加友好。这个版本更新的真是快
NVIDIA TensorRT是一个用于深度学习推理的SDK。TensorRT提供api和解析器,从所有主要的深度学习框架导入训练过的模型。然后生成可部署在数据中心、汽车和嵌入式环境中的优化运行时引擎。
这篇文章简单介绍了如何使用TensorRT。您将了解如何将深度学习应用程序部署到GPU上,提高吞吐量并减少推理期间的延迟。它使用一个c++示例引导您将PyTorch模型转换为ONNX模型,并将其导入TensorRT,应用优化,并为数据中心环境生成高性能运行时引擎。
tensort同时支持c++和Python;如果您使用其中任何一种,那么这个工作流讨论可能会很有用。如果您更喜欢使用Python,请参见TensorRT文档中的使用Python API。see Using the Python API in the TensorRT documentation.
深度学习可广泛应用于自然语言处理、推荐系统、图像和视频分析等领域。随着越来越多的应用程序在生产中使用深度学习,对准确性和性能的要求导致模型的复杂性和规模急剧增长。
安全关键型应用(如汽车)对深度学习模型的吞吐量和延迟提出了严格要求。对于一些消费者应用程序(包括推荐系统)也是如此。
TensorRT旨在帮助这些用例部署深度学习。TensorRT支持每一个主要的框架,通过强大的优化,使用降低的精度和高效的内存使用,TensorRT帮助处理大量的数据和低延迟。
示例应用程序使用来自Kaggle的脑MRI分割数据的输入数据来执行推断。
The sample application uses input data from Brain MRI segmentation data from Kaggleto perform inference.
Requirements(基本需求)
为了继续阅读这篇文章,你需要一台具有cuda功能的GPU的计算机,或者一个具有GPU和tensort安装的云实例。在Linux上,最简单的入门方法是下载gpu加速
PyTorch容器与NVIDIA容器注册中心的TensorRT集成(在NGC上)。该链接将包含容器的更新版本,但为了确保本教程正常工作,我们指定了本文使用的版本:
PyTorch container with TensorRT integration from the NVIDIA Container Registry (on NGC). The link will have an updated version of the container, but to make sure that this tutorial works properly, we specify the version used for this post:
# Pull PyTorch container
docker pull nvcr.io/nvidia/pytorch:20.07-py3
这里的dock 和 conda的虚拟环境类似。如果你是高手 可以用conda代替,如果你刚入门,那还是挺官网的指导 用dock
此容器具有以下规格:
- Ubuntu 18.04
- Python 3.6.10
- CUDA 11.0
- Torch 1.6.0a
- TensorRT 7.1.3
因为在本演练中使用TensorRT 8,所以必须在容器中升级它。下一步是下载。
.deb package for TensorRT 8 (CUDA 11.0, Ubuntu 18.04),
并安装以下要求:
# Export absolute path to directory hosting TRT8.deb
export TRT_DEB_DIR_PATH=$HOME/trt_release # Change this path to where you’re keeping your .deb file
# Run container
docker run --rm --gpus all -ti --volume $TRT_DEB_DIR_PATH:/workspace/trt_release --net host nvcr.io/nvidia/pytorch:20.07-py3
# Update TensorRT version to 8
dpkg -i nv-tensorrt-repo-ubuntu1804-cuda11.0-trt8.0.0.3-ea-20210423_1-1_amd64.deb
apt-key add /var/nv-tensorrt-repo-ubuntu1804-cuda11.0-trt8.0.0.3-ea-20210423/7fa2af80.pub
apt-get update
apt-get install -y libnvinfer8 libnvinfer-plugin8 libnvparsers8 libnvonnxparsers8
apt-get install -y libnvinfer-bin libnvinfer-dev libnvinfer-plugin-dev libnvparsers-dev
apt-get install -y tensorrt
# Verify TRT 8.0.0 installation
dpkg -l | grep TensorRT
Simple TensorRT example(简单TensorRT实例)
以下是这个示例应用程序的四个步骤:
- Convert the pretrained image segmentation PyTorch model into ONNX.
- Import the ONNX model into TensorRT.
- Apply optimizations and generate an engine.
- Perform inference on the GPU.
也就是说:
1.将预训练的图像分割PyTorch模型转换为ONNX。
2.将ONNX模型导入TensorRT。
3.应用优化并生成引擎。
4.在GPU上进行推理。(这个根据编译环境 分为C++ 和 python )
就这四步。
导入ONNX模型包括从磁盘上的保存文件加载它,并将其从本机框架或格式转换为TensorRT网络。
ONNX 是表示深度学习模型的标准,使它们能够在框架之间传输。
许多框架,如caff2、Chainer、CNTK、PaddlePaddle、PyTorch和MXNet都支持ONNX格式。然后,根据指定的输入模型、目标GPU平台和其他配置参数构建优化的TensorRT引擎。最后一步是向tensort引擎提供输入数据以执行推断。
应用程序在TensorRT中使用了以下组件:
----- ONNX解析器:将经过转换的PyTorch训练的模型转换为ONNX格式作为输入,并在TensorRT中填充一个网络对象。
----- 构建器:使用TensorRT中的网络,生成针对目标平台优化的引擎。
----- 引擎:接受输入数据,执行推理,并发出推理输出。
----- Logger:与构建器和引擎相关联,以捕获构建和推断阶段的错误、警告和其他信息。
将预训练的图像分割PyTorch模型转换为ONNX
成功地从NGC注册中心安装PyTorch container 并将其升级为TensorRT 8.0之后,运行以下命令下载运行这个示例应用程序所需的所有内容(示例代码、测试输入数据和参考输出)。然后,更新依赖项并使用提供的makefile编译应用程序。
>> sudo apt-get install libprotobuf-dev protobuf-compiler # protobuf is a prerequisite library
>> git clone --recursive https://github.com/onnx/onnx.git # Pull the ONNX repository from GitHub
>> cd onnx
>> mkdir build && cd build
>> cmake .. # Compile and install ONNX
>> make # Use the ‘-j’ option for parallel jobs, for example, ‘make -j $(nproc)’
>> make install
>> cd ../..
>> git clone https://github.com/parallel-forall/code-samples.git
>> cd code-samples/posts/TensorRT-introduction
Modify $TRT_INSTALL_DIR in the Makefile.
>> make clean && make # Compile the TensorRT C++ code
>> cd ..
>> wget https://developer.download.nvidia.com/devblogs/speeding-up-unet.7z // Get the ONNX model and test the data
>> sudo apt install p7zip-full
>> 7z x speeding-up-unet.7z # Unpack the model data into the unet folder
>> cd unet
>> python create_network.py #Inside the unet folder, it creates the unet.onnx file
将经过pytorch训练的UNet模型转换为ONNX,如下面的代码示例所示:
import torch
from torch.autograd import Variable
import torch.onnx as torch_onnx
import onnx
def main():
input_shape = (3, 256, 256)
model_onnx_path = "unet.onnx"
dummy_input = Variable(torch.randn(1, *input_shape))
model = torch.hub.load('mateuszbuda/brain-segmentation-pytorch', 'unet',
in_channels=3, out_channels=1, init_features=32, pretrained=True)
model.train(False)
inputs = ['input.1']
outputs = ['186']
dynamic_axes = {'input.1': {0: 'batch'}, '186':{0:'batch'}}
out = torch.onnx.export(model, dummy_input, model_onnx_path, input_names=inputs, output_names=outputs, dynamic_axes=dynamic_axes)
if __name__=='__main__':
main()
接下来,为推断准备输入数据。从Kaggle 目录下载所有图片。从 brain-segmentation-pytorch存储库中将文件名和utils.py文件中没有_mask的任何三张图像复制到/unet目录。准备三张图片作为本文后面的输入数据。准备input_0。pb和ouput_0。Pb文件供以后使用,运行以下代码示例:
import torch
import argparse
import numpy as np
from torchvision import transforms
from skimage.io import imread
from onnx import numpy_helper
from utils import normalize_volume
def main(args):
model = torch.hub.load('mateuszbuda/brain-segmentation-pytorch', 'unet',
in_channels=3, out_channels=1, init_features=32, pretrained=True)
model.train(False)
filename = args.input_image
input_image = imread(filename)
input_image = normalize_volume(input_image)
input_image = np.asarray(input_image, dtype='float32')
preprocess = transforms.Compose([
transforms.ToTensor(),
])
input_tensor = preprocess(input_image)
input_batch = input_tensor.unsqueeze(0)
tensor1 = numpy_helper.from_array(input_batch.numpy())
with open(args.input_tensor, 'wb') as f:
f.write(tensor1.SerializeToString())
if torch.cuda.is_available():
input_batch = input_batch.to('cuda')
model = model.to('cuda')
with torch.no_grad():
output = model(input_batch)
tensor = numpy_helper.from_array(output[0].cpu().numpy())
with open(args.output_tensor, 'wb') as f:
f.write(tensor.SerializeToString())
if __name__=='__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--input_image', type=str)
parser.add_argument('--input_tensor', type=str, default='input_0.pb')
parser.add_argument('--output_tensor', type=str, default='output_0.pb')
args=parser.parse_args()
main(args)
To generate processed input data for inference, run the following commands:
>> pip install medpy #dependency for utils.py file
>> mkdir test_data_set_0
>> mkdir test_data_set_1
>> mkdir test_data_set_2
>> python prepareData.py --input_image your_image1 --input_tensor test_data_set_0/input_0.pb --output_tensor test_data_set_0/output_0.pb # This creates input_0.pb and output_0.pb
>> python prepareData.py --input_image your_image2 --input_tensor test_data_set_1/input_0.pb --output_tensor test_data_set_1/output_0.pb # This creates input_0.pb and output_0.pb
>> python prepareData.py --input_image your_image3 --input_tensor test_data_set_2/input_0.pb --output_tensor test_data_set_2/output_0.pb # This creates input_0.pb and output_0.pb
就这样,您已经准备好了执行推断的输入数据。
将ONNX模型导入到TensorRT中,生成引擎,并执行推理
使用训练过的模型和作为输入传递的输入数据运行示例应用程序。数据以ONNX protobuf文件的形式提供。示例应用程序将TensorRT生成的输出与同一文件夹中的ONNX .pb文件中的参考值进行比较,并在提示符中总结结果。
导入UNet ONNX模型并生成引擎可能需要几秒钟的时间。它还生成便携灰度图(PGM)格式的输出图像作为output. PGM。
>> cd to code-samples/posts/TensorRT-introduction-updated
>> ./simpleOnnx path/to/unet/unet.onnx fp32 path/to/unet/test_data_set_0/input_0.pb # The sample application expects output reference values in path/to/unet/test_data_set_0/output_0.pb
...
...
: --------------- Timing Runner: Conv_40 + Relu_41 (CaskConvolution)
: Conv_40 + Relu_41 Set Tactic Name: volta_scudnn_128x128_relu_exp_medium_nhwc_tn_v1 Tactic: 861694390046228376
: Tactic: 861694390046228376 Time: 0.237568
...
: Conv_40 + Relu_41 Set Tactic Name: volta_scudnn_128x128_relu_exp_large_nhwc_tn_v1 Tactic: -3853827649136781465
: Tactic: -3853827649136781465 Time: 0.237568
: Conv_40 + Relu_41 Set Tactic Name: volta_scudnn_128x64_sliced1x2_ldg4_relu_exp_large_nhwc_tn_v1 Tactic: -3263369460438823196
: Tactic: -3263369460438823196 Time: 0.126976
: Conv_40 + Relu_41 Set Tactic Name: volta_scudnn_128x32_sliced1x4_ldg4_relu_exp_medium_nhwc_tn_v1 Tactic: -423878181466897819
: Tactic: -423878181466897819 Time: 0.131072
: Fastest Tactic: -3263369460438823196 Time: 0.126976
: >>>>>>>>>>>>>>> Chose Runner Type: CaskConvolution Tactic: -3263369460438823196
...
...
INFO: [MemUsageChange] Init cuDNN: CPU +1, GPU +8, now: CPU 1148, GPU 1959 (MiB)
: Total per-runner device memory is 79243264
: Total per-runner host memory is 13840
: Allocated activation device memory of size 1459617792
Inference batch size 1 average over 10 runs is 2.21147ms
Verification: OK
INFO: [MemUsageChange] Init cuBLAS/cuBLASLt: CPU +0, GPU +0, now: CPU 1149, GPU 3333 (MiB)
就是这样,你就有了一个用tensort优化过的应用程序,并运行在你的GPU上。图2显示了一个示例测试用例的输出。
图2:使用TensorRT对大脑MRI图像进行推断。
看到上面的准确率,我感觉有些低。虽然速度提高了,分割效果 并不一定完美
下面是前面的示例应用程序中使用的几个关键代码示例。
下面代码示例中的主要函数首先声明一个CUDA引擎来保存网络定义和训练过的参数。引擎是在SimpleOnnx::createEngine函数中生成的,该函数将ONNX模型的路径作为输入。
// Declare the CUDA engine
SampleUniquePtr<nvinfer1::ICudaEngine> mEngine{nullptr};
...
// Create the CUDA engine
mEngine = SampleUniquePtr<nvinfer1::ICudaEngine> (builder->buildEngineWithConfig(*network, *config));
SimpleOnnx:: buildEngine函数解析ONNX模型并将其保存在(network)网络对象中。要处理U-Net模型的输入图像的动态输入维度和形状张量,必须从(builder )构建器类创建一个优化概要文件,如下面的代码示例所示。
(optimization profile )优化概要文件使您能够设置概要文件的最佳输入、最小和最大尺寸。构建器选择的内核,其结果是输入张量维的最低运行时,并且在最小和最大维之间的所有输入张量维都有效。它还将网络对象转换为TensorRT引擎。
下面代码示例中的setMaxBatchSize函数用于指定TensorRT引擎期望的最大批处理大小。setMaxWorkspaceSize函数允许您在引擎构建阶段增加GPU内存占用。
bool SimpleOnnx::createEngine(const SampleUniquePtr<nvinfer1::IBuilder>& builder)
{
// Create a network using the parser.
const auto explicitBatch = 1U << static_cast<uint32_t>(NetworkDefinitionCreationFlag::kEXPLICIT_BATCH);
auto network = SampleUniquePtr<nvinfer1::INetworkDefinition>(builder->createNetworkV2(explicitBatch));
...
auto parser= SampleUniquePtr<nvonnxparser::IParser>(nvonnxparser::createParser(*network, gLogger));
auto parsed = parser->parseFromFile(mParams.onnxFilePath.c_str(), static_cast<int>(nvinfer1::ILogger::Severity::kINFO));
auto config = SampleUniquePtr<nvinfer1::IBuilderConfig>(builder->createBuilderConfig());
auto profile = builder->createOptimizationProfile();
profile->setDimensions("input.1", OptProfileSelector::kMIN, Dims4{1, 3, 256, 256});
profile->setDimensions("input.1", OptProfileSelector::kOPT, Dims4{1, 3, 256, 256});
profile->setDimensions("input.1", OptProfileSelector::kMAX, Dims4{32, 3, 256, 256});
config->addOptimizationProfile(profile);
...
// Setup model precision.
if (mParams.fp16)
{
config->setFlag(BuilderFlag::kFP16);
}
// Build the engine.
mEngine = SampleUniquePtr<nvinfer1::ICudaEngine>(builder->buildEngineWithConfig(*network, *config));
...
return true;
}
After an engine has been created, create an execution context to hold intermediate activation values generated during inference. The following code shows how to create the execution context.
// Declare the execution context
SampleUniquePtr<nvinfer1::IExecutionContext> mContext{nullptr};
...
// Create the execution context
mContext = SampleUniquePtr<nvinfer1::IExecutionContext>(mEngine->createExecutionContext());
这个应用程序在函数launchInference中异步地将推断请求放到GPU上,如下面的代码示例所示。输入从主机(CPU)复制到launchInference内的设备(GPU),然后使用enqueueV2函数执行推断,并异步复制回结果。
该示例使用CUDA流来管理GPU上的异步工作。异步推理执行通常通过重叠计算提高性能,因为它最大化了GPU利用率。enqueueV2函数将推断请求放在CUDA流上,并接受输入运行时批处理大小、指向输入和输出的指针,以及用于内核执行的CUDA流。使用cudaMemcpyAsync从主机执行异步数据传输到设备,反向执行异步数据传输。
void SimpleOnnx::launchInference(IExecutionContext* context, cudaStream_t stream, vector<float> const& inputTensor, vector<float>& outputTensor, void** bindings, int batchSize)
{
int inputId = getBindingInputIndex(context);
cudaMemcpyAsync(bindings[inputId], inputTensor.data(), inputTensor.size() * sizeof(float), cudaMemcpyHostToDevice, stream);
context->enqueueV2(bindings, stream, nullptr);
cudaMemcpyAsync(outputTensor.data(), bindings[1 - inputId], outputTensor.size() * sizeof(float), cudaMemcpyDeviceToHost, stream);
}
在调用launchInference之后使用cudaStreamSynchronize函数可以确保GPU计算在访问结果之前完成。输入和输出的数量,以及它们的值和维度,可以使用ICudaEngine类中的函数进行查询。该示例最后将参考输出与tensorrt生成的推理进行比较,并将差异打印到提示中。
有关类的更多信息,请参见TensorRT Class List.。完整的代码示例在simpleOnnx_1.cpp中。
Batch your inputs(批处理你的输入)
此应用程序示例期望一个输入,并在对其执行推断后返回输出。真正的应用通常是批量输入,以实现更高的性能和效率。一批形状和大小相同的输入可以在神经网络的不同层上并行计算。
更大的批处理通常能够更有效地使用GPU资源。例如,在Volta和Turing gpu上,使用32的倍数的批处理尺寸可能在较低的精度下特别快速和有效,因为TensorRT可以为矩阵乘法和充分连接层使用特殊的内核,利用张量核。
使用以下代码在命令行上将图像传递给应用程序。图像的数量(。作为命令行上的输入参数传递的Pb文件)决定了本例中的批处理大小。使用test_data_set_*获取所有的input_0。所有目录中的Pb文件。下面的命令读取文件夹中可用的所有输入,而不是只读取一个输入。
目前下载的数据有三个输入目录,所以批处理大小为3。这个版本的示例配置应用程序,并将结果打印到提示符。有关更多信息,请参见下一节,配置应用程序。
>> ./simpleOnnx path/to/unet/unet.onnx fp32 path/to/unet/test_data_set_*/input_0.pb # Use all available test data sets.
...
INFO: [MemUsageChange] Init cuDNN: CPU +1, GPU +8, now: CPU 1148, GPU 1806 (MiB)
: Total per-runner device memory is 79243264
: Total per-runner host memory is 13840
: Allocated activation device memory of size 1459617792
Inference batch size 3 average over 10 runs is 4.99552ms
要在一个推断过程中处理多个图像,需要对应用程序进行一些更改。首先,收集所有的图像(。Pb文件)在循环中用作应用程序的输入:
for (int i = 2; i < argc; ++i)
input_files.push_back(string{argv[i]});
接下来,使用setMaxBatchSize函数指定TensorRT引擎期望的最大批处理大小。然后,构建器通过选择在目标平台上最大化其性能的算法,生成针对该批处理大小进行优化的引擎。虽然引擎不接受较大的批处理大小,但允许在运行时使用较小的批处理大小。
maxBatchSize值的选择取决于应用程序以及在任何给定时间的预期推理流量(例如,图像数量)。一种常见的做法是构建针对不同批处理大小优化的多个引擎(使用不同的maxBatchSize值),然后在运行时选择最优化的引擎。
如果不指定,默认批处理大小为1,这意味着引擎不处理大于1的批处理大小。设置该参数的代码示例如下:
builder->setMaxBatchSize(batchSize);
Profile the application(概要文件应用程序)
现在您已经看到了一个示例,下面介绍如何度量它的性能。对于网络推理来说,最简单的性能度量是将输入呈现给网络和返回输出之间所经过的时间,称为延迟。
对于嵌入式平台上的许多应用程序来说,延迟是至关重要的,而消费者应用程序需要服务质量。较低的延迟使这些应用程序更好。这个示例使用GPU上的时间戳测量应用程序的平均延迟。在CUDA中有许多方法来分析应用程序。有关更多信息,How to Implement Performance Metrics in CUDA C/C++ .
CUDA提供了轻量级的事件API函数来创建、销毁和记录事件( create, destroy, and record),以及计算它们之间的时间。应用程序可以在CUDA流中记录事件,一个在启动推断之前,另一个在推断完成之后,如下面的代码示例所示。
在某些情况下,你可能会关心在GPU和CPU之间传输数据在推断开始之前和推断完成之后所花费的时间。现有的技术可以预取数据到GPU,以及重叠计算与数据传输,可以显著地隐藏数据传输开销。函数cudaEventElapsedTime度量CUDA流中遇到这两个事件之间的时间间隔。
内的延迟计算使用以下代码示例
SimpleOnnx::infer:
// Number of times to run inference and calculate average time
constexpr int ITERATIONS = 10;
...
bool SimpleOnnx::infer()
{
CudaEvent start;
CudaEvent end;
double totalTime = 0.0;
CudaStream stream;
for (int i = 0; i < ITERATIONS; ++i)
{
float elapsedTime;
// Measure time it takes to copy input to GPU, run inference and move output back to CPU.
cudaEventRecord(start, stream);
launchInference(mContext.get(), stream, mInputTensor, mOutputTensor, mBindings, mParams.batchSize);
cudaEventRecord(end, stream);
// Wait until the work is finished.
cudaStreamSynchronize(stream);
cudaEventElapsedTime(&elapsedTime, start, end);
totalTime += elapsedTime;
}
cout << "Inference batch size " << mParams.batchSize << " average over " << ITERATIONS << " runs is " << totalTime / ITERATIONS << "ms" << endl;
return true;
}
许多应用程序对大量的输入数据进行推理,这些数据是为离线处理而积累和批处理的。每秒可能的最大推理数量(称为吞吐量)对于这些应用程序来说是一个有价值的指标。
您可以通过为更大的特定批处理大小生成优化的引擎、运行推断和测量每秒可以处理的批的数量来度量吞吐量。使用每秒的批次数量和批大小来计算每秒的推理数量,但这超出了本文的范围。
Optimize your application(优化您的应用程序)
现在您已经知道了如何批量运行推理并分析应用程序,接下来优化它。TensorRT的关键优势是它的灵活性和对技术的使用,包括混合精度,在所有GPU平台上的高效优化,以及跨广泛模型类型的优化能力。
在本节中,我们将介绍一些提高吞吐量和减少应用程序延迟的技术。有关更多信息,see Best Practices for TensorRT Performance.
下面是一些常见的技巧:
------- 使用混合精度计算
------- 更改工作区大小
------- 重用tensort引擎
Use mixed precision computation(使用混合精度计算)
TensorRT采用FP32算法进行推理,默认情况下可以获得最高的推理精度。然而,在许多情况下,可以使用FP16和INT8精度进行推断,对结果的准确性影响最小。
使用降低精度来表示模型使您能够在内存中适合更大的模型,并在降低精度的前提下,在较低的数据传输需求下实现更高的性能。您还可以将FP32和FP16精度中的计算与TensorRT混合,称为混合精度,或者对权值、激活和执行层使用INT8量化精度。
对于支持快速FP16数学运算的设备,通过将setFlag(BuilderFlag::kFP16)参数设置为true来启用FP16内核。
if (mParams.fp16)
{
config->setFlag(BuilderFlag::kFP16);
}
setFlag(BuilderFlag::kFP16)参数指示构建器可以接受较低的计算精度。TensorRT使用FP16优化的内核,如果它们在所选的配置和目标平台上表现更好。
当此模式开启时,可以在FP16或FP32中指定权重,并自动转换为计算所需的适当精度。您还可以灵活地为输入和输出张量指定16位浮点数据类型,这超出了本文的范围。
Change the workspace size(更改工作区大小)
TensorRT允许您在引擎构建阶段使用setMaxWorkspaceSize参数增加GPU内存占用。增加该限制可能会影响同时使用该GPU的应用数量。设置过低的限制可能会过滤掉一些算法,从而产生一个次优引擎。即使在IBuilder::setMaxWorkspaceSize中设置的内存量要高得多,TensorRT也只分配所需的内存。因此,应用程序应该允许TensorRT构建器尽可能多的工作空间。TensorRT的分配不会超过这个值,通常会更少。
本例使用1 GB,这允许TensorRT选择任何可用的算法。
// Allow TensorRT to use up to 1 GB of GPU memory for tactic selection
constexpr size_t MAX_WORKSPACE_SIZE = 1ULL << 30; // 1 GB worked well for this example
...
// Set the builder flag
config->setMaxWorkspaceSize(MAX_WORKSPACE_SIZE);
Reuse the TensorRT engine(重用tensort引擎)
在构建引擎时,构建器对象为所选平台和配置选择最优化的内核。从网络定义文件构建引擎可能很耗时,并且不应该在每次执行推断时重复,除非模型、平台或配置发生更改。
图3显示,您可以在生成后转换引擎的格式,并将其存储在磁盘上以供以后重用,即serializing the engine。当您将引擎从磁盘加载到内存并继续使用它进行推断时,就会发生反序列化。
运行时对象反序列化引擎。
buildEngine函数首先尝试加载和使用引擎(如果它存在的话)。如果引擎不可用,它将在当前目录中创建并保存引擎,名称为unet_batch4.engine。在此示例尝试构建新引擎之前,如果该引擎在当前目录中可用,它将选择该引擎。
要强制使用更新的配置和参数构建新引擎,请在重新运行代码示例之前使用make clean_engines命令删除存储在磁盘上的所有现有序列化引擎。
bool SimpleOnnx::buildEngine()
{
auto builder = SampleUniquePtr<nvinfer1::IBuilder>(nvinfer1::createInferBuilder(gLogger));
string precison = (mParams.fp16 == false) ? "fp32" : "fp16";
string enginePath{getBasename(mParams.onnxFilePath) + "_batch" + to_string(mParams.batchSize)
+ "_" + precison + ".engine"};
string buffer = readBuffer(enginePath);
if (buffer.size())
{
// Try to deserialize engine.
SampleUniquePtr<nvinfer1::IRuntime> runtime{nvinfer1::createInferRuntime(gLogger)};
mEngine = SampleUniquePtr<nvinfer1::ICudaEngine>(runtime->deserializeCudaEngine(buffer.data(), buffer.size(), nullptr));
}
if (!mEngine)
{
// Fallback to creating engine from scratch.
createEngine(builder);
if (mEngine)
{
SampleUniquePtr<IHostMemory> engine_plan{mEngine->serialize()};
// Try to save engine for future uses.
writeBuffer(engine_plan->data(), engine_plan->size(), enginePath);
}
}
return true;
}
现在,您已经学习了如何使用tensort加速简单应用程序的推断。在这篇文章中,我们使用TensorRT 8测试了NVIDIA TITAN V gpu的早期性能。
Next steps(下一步)
现实世界的应用程序有更高的计算需求,有更大的深度学习模型、更多的数据处理需求和更紧的延迟边界。TensorRT为计算量大的深度学习应用提供了高性能优化,是一种非常宝贵的推理工具。
希望这篇文章已经让您熟悉了使用TensorRT获得惊人性能所需的关键概念。这里有一些想法,可以应用您所学到的,使用其他模型,并通过改变本文介绍的参数来探索设计和性能权衡的影响。
---- TensorRT(TensorRT support matrix )支持矩阵提供了对TensorRT api、解析器和层的支持特性和软件的了解。虽然本例使用c++,但TensorRT同时提供c++和Python api。要运行本文中包含的示例应用程序,请参阅TensorRT开发指南(TensorRT Developer Guide)中的api、Python和c++代码示例。
---- 使用参数setFp16Mode将模型的允许精度更改为true/false,并对应用程序进行概要分析,以查看性能的差异。
---- 更改在运行时用于推断的批处理大小,并查看它如何影响模型和数据集的性能(延迟、吞吐量)。
---- 将maxbatchsize参数从64更改为4,可以看到在前5个内核中选择不同的内核。使用nvprof查看分析结果中的内核。
本文没有涉及的一个主题是在TensorRT中以INT8精度准确地执行推断。TensorRT可以将FP32网络转换为INT8精度降低的部署,同时最小化精度损失。为了实现这一目标,可以使用后训练量化和TensorRT量化感知训练对模型进行量化。有关更多信息,see Achieving FP32 Accuracy for INT8 Inference using Quantization Aware Training with TensorRT.
有许多资源可以帮助您加速图像/视频、语音应用程序和推荐系统的应用程序。这些内容包括代码示例、自节奏深度学习研究所实验室和教程,以及用于分析和调试应用程序的开发人员工具。
- Accelerating Deep Learning Inference with TensorRT 8.0 (webinar)
- TensorRT Quick Start guide
- Accelerating Inference with Sparsity using the NVIDIA Ampere Architecture and NVIDIA TensorRT
- Real-Time Natural Language Processing with BERT Using NVIDIA TensorRT
If you have issues with TensorRT, check the NVIDIA TensorRT Developer Forum to see if other members of the TensorRT community have a resolution first. NVIDIA Registered Developers can also file bugs on the Developer Program page.