TensorRT 4 开发者手册 中文版 自定义层/混合精度校准/部署(三-5)

原创作品,转载时请务必以超链接形式标明文章原始出处: http://www.dapalm.com/?p=206,作者:大数据,怕了么?

  本手册为TensorRT 4.0.1.6 GA版英文手册翻译而来,博主英文水平一般般,主要作为备忘所用,分享出来以供更多开发者使用。TensorRT Developer Guide手册一共分为四个章节,主要内容在第二、三章,看懂这两章,写代码够用了。第一章为TensorRT综述,就是自吹有多牛逼。第四章为示例,介绍demo的代码结构及功能。开篇是目录,前三章每章为两到三篇,最后第四章示例,会拆分几个关键示例进行详细说明。
  这是第二章最后一部分内容,关于自定义层、混合精度校准、部署。前两个非常重要需要通过具体编程来体会。尤其是自定义层一个新的模型是否能够部署,现阶段一定要保证常见层是支持int8量化的,不然对性能影响很大。

  • 第二章 TensorRT任务
    • 2.14 通过自定义层拓展TensorRT
      • 2.14.1 使用C++API添加自定义层
      • 2.14.2 从框架中导入模型时使用自定义层
    • 2.15 混合精度工作模式
      • 2.15.1 使用C++使能FP16推理
      • 2.15.2 使用Python使能FP16推理
      • 2.15.3 使用C++ API 优化INT8校准
      • 2.15.4 使用Python优化INT8校准
    • 2.16 部署TensorRT优化模型
      • 2.16.1 部署云
      • 2.16.2 部署嵌入式系统

第二章 TensorRT任务

2.14 通过自定义层拓展TensorRT

  TensorRT支持多种类型的层,其功能不断扩展; 但是,可能存在支持的层不满足模型的特定需求。 在这种情况下,用户可以通过使用C ++ API实现自定义层来扩展TensorRT功能。 自定义层(通常称为插件)由应用程序实现和实例化,它们必须跨越TensorRT引擎的生命周期。

2.14.1 使用C++API添加自定义层

  通过扩展IPluginExt类来实现自定义层。 尽管用户在4.0.1.6版本之前的TensorRT中使用IPlugin类,但现在建议用户扩展IPluginExt类,它包括版本控制(以便在未来的TensorRT版本中保持插件的可移植性),并支持除NCHW和单精度之外支持其他数据格式的自定义层。 本节的其余部分是关于IPluginExt类型插件的使用,它除了支持多数据格式外,其他与IPlugin类型插件基本相同。
  注:IPlugin类型插件只支持单精度NCHW张量。
  插件层使用addPluginExt成员函数(请参考TensorRT API)添加到TensorRT网络中,该方法创建并向网络添加层,然后将层绑定到给定插件。 该方法还返回一个指向图层(IPluginLayerExt类型)的指针,该指针可用于访问层或插件本身(通过getPluginExt)。
要将插件层正确连接到相邻图层,并设置输入和输出数据结构,构建器会调用插件成员函数来检查输出的数量及其维度:

getNbOutputs

  用于指定输出张量的数量。

getOutputDimensions

  用于指定输出维度作为相邻下一层的输入维度的函数。
  此外,在构建阶段中,网络被构造和分析去生成引擎,并且该插件检查支持格式:

supportsFormat

  用于检查插件是否支持给定的数据格式。
  插件层可以支持四种数据格式和布局,分别是单精度NCHW、半精度NCHW、半精度NC/2HW2和半精度NHWC8张量。这些格式由PluginFormatType枚举。
  除了输入和输出张量之外,插件层不进行原位计算(in-place),这需要通过getWorkspaceSize成员函数来指定额外内存空间的要求,以便构建器调用该成员函数来确定和预分配临时空间。
  在构建和推理期间,插件层可能被多次配置和执行。在构建时,为了发现最佳配置,该插件层被配置,初始化,执行和终止。一旦插件层选择了最佳格式,它将再次配置,然后被初始化一次,在推理生命周期内执行多次,并最终在引擎被销毁时终止。这些步骤由构建器和引擎使用插件成员函数控制:

configureWithFormat

  传递输入和输出数量,维度,数据类型,格式和最大批大小。 此时,插件设置其内部状态,并为给定配置选择最合适的算法和数据结构。

initialize

  已知配置以后,将创建推理引擎,因此插件可以设置内部数据结构并为执行作准备。

enqueue

  调用封装算法和核函数的插件,并提供运行时批大小,指向输入,输出和暂存空间的指针,以及用于核函数执行的CUDA流。

terminate

  销毁引擎上下文并释放插件保存的所有资源。

  2.5节Serializing A Model In C++中介绍了引擎的序列化和反序列化,实现存储和部署引擎(例如,避免在部署中重复构建阶段)。为了支持这种功能,插件还必须支持序列化和反序列化;这是通过实现getSerializationSize来返回存储插件的状态所需的大小(包括相关的配置细节)和实现serialize将状态存储到给定的缓冲区中。当引擎被序列化时,它将首先检查插件的序列化大小,然后序列化提供所需大小的缓冲区的插件。

  反序列化需要额外的IPluginFactory来识别插件层并实例化相应的插件对象。在运行时,请参考Serializing A Model In C++中序列化模型,反序列化引擎,它使用插件工厂createPlugin成员函数为给定的层名称和序列化图像创建插件对象。

  C ++ API也可用于创建在Python中使用的自定义层。 C ++是实现自定义层的首选语言(例如,轻松访问CUDA和cuDNN等库)。可以使用Python setuptools中的SWIG插件打包在C ++中创建的自定义层,然后可以将插件加载到Python应用程序中(请参考使用Python API和TensorRT Python绑定创建网络)。相同的自定义层实现可用于C ++和Python。

2.14.2 从框架中导入模型时使用自定义层

  自定义层也可以被集成到模型解析器,用于导入模式时使用。扩展解析,需要用户指定特定的解析器工厂,请参考2.2节Creating A Network Definition In C++和2.9节Creating A Network Definition In Python。
命名空间nvcaffeparser1和nvuffparser,分别用于Caffe和UFF,包括一个IPluginFactoryExt类(和IPluginFactory),它补充了命名空间nvinfer1中定义的IPluginFactoryExt。 要在解析器中使用,插件工厂必须扩展通用类和特定的解析器工厂类。
  解析器的setPluginFactoryExt方法在解析器中设置工厂来启用自定义层。 在解析模型描述时,对于每个层,解析器调用isPluginExt来检查工厂是否对应于自定义层; 如果是,解析器调用createPlugin来实例化对应层名称的插件层,权重数组和权重数(和UFF的FieldCollection)作为参数。 如果单个工厂与不同的层名称关联,则单个工厂可以支持的插件数量没有限制。
  示例samplePlugin详细说明了自定义层的使用以及如何扩展Caffe解析器。

2.15 混合精度工作模式

  混合精度是在计算中不同数值精度的组合使用。 TensorRT可以存储权重和激活,并以32位浮点,16位浮点或量化的8位整数执行网络。
  使用低于FP32的精度可减少内存使用,允许部署更大的网络。 数据传输花费的时间更少,计算性能也会提高,尤其是在Tensor Core支持该精度的GPU上。
默认情况下,TensorRT使用FP32推理,但它也支持FP16和INT8。 在运行FP16推理时,它会自动将FP32权重转换为FP16权重。
  注:一般直接指定应用程序所使用的网络的最小可接受精度。 如果对于某些特定的核函数参数集更快,或者如果不存在低精度核函数,则可以选择更高精度的核函数。

2.15.1 使用C++使能FP16推理

  构建器设置Fp16Mode标志,表明16位精度可被接受。

builder->setFp16Mode(true);

  这个标志表示允许,但是不保证在构建引擎时一定能使用16位核函数。
  指定权重FP16或FP32精度,它们将自动转换为适当的计算精度。有关运行FP16推断的示例,请参考示例sampleGoogleNet。

2.15.2 使用Python使能FP16推理

  在Python中设置fp16mode标志如下:

builder.set_fp16_mode(True)

  更多信息,请参考示例sample_onnx、sampleMNIST、pytorch_to_trt。

2.15.3 使用C++ API 优化INT8校准

  当使用8位量化表示时,TensorRT需要理解每个激活张量的动态范围,以便它可以选择适当的量化缩放比例(scale)。
  确定这些缩放比例因子的过程称为校准,并要求应用程序为网络传递批量代表性输入(通常来自训练集的批次)。实验表明,大约500张图像足以校准ImageNet分类网络。
  提供校准数据给TensorRT,请实现IInt8Calibrator接口。构建器调用校准器如下:
   ‣首先,它调用getBatchSize()来确定所期望的输入批大小
   ‣然后,它反复调用getBatch()来获取批数据输入。批数据是getBatchSize()的批大小。当没有批数据时,getBatch()应该返回false。
  校准可能很慢,因此,IInt8Calibrator接口提供了缓存中间数据的方法。有效地使用这些方法需要更详细地了解校准。
  构建INT8引擎时,构建器执行以下步骤:
   1. 构建一个32位引擎,在校准集上运行它,并记录激活值分布的每个张量的直方图。
   2. 从直方图构建校准表。
   3. 根据校准表和网络定义构建INT8引擎。
  校准表可以缓存。在多次构建同一网络时(例如,在多个平台上),缓存非常有用。它捕获从网络和校准集派生的数据。参数记录在表中。如果网络或校准集发生更改,则应用程序负责使缓存无效。
  缓存使用如下:
   ‣如果找到校准表,则跳过校准,否则:
   ‣校准表由直方图和参数构成
   ‣然后INT8网络由网络定义和校准表构建。
  缓存数据使用指针和长度来传递。
  在实现了校准器后,你可以配置构建器去使用它:

builder->setInt8Mode(true);
builder->setInt8Calibrator(calibrator);

  使用writeCalibrationCache()和readCalibrationCache()成员函数来缓存和读取校准缓存文件。 构建器在执行校准之前检查缓存,如果找到缓存文件,则跳过校准。
  有关配置INT8 Calibrator对象的更多信息,请参考示例sampleINT8。

2.15.4 使用Python优化INT8校准

  示例说明如何使用Python API创建INT8 Calibrator对象。默认情况下,TensorRT支持INT8校准。
  1.导入TensorRT,就像导入任何其他包一样:

import tensorrt as trt

  2.类似的,测试/验证文件作为校准数据集来使用。确保校准数据代表整体推断数据。要让TensorRT使用校准数据,我们需要创建批处理流对象。 Batchstream对象将用于配置校准器。

NUM_IMAGES_PER_BATCH = 5
batchstream = ImageBatchStream(NUM_IMAGES_PER_BATCH,calibration_files)

3.使用输入节点名称和批处理流创建Int8_calibrator对象:

Int8_calibrator = trt.infer.EntropyCalibrator([“input_node_name”],batchstream)

  4.设置INT8模式和INT8校准器:

trt_builder = trt.infer.create_infer_builder(G_LOGGER)
trt_builder.set_int8_mode(True)
trt_builder.set_int8_calibrator(Int8_calibrator)

  引擎创建和推理的其余逻辑类似于2.9.4节Importing From ONNX Using Python。

2.16 部署TensorRT优化模型

  在创建包含优化推理模型的计划文件后,可以将该文件部署到生产环境中。 如何创建和部署计划文件取决于您的环境。 例如,您可以为模型提供专用推理可执行文件,用于加载计划文件,然后使用TensorRT API将输入数据传递给模型,执行模型完成推理,最后从模型中读取输出。
本节讨论如何在一些常见的部署环境中部署TensorRT。

2.16.1 部署云

  用于推理的一种常见云部署策略是通过为模型实现HTTP REST或gRPC端点的服务器去公开模型。 然后,远程客户端可以通过向该端点发送格式正确的请求来执行推理。 该请求将选择模型,提供模型所需的输入张量值,并指出应该计算哪些模型作为输出。
  要在此部署策略中利用TensorRT优化模型,不需要进行任何根本性更改。 必须更新推理服务器以接受由TensorRT计划文件表示的模型,并且必须使用TensorRT API来加载和执行这些计划文件。 可以在NVIDIA Inference Server Container和Inference Server User Guide中找到为推理提供REST终结点的推理服务器示例。

2.16.2 部署嵌入式系统

  TensorRT也可用于将经过训练好的网络部署到嵌入式系统,如NVIDIA Drive PX。在上下文中,部署意味着获取网络并在嵌入式设备上运行和使用软件应用程序,例如对象检测或地图服务。将训练好的网络部署到嵌入式系统涉及以下步骤:
  1.将训练好的网络导出为UFF或ONNX等可导入TensorRT的格式(有关详细信息,请参考第三章Working With Deep Learning Frameworks)。
  2.编写一个程序,使用TensorRT C ++ API将训练好的网络导入,优化和序列化为计划文件(请参考3.Working With Deep Learning Frameworks、2.15.Working With Mixed Precision和2.6.Performing Inference In C++)。出于讨论的目的,我们将此程序称为make_plan(生成计划文件)
   a)(可选)执行INT8校准并导出校准缓存(请参考2.15.Working With Mixed Precision)。
  3.在部署到目标系统之前,在主机系统上构建并运行make_plan验证训练好的模型。
  4.将训练好的网络INT8校准缓存复制到目标系统。在目标系统上重新构建并重新运行make_plan程序以生成计划文件。
  注:make_plan程序必须在目标系统上运行才能为该系统正确优化TensorRT引擎。 但是,如果在主机上生成INT8校准高速缓存,则在生成引擎时,构建器可以在目标上重新使用高速缓存(换句话说,不需要在目标系统本身上进行INT8校准)。
  计划文件被生成后,嵌入式应用可以使用TensorRT C++ API利用计划文件创建引擎执行推理。更多信息,参考2.6Performing Inference In C++。
  典型用例,参考:
  ‣Deploying INT8 Inference For Autonomous Vehicles for DRIVE PX
  ‣GitHub for Jetson and Jetpack

你可能感兴趣的:(TensorRT编程指南)