TensorRT笔记(2)使用Python API

3.1将TensorRT导入Python

程序
导入TensorRT:

import tensorrt as trt

实施一个日志记录界面,TensorRT通过该界面报告错误,警告和参考消息。以下代码显示了如何实现日志记录接口。在这种情况下,我们取消了提示性消息,仅报告警告和错误。TensorRT Python绑定中包含一个简单的记录器。

TRT_LOGGER = trt.Logger(trt.Logger.WARNING)

3.2 在Python中创建网络定义

使用TensorRT进行推理的第一步是根据您的模型创建一个TensorRT网络。
实现这一目标的最简单方法是使用导入模型TensorRT 解析库(见在Python中使用解析器导入模型使用Python从Caffe中导入使用Python从TensorFlow导入,并使用Python从ONNX导入),它支持具有以下格式的序列化模型:

  • Caffe(BVLC和NVCaffe两者)
  • 支持高达ONNX 1.6的ONNX版本,以及ONNX opset 7至11,以及
  • UFF(用于TensorFlow)

另一种选择是直接使用TensorRT Network API定义模型(请参阅使用Python API从头开始创建网络定义)。这要求您进行少量的API调用,以定义网络图中的每一层,并为模型的训练参数实现自己的导入机制。

注:该TensorRT Python API不是适用于所有平台。有关更多信息,请参见TensorRT支持列表

3.2.1 使用Python API从头开始创建网络定义

创建网络时,必须首先定义引擎并创建用于推理的构建器对象。Python API用于从网络API创建网络和引擎。网络定义参考用于向网络添加各种层。
关于此任务
有关使用Python API创建网络和引擎的更多信息,请参见使用PyTorch和Python的TensorRT的“ Hello World”示例(network_api_pytorch_mnist)

以下代码说明了如何使用输入,卷积,池化,完全连接,激活和SoftMax层创建简单的网络。

# Create the builder and network
with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network:
	# Configure the network layers based on the weights provided. In this case, the weights are imported from a pytorch model. 
	# Add an input layer. The name is a string, dtype is a TensorRT dtype, and the shape can be provided as either a list or tuple.
	input_tensor = network.add_input(name=INPUT_NAME, dtype=trt.float32, shape=INPUT_SHAPE)

	# Add a convolution layer
	conv1_w = weights['conv1.weight'].numpy()
	conv1_b = weights['conv1.bias'].numpy()
	conv1 = network.add_convolution(input=input_tensor, num_output_maps=20, kernel_shape=(5, 5), kernel=conv1_w, bias=conv1_b)
	conv1.stride = (1, 1)

	pool1 = network.add_pooling(input=conv1.get_output(0), type=trt.PoolingType.MAX, window_size=(2, 2))
	pool1.stride = (2, 2)
	conv2_w = weights['conv2.weight'].numpy()
	conv2_b = weights['conv2.bias'].numpy()
	conv2 = network.add_convolution(pool1.get_output(0), 50, (5, 5), conv2_w, conv2_b)
	conv2.stride = (1, 1)

	pool2 = network.add_pooling(conv2.get_output(0), trt.PoolingType.MAX, (2, 2))
	pool2.stride = (2, 2)

	fc1_w = weights['fc1.weight'].numpy()
	fc1_b = weights['fc1.bias'].numpy()
	fc1 = network.add_fully_connected(input=pool2.get_output(0), num_outputs=500, kernel=fc1_w, bias=fc1_b)

	relu1 = network.add_activation(fc1.get_output(0), trt.ActivationType.RELU)

	fc2_w = weights['fc2.weight'].numpy()
	fc2_b = weights['fc2.bias'].numpy()
	fc2 = network.add_fully_connected(relu1.get_output(0), OUTPUT_SIZE, fc2_w, fc2_b)

	fc2.get_output(0).name =OUTPUT_NAME
	network.mark_output(fc2.get_output(0))

3.2.2 在Python中使用解析器导入模型

要使用解析器导入模型,您将需要执行以下高级步骤:

  1. 创建TensorRT构建器网络
  2. 创建特定格式的TensorRT解析器。
  3. 使用解析器解析导入的模型并填充网络。

对于一步一步的说明,请参阅使用Python从Caffe中导入使用Python从TensorFlow导入使用Python从ONNX导入
必须在网络之前创建该构建器,因为它是网络的工厂。不同的解析器具有不同的机制来标记网络输出。有关更多信息,请参见UFF Parser APICaffe Parser APIONNX Parser API

3.2.3 使用Python从Caffe导入

以下步骤说明了如何直接使用CaffeParser和Python API导入Caffe模型。
关于此任务
有关更多信息,请参阅使用Python将Caffe,TensorFlow和ONNX模型导入TensorRT简介(introductory_parser_samples)示例。

程序

  1. 导入TensorRT。
import tensorrt as trt
  1. 定义数据类型。在此示例中,我们将使用float32。
datatype = trt.float32
  1. 此外,定义一些路径。更改以下路径以反映放置样本附带的模型的位置:
deploy_file = 'data/mnist/mnist.prototxt'
model_file = 'data/mnist/mnist.caffemodel'
  1. 创建构建器,网络和解析器:
with trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, trt.CaffeParser() as parser:
model_tensors = parser.parse(deploy=deploy_file, model=model_file, network=network, dtype=datatype)

解析器返回model_tensors,该表包含从张量名称到 ITensor 对象。

3.2.4 使用Python从TensorFlow导入

以下步骤说明了如何使用UffParser和Python API直接导入TensorFlow模型。
关于此任务
该示例可以在 / tensorrt / samples / python / end_to_end_tensorflow_mnist 目录。有关更多信息,请参见使用TensorFlow和Python的TensorRT的“ Hello World”示例(end_to_end_tensorflow_mnist)

程序

  1. 导入TensorRT:
import tensorrt as trt
  1. 为以下项目创建冻结的TensorFlow模型:张量流模型。将TensorFlow模型冻结为流的说明可在冻结TensorFlow图中找到。
  2. 使用UFF转换器转换冻结的 张量流建模为UFF文件。通常,这很简单:
convert-to-uff frozen_inference_graph.pb

根据您安装TensorRT的方式, 转换为uff实用程序可能未安装在系统路径中。在这种情况下,请直接调用基础Python脚本。它应该位于 箱子UFF模块的目录;例如, 〜/ .local / lib / python3.6 / site-packages / uff / bin / convert_to_uff.py。

要查找UFF模块的位置,请运行 python -c “import uff; print(uff.path)” 命令。

或者,您可以使用UFF Parser API并 直接转换TensorFlow GraphDef。

  1. 定义一些路径。更改以下路径以反映放置样本所包括的模型的位置:
model_file = '/data/mnist/mnist.uff'
  1. 创建构建器,网络和解析器:
with builder = trt.Builder(TRT_LOGGER) as builder, builder.create_network() as network, trt.UffParser() as parser:
    	parser.register_input("Placeholder", (1, 28, 28))
    	parser.register_output("fc2/Relu")
parser.parse(model_file, network)

3.2.5 使用Python从ONNX导入

以下步骤说明了如何使用OnnxParser和Python API直接导入ONNX模型。
关于此任务
有关更多信息,请参阅使用Python将Caffe,TensorFlow和ONNX模型导入TensorRT简介(introductory_parser_samples)示例。

注意:
通常,新版本的OnnxParser设计为向后兼容,因此,遇到由早期版本的ONNX导出器生成的模型文件不会造成问题。当更改不向后兼容时,可能会有一些例外。在这种情况下,请将早期的ONNX模型文件转换为更高支持的版本。有关此主题的更多信息,请参见ONNX模型Opset版本转换器

用户模型也可能是由导出工具生成的,该工具支持比TensorRT随附的ONNX解析器支持的操作集晚的操作集。在这种情况下,请检查TensorRT的最新版本是否已发布到GitHub,onnx-tensorrt,支持所需的版本。有关更多信息,请参阅Object Detection With The ONNX TensorRT Backend In Python (yolov3_onnx) 示例。

受支持的版本由 BACKEND_OPSET_VERSION 可变的 onnx_trt_backend.cpp。从GitHub下载并构建最新版本的ONNX TensorRT Parser。可以在以下位置找到构建说明:适用于ONNX的TensorRT后端。

在TensorRT 7.0中,ONNX解析器仅支持全尺寸模式,这意味着您的网络定义必须使用 显式批处理标志集。有关更多信息,请参见使用动态形状。

程序

  1. 导入TensorRT:
import tensorrt as trt
  1. 创建构建器,网络和解析器:
EXPLICIT_BATCH = 1 << (int)(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
with trt.Builder(TRT_LOGGER) as builder, builder.create_network(EXPLICIT_BATCH) as network, trt.OnnxParser(network, TRT_LOGGER) as parser:
with open(model_path, 'rb') as model:
if not parser.parse(model.read()):
    for error in range(parser.num_errors):
        print(parser.get_error(error))

3.2.6 从PyTorch和其他框架导入

关于此任务
将TensorRT与PyTorch(或具有NumPy兼容权重的任何其他框架)一起使用,涉及使用TensorRT API复制网络体系结构(请参阅使用Python API从头开始创建网络定义),然后从PyTorch复制权重。有关更多信息,请参见使用PyTorch和其他框架

要执行推理,请遵循在Python中执行推理中概述的说明

3.3 用Python构建引擎

构建器的功能之一是搜索其CUDA内核目录,以获取可用的最快实现,因此有必要使用与运行优化引擎的GPU相同的GPU进行构建。
关于此任务
IBuilderConfig 有许多属性来控制诸如网络运行的精度以及自动调整参数,例如确定最快的时间(例如,更多的迭代会导致更长的运行时间,TensorRT为每个内核计时的次数)。您还可以查询构建器,以找出硬件本机支持的混合精度类型。

一个特别重要的属性是最大工作空间大小。

  • 层算法通常需要临时工作空间。此参数限制网络中任何层可以使用的最大大小。如果提供的刮擦不足,则TensorRT可能无法找到给定层的实现。

有关在Python中构建引擎的更多信息,请参阅使用Python将Caffe,TensorFlow和ONNX模型导入TensorRT简介(introductory_parser_samples)示例。

程序
使用构建器对象构建引擎:

with trt.Builder(TRT_LOGGER) as builder, builder.create_builder_config() as config:
    config.max_workspace_size = 1 << 20 # This determines the amount of memory available to the builder when building an optimized engine and should generally be set as high as possible.
    with builder.build_engine(network, config) as engine:

#Do inference here.

构建引擎后,TensorRT会复制砝码。

执行推理。要执行推理,请遵循在Python中执行推理中概述的说明。

3.4 在Python中序列化模型

从这里开始,您可以序列化引擎,也可以直接使用引擎进行推理。对模型进行序列化和反序列化是将其用于推理之前的可选步骤-如果需要,可以将引擎对象直接用于推理。
关于此任务
当你序列化,你正在改变发动机进的格式来存储和使用在以后的时间推断。为了进行推断,您只需反序列化引擎即可。序列化和反序列化是可选的。由于从网络定义中创建引擎可能很耗时,因此可以通过序列化一次并在推理时反序列化来避免每次应用程序重新运行时都重新构建引擎。因此,在构建引擎之后,用户通常希望对其进行序列化以供以后使用。
注意:序列化引擎不可跨平台或TensorRT 版本移植。引擎特定于它们所构建的确切GPU模型(除了平台和TensorRT版本)。

  1. 将模型序列化为模型流:
serialized_engine = engine.serialize()
  1. 反序列化模型流以执行推理。反序列化需要创建运行时对象:
with trt.Runtime(TRT_LOGGER) as runtime:    engine = runtime.deserialize_cuda_engine(serialized_engine)

也可以将序列化引擎保存到文件中,然后从文件中读回:
序列化引擎并写入文件:

with open(“sample.engine”, “wb”) as f:
		f.write(engine.serialize())

从文件中读取引擎并反序列化:

with open(“sample.engine”, “rb”) as f, trt.Runtime(TRT_LOGGER) as runtime:
		engine = runtime.deserialize_cuda_engine(f.read())

3.5 在Python中执行推理

有了引擎,以下步骤说明了如何在Python中执行推理。
程序
1.为输入和输出分配一些主机和设备缓冲区。本示例假定 context.all_binding_dimensions ==True 并且引擎只有一个输入 binding_index = 0 和一个输出 binding_index = 1:

#Determine dimensions and create page-locked memory buffers (i.e. won't be swapped to disk) to hold host inputs/outputs.
		h_input = cuda.pagelocked_empty(trt.volume(context.get_binding_shape(0)), dtype=np.float32)
		h_output = cuda.pagelocked_empty(trt.volume(context.get_binding_shape(1)), dtype=np.float32)
		# Allocate device memory for inputs and outputs.
		d_input = cuda.mem_alloc(h_input.nbytes)
		d_output = cuda.mem_alloc(h_output.nbytes)
		# Create a stream in which to copy inputs/outputs and run inference.
		stream = cuda.Stream()

2.创建一些空间来存储中间激活值。由于引擎保留了网络定义和训练有素的参数,因此需要额外的空间。这些是在执行上下文中保存的:

with engine.create_execution_context() as context:
		# Transfer input data to the GPU.
		cuda.memcpy_htod_async(d_input, h_input, stream)
		# Run inference.
		context.execute_async_v2(bindings=[int(d_input), int(d_output)], stream_handle=stream.handle)
		# Transfer predictions back from the GPU.
		cuda.memcpy_dtoh_async(h_output, d_output, stream)
		# Synchronize the stream
		stream.synchronize()
		# Return the host output. 
return h_output

引擎可以具有多个执行上下文,从而允许将一组权重用于多个重叠的推理任务。例如,您可以使用一个引擎和每个流一个上下文来处理并行CUDA流中的图像。每个上下文将在与引擎相同的GPU上创建。

猜你喜欢

转载自blog.csdn.net/qq_33287871/article/details/113702652