在深度学习和计算机视觉领域,目标检测是一个至关重要的任务。 面对日益增长的实时性和性能要求,将已经训练好的模型高效部署到实际环境中是极大的挑战。 TensorRT作为NVIDIA提供的高性能推理引擎,能够显著提升模型在GPU上的推理速度。 通过将ONNX格式的模型转换为TensorRT引擎,再使用TensorRT执行推理过程,我们可以轻松获得更高的吞吐量和更低的延迟。
本篇教程将详细介绍如何将ONNX模型导出到TensorRT引擎,并使用TensorRT对目标检测模型进行高效推理。 我们将从环境准备、代码示例到优化建议,为您展示完整的实现路径。
目录
为什么选择TensorRT?
TensorRT 是NVIDIA推出的深度学习推理优化工具,可以充分发挥NVIDIA GPU的计算能力。 TensorRT通过层融合、FP16/INT8量化、优化内存访问和内核自动选择等手段,在保持模型精度的同时大幅缩短推理延迟,提升吞吐量。
选择TensorRT的理由包括:
- 高性能:利用GPU硬件特性,将推理速度提升数倍。
- 多框架支持:支持从ONNX、PyTorch、TensorFlow等框架导出的模型。
- 灵活精度支持:可选择FP32、FP16或INT8,达到性能与精度的平衡。
- 易于集成:提供Python和C++ API,方便与现有代码库整合。
环境准备
在开始之前,请确保已安装以下组件:
- Python 3.7+
- TensorRT(请参考NVIDIA官方文档进行安装)
- pycuda、NumPy、OpenCV
使用pip安装所需Python依赖:
pip install pycuda numpy opencv-python tensorrt==10.7.0
从ONNX导出TensorRT引擎
下面的代码示例展示了如何从ONNX模型构建TensorRT引擎。请根据您的实际模型输入名称和形状进行修改。
import tensorrt as trt
def build_engine(onnx_file_path, trt_model_path, max_workspace_size=1 << 30, fp16_mode=True):
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
builder = trt.Builder(TRT_LOGGER)
network = builder.create_network(flags=1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
parser = trt.OnnxParser(network, TRT_LOGGER)
with open(onnx_file_path, 'rb') as model:
if not parser.parse(model.read()):
print("ERROR: Failed to parse the ONNX file.")
for error in range(parser.num_errors):
print(parser.get_error(error))
return None
config = builder.create_builder_config()
config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, max_workspace_size)
if fp16_mode and builder.platform_has_fast_fp16:
config.set_flag(trt.BuilderFlag.FP16)
profile = builder.create_optimization_profile()
# 根据您的模型输入名称和形状进行调整
input_name = "input"
min_shape = (1, 3, 640, 640)
opt_shape = (1, 3, 640, 640)
max_shape = (1, 3, 640, 640)
profile.set_shape(input_name, min_shape, opt_shape, max_shape)
config.add_optimization_profile(profile)
serialized_engine = builder.build_serialized_network(network, config)
if serialized_engine is None:
print("Failed to build serialized engine.")
return None
with open(trt_model_path, 'wb') as f:
f.write(serialized_engine)
print(f"TensorRT engine saved as {trt_model_path}")
runtime = trt.Runtime(TRT_LOGGER)
engine = runtime.deserialize_cuda_engine(serialized_engine)
return engine
# 示例调用
onnx_model_path = '/path/to/best.onnx'
trt_model_path = '/path/to/best.trt'
engine = build_engine(onnx_model_path, trt_model_path, fp16_mode=True)
通过上述代码,您可以将ONNX模型转换为TensorRT引擎文件,从而在后续推理中加载并使用该引擎。
TensorRT推理流程
在获得TensorRT引擎后,我们即可使用TensorRT完成高效的目标检测推理。以下代码示例展示了从引擎加载、图像预处理、推理执行到后处理及可视化的完整流程。
代码解析与示例
导入与类别定义
import cv2
import numpy as np
import hashlib
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import time
CLASSES = [
'book', 'bottle', 'cellphone', 'drink', 'eat', 'face',
'food', 'head', 'keyboard', 'mask', 'person', 'talk'
]
def name_to_color(name):
hash_str = hashlib.md5(name.encode('utf-8')).hexdigest()
r = int(hash_str[0:2],16)
g = int(hash_str[2:4],16)
b = int(hash_str[4:6],16)
return (b,g,r)
辅助函数
包括激活函数、坐标转换、IoU计算和前后处理辅助方法。
def sigmoid(x):
return 1/(1+np.exp(-x))
def xywh2xyxy(x):
y = np.copy(x)
y[...,0] = x[...,0]-x[...,2]/2
y[...,1] = x[...,1]-x[...,3]/2
y[...,2] = x[...,0]+x[...,2]/2
y[...,3] = x[...,1]+x[...,3]/2
return y
def compute_iou(box, boxes):
xmin = np.maximum(box[0], boxes[:,0])
ymin = np.maximum(box[1], boxes[:,1])
xmax = np.minimum(box[2], boxes[:,2])
ymax = np.minimum(box[3], boxes[:,3])
inter_w = np.maximum(0, xmax - xmin)
inter_h = np.maximum(0, ymax - ymin)
intersection = inter_w*inter_h
box_area = (box[2]-box[0])*(box[3]-box[1])
boxes_area = (boxes[:,2]-boxes[:,0])*(boxes[:,3]-boxes[:,1])
union = box_area+boxes_area-intersection
iou = intersection/union
return iou
引擎加载与内存分配
加载TensorRT引擎,并分配内存。
load_engine
: 函数用于加载TensorRT引擎,并返回引擎对象。allocate_buffers
: 函数用于分配输入和输出缓冲区,并返回输入、输出和流对象。
为什么需要分配内存?
- 输入缓冲区:用于存储输入数据。
- 输出缓冲区:用于存储输出数据。
- 流对象:用于管理CUDA流,确保输入和输出数据在GPU上正确传输。
def load_engine(trt_engine_path):
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
runtime = trt.Runtime(TRT_LOGGER) # 创建TensorRT运行时对象
try:
with open(trt_engine_path,'rb') as f:
engine = runtime.deserialize_cuda_engine(f.read()) # 反序列化引擎,将引擎从文件中加载到内存中,加载到引擎对象中
print(f"成功加载引擎: {trt_engine_path}")
return engine
except Exception as e:
print(f"Failed to deserialize the engine: {e}")
return None
def allocate_buffers(engine, context, batch_size=1):
inputs = [] # 输入缓冲区
outputs = [] # 输出缓冲区
stream = cuda.Stream() # 创建CUDA流对象
for i in range(engine.num_io_tensors):
name = engine.get_tensor_name(i) # 获取张量名称
dtype = trt.nptype(engine.get_tensor_dtype(name)) # 获取张量数据类型
mode = engine.get_tensor_mode(name) # 获取张量模式
is_input = (mode == trt.TensorIOMode.INPUT) # 判断是否为输入张量
shape = engine.get_tensor_shape(name) # 获取张量形状
print(f"Binding {i}: Name={name}, Shape={shape}, Dtype={dtype}, Input={is_input}")
size = trt.volume(shape) # 计算张量大小
host_mem = cuda.pagelocked_empty(size, dtype) # 创建主机内存
device_mem = cuda.mem_alloc(host_mem.nbytes) # 分配设备内存
if is_input:
inputs.append({'name':name,'host':host_mem,'device':device_mem,'shape':shape}) # 添加输入张量
else:
outputs.append({'name':name,'host':host_mem,'device':device_mem,'shape':shape}) # 添加输出张量
return inputs, outputs, stream