TensorRT_Document

<转载自: http://blog.ijunyu.top/2019/02/27/TensorRT-Document/>

TensorRT开发者手册

1.什么是TensorRT?

TensorRT源程序是一个能提高英伟达GPU(Griphics processing units)推理性能的C++库。它与TensorFlow、Caffe、PyTorch、MXNet等训练框架相辅相成。它更加关注在GPU上快速高效地运行一个已经存在的学习网络,这个学习网络的目标是生成一个结果(在许多地方也称为得分、检测、回归或推理的过程)。

一些训练框架类似于tensorflow已经集成了TensorRT,所以TensorRT能够被用来加速框架中的推理过程。作为一种选择,TensorRT可以用作用户应用程序中的库。它包含了很多解析器,这些解析器可以从Caffe、ONNX或者tensorflow中解析已经存在的模型,还可以用于以编程方式构建模型的c++和Python api。

avatar
图1:TensorRT是用于产品部署的高性能神经网络推理优化器和运行时引擎

TensorRT通过合并层和优化核选择来优化网络结构,以达到改善延迟、吞吐量、功率效率和内存消耗。如果应用程序指定,TensorRT还将优化网络以降低运行精度,进一步提高性能和减少内存需求。

下面的图展示了TensorRT被定义为部分高性能推理优化器和部分运行时引擎。它可以接收在这些流行框架上训练的神经网络,优化神经网络计算,生成一个轻量级运行时引擎(你唯一需要部署到你产品环境中的东西)。然后它还会最大化在这些GPU平台上的吞吐量、降低延迟、增强表现。

avatar
图2:TensorRT是一个可编码的推理加速器

TensorRT API包含了最通用的深度学习层的实现。关于层的更多信息,请参阅TensorRT Layers.你也可以使用C++ Plugin API或者Python Plugin API来实现不常用或者最新的层,这些层暂时还不被TensorRT所支持。

1.1 TensorRT的优势

当神经网络被训练完成后,TensorRT可以压缩、优化这些网络,还可以部署成一个运行环境而不用承担整个框架的总开销。

TensorRT组合各个层,优化内核选择,还根据指定的精度(FP32、FP16或INT8)执行规范化和优化矩阵转换来改善延迟、吞吐量和效率。

对于深度学习推理,有5个用于衡量软件的关键指标:

  • 吞吐量(Throuthput): 指定时间区间内的输出量,通常用inference/second 或者samples/second来度量;
  • 效率(Efficiency):单位功率的吞吐量,通常用performance/watt来度量;
  • 延迟(Latency):运行推理的时间,通常用ms度量;
  • 精确度(Accuracy):训练过的神经网络给出正确结果的能力;
  • 内存占用(Memory usage):主机和设备内存决定于所用的神经网络算法需要申请多少内存空间进行推理;

使用TensorRT的可选方案包括:

  • 使用训练框架本身进行推理;
  • 写一个使用低层库和数学操作的专门用来执行特定网络的自定义应用程序;

使用训练框架执行推理很容易,但是在给定的额GPU上,与使用推理优化方案相比,性能会低很多。训练框架倾向于实现更加通用的代码,当优化时,优化往往集中在有效的训练上;
自定义程序编写可以获得更高的效率,但是对开发人员知识技能要求非常高,而且针对一个GPU的优化无法完全转移到另一个型号不同的GPU上,因而开发成本非常高;
而TensorRT通过将API与特定硬件细节的高级抽象结合来解决这些问题,可以提高吞吐量、降低延迟、并尽可能降低内存占用。

1.1.1 谁能够从TensorRT中获益?

TensorRT的目标用户是负责基于新的或现有的深度学习模型构建特征和应用程序,或将模型部署到生产环境中的工程师。这些部署可能部署到数据中心或云中的服务器、嵌入式设备、机器人或车辆中,或将在用户工作站上运行的应用程序软件中。

TensorRT已经成功地应用于各种场景,包括:

  • Robots:公司销售的机器人使用TensorRT运行各种计算机视觉模型,在动态环境中自动引导无人机系统飞行;
  • Autonomous Vehicles:TensorRT被用于为NVIDIA驱动产品的计算机视觉提供支持;
  • Scientific and Technical Computing:一种流行的技术计算包嵌入TensorRT以支持神经网络模型的高吞吐量执行;
  • Deep Learning Training and Deployment Frameworks:TensorRT在几个有名的深度学习框架比如TensorFlowMXNet中都被包含;
  • Video Analytics:TensorRT被应用于英伟达的DeepStream产品中,为复杂的视频分析提供解决方案;
  • Automatic Speech Recognition:TensorRT用于小型桌面设备上的语音识别。该设备支持有限的词汇量,云计算中提供了更大的词汇量语音识别系统。

1.2 TensorRT适用于哪里?

通常情况下,开发和部署深度学习模型的工作流经历三个阶段:

  • 阶段1:训练;
  • 阶段2:提出部署方案;
  • 阶段3:执行部署方案;
阶段1:训练

在整个训练阶段,数据科学家和开发者根据他们想解决的问题选择精确的输入输出和损失函数。他们还将收集、管理、扩充、甚至可能标记培训、测试和验证数据集。他们会设计网络结构并且训练模型。在训练过程中,他们将监控学习过程,学习过程可能会提供反馈,从而使他们修正损失函数,获取或增加培训数据。在这个流程的最后,他们会验证模型表现并保存模型。训练和验证通常使用Titano或者Tesla datacenter GPU。在这一阶段通常不会用到TensorRT。

阶段2:提出部署方案

在第二个阶段,数据科学家和开发人员将从训练过的模型开始,并使用这个训练过的模型创建和验证部署解决方案。把这个阶段分解成几个步骤,如下:

1.考虑神经网络是如何在它所处的大系统中工作的,并设计和实现一个合适的解决方案,可能包含神经网络的系统的范围非常广泛。例如:

  • 交通工具中的自动驾驶系统;
  • 公共场所或公司校园的视频安全系统;
  • 消费者设备的语音接口;
  • 工业生产线自动化质量保证系统;
  • 提供产品推荐的在线零售系统;
  • 提供娱乐过滤器的消费者web服务;

决定好你的优先级,考虑到可以实现的不同系统的多样性,在设计和实现部署体系结构时可能需要考虑很多事情:

  • 你有一个单独的网络还是许多网络?例如,你是否基于一个单一的网络(人脸识别)开发一个特征或系统?你的系统会不会由混合的、附加的或者不同的模型组成?也许是提供最终用户可能提供的集合模型的更通用的工具?
  • 你使用什么设备或者计算工具来运行网络?是CPU/GPU还是其他,或者二者结合?是不是只有一种类型的GPU?是否需要设计一个应用程序可以运行在不同种类的GPU上?
  • 数据怎样加载到模型?什么是数据管道?数据是来自相机、传感器还是一系列的文件?
  • 需要怎样的延迟和吞吐量?
  • 你能把很多需求进行批处理吗?
  • 你需要单个网络的多个实例来实现所需的总体系统吞吐量和延迟吗?
  • 你会如何处理网络输出?
  • 需要哪些后处理步骤?

TensorRT提供了一个快速、模块化、紧凑、健壮、可靠的推理引擎,可以支持部署体系结构中的推理需求。

2.数据科学家和开发者定义完推理解决方案的结构,也就是决定了他们的优先级之后,接下来他们使用TensorRT从已保存的模型中创建一个推理引擎。根据所使用的培训框架和网络体系结构,有许多方法可以做到这一点。通常来说,这意味着你需要拿到训练过的神经网络并且使用ONNX解析器、Caffe解析器或者TensorFlow/UFF解析器将原格式解析为TensorRT支持的格式。如下图:
avatar
图三:ONNX 工作流 V1

3.当网络被解析以后,你就需要考虑优化选项——batch size,workspace size,mixed precision。选择并指定这些选项作为TensorRT构建步骤的一部分,在该步骤中,您将实际构建基于网络的优化推理引擎。本指南的后续部分提供了关于工作流这一部分的详细说明和大量示例,将您的模型解析为TensorRT并选择优化参数,如图4所示:
avatar
图四:TensorRT优化训练过的神经网络模型,以生成可部署的运行时推理引擎。

4.当你已经使用TensorRT创建好一个推理引擎之后,你会想验证一下推理结果和训练中的结果是否一致。如果你选择了FP32或FP16,那么结果应该非常接近。如果你选择了INT8,那么在训练中获得的准确率和推理准确率之间可能会有一个小的差距。

5.以序列化格式写出推理引擎,这也称为计划文件。

阶段三:执行部署方案

TensorRT库链接到部署的应用程序,该应用程序在需要推理结果时调用库。为了初始化推理引擎,应用程序首先要将模型从计划文件反序列化为推理引擎。

TensorRT通常情况下是异步使用的,所以,当输入数据到达时,程序调用enqueue函数并传入输入缓冲区和输出缓冲区的指针地址。

1.3 TensorRT是如何工作的?

为了优化推理引擎,TensorRT会接收你的网络定义,执行包括特定平台优化在内的优化然后生成推理引擎,这个过程称为构建阶段。构建阶段可能需要相当长的时间,尤其是在嵌入式平台上运行的时候。因此,典型的应用程序将构建一次引擎,然后将其序列化为计划文件供以后使用。

NOTE:生成的计划文件不能跨平台或TensorRT版本移植。计划是特定于它们所构建的精确GPU模型的(除了平台和TensorRT版本之外),如果您想在不同的GPU上运行它们,则必须针对特定的GPU重新制定计划。

构建阶段对层图执行以下优化:

  • 消除输出未被使用的层;
  • 融合convolution,bias和ReLU operations;
  • 聚合具有足够相似参数和相同源张量的operations,例如在GoogleNet v5中初始模块中的1*1卷积;
  • 通过非拷贝方式将层输出定向到正确的最终地址来合并连接层;

如有必要,构建过程也可以改变权重的精度。当生成8-bit整数精度的网络时,TensorRT使用一个叫做calibration的进程来决定中间激活的动态范围,并因此确定用于量化的适当缩放因子。

另外,构建阶段还在虚拟数据上运行层,以便从其内核目录中选择最快的层,并在适当的情况下执行权重预格式化和内存优化。

1.4 TensorRT核心API

TensorRT让开发者们能够import/calibrate/generate and deploy optimized networks。网络结构能直接通过Caffe导入,也可以通过uff或ONNX等格式从其他框架导入。

TensorRT核心库中的关键接口是:

  • 网络定义(Network Definition):网络定义接口提供了应用程序指定网络定义的方法。可以指定输入输出张量,可以添加层,还有用于配置每种支持的层类型的接口。除了层类型(例如卷积层或循环层)之外,插件层类型还可以使应用程序实现TensorRT本身并不支持的功能。
  • 生成器(Builder):生成器接口允许通过网络定义建立优化引擎。它允许应用程序指定最大批次大小、工作空间大小、最低可接受精度级别、自动调整的校准迭代次数,以及用于INT8量化的接口。
  • 引擎(Engine):引擎接口允许应用程序执行推理,支持同步和异步支持、分析、枚举和查询绑定缓冲区信息,即引擎输入和输出。单个引擎可以具有多个执行上下文,允许一组训练参数用于同时执行多个批次。

同时,TensorRT还提供解析器,用于导入经过训练的网络来创建网络定义:

  • Caffe Parser:此解析器可用于解析在BVLV Caffe或NVCaffe中创建的Caffe网络;
  • Uff Parser:此解析器用于UFF格式的解析网络;
  • ONNX Parser:此解析器可用于解析ONNX模型;

2.使用C++ API与TensorRT一起工作

本章内容:

  • 从模型中创建TensorRT网络定义;
  • 调用TensorRT构建器从网络创建优化的运行时引擎;
  • 序列化和反序列化引擎,以便在运行时快速重新创建它;
  • 向引擎提供数据以执行推理;

2.1 在C++中实例化TensorRT对象

为了运行推理,你需要使用IExecutionContext对象。为了创建一个IExecutionContext对象,你首先需要创建一个iICudaEngine类型的对象(也就是引擎)。
引擎可以通过以下两种方法构建:

  • 从用户模型中的网络定义。在这种情况下,可以选择序列化引擎,并将其保存以供以后使用;
  • 通过从磁盘读取序列化引擎。在这种情况下,由于绕过了解析模型和创建中间对象的步骤,所以性能更好;

ILogger类型的对象需要全局创建,它被用作TensorRT API的各种方法的参数。下面是一个简单的示例,演示如何创建日志记录器:

1
2
3
4
5
6
7
8
9
class Logger : public ILogger
{
void log(Severity severity, const char* msg) override
{
// suppress info-level messages
if (severity != Severity::kINFO)
std::cout << msg << std::endl;
}
} gLogger;

一个叫做createInferBuilder(gLogger)的全局TensorRT API方法被用作创建iBuilder类型的对象,如图5所示:
avatar
图五:使用iLogger作为参数创建iBuilder.

iBuilder定义的createNetwork方法用于创建iNetworkDefinition类型的对象,如图6所示:
avatar
图六:createNetwork()被用来创建网络。

其中一个可用的解析器是使用iNetworkDefinition作为输入创建的:

  • ONNX:  parser = nvonnxparser::createParser(*network, gLogger);
  • NVCaffe:  ICaffeParser* parser = createCaffeParser();
  • UFF:  parser = createUffParser();

调用类型iParser的对象中的parse()方法来读取模型文件并填充TensorRT网络,如图7所示:
avatar
图七:解析模型文件

调用iBuilderbuildCudaEngine()方法来创建iCudaEngine类型的对象,如图8所示:
avatar
图八:创建TensorRT引擎。

可以选择序列化引擎并将其转储到文件中,如图9所示:
avatar
图九:创建TensorRT引擎。

执行上下文用于执行推理,如图10所示:
avatar
图十:创建执行上下文。

一个名为createInferRuntime(gLogger)的全局TensorRT API方法用于创建iRuntime类型的对象,如图11所示:
avatar
图十一:创建TensorRT runtime

更多关于TensorRT的信息请参见IRuntime class reference。该引擎是通过调用运行时方法deserializeCudaEngine()创建的。对于这两个使用模型,推论的其余部分是相同的。

尽管可以避免创建CUDA上下文(将为你创建默认上下文),但这是不可取的。
建议在创建运行时或构建器对象之前创建和配置CUDA上下文。

构建器或运行时将使用与创建线程关联的GPU上下文创建。虽然如果还不存在缺省上下文,将创建它,但是建议在创建运行时或构建器对象之前创建和配置CUDA上下文。

2.2 用C++创建网络定义

执行TensorRT推理的第一步是从你的模型创建一个TensorRT网络。实现这一目标最简单的方法就是使用TensorRT parser library,这个库支持下面这些格式模型的序列化:

另一个可选的办法是直接使用TensorRT API定义模型。这样的话就要求进行少量的API调用来定义网络图中的每一层,并为模型的训练参数实现你自己的导入机制。

在另一种情况下,你需要显式地告诉TensorRT哪些张量是推理输出所必需的,没有标记为输出的张量被认为是可能被构建器优化掉的临时值。输出张量的数量没有限制,然而,将张量标记为输出可能会禁止对该张量进行某些优化。还必须为输入和输出张量指定名称(使用ITensor::setName())。在推理时,你将为引擎提供一个指向输入和输出缓冲区的指针数组。为了确定引擎期望这些指针的顺序,可以使用张量名称进行查询。

TensorRT网络定义的一个重要方面是,它包含指向模型权重的指针,构建器将这些指针复制到优化的引擎中。如果通过解析器创建网络,那么解析器将拥有权重占用的内存,因此在构建器运行之前不应该删除解析器对象。

2.2.1 使用C++ UFF解析器API导入TensorFlow模型

NOTE:对于新项目来说,建议使用TensorFlow-TensorRT集成作为将TensorFlow网络转换为使用TensorRT进行推理的方法。

从TensorFlow框架导入需要将TensorFlow模型转换为中间格式UFF(通用框架格式)。下面是转换的方法:

可以使用下面这段简单的代码转换后缀.pb的冻结图为.uff格式:
convert-to-uff input_file [-o output_file] [-O output_node]
列出TensorFlow中的层:
convert-to-uff input_file -l

下面的步骤演示了如何使用C++解析器API导入TensorFlow模型:

1.构建 buildernetwork
IBuilder* builder = createInferBuilder(gLogger);
INetworkDefinition* network = builder->createNetwork();

2.构建UFF解析器:
IUFFParser* parser = createUffParser();

3.向UFF解析器声明网络输入和输出:
parser->registerInput("Input_0", DimsCHW(1, 28, 28), UffInputOrder::kNCHW);
parser->registerOutput("Binary_3");
NOTE:TensorRT期望输入张量是CHW格式的。从TensorFlow导入时,确保输入张量的顺序是符合要求的,如果不是,则将其转换为CHW。

4.解析导入的模型来填充网络:
parser->parse(uffFile, *network, nvinfer1::DataType::kFLOAT);

2.3 用C++构造引擎

接下来的步骤是调用TensorRT构建器来创建优化的runtime。构建器的功能之一是在CUDA内核目录中搜索可用的最快实现,因此必须使用与优化引擎将要运行的相同的GPU进行构建。
构造器有很多属性,你可以按照顺序设置它们以便控制网络运行中诸如精度等信息,并且自动调谐参数,例如,当确定哪一次是最快的时候,TensorRT应该测定每一个内核的时间(更多的迭代导致更长时间的运行时,但更易受到噪声影响)。
你也可以通过查询构造器来了解硬件本地支持哪些降低的精度类型。

两个特别重要的属性是最大批处理大小和最大工作区大小:

  • 最大批处理大小指定TensorRT要优化的批大小,在运行时,可以选择较小的批处理大小;
  • 层算法通常需要临时工作区,此参数限制网络中任何层可以使用的最大工作区大小。如果提供的空间不够,TensorRT可能无法找到给定层的实现;

1.使用builder对象构建引擎:

1
2
3
builder->setMaxBatchSize(maxBatchSize);
builder->setMaxWorkspaceSize(1 << 20);
ICudaEngine* engine = builder->buildCudaEngine(*network);

当引擎被构造出来时,TensorRT会复制权重。

2.如果只需要network、builder、parser中的一个时,可以销毁其他:

1
2
3
engine->destroy();
network->destroy();
builder->destroy();

2.4 使用C++序列化一个模型

序列化模型的做法是将引擎转换为一种特殊格式,以便在后续的推理中存储和使用。在执行推理的时候,你可以简单的反序列化这个引擎。序列化和反序列化是可选的。从一个网络定义中构造引擎是很费时的,你可以把它只做一次序列化然后在每次推理的时候将其反序列化,以此来避免每次应用程序重新运行时都要重新构建引擎。因此,在引擎构建好之后,用户通常希望序列化它以供以后使用。
在使用模型进行推理之前,并不一定需要对其进行序列化和反序列化——如果需要的话,引擎对象可以直接用于推理。

NOTE:序列化引擎不能跨平台或TensorRT版本移植,引擎特定于它们所构建的精确GPU模型(除了平台和TensorRT版本)。

1.运行builder作为一个提前的脱机运行步骤,然后执行序列化:

1
2
3
4
IHostMemory *serializedModel = engine->serialize();
// store model to disk
// <…>
serializedModel->destroy();

2.构建一个runtime对象来反序列化:

1
2
IRuntime* runtime = createInferRuntime(gLogger);
ICudaEngine* engine = runtime->deserializeCudaEngine(modelData, modelSize, nullptr);

最后一个参数是当应用程序使用自定义层时的插件层工厂,更多详细信息参见使用自定义层拓展TensorRT。

2.5 使用C++执行推理

当有一个引擎之后,下面的步骤阐述了如何使用C++执行推理的过程:

1.创建一些空间来存储中间激活值。由于引擎包含网络定义和训练参数,因此需要额外的空间,它们在执行上下文中保存:

IExecutionContext *context = engine->createExecutionContext();
引擎可以有多个执行上下文,允许一组权重用于多个重叠推理任务。例如,您可以在并行CUDA流中处理图像,每个流使用一个引擎和一个上下文,每个上下文将在与引擎相同的GPU上创建。

2.使用输入和输出blob名称来获得相应的输入和输出索引:

1
2
int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME);
int outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME);

3.使用这些索引,设置一个缓冲区数组指向GPU上的输入和输出缓冲区:

1
2
3
void* buffers[2];
buffers[inputIndex] = inputbuffer;
buffers[outputIndex] = outputBuffer;

4.TensorRT的执行通常是异步的,因此将内核排队到CUDA流中:

context.enqueue(batchSize, buffers, stream, nullptr);

通常在内核之前和之后对异步memcpy()进行排队,以便在GPU中还没有数据时将其移动。enqueue()的最后一个参数是一个可选的CUDA事件,当输入缓冲区被使用并且它们的内存可以安全地重用时,将发出信号。
要确定内核(可能还有memcpy())什么时候完成,可以使用标准的CUDA同步机制,比如事件,或者等待流。

2.6 用C++进行内存管理

TensorRT提供了两种机制,允许应用程序更多地控制设备内存。
默认情况下,当创建一个IExecutionContext的时候,会分配持久设备内存以保存激活数据。为了避免这种分配,调用createExecutionContextWithoutDeviceMemory,然后,应用程序负责调用IExecutionContext::setDeviceMemory()来提供运行网络所需的内存,内存块的大小由ICudaEngine::getDeviceMemorySize()返回。
此外,通过实现IGpuAllocator接口,应用程序可以提供一个定制的分配器,以便在构建和运行时使用。当接口实现之后,在IBuilder或IRuntime接口上调用setGpuAllocator(&allocator); 。然后,所有设备内存将通过该接口分配和释放。

3.使用plugin添加TensorRT中对TensorFlow不支持的op

3.1 通过使用 C++ API 添加自定义层

通过继承IPluginV2IPluginCreator类来实现自定义层。
IPlugin
IPluginV2类是实现plugin的基类,它包括版本控制支持并且能够使自定义层支持包括NCHW和单精度之外的其他数据格式;
IPluginCreator:
IPluginCreator类是自定义层的Creator类,用户可以使用它来获取插件名称、版本和插件字段参数。它还提供了在网络构建阶段生成插件对象和在推理中反序列化的方法;

TensorRT还提供了通过调用REGISTER_TENSORRT_PLUGIN(pluginCreator)来注册一个plugin的功能,将Plugin Creator通过静态方式注册到Plugin Registry中。在运行过程中,Plugin Registry能够通过外部函数getPluginRegistry()来查询。Plugin Registry中存储了所有注册过的Plugin Creator的指针并且被应用到基于plugin名称和版本的特定Plugin Creator的查找。TensorRT库包含了很多能被加载到应用程序中的plugin。这些plugin的版本被置为1。这些plugin的名称为:

  • RPORT_TRT
  • Normalize_TRT
  • PriorBox_TRT
  • GridAnchor_TRT
  • NMS_TRT
  • LReLU_TRT
  • Reorg_TRT
  • Region_TRT
  • Clip_TRT

为了在应用程序中使用TensorRT中已注册的plugins,libnvinfer_plugin.so库必须被加载,并且所有的plugin必须是已注册的。这些能够通过在应用程序代码中调用initLibNvInferPlugins(void* logger, const char* libNamespace)()方法来实现。

如果你有你自己的plugin库,你可以包含一个类似的入口指针,将注册表中的所有插件注册到一个唯一的命名空间中。这样可以保证在不同的插件库之间build时没有插件命名冲突。

在使用Plugin Creator的时候,IPluginCreator::createPlugin()方法可以返回IPluginV2类型的plugin对象,这个对象可以使用addPluginV2()被添加到TensorRT network中,addPluginV2()的作用就是创建并且添加一个层到网络中,然后把这个层和对应的plugin绑定。这个方法还会返回一个到这个层的指针(类型为IPluginV2Layer),这一指针可以用来指向该层或者插件本身(通过getPlugin());

举个例子,为了将插件层添加到你的网络中,并把插件名称设置为pluginName,版本设置为pluginVersion,你可以执行如下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//Use the extern function getPluginRegistry to access the global TensorRT Plugin Registry
//使用外部函数getPluginRegistry来访问全局TensorRT plugin注册表
auto creator = getPluginRegistry()->getPluginCreator(pluginName, pluginVersion);
const PluginFieldCollection* pluginFC = creator->getFieldNames();
//populate the field parameters (say layerFields) for the plugin layer
//填充插件层的字段参数(比如layerFields)
PluginFieldCollection *pluginData = parseAndFillFields(pluginFC, layerFields);
//create the plugin object using the layerName and the plugin meta data
//使用layerName和plugin meta data来创建plugin对象
IPluginV2 *pluginObj = creator->createPlugin(layerName, pluginData);
//add the plugin to the TensorRT network using the network API
//使用network API来把plugin添加到TensorRT network中
auto layer = network.addPluginV2(&inputs[0], int(inputs.size()), pluginObj);
… (build rest of the network and serialize engine)
// Destroy the plugin object
pluginObj->destroy()
… (destroy network, engine, builder)
… (free allocated pluginData)

在传递给createPlugin之前,pluginData应该在堆上分配PluginField条目;

上面的createPlugin方法将在堆上创建一个新的plugin对象,并返回指向该对象的指针。确保你销毁了plugin对象,就像上面所展示的那样,这样可以避免内存泄漏的问题;

在序列化过程中,TensorRT引擎会在内部为所有IPluginV2类型的plugin储存其plugin类型、版本以及命名空间(如果存在的话)。在反序列化过程中,TensorRT引擎会查找这些信息,以便从插件注册表中找到Plugin Creator。这就使TensorRT引擎能够在内部调用IPluginCreator::deserializePlugin()方法。在反序列化过程中创建的插件对象将由TensorRT引擎通过调用IPluginV2::destroy()方法在内部销毁。

3.2 在c++中为TensorFlow网络添加一个定制的插件层(custom layer)

(1).实现IPluginV2IPluginCreator类;
(2).将TensorFlow操作映射到plugin操作,可以使用graphsurgeon来实现。例如,参考下面的代码片段将TensorFlow Relu6操作映射到一个插件:

1
2
3
4
5
6
7
8
9
10
11
import graphsurgeon as gs

lrelu_node = gs.create_plugin_node(name=”trt_lrelu”, op=”LReLU_TRT”,
negSlope=0.2)
namespace_plugin_map = { “tf_lrelu” : lrelu_node }
# Transform TensorFlow graph using graphsurgeon and save to UFF
dynamic_graph = gs.DynamicGraph(tf_lrelu.graph)
dynamic_graph.collapse_namespaces(namespace_plugin_map)
# Run UFF converter using new graphdef
uff_model = uff.from_tensorflow(dynamic_graph.as_graph_def(), ["trt_lrelu"],
output_filename=”test_lrelu.uff”, text=True)

在上面的代码中,tf_lrelu是TensorFlow图中LRelu结点的名称。它通过op LReLU_TRT将tf_lrelu结点映射到一个自定义plugin结点,LReLU_TRT是被使用的plugin的名称。将上面的代码保存为config.py文件。如果plugin层需要参数,它们应该作为参数传递给s.create_plugin_node

(3).调用UFF-converter,设置预处理-p标志:
convert-to-uff frozen_inference_graph.pb -p config.py -t
这将生成一个UFF文件,TensorRT plugin结点将替换TensorFlow操作。

(4).TensorRT使用UFF解析器运行预处理和转换的UFF文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值