使用 TensorRT 加速深度学习推理

https://developer.nvidia.com/zh-cn/blog/speeding-up-deep-learning-inference-using-tensorrt/

这是的更新版本 如何用 TensorRT 加速深度学习推理 . 此版本从 PyTorch 模型开始,而不是 ONNX 模型,将示例应用程序升级为使用 TensorRT 7 ,并将 ResNet-50 分类模型替换为 UNet ,这是一个分段模型。

图 1 。 TensorRT 徽标

NVIDIA TensorRT 是一个用于深度学习推理的 SDK 。 TensorRT 提供 api 和解析器来从所有主要的深度学习框架中导入经过训练的模型。然后生成可部署在数据中心、汽车和嵌入式环境中的优化运行时引擎。

这篇文章简单介绍了如何使用 TensorRT 。您将学习如何在 GPU 上部署深度学习应用程序,从而提高吞吐量并减少推理过程中的延迟。它使用 C ++示例来将您通过将 PyTorch 模型转换成 ONX 模型并将其导入 TensorRT ,应用优化,并为数据中心环境生成高性能运行时引擎。

TensorRT 支持 C ++和 Python ;如果使用了两者,工作流讨论可能是有用的。如果您喜欢使用 Python ,请参见 TensorRT 文档中的 使用 Python API 。

深度学习应用于广泛的应用,如自然语言处理、推荐系统、图像和视频分析。随着越来越多的应用程序在生产中使用深度学习,对准确性和性能的要求导致了模型复杂性和规模的强劲增长。

安全关键型应用(如汽车)对深度学习模型的吞吐量和延迟提出了严格的要求。对于包括推荐系统在内的一些消费者应用程序也是如此。

TensorRT 旨在帮助这些用例部署深度学习。通过对每一个主要框架的支持, TensorRT 通过强大的优化、降低精度的使用和高效的内存使用,帮助以低延迟处理大量数据。

为了继续这篇文章,您需要一台具有 CUDA -TensorRT 功能的计算机,或者一个安装了 GPUs 的云实例。在 Linux 上,最容易开始的地方是从 GPU 容器注册表下载带有 TensorRT 集成的 GPU – 加速的 PyTorch 集装箱 。

示例应用程序使用来自 Kaggle 的 脑 MRI 分割数据 的输入数据来执行推断。

简单 TensorRT 示例

以下是此示例应用程序的四个步骤:

  1. 将预训练图像分割 PyTorch 模型转换为 ONNX 。
  2. 将 ONNX 模型导入到 TensorRT 中。
  3. 应用优化并生成引擎。
  4. 对 GPU 执行推理。

导入 ONNX 模型包括从磁盘上保存的文件加载它,并将其从其本机框架或格式转换为 TensorRT 网络。 盒子 是一个表示深度学习模型的标准,使其能够在框架之间进行转换。

许多框架,如 Caffe2 、 Chainer 、 CNTK 、 paddlate 、 PyTorch 和 MXNet 都支持 ONNX 格式。接下来,根据输入模型、目标 GPU 平台和其他指定的配置参数,构建优化的 TensorRT 引擎。最后一步是向 TensorRT 引擎提供输入数据以执行推理。

应用程序在 TensorRT 中使用以下组件:

  • ONX parser : 将一个经过 PyTorch 训练的模型转换成 ONNX 格式作为输入,并在 TensorRT 中填充一个网络对象。
  • 建设者: 使用 TensorRT 中的网络并生成针对目标平台优化的引擎。
  • 发动机: 获取输入数据,执行推断,并发出推断输出。
  • 记录器: 与生成器和引擎关联,以在构建和推理阶段捕获错误、警告和其他信息。

将预训练图像分割 PyTorch 模型转换为 ONNX

从 NGC 注册表中的 PyTorch 集装箱 开始,预先安装框架和 CUDA 组件,准备就绪。成功安装 PyTorch 容器后,运行以下命令下载运行此示例应用程序所需的所有内容(示例代码、测试输入数据和引用输出)、更新依赖项,并使用提供的 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
>> 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
>> tar xvf 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 目录下载所有图像。将文件名中没有_ mask 的任何三个映像复制到/ unet 目录,然后实用工具. py 来自 脑分割 -PyTorch 存储库的文件。准备三张图片作为后面文章的输入数据。准备输入“ 0 ”。 pb 和输出_ 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) 

要生成用于推断的已处理输入数据,请运行以下命令:

>> 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 

就这样,输入数据就可以进行推断了。从应用程序的简化版本 simpleONNX _ 1 . cpp 开始并在此基础上进行构建。后续版本可在同一文件夹 simpleonx _ 2 . cpp 和 simpleonx . cpp .

将 ONNX 模型导入到 TensorRT ,生成引擎并进行推理

使用经过训练的模型和作为输入传递的输入数据运行示例应用程序。数据以 ONNX 协议文件的形式提供。示例应用程序将从 TensorRT 生成的输出与同一文件夹中可用的 ONNX . pb 文件的参考值进行比较,并在提示符上总结结果。

导入 UNet ONNX 模型并生成引擎可能需要几秒钟的时间。它还生成便携式灰度图( PGM )格式的输出图像,如下所示:输出. pgm .

>> cd to code-samples/posts/TensorRT-introduction
>> ./simpleOnnx_1 path/to/unet/unet.onnx 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
... Tactic: 0 is the only option, timing skipped
: Fastest Tactic: 0 Time: 0
: Formats and tactics selection completed in 2.26589 seconds.
: After reformat layers: 32 layers
: Block size 1073741824
: Block size 536870912 ...
: Total Activation Memory: 2248146944
INFO: Detected 1 inputs and 1 output network tensors.
Engine generation completed in 3.37261 seconds.
OK 

就这样,你有一个用 TensorRT 优化并运行在你的 GPU 上的应用程序。图 2 显示了一个示例测试用例的输出。

  • ( 2a ):原始 MRI 输入图像

  • ( 2b ):从测试数据集分割出的地面真实情况

  • ( 2c ):使用 TensorRT 预测分割图像

图 2 :使用 TensorRT 对大脑 MRI 图像进行推断。

下面是前面示例应用程序中使用的几个关键代码示例。

下面代码示例中的 main 函数首先声明一个 CUDA 引擎来保存网络定义和经过训练的参数。引擎是在 createCudaEngine 函数中生成的,该函数将指向 ONNX 模型的路径作为输入。

// Declare the CUDA engineunique_ptr<ICudaEngine, Destroy<ICudaEngine>> engine{nullptr};
...
// Create the CUDA engine
engine.reset(createCudaEngine(onnxModelPath));

函数解析 ONNX 模型并将其保存在 createCudaEngine 对象中。要处理 U-Net 模型的输入图像和形状张量的动态输入尺寸,必须从 建设者 类创建一个优化配置文件,如下面的代码示例所示。

优化配置文件 允许您设置外形的最佳输入、最小和最大尺寸。构建器选择一个内核,该内核将导致输入张量维度的最低运行时间,并且对于最小和最大维度之间范围内的所有输入张量维度都有效。它还将网络对象转换为 TensorRT 引擎。

下面代码示例中的 setMaxBatchSize 函数用于指定 TensorRT 引擎预期的最大批处理大小。 setMaxWorkspaceSize 函数允许您在引擎构建阶段增加 GPU 内存占用。

nvinfer1::ICudaEngine* createCudaEngine(string const& onnxModelPath, int batchSize){ unique_ptr<nvinfer1::IBuilder, Destroy<nvinfer1::IBuilder>> builder{nvinfer1::createInferBuilder(gLogger)}; const auto explicitBatch = 1U << static_cast<uint32_t>(nvinfer1::NetworkDefinitionCreationFlag::kEXPLICIT_BATCH); unique_ptr<nvinfer1::INetworkDefinition, Destroy<nvinfer1::INetworkDefinition>> network{builder->createNetworkV2(explicitBatch)}; unique_ptr<nvonnxparser::IParser, Destroy<nvonnxparser::IParser>> parser{nvonnxparser::createParser(*network, gLogger)}; unique_ptr<nvinfer1::IBuilderConfig,Destroy<nvinfer1::IBuilderConfig>> config{builder->createBuilderConfig()}; if (!parser->parseFromFile(onnxModelPath.c_str(), static_cast<int>(ILogger::Severity::kINFO))) { cout << "ERROR: could not parse input engine." << endl; return nullptr; } builder->setMaxBatchSize(batchSize); config->setMaxWorkspaceSize((1 << 30)); auto profile = builder->createOptimizationProfile(); profile->setDimensions(network->getInput(0)->getName(), OptProfileSelector::kMIN, Dims4{1, 3, 256 , 256}); profile->setDimensions(network->getInput(0)->getName(), OptProfileSelector::kOPT, Dims4{1, 3, 256 , 256}); profile->setDimensions(network->getInput(0)->getName(), OptProfileSelector::kMAX, Dims4{32, 3, 256 , 256}); config->addOptimizationProfile(profile); return builder->buildEngineWithConfig(*network, *config);
} 

在创建引擎之后,创建一个 执行上下文 来保存在推断过程中生成的中间激活值。下面的代码演示如何创建执行上下文。

// Declare the execution context
unique_ptr<IExecutionContext, Destroy<IExecutionContext>> context{nullptr};
...
// Create the execution context
context.reset(engine->createExecutionContext()); 

此应用程序在下面的代码示例所示的函数 launchInference 中异步地向 GPU 发送推理请求。输入从主机( CPU )复制到 launchInference 内的设备( GPU ),然后使用 enqueue 函数执行推理,结果异步复制回来。

CUDA 使用异步流 GPU 管理流。异步推理执行通常通过重叠计算来提高性能,因为它最大化了 GPU 的利用率。 enqueue 函数将推理请求放在 CUDA 流上,并将批大小、指向输入和输出的指针以及用于内核执行的 CUDA 流作为输入。使用 cudaMemcpyAsync 从主机到设备执行异步数据传输,反之亦然。

void 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 计算。可以使用 ICUDA 发动机 类中的函数查询输入和输出的数量以及每个输入和输出的值和维数。示例最后将参考输出与 TensorRT 生成的推论进行比较,并将差异打印到提示。

有关类的更多信息,请参阅 TensorRT 班级名单 。完整的代码示例在 simpleonx _ 1.cpp 中。

批量输入

此应用程序示例期望单个输入,并在对其执行推理后返回输出。实际应用通常是批量输入,以获得更高的性能和效率。一批形状和大小相同的输入可以在神经网络的不同层上并行计算。

较大的批处理通常能够更有效地使用 GPU 资源。例如,在 Volta 和 TuringGPUs 上,使用 32 倍倍数的批处理大小可能特别快速有效,因为 TensorRT 可以使用特殊的核来进行矩阵乘法和充分利用张量核的全连接层。

使用以下代码在命令行上将图像传递给应用程序。在本例中,作为输入参数在命令行上传递的图像(. pb 文件)的数量决定了批处理的大小。使用 test _ data _ set _ x 从所有目录获取所有输入的_ 0 . pb 文件。下面的命令不是只读取一个输入,而是读取文件夹中所有可用的输入。

目前,下载的数据有三个输入目录,因此批处理大小为 3 。此版本的示例分析应用程序并将结果打印到提示符。有关更多信息,请参阅下一节,概要介绍应用程序。

>> ./simpleOnnx_2 path/to/unet.onnx path/to/unet/test_data_set_*/input_0.pb # Use all available test data sets.
...
: Formats and tactics selection completed in 2.33156 seconds.
: After reformat layers: 32 layers
: Block size 1073741824
: Block size 536870912
...
: Total Activation Memory: 2248146944
INFO: Detected 1 inputs and 1 output network tensors.
: Engine generation completed in 3.45499 seconds.
Inference batch size 3 average over 10 runs is 5.23616ms
OK 

要在一个推理过程中处理多个图像,请对应用程序进行一些更改。首先,在循环中收集所有图像(. pb 文件)以用作应用程序中的输入:

input_files.push_back(string{argv[2]}); for (int i = 2; i < argc; ++i) input_files.push_back(string{argv[i]}); 

接下来,使用 setMaxBatchSize 函数指定 TensorRT 引擎预期的最大批处理大小。然后,构建器通过选择在目标平台上最大化其性能的算法来生成一个针对该批处理大小进行调整的引擎。虽然引擎不接受较大的批处理大小,但允许在运行时使用较小的批处理大小。

最大 值的选择取决于应用程序以及任何给定时间的预期推理流量(例如,图像的数量)。一个常见的做法是构建多个针对不同批量大小优化的引擎(使用不同的 最大 值),然后在运行时选择最优化的引擎。

未指定时,默认批处理大小为 1 ,这意味着引擎不处理大于 1 的批大小。按以下代码示例所示设置此参数:

 builder->setMaxBatchSize(batchSize); 

分析应用程序

现在您已经看到了一个示例,下面是如何度量它的性能。网络推理最简单的性能度量是输入到网络和输出返回之间经过的时间,称为 延迟。

对于嵌入式平台上的许多应用程序,延迟是至关重要的,而消费者应用程序需要服务质量。较低的延迟使这些应用程序更好。此示例使用 GPU 上的时间戳测量应用程序的平均延迟。在 CUDA 中有很多方法可以评测您的应用程序。有关详细信息,请参见 如何在 CUDA C / C ++中实现性能度量 。

CUDA 为 创造 、 破坏 和 记录 事件提供轻量级事件 API 函数,并计算它们之间的时间。应用程序可以在 CUDA 流中记录事件,一个在启动推理之前,另一个在推理完成后,如下面的代码示例所示。

在某些情况下, MIG ht 关心在推理开始之前和推理完成之后在 GPU 和 CPU 之间传输数据所需的时间。有一些技术可以将数据预取到 GPU 中,也可以将计算与数据传输重叠,从而显著地隐藏数据传输开销。函数 cudaEventElapsedTime 测量在 CUDA 流中遇到这两个事件之间的时间。

使用上一节开头的代码示例运行此示例并查看分析输出。要评测应用程序,请将推理启动包装在 simpleONNX _ 2 . cpp 中的函数 doInference 中。这个例子包括一个更新的函数调用。

launchInference(context, stream, inputTensor, outputTensor, bindings, batchSize);
//Wait until the work is finished
cudaStreamSynchronize(stream);
doInference(context.get(), stream, inputTensor, outputTensor, bindings, batchSize); 

计算 doInference 内的延迟,如下所示:

// Number of times to run inference and calculate average timeconstexpr int ITERATIONS = 10;
...
void doInference(IExecutionContext* context, cudaStream_t stream, vector<float> const& inputTensor, vector<float>& outputTensor, void** bindings, int batchSize)
{ CudaEvent start; CudaEvent end; double totalTime = 0.0; for (int i = 0; i < ITERATIONS; ++i) { float elapsedTime; // Measure time that it takes to copy input to GPU, run inference, and move output back to CPU cudaEventRecord(start, stream); launchInference(context, stream, inputTensor, outputTensor, bindings, batchSize); cudaEventRecord(end, stream); // Wait until the work is finished cudaStreamSynchronize(stream); cudaEventElapsedTime(&elapsedTime, start, end); totalTime += elapsedTime; } cout << "Inference batch size " << batchSize << " average over " << ITERATIONS << " runs is " << totalTime / ITERATIONS << "ms" << endl;
} 

许多应用程序对为脱机处理而积累和批处理的大量输入数据执行推断。每秒可能的最大推断数被称为 吞吐量 ,是这些应用程序的一个有价值的指标。

您可以通过为更大的特定批处理大小生成优化的引擎来测量吞吐量,运行推断,并测量每秒可处理的批处理数。使用每秒的批数和批大小来计算每秒的推断数,但这超出了本文的范围。

优化应用程序

现在,您已经知道如何批量运行推理并分析应用程序,请对其进行优化。 TensorRT 的关键优势在于其灵活性和技术的使用,包括混合精度、在所有 GPU 平台上的高效优化,以及跨多种模型类型进行优化的能力。

在本节中,我们将介绍一些提高吞吐量和减少应用程序延迟的技术。有关详细信息,请参见 TensorRT 性能最佳实践 。

以下是一些常见的技巧:

  • 使用混合精度计算
  • 更改工作区大小
  • 重新使用 TensorRT 引擎

使用混合精度计算

TensorRT 默认情况下使用 FP32 算法进行推理,以获得最高的推理精度。但是,在许多情况下,可以使用 FP16 和 INT8 精度进行推理,对结果的准确性影响最小。

使用降低的精度来表示模型可以使您在内存中适合更大的模型,并在降低精度的数据传输要求较低的情况下实现更高的性能。您还可以将 FP32 和 FP16 精度中的计算与 TensorRT 混合使用,称为混合精度,或者对权重、激活和执行层使用 INT8 量化精度。

对于支持快速 FP16 数学的设备,通过将 setFp16Mode 参数设置为 true 来启用 FP16 内核。

 builder->setFp16Mode(builder->platformHasFastFp16());

setFp16Mode 参数向构建器表明,较低的计算精度是可以接受的。 TensorRT 如果 FP16 优化内核在所选配置和目标平台上表现更好,则使用它们。

启用此模式后,可以在 FP16 或 FP32 中指定权重,并自动转换为计算所需的适当精度。您还可以灵活地为输入和输出张量指定 16 位浮点数据类型,这超出了本文的范围。

更改工作区大小

TensorRT 允许您在引擎构建阶段使用 setMaxWorkspaceSize 函数增加 GPU 内存占用。增加限制可能会影响同时共享 GPU 的应用程序的数量。将此限制设置得太低可能会过滤掉一些算法并创建一个次优引擎。 TensorRT 只分配所需的内存,即使在 IBuilder::setMaxWorkspaceSize 中设置的内存量要高得多。因此,应用程序应该允许 TensorRT 生成器尽可能多的工作空间。 TensorRT 分配不超过这个值,通常更少。

这个例子使用 1GB ,这让 TensorRT 选择任何可用的算法。

// Allow TensorRT to use up to 1 GB of GPU memory for tactic selectionconstexpr size_t MAX_WORKSPACE_SIZE = 1ULL << 30; // 1 GB worked well for this example
...
// Set the builder flag
builder->setMaxWorkspaceSize(MAX_WORKSPACE_SIZE); 

重新使用 TensorRT 引擎

构建引擎时, builder 对象为所选平台和配置选择最优化的内核。从网络定义文件构建引擎可能非常耗时,并且不应在每次执行推断时重复,除非模型、平台或配置发生更改。

图 3 显示,您可以在生成后转换引擎的格式,并将其存储在磁盘上以供以后重用,称为 序列化引擎 。反序列化发生在将引擎从磁盘加载到内存中并继续使用它进行推理时。

图 3 。序列化和反序列化 TensorRT 引擎。

运行时对象反序列化引擎。

而不是每次都创建引擎, simpleonx . cpp 包含 getCudaEngine 函数来加载并使用引擎(如果存在)。如果引擎不可用,它将创建引擎并将其保存在当前目录中,名称为 unet _ batch4 . engine 。在这个例子尝试构建一个新引擎之前,如果当前目录中有这个引擎,它会选择它。

要强制使用更新的配置和参数构建新引擎,请在重新运行代码示例之前,使用 makeclean _ engines 命令删除存储在磁盘上的所有现有序列化引擎。

engine.reset(createCudaEngine(onnxModelPath, batchSize))engine.reset(getCudaEngine(onnxModelPath, batchSize)); ICudaEngine* getCudaEngine(string const& onnxModelPath)
{ string enginePath{getBasename(onnxModelPath) + ".engine"}; ICudaEngine* engine{nullptr}; string buffer = readBuffer(enginePath); if (buffer.size()) { // Try to deserialize the engine unique_ptr<IRuntime, Destroy<IRuntime>> runtime{createInferRuntime(gLogger)}; engine = runtime->deserializeCudaEngine(buffer.data(), buffer.size(), nullptr); } if (!engine) { // Fall back to creating the engine from scratch engine = createCudaEngine(onnxModelPath); if (engine) { unique_ptr<IHostMemory, Destroy<IHostMemory>> engine_plan{engine->serialize()}; // Try to save the engine for future uses writeBuffer(engine_plan->data(), engine_plan->size(), enginePath); } } return engine;
} 

将此保存的引擎用于不同的批处理大小。下面的代码示例获取输入数据,与批处理大小变量重复相同的次数,然后将附加的输入传递给示例。第一次运行创建引擎,第二次运行尝试反序列化引擎。

>> for x in seq {1..4} ; do echo path/to/unet/test_data_set_0/input_0.pb ; done | xargs ./simpleOnnx path/to/unet/unet.onnx... : Tactic: 0 is the only option, timing skipped
: Fastest Tactic: 0 Time: 0
: Formats and tactics selection completed in 2.3837 seconds.
: After reformat layers: 32 layers
: Block size 1073741824
: Block size 536870912
...
: Total Activation Memory: 2248146944
Inference batch size 4 average over 10 runs is 6.86188ms >> for x in seq {1 4}; do echo unet/test_data_set_0/input_0.pb ; done | xargs ./simpleOnnx unet/unet.onnx
: Deserialize required 1400284 microseconds.
Inference batch size 4 average over 10 runs is 6.80197ms
OK 

现在您已经了解了如何使用 TensorRT 加快简单应用程序的推理速度。我们用 TensorRT 7 测量了 NVIDIA Titan VGPUs 的早期性能。

下一步行动

真实世界的应用程序有更高的计算需求,更大的深度学习模型,更多的数据处理需求,以及更严格的延迟限制。 TensorRT 为计算量大的深度学习应用程序提供了高性能的优化,是一个非常宝贵的推理工具。

希望这篇文章让您熟悉了使用 TensorRT 获得惊人性能所需的关键概念。这里有一些想法可以应用您所学的,使用其他模型,并通过更改本文中介绍的参数来探索设计和性能权衡的影响。

  1. TensorRT 支持矩阵 为 TensorRT api 、解析器和层提供了受支持的特性和软件。这个例子使用 C ++, TensorRT 同时提供 C ++和 Python API 。若要运行此帖子中包含的示例应用程序,请参见 TensorRT 开发者指南 中的 API 和 Python 和 C ++代码示例。
  2. 使用参数 setFp16Mode 将模型的允许精度更改为 true / false ,并分析应用程序以查看性能差异。
  3. 更改运行时用于推断的批处理大小,并查看这如何影响模型和数据集的性能(延迟、吞吐量)。
  4. 将 最大值 参数从 64 改为 4 ,可以看到在前五个内核中选择了不同的内核。使用 Nprof 公司 查看分析结果中的内核。

本文未涉及的一个主题是在 TensorRT 中以 INT8 精度精确地执行推理。 TensorRT 自动转换 FP32 网络以进行部署,同时降低精度损失。为了实现这一目标, TensorRT 使用了一种校准过程,在用有限的 8 位整数表示来近似 FP32 网络时,可以将信息损失最小化。有关详细信息,请参见 基于 TensorRT 3 的自主车辆快速 INT8 推理 。

有许多资源可以帮助您加速图像/视频、语音应用程序和推荐系统的应用程序。这些工具从代码示例、自行掌握进度的深度学习机构实验室和教程到用于分析和调试应用程序的开发工具。

如果您对 TensorRT 有问题,请检查 NVIDIA TensorRT 开发者论坛 ,看看 TensorRT 社区的其他成员是否有解决方案。 NVIDIA 注册的开发人员也可以在 开发人员计划 页面上提交 bug 。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值