- Python API 与 C++ API
- 1. 将TensorRT导入 Python
- 程序
- 2. 在Python中创建网络定义
- 2.1. 使用Python API从头开始创建网络定义
- 关于这个任务
- 2.2. 在 Python 中使用解析器导入模型
- 2.3. 使用Python从Caffe导入
- 关于这个任务
- 程序
- 2.4. 使用 Python从TensorFlow导入
- 关于这个任务
- 程序
- 2.5. 使用 Python从ONNX导入
- 关于这个任务
- 程序
- 2.6. 从PyTorch和其他框架导入
- 关于这个任务
- 3. 用 Python构建引擎
- 关于这个任务
- 程序
- 4. 在Python中序列化模型
- 关于这个任务
- 5. 在 Python 中执行推理
- 程序
本文介绍在没有任何框架的情况下使用 Python API进行TensorRT7.2.3的任务。TensorRT7.2.3样例支持指南中提供了更多详细信息,并在适当的情况下链接到以下内容。
假设您从训练有素的模型开始。本章将介绍使用TensorRT的以下必要步骤:
-
从您的模型创建TensorRT网络定义
-
调用TensorRT构建器从网络创建优化的运行时引擎
-
序列化和反序列化引擎,以便它可以在运行时快速重新创建
-
为引擎提供数据以执行推理
本质上,C++ API 和 Python API 在支持您的需求方面应该接近相同。Python API 的主要优点是数据预处理和后处理易于使用,因为您可以使用各种库,如 NumPy 和 SciPy。
C++ API 应该用于安全性和性能很重要的情况,例如在汽车中。有关C++ API的更多信息,请参阅使用C++部署TensorRT。
有关如何使用 Python 优化性能的更多信息,请参阅来自TensorRT最佳实践指南的如何优化 Python 性能?。
1. 将TensorRT导入 Python 程序-
导入TensorRT:
import tensorrt as trt
-
实现一个logging接口,TensorRT通过它报告errprs、warnings和info。以下代码展示了如何实现logging接口。在这种情况下,我们抑制了info,只报告errors和warnings。TensorRT Python绑定中包含一个简单的logger。
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
尽管可以避免创建 CUDA 上下文(将创建默认上下文),但这是不可取的。 建议在创建运行时runtime或builder对象之前创建和配置 CUDA 上下文。
将使用与创建线程关联的 GPU 上下文创建构建器或运行时。 虽然如果默认上下文尚不存在,但会创建它,但建议在创建运行时或构建器对象之前创建和配置 CUDA 上下文
import pycuda.driver as cuda ctx = cuda.Device(0).make_context() ctx.push() ... # 申请空间,传数据,推理执行,回传 ctx.pop()2. 在Python中创建网络定义
使用 TensorRT 执行推理的第一步是从您的模型创建一个 TensorRT 网络。
实现这一目标的最简单方法是使用TensorRT解析库导入模型(见本文2.2,2.3,2.4,2.5),它支持以下格式的序列化模型:
- Caffe(BVLC 和 NVCaffe)
- 支持最高ONNX1.6发行版,7至11级ONNX算子集
- UFF(用于TensorFlow)
另一种方法是直接使用TensorRT Network API定义模型(请参阅本文2.1)。这需要您进行少量API调用来定义网络图中的每一层,并为模型的训练参数实现您自己的导入机制。
注: 该TensorRT Python的API不是适用于所有平台。有关更多信息,请参阅TensorRT支持矩阵
2.1. 使用Python API从头开始创建网络定义创建网络时,必须首先定义engine并创建用于推理的builder对象。Python API用于从Network APIs创建网络和引擎。网络定义参考用于向网络添加各种层。
关于这个任务有关使用 Python API创建网络和引擎的更多信息,请参阅“Hello World” For TensorRT Using PyTorch And Python (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))2.2. 在 Python 中使用解析器导入模型
要使用解析器导入模型,您需要执行以下高级步骤:
-
创建TensorRT的Builder和Network
-
为特定格式创建TensorRT解析器。
-
使用解析器解析导入的模型并填充网络。
对于一步步的说明,请参阅本文2.3,2.4,2.5。
构建器必须在网络之前创建,因为它充当网络的工厂。不同的解析器有不同的机制来标记网络输出。有关更多信息,请参阅UFF Parser API、Caffe Parser API和onNX Parser API。
2.3. 使用Python从Caffe导入以下步骤说明了如何使用CaffeParser和Python API 直接导入Caffe模型。
关于这个任务有关更多信息,请参阅使用Python将Caffe、TensorFlow和ONNX模型导入TensorRT简介(introductory_parser_samples)示例。
程序-
导入TensorRT。
import tensorrt as trt
-
定义数据类型。在这个例子中,我们将使用float32。
datatype = trt.float32
-
另外,定义一些路径。更改以下路径以反映你放置示例中包含的模型的位置:
deploy_file = 'data/mnist/mnist.prototxt' model_file = 'data/mnist/mnist.caffemodel'
-
创建builder、network和parser:
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_tensor,这是一个包含从张量名称到ITtensor对象的表。
以下步骤说明了如何使用UffParser和Python API直接导入TensorFlow模型。
关于这个任务这个样例在
-
导入TensorRT:
import tensorrt as trt
-
创建一个TensorFlow模型。有关将TensorFlow模型冻结为流的说明,请参见Freezing A TensorFlow Graph。
-
使用UFF转换器转换frozen tensorflow 模型到UFF文件。通常,这很简单:
convert-to-uff frozen_inference_graph.pb
根据TensorRT的安装方式,convert-to-uff组件可能未安装在您的系统路径中。在这种情况下,直接调用底层Python脚本。它应该位于UFF模块的bin目录;例如, ~/.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。
-
定义一些路径。更改以下路径以反映你放置示例中包含的模型的位置:
model_file = '/data/mnist/mnist.uff'
-
创建builder, network和parser:
with 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)
以下步骤说明了如何使用OnnxParser和Python API直接导入ONNX模型。
关于这个任务有关更多信息,请参阅使用使用Python将Caffe、TensorFlow和ONNX模型导入TensorRT(introductory_parser_samples)示例。
注意:
一般来说,较新版本的OnnxParser被设计为向后兼容,因此,遇到由较早版本的ONNX导出器生成的模型文件应该不会导致问题。当更改不向后兼容时,可能会有一些例外。在这种情况下,请将较早的ONNX模型文件转换为较晚支持的版本。有关此更多信息,请参阅ONNX模型算子集版本转换器。
用户模型也可能是由导出工具生成的,该工具支持的算子集比TensorRT附带的ONNX解析器支持的算子集要晚。在这种情况下,检查是否有最新版本的TensorRT发布到GitHub,onnx-tensorrt, 支持所需版本。有关更多信息,请参阅Python中使用onNX TensorRT后端实现目标检测(yolov3_onnx)示例。
支持的版本由onnx_trt_backend.cpp中的BACKEND_OPSET_VERSION变量决定. 从 GitHub下载并构建最新版本的onNXTensorRT Parser。可以在此处找到构建说明:ONNX的 TensorRT后端。
在 TensorRT 7中,onNX 解析器仅支持全维模式,这意味着你的网络定义必须使用显式批处理explicitBatch标志设置。有关更多信息,请参阅使用动态shapes。
程序- 导入TensorRT:
import tensorrt as trt
- 创建builder,network和parser:
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))
使用带有Pytorch(或任何其他具有 NumPy兼容权重的框架)的TensorRT包含使用API复制网络架构(请参阅本文2.1),然后从PyTorch复制权重。有关更多信息,请参阅使用PyTorch和其他框架。
对于PyTorch来说,一般采用PyTorch ——> onNX ——> TensorRT的转换路径构建engine。
要执行推理,请按照本文第5节中概述的说明进行 *** 作。
3. 用 Python构建引擎builder的功能之一是在其 CUDA 内核目录中搜索可用的最快实现,因此有必要使用与优化引擎将运行的相同GPU来构建。
关于这个任务这IBuilderConfig有许多属性可以设置,以便控制网络运行的精度和自动调整参数,例如在确定哪个内核最快时,TensorRT应该为每个内核计时多少次(更多的迭代会导致更长的运行时间,但是对噪声的敏感性较低。)您还可以查询构建器以了解硬件本身支持哪些混合精度类型。
一个特别重要的特性是最大工作空间大小。
- 层算法通常需要临时工作空间。此参数限制网络中任何层可以使用的最大大小。如果提供的缓存不足,TensorRT可能无法找到给定层的实现。
有关在Python中构建引擎的更多信息,请参阅使用Python将Caffe、TensorFlow和ONNX模型导入TensorRT(introductory_parser_samples)示例。
程序-
使用builder对象构建引擎:
with trt.Builder(TRT_LOGGER) as builder, builder.create_builder_config() as config: # max_workspace_size determines the amount of memory available to the builder when building an optimized engine config.max_workspace_size = 1 << 20 with builder.build_engine(network, config) as engine: # Do inference here.
构建引擎时,TensorRT会复制权重。
-
进行推理。要执行推理,请按照本文第5节中概述的说明进行 *** 作。
从这里开始,您可以序列化引擎,也可以直接使用引擎进行推理。在将模型用于推理之前,序列化和反序列化是一个可选步骤 - 如果需要,引擎对象可以直接用于推理。
关于这个任务当您序列化时,您将引擎转换为一种格式,以便稍后存储和使用以进行推理。要用于推理,您只需反序列化引擎。序列化和反序列化是可选的。由于从网络定义创建引擎可能很耗时,因此您可以通过将其序列化一次并在推理时反序列化,从而避免每次应用程序重新运行时重新构建引擎。因此,引擎构建完成后,用户通常希望将其序列化以备后用。
注意: 序列化引擎不可跨平台或TensorRT版本移植。引擎特定于它们构建的具体GPU模型(包括平台和TensorRT版本)。
- 将模型序列化为模型流:
serialized_engine = engine.serialize()
- 反序列化模型流以执行推理。反序列化需要创建一个运行时对象:
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())5. 在 Python 中执行推理
以下步骤说明了如何在 Python 中执行推理,现在您已经有了一个引擎。
程序- 为输入和输出分配一些主机和设备缓冲区。这个例子假设 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()
- 创建一些空间来存储中间激活值。由于引擎保存网络定义和训练参数,因此需要额外的空间。这些保存在执行上下文中:
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上创建。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)