TensorRT 4 开发者手册 中文版 Python接口使用(三-4)

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

  本手册为TensorRT 4.0.1.6 GA版英文手册翻译而来,博主英文水平一般般,主要作为备忘所用,分享出来以供更多开发者使用。TensorRT Developer Guide手册一共分为四个章节,主要内容在第二、三章,看懂这两章,写代码够用了。第一章为TensorRT综述,就是自吹有多牛逼。第四章为示例,介绍demo的代码结构及功能。开篇是目录,前三章每章为两到三篇,最后第四章示例,会拆分几个关键示例进行详细说明。
  
[toc]

#第二章 TensorRT任务

##2.8Python初始化TensorRT
  TensorRT的两种初始化方法(流程与C++一样):
   ‣创建IBuilder对象去优化网络(创建后可生成序列化文件)
   ‣创建IRuntime对象去执行优化网络(从序列化文件导入)
  在任何一种情况下,您都必须实现一个日志记录接口,TensorRT通过该接口打印错误,警告和信息性消息。 以下代码显示了如何实现日志记录接口。 在这种用例中,我们已经禁止了信息性消息,只打印警告性和错误性消息。简单地调用trt.infer.ConsoleLogger获得句柄即可。

G_LOGGER = trt.infer.ConsoleLogger(trt.infer.LogSeverity.ERROR)

  日志记录接口可用于创建多个运行时和构建器实例,但是日志记录接口是一个单件(整个应用程序中只有一个类实例且这个实例所占资源在整个应用程序中是共享的),所以你必须为每个对象使用同一个日志记录实例。
  创建构建器或运行时时,将随着线程而创建GPU上下文。 虽然默认上下文如果不存在,会自动创建它,但还是建议创建构建器或运行时实例之前,创建和配置CUDA上下文。

##2.9 Python创建网络定义
  使用TensorRT首先需要将训练好的模型转换成TensorRT的表示形式,然后再构建优化的运行时。
  下面将在Python中使用两种方式导入模型,一种是通过解析器导入,一种从框架导入现有模型。不管怎样,Python接口的使用方式与2.3节中使用C++创建网络定义非常类似。
注:TensorRT Python API只在x86_64平台有效。

###2.9.1 Python使用解析器导入模型

  要使用Python 解析器API导入模型,你需要执行以下主要步骤:
   1.创建构建器和网络
   2.针对指定格式,创建相应的解析器
   3.使用解析器解析导入的模型并填充网络。
  先创建构建器(作为网络工厂),再创建网络。 不同的解析器有不同的标记网络输出机制。

##2.9.2 使用Python导入Caffe模型

  这个例子教你使用NvCaffeParser和Python API直接导入Caffe模型,例子在../examples/caffe_to_trt/caffe_mnist.py中,更多信息,请参考示例sampleMNIST。
   1.导入TensorRT包和其他包

Import tensorrt as trt

   2.定义数据类型,这里使用float32

datatype = trt.infer.DataType.FLOAT

   3.传入所需文件路径

MODEL_PROTOTXT = ‘/data/mnist/mnist.prototxt’
CAFFE_MODEL = ‘/data/mnist/mnist.caffemodel’

   4.创建构建器

builder = trt.infer.create_infer_builder(G_LOGGER)

   5.创建网络

network = builder.create_network()

   6.创建解析器

parser = parsers.caffeparser.create_caffe_parser()

   7.解析Caffe网络和权重,然后生成TensorRT网络

blob_name_to_tensor = parser.parse(CAFFE_MODEL,MODEL_PROTOTXT,network,datatype)

  输出是填充好的TensorRT网络(作为参数传递给解析器)。 此外,解析器返回blob_name_to_tensor - 一个包含从张量名称到ITensor对象的映射表。

###2.9.3 使用Python导入TensorFlow模型

  这个例子教你使用NvCaffeParser和Python API直接导入TensorFlow模型,例子在/tensorrt/examples/tf_to_trt中,更多信息,请参考示例tf_to_trt。
   1.导入TensorRT和UFF解析器的Python包:

import tensorrt as trt
from tensorrt.parsers import uffparser

   2.TensorFlow模型转换为模型冻结图。 关于TensorFlow模型的冻结图转换方法,参考3.2.1Freezing A TensorFlow Graph。
   3.使用UFF转换器将冻结图转换为UFF文件。 关于UFF文件的转换方法,参考Convert a Tensorflow Model to UFF

import uff
uff.from_tensorflow_frozen_model(frozen_file, ["fc2/Relu"])

   4.使用UFF解析器将UFF文件解析为TensorRT网络,并指定输入节点(维度)和输出节点:

parser = uffparser.create_uff_parser()

  这可以通过以下方式完成:

parser.register_input("Placeholder", (1, 28, 28), 0)
parser.register_output("fc2/Relu")

注:TensorRT默认输入张量是CHW,从TensorFlow(默认NHWC)导入时,确保输入张量也是CHW,如果不是先转换为CHW。
   5.创建引擎

engine = trt.utils.uff_to_trt_engine(G_LOGGER,
                                    uff_model,
                                    parser,
                                    MAX_BATCHSIZE,
                                    MAX_WORKSPACE)

###2.9.4 使用Python导入ONNX模型

  以下示例展示如何使用NvOnnxParser和Python API直接导入ONNX模型。有关更多信息,请参考示例sample_onnx和sampleOnnxMNIST。
   1.导入TensorR包:

import tensorrt as trt

   2.导入NvOnnxParser包并将ONNX模型直接转换为TensorRT网络。与C ++ API类似,sample_onnx 的Python示例也是通过配置文件将用户参数传递给解析器对象。

from tensorrt.parsers import onnxparser
apex = onnxparser.create_onnxconfig()

   3.解析图像分类模型,然后生成TensorRT引擎进行推理。这里我们解析用户输入参数并生成配置对象:

apex.set_model_filename("model_file_path")
apex.set_model_dtype(trt.infer.DataType.FLOAT)
apex.set_print_layer_info(True) # Optional debug option
apex.report_parsing_info() # Optional debug option

   提高或降低调试输出信息的冗余级别:

apex.add_verbosity()
apex.reduce_verbosity()

   或者直接设置冗余级别。

apex.set_verbosity_level(3)

   4.创建并配置对象后,然后创建解析器,并从配置对象中检索参数并解析输入模型文件:

trt_parser = onnxparser.create_onnxparser(apex)
data_type = apex.get_model_dtype()
onnx_filename = apex.get_model_file_name()

   5.解析模型文件,生成TensorRT网络:

trt_parser.parse(onnx_filename, data_type)
# retrieve the network from the parser
trt_parser.convert_to_trt_network()
trt_network = trt_parsr.get_trt_network()

  执行推理按照2.13节Performing Inference In Python中进行操作。

###2.9.5 使用Python导入Pytorch和其他框架模型

  导入PyTorch(或NumPy兼容权重的任何其他框架)模型需要先调用TensorRT API复制网络结构,然后再从PyTorch复制权重。 有关更多信息,请参考3.3节Working With PyTorch And Other Frameworks。
执行推理按照2.13节Performing Inference In Python中进行操作。

##2.10 使用Python API 创建网络
  创建网络时,必须首先定义引擎并创建用于推理的构建器对象。 使用Network API创建网络和引擎。网络定义引用用来向网络添加各种层。有关更多信息,请参考示例sampleMNISTAPI。
  在此示例中,我们将创建一个包含Input,Convolution,Pooling,FullyConnected,Activation和SoftMax层的简单网络。
   1.创建构建器和网络:

builder = trt.infer.create_infer_builder(G_LOGGER)
network = builder.create_network()

   2.将输入层添加到网络。我们定义带维度的输入blob名称。任何网络都可以有多个输入。在此示例中,我们有一个给定名称和维度的输入。维度定义为C、H、W的元组。我们还可以加载权重到权重映射图中。

data = network.add_input(INPUT_LAYERS[0], dt, (1, INPUT_H, INPUT_W))
weight_map = trt.utils.load_weights(weights_file)

   3.添加带有隐藏层输入节点、步幅和权重(权值、偏置)的Convolution层:

conv1 = network.add_convolution(scale1.get_output(0), 20, (5,5),
weight_map["conv1filter"], weight_map["conv1bias"])
conv1.set_stride((1,1))

   注:传给TensorRT的权重保存在主机内存中
   4.添加具有池类型和维度的池化层。我们还可以为池化层设置相应的步幅:

pool1 = network.add_pooling(conv1.get_output(0), trt.infer.PoolingType.MAX,(2,2))
pool1.set_stride((2,2))

   5.添加FullyConnected和Activation层:

ip1 = network.add_fully_connected(pool2.get_output(0), 500, weight_map["ip1filter"], weight_map["ip1bias"])
relu1 = network.add_activation(ip1.get_output(0),trt.infer.ActivationType.RELU)

   6.添加SoftMax层去计算最终概率并将其设置为输出:

prob = network.add_softmax(ip2.get_output(0))
prob.get_output(0).set_name(OUTPUT_LAYERS[0])

   7.绑定输出:

network.mark_output(prob.get_output(0))

##2.11 Python构建推理引擎
  构建器的其中一个功能是搜索CUDA kernel目录以获得最快的可用实现,因此必须使用相同的GPU来运行优化后的引擎。
  构建器具有许多属性,您可以设置这些属性以控制网络运行的精度(一般有fp32、fp16、int8),以及自动调整参数,例如TensorRT在确定哪个实现最快时,需要迭代每个内核多少次(多次迭代会导致更长的运行时间,但是会降低噪声的敏感性)。您还可以查询构建器,以找出硬件本身支持的哪些精度类型。
两个特别重要的属性是最大批大小和最大工作空间大小。
   ‣最大批大小指定TensorRT将优化的批大小。 在运行时,可以选择较小的批大小。
   ‣层算法通常需要临时工作空间。 此参数限制网络中任何层可以使用的最大空间大小。 如果提供的空间(scratch)不足,则TensorRT可能无法搜索到给定层的优化实现。
   1.使用构建器实例构建引擎:

builder.set_max_batch_size(max_batch_size)
builder.set_max_workspace_size(1 << 20)
engine = builder.build_cuda_engine(network)

   在构建引擎时,TensorRT会复制权重。
   2.如果需要使用,请先分配构建器、网络和解析器
  
  有关使用Python构建引擎的更多信息,请参参考示例sampleOnnxMNIST和sampleMNIST。

##2.12 Python序列化模型

  你可以序列化引擎,也可以直接使用引擎进行推理。 在将模型用于推理之前,序列化和反序列化是一个可选步骤 - 如果需要,可以直接使用引擎对象进行推理。
  注:序列化引擎不能跨GPU和TensorRT版本。
   1.(这里代码文档上写错了)构建器运行后,再进行序列化

builder = trt.infer.create_infer_builder(G_LOGGER)
network = builder.create_network()
#......
engine = builder.build_cuda_engine(network)

   2.序列化模型,释放引擎和构建器对象

modelstream = engine.serialize()
engine.destroy()
builder.destroy()

   3.创建运行时对象,然后反序列化引擎,最后执行推理

runtime = trt.infer.create_infer_runtime(GLOGGER)
engine =runtime.deserialize_cuda_engine(modelstream.data(),modelstream.size(),None)
modelstream.destroy()

  最后一个参数是使用自定义层的应用程序的插件层工厂对象。 更多细节,请参考2.14节Extending TensorRT With Custom Layers.

##2.13 Python执行推理
  一旦有了引擎,就可以进行推理。
   1.创建存储中间激活值的空间。由于引擎包含网络定义和训练参数,因此需要额外的空间。这些都保存在执行上下文中:

context = engine.create_execution_context()

   引擎可以具有多个执行上下文(每个上下文异步执行一路),允许一组权重用于多个并行推理任务。例如,您可以使用一个引擎和一个上下文(每个CUDA流异步执行一路),上下文使用多个并行CUDA流来实现并行图像处理。每个上下文需要与引擎在相同的GPU上创建。
   2.设置指向GPU上输入和输出缓冲区的缓冲区数组:

d_input = cuda.mem_alloc(insize)
d_output = cuda.mem_alloc()
bindings = [int(d_input),int(d_output)]

   3.TensorRT执行通常是使用CUDA流异步执行核函数kernel:

context.enqueue(batch_size,bindings,stream.handle,None)

   4.将结果从设备输出缓冲区复制回输出数组:

cuda.memcpy_dtoh_async(output,d_output,stream)

   通常先从主机内存异步拷贝数据到GPU显存(前面提到的输入缓冲区),之后enqueue函数将执行核函数。 enqueue()的最后一个参数是一个可选的CUDA事件,当输入缓冲区的数据已经消费并且可以安全地重用这片显存时,它将被发出信号(异步中断系统中很有用,即生产者-消费者模型)。
   5.要确定核函数以及异步拷贝何时完成,请使用标准CUDA同步机制(如事件)或等待流。

stream.synchronize()
return output

发表评论

电子邮件地址不会被公开。 必填项已用*标注