Tensorrt
January 2024 (4258 Words, 24 Minutes)
TensorRT
TensorRT是可以在NVIDIA各种GPU硬件平台下运行的一个C++推理框架。我们利用Pytorch、TF或者其他框架训练好的模型,可以转化为TensorRT的格式,然后利用TensorRT推理引擎去运行我们这个模型,从而提升这个模型在英伟达GPU上运行的速度。
在GPU服务器上部署的话,TensorRT是首选!
TensorRT是硬件相关的,不同的硬件不同的优化。TensorRT是半开源的,除核心部分外基本都开源了。
TensorRT模型转换
TF-TRT
,TensorRT集成在TensorFlow中ONNX2TensorRT
,即ONNX转换trt的工具/usr/src/tensorrt/bin/trtexec --onnx=yolov7-tiny.onnx --saveEngine=yolov7-tiny-nms.trt --fp16
- 手动构造模型结构,然后手动将权重信息挪过去,非常灵活但是时间成本略高,有大佬已经尝试过了.
Project
https://github.com/wang-xinyu/pytorchx Implement popular deep learning networks in pytorch, used by
PyTorch与TensorRT
模型转换方式:PyTorch → ONNX → TensorRT C++API
- torch2trt https://github.com/NVIDIA-AI-IOT/torch2trt
- torch2trt_dynamic https://github.com/grimoire/torch2trt_dynamic
- TRTorch https://github.com/pytorch/TensorRT,pytorch官方文档
理解TensorRT Runtimes
在TensorRT Runtime环境中运行模型,就是直接使用TensorRT;
TensorRT/5. Understanding TensorRT Runtimes.ipynb at main · NVIDIA/TensorRT
Python API
C++ API https://developer.nvidia.com/blog/speed-up-inference-tensorrt/
Tensorflow/TF-TRT Runtime: (Tensorflow Only) 操作简单,加速效果不好
TRITON Inference Server 完美支持TensorRT,用在生产环境https://github.com/NVIDIA/TensorRT/tree/main/quickstart/deploy_to_triton
Nvidia 官方镜像
nvidia官方提供了tensorrt以及包含tensorrt的流行深度学习环境的镜像,例如tensorflow、paddlepaddle、torch
推荐pytorch镜像
PyTorch Release Notes :: NVIDIA Deep Learning Frameworks Documentation
查看nvidia的pytorch镜像包含的库版本
Tensorrt 仅支持在GPU环境下使用
Tensorrt C++部署
环境配置
Dockerfile
由官方https://github.com/NVIDIA/TensorRT 提供的Ubuntu18.04.Dockerfile修改而来,参考 https://github.com/NVIDIA/cuda-repo-management/issues/4解决GPG error,参考官方安装文档 https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html#installing-debian 构建
-
构建基础tensorrt版本
# 查看适配tensorrt的cuda版本 # https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/ ARG CUDA_VERSION=10.2 #ARG CUDA_VERSION=11.4.2 ARG OS_VERSION=18.04 # 基础镜像 FROM nvidia/cuda:${CUDA_VERSION}-cudnn8-devel-ubuntu${OS_VERSION} LABEL maintainer="zhanglq" ENV TRT_VERSION 8.4.1.3 # ENV TRT_VERSION 8.2.5.1 SHELL ["/bin/bash", "-c"] # Setup user account # ARG uid=1000 # ARG gid=1000 # RUN groupadd -r -f -g ${gid} trtuser && useradd -o -r -l -u ${uid} -g ${gid} -ms /bin/bash trtuser # RUN usermod -aG sudo trtuser # RUN echo 'trtuser:nvidia' | chpasswd # RUN mkdir -p /workspace && chown trtuser /workspace # Repair the GPG error # https://github.com/NVIDIA/cuda-repo-management/issues/4 RUN rm /etc/apt/sources.list.d/cuda.list RUN rm /etc/apt/sources.list.d/nvidia-ml.list RUN apt-key del 7fa2af80 RUN apt-get update && apt-get install -y --no-install-recommends wget RUN wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-keyring_1.0-1_all.deb RUN dpkg -i cuda-keyring_1.0-1_all.deb # Install requried libraries RUN apt-get update && apt-get install -y software-properties-common RUN add-apt-repository ppa:ubuntu-toolchain-r/test RUN apt-get update && apt-get install -y --no-install-recommends \ libcurl4-openssl-dev \ zlib1g-dev \ git \ pkg-config \ sudo \ ssh \ libssl-dev \ pbzip2 \ pv \ bzip2 \ unzip \ devscripts \ lintian \ fakeroot \ dh-make \ build-essential # Install python3 # 软连接 使pip3命令等于pip RUN apt-get install -y --no-install-recommends \ python3 \ python3-pip \ python3-dev \ python3-wheel &&\ cd /usr/local/bin &&\ ln -s /usr/bin/python3 python &&\ ln -s /usr/bin/pip3 pip; # Install TensorRT RUN if [ "${CUDA_VERSION}" = "10.2" ] ; then \ v="${TRT_VERSION%.*}-1+cuda${CUDA_VERSION}" &&\ apt-get update &&\ sudo apt-get install libnvinfer8=${v} libnvonnxparsers8=${v} libnvparsers8=${v} libnvinfer-plugin8=${v} \ libnvinfer-dev=${v} libnvonnxparsers-dev=${v} libnvparsers-dev=${v} libnvinfer-plugin-dev=${v} \ python3-libnvinfer=${v}; \ else \ v="${TRT_VERSION%.*}-1+cuda${CUDA_VERSION%.*}" &&\ apt-get update &&\ apt-get install libnvinfer8=${v} libnvonnxparsers8=${v} libnvparsers8=${v} libnvinfer-plugin8=${v} \ libnvinfer-dev=${v} libnvonnxparsers-dev=${v} libnvparsers-dev=${v} libnvinfer-plugin-dev=${v} \ python3-libnvinfer=${v}; \ fi # 阻止tensorrt自动升级 RUN apt-mark hold libnvinfer8 libnvonnxparsers8 libnvparsers8 libnvinfer-plugin8 libnvinfer-dev libnvonnxparsers-dev libnvparsers-dev libnvinfer-plugin-dev python3-libnvinfer # 如果想要升级最新版本的tensorrt # sudo apt-mark unhold libnvinfer8 libnvonnxparsers8 libnvparsers8 libnvinfer-plugin8 libnvinfer-dev libnvonnxparsers-dev libnvparsers-dev libnvinfer-plugin-dev python3-libnvinfer # Install PyPI packages RUN pip3 install --upgrade pip RUN pip3 config set global.index-url https://pypi.douban.com/simple/ RUN pip3 install setuptools>=41.0.0 # Install jupyter RUN pip3 install jupyter jupyterlab # Workaround to remove numpy installed with tensorflow RUN pip3 install --upgrade numpy RUN pip3 install ipykernal RUN pip3 install ipywidgets --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host=files.pythonhosted.org RUN jupyter nbextension enable --py widgetsnbextension # Install Cmake RUN cd /tmp && \ wget https://github.com/Kitware/CMake/releases/download/v3.14.4/cmake-3.14.4-Linux-x86_64.sh && \ chmod +x cmake-3.14.4-Linux-x86_64.sh && \ ./cmake-3.14.4-Linux-x86_64.sh --prefix=/usr/local --exclude-subdir --skip-license && \ rm ./cmake-3.14.4-Linux-x86_64.sh # # Download NGC client # RUN cd /usr/local/bin && wget https://ngc.nvidia.com/downloads/ngccli_cat_linux.zip && unzip ngccli_cat_linux.zip && chmod u+x ngc && rm ngccli_cat_linux.zip ngc.md5 && echo "no-apikey\nascii\n" | ngc config set # Set environment and working directory ENV TRT_LIBPATH /usr/lib/x86_64-linux-gnu ENV TRT_OSSPATH /workspace/TensorRT ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${TRT_OSSPATH}/build/out:${TRT_LIBPATH}" WORKDIR /workspace # USER trtuser RUN ["/bin/bash"]
-
自动化构建脚本
#!/usr/bin/env bash arg_dockerfile=Dockerfile arg_imagename=tensorrt:trt8.4.1-cu10.2-cudnn8-ubuntu1804-py36 arg_help=0 while [[ "$#" -gt 0 ]]; do case $1 in -f) arg_dockerfile="$2"; shift;; -t) arg_imagename="$2"; shift;; -h|--help) arg_help=1;; *) echo "Unknown parameter passed: $1"; echo "For help type: $0 --help"; exit 1; esac; shift; done if [ "$arg_help" -eq "1" ]; then echo "Usage: $0 [options]" echo " --help or -h : Print this help menu." echo " -f <dockerfile> : Docker file to use for build." echo " -t <imagename> : Image name for the generated container." exit; fi docker_args="-f $arg_dockerfile -t $arg_imagename ." echo "Building container:" echo "> docker build $docker_args" docker build $docker_args
-
二次构建包含torch和tf的环境
from tensorrt:trt8.4.1_cu10.2-cudnn8 LABEL maintainer="zhanglq" RUN pip3 install onnx==1.10.2; python_version<"3.10" RUN pip3 install onnx==1.12.0; python_version=="3.10" RUN pip3 install onnxruntime==1.8.1; python_version<"3.10" RUn pip3 install onnxruntime==1.12.1; python_version=="3.10" RUN pip3 install onnx-graphsurgeon --extra-index-url https://pypi.ngc.nvidia.com # Install Tensorflow RUN pip3 tensorflow-gpu==2.9.1; (platform_machine=="x86_64" and sys.platform=="linux") RUN pip3 install nvidia-pyindex --index-url https://pypi.ngc.nvidia.com RUN pip3 install polygraphy \ pandas \ seaborn \ scikit-image \ scikit-learn \ cuda-python \ opencv-python-headless==3.4.16.59 \ colored \ scipy \ tqdm \ tf2onnx \ Pillow # Install Pytorch # 稳定版本 # RUN pip3 install --pre torch torchvision torchaudio # 最新的torch RUN pip3 install --pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cu102
-
自动化构建容器脚本
#!/usr/bin/env bash arg_dockerfile=Dockerfile arg_imagename=tensorrt:trt8.4.1-cu10.2-cudnn8-ubuntu1804-py36 arg_help=0 while [[ "$#" -gt 0 ]]; do case $1 in -f) arg_dockerfile="$2"; shift;; -t) arg_imagename="$2"; shift;; -h|--help) arg_help=1;; *) echo "Unknown parameter passed: $1"; echo "For help type: $0 --help"; exit 1; esac; shift; done if [ "$arg_help" -eq "1" ]; then echo "Usage: $0 [options]" echo " --help or -h : Print this help menu." echo " -f <dockerfile> : Docker file to use for build." echo " -t <imagename> : Image name for the generated container." exit; fi docker_args="-f $arg_dockerfile -t $arg_imagename ." echo "Building container:" echo "> docker build $docker_args" docker build $docker_args
-
运行容器脚本
arg_jupyter=0 则不运行jupyter
#!/usr/bin/env bash # arg_tag=nvcr.io/nvidia/pytorch:21.10-py3 # arg_tag=nvidia/cuda:10.2-cudnn8-devel-ubuntu18.04 arg_tag=nvidia/tensorrt:trt8.4.1-cu10.2-cudnn8-ubuntu1804-py36-torch-tf2 #arg_tag=nvidia/tensorrt:trt8.4.1-cu11.4.2-cudnn8-ubuntu1804-py36-torch-tf2 arg_gpus=all arg_jupyter=8887 arg_help=0 while [[ "$#" -gt 0 ]]; do case $1 in --tag) arg_tag="$2"; shift;; --gpus) arg_gpus="$2"; shift;; --jupyter) arg_jupyter="$2"; shift;; -h|--help) arg_help=1;; *) echo "Unknown parameter passed: $1"; echo "For help type: $0 --help"; exit 1; esac; shift; done if [ "$arg_help" -eq "1" ]; then echo "Usage: $0 [options]" echo " --help or -h : Print this help menu." echo " --tag <imagetag> : Image name for generated container." echo " --gpus <number> : Number of GPUs visible in container. Set 'none' to disable, and 'all' to make all visible." echo " --jupyter <port> : Launch Jupyter notebook using the specified port number." exit; fi extra_args="" if [ "$arg_gpus" != "none" ]; then extra_args="$extra_args --gpus $arg_gpus" fi if [ "$arg_jupyter" -ne "0" ]; then extra_args+=" -p $arg_jupyter:$arg_jupyter" fi docker_args="$extra_args --shm-size=8gb --ipc=host --ulimit memlock=-1 --ulimit stack=67108864 -v ${PWD}:/workspace/code --rm -it $arg_tag" if [ "$arg_jupyter" -ne "0" ]; then docker_args+=" jupyter-lab --port=$arg_jupyter --no-browser --ip 0.0.0.0 --allow-root" fi echo "Launching container:" echo "> docker run $docker_args" docker run $docker_args
TensorRT版本适配
To build the TensorRT-OSS components, you will first need the following software packages.
TensorRT GA build675
- TensorRT v8.4.3.1
System Packages
- CUDA
- Recommended versions:
- cuda-11.6.x + cuDNN-8.4
- cuda-10.2 + cuDNN-8.4
- GNU make >= v4.1
- cmake >= v3.13
- python >= v3.6.9
- pip >= v19.0
- Essential utilities
Optional Packages
-
Containerized build
- Docker >= 19.03
- NVIDIA Container Toolkit
-
Toolchains and SDKs
- (Cross compilation for Jetson platform) NVIDIA JetPack >= 5.0 (current support only for TensorRT 8.4.0)
- (For Windows builds) Visual Studio 2017 Community or Enterprise edition
- (Cross compilation for QNX platform) QNX Toolchain
-
PyPI packages (for demo applications/tests)
- onnx 1.9.0
- onnxruntime 1.8.0
- tensorflow-gpu >= 2.5.1
- Pillow >= 9.0.1
- pycuda < 2021.1
- numpy
- pytest
-
Code formatting tools (for contributors)
NOTE: onnx-tensorrt, cub, and protobuf packages are downloaded along with TensorRT OSS, and not required to be installed.
Torch转ONNX
将PyTorch模型转换为ONNX格式,一般可按导入必要库、加载PyTorch模型、设置输入和导出等步骤进行,以下是具体步骤和代码示例:
1. 导入必要的库
首先,需要导入PyTorch和ONNX相关的库。
import torch
import torch.onnx
2. 加载PyTorch模型
假设已经训练好了一个PyTorch模型,并保存为 .pth
文件,可以使用以下代码加载模型。这里以一个简单的卷积神经网络模型为例:
# 定义模型类
class SimpleCNN(torch.nn.Module):
def __init__(self):
super(SimpleCNN, self).__init__()
self.conv1 = torch.nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1)
self.relu = torch.nn.ReLU()
self.fc1 = torch.nn.Linear(16 * 32 * 32, 10)
def forward(self, x):
x = self.conv1(x)
x = self.relu(x)
x = x.view(x.size(0), -1)
x = self.fc1(x)
return x
# 加载模型权重
model = SimpleCNN()
model.load_state_dict(torch.load('model.pth'))
model.eval()
3. 设置输入示例和动态轴
创建一个输入示例,用于指定模型的输入形状。这个输入示例的形状应该与模型期望的输入形状一致。同时,可以指定动态轴,以便在ONNX模型中支持不同大小的输入。
# 创建输入示例
batch_size = 1
input_channels = 3
input_height = 32
input_width = 32
input_tensor = torch.randn(batch_size, input_channels, input_height, input_width)
# 指定动态轴
dynamic_axes = {
'input': {0: 'batch_size'},
'output': {0: 'batch_size'}
}
4. 导出模型到ONNX
使用 torch.onnx.export
函数将PyTorch模型导出为ONNX格式。需要指定模型、输入示例、输出文件名、动态轴等参数。
# 导出模型
output_file ='model.onnx'
torch.onnx.export(
model,
input_tensor,
output_file,
export_params=True,
opset_version=11,
do_estimation=True,
dynamic_axes=dynamic_axes
)
上述代码中,export_params=True
表示将模型的参数一起导出,opset_version=11
指定了ONNX的操作集版本,do_estimation=True
会尝试优化模型,dynamic_axes
指定了动态轴。
在转换过程中,可能会遇到一些模型不支持的操作或数据类型等问题。这时需要检查模型的代码和结构,确保模型中的操作都能被ONNX支持。可能需要对模型进行一些修改,如将一些自定义的操作转换为ONNX支持的标准操作等。
Onnxrunime 推理验证
使用ONNX Runtime进行推理验证,主要包括安装ONNX Runtime、加载ONNX模型、准备输入数据、进行推理及验证结果等步骤,以下是详细说明及代码示例:
1. 安装ONNX Runtime
可以使用 pip
命令安装ONNX Runtime,根据使用的平台和需求选择合适的版本。
- CPU版本:适用于没有GPU或对GPU加速需求不高的场景。
pip install onnxruntime
- GPU版本:需要系统安装了相应的NVIDIA显卡驱动和CUDA等环境,能利用GPU加速推理。
pip install onnxruntime-gpu
2. 加载ONNX模型
使用ONNX Runtime加载已经转换好的ONNX模型。
import onnxruntime as ort
# 创建ONNX Runtime推理会话
ort_session = ort.InferenceSession("model.onnx")
3. 准备输入数据
准备与模型输入格式和形状匹配的数据。数据可以是随机生成的,也可以是真实的样本数据。
import numpy as np
# 创建与模型输入形状匹配的随机数据
input_shape = (1, 3, 32, 32) # 根据模型实际输入形状调整
input_data = np.random.randn(*input_shape).astype(np.float32)
# 创建输入字典,键为模型输入名称,值为输入数据
input_name = ort_session.get_inputs()[0].name
inputs = {input_name: input_data}
4. 进行推理
使用加载的模型和准备好的输入数据进行推理。
# 运行推理
outputs = ort_session.run(None, inputs)
5. 验证结果
可以根据具体任务和需求,采用不同的方式验证推理结果。比如对于分类任务,可以检查输出的类别概率是否合理;对于回归任务,可以计算预测值与真实值的误差等。
# 假设是分类任务,输出结果是类别概率,打印最高概率的类别
predicted_class = np.argmax(outputs[0], axis=1)
print("Predicted Class:", predicted_class)
在实际应用中,可能还需要处理模型有多个输入和输出的情况,以及对推理结果进行更复杂的评估和分析等。此外,如果在推理过程中出现错误,如模型不支持的操作、数据类型不匹配等,需要根据错误信息检查模型和输入数据,进行相应的调整和修复。
ONNX转TensorRT
将ONNX模型转换为TensorRT模型,一般需要安装TensorRT、加载ONNX模型、创建TensorRT构建器等步骤,以下是详细的步骤和代码示例:
1. 安装TensorRT和相关依赖
- 首先需要安装TensorRT,可以从NVIDIA官方网站下载适合自己系统和CUDA版本的TensorRT安装包进行安装。
- 同时,确保安装了Python的TensorRT绑定库,可以使用
pip
安装:
pip install nvidia-tensorrt
- 还需要安装
onnx-tensorrt
插件,它是ONNX和TensorRT之间的桥梁:
pip install onnx-tensorrt
2. 加载ONNX模型
使用Python的 onnx
库加载ONNX模型。
import onnx
# 加载ONNX模型
onnx_model = onnx.load("model.onnx")
3. 创建TensorRT构建器和网络定义
使用TensorRT的Python API创建构建器和网络定义。
import tensorrt as trt
# 创建TensorRT日志记录器
TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
# 创建TensorRT构建器
builder = trt.Builder(TRT_LOGGER)
# 创建TensorRT网络定义
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
# 创建ONNX解析器
parser = trt.OnnxParser(network, TRT_LOGGER)
# 解析ONNX模型
if not parser.parse(onnx_model.SerializeToString()):
print("Error parsing ONNX model")
for error in range(parser.num_errors):
print(parser.get_error(error))
4. 设置TensorRT构建器配置
配置构建器,例如设置最大批处理大小、工作空间大小等参数。
# 设置最大批处理大小
builder.max_batch_size = 1
# 设置工作空间大小(以字节为单位)
builder.max_workspace_size = 1 << 30 # 1GB
# 启用FP16模式(如果硬件支持且需要)
if builder.platform_has_fast_fp16:
builder.fp16_mode = True
5. 构建TensorRT引擎
使用配置好的构建器和网络定义构建TensorRT引擎。
# 构建TensorRT引擎
engine = builder.build_cuda_engine(network)
6. 保存TensorRT引擎
将构建好的TensorRT引擎保存到文件中,以便后续使用。
import os
# 保存TensorRT引擎到文件
def save_engine(engine, engine_file_path):
with open(engine_file_path, "wb") as f:
f.write(engine.serialize())
# 指定保存引擎的文件路径
engine_file_path = "model.engine"
save_engine(engine, engine_file_path)
上述代码中,通过一系列的操作将ONNX模型转换为TensorRT引擎,并保存为 .engine
文件。在实际应用中,可能需要根据模型的特点和需求,进一步调整构建器的配置参数,以优化TensorRT引擎的性能。
Tensort 推理
TensorRT模型推理一般包括加载TensorRT引擎、准备输入数据、执行推理、处理输出结果等步骤,以下是详细的步骤和代码示例:
1. 导入必要的库
导入TensorRT、PyCUDA等相关库,用于模型推理和CUDA操作。
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import numpy as np
2. 加载TensorRT引擎
从之前保存的 .engine
文件中加载TensorRT引擎,并创建执行上下文。
# 加载TensorRT引擎
def load_engine(engine_file_path):
with open(engine_file_path, "rb") as f, trt.Runtime(trt.Logger(trt.Logger.WARNING)) as runtime:
return runtime.deserialize_cuda_engine(f.read())
# 创建执行上下文
engine_file_path = "model.engine"
engine = load_engine(engine_file_path)
context = engine.create_execution_context()
3. 准备输入数据
根据模型的输入要求,准备输入数据。将数据转换为合适的格式,并分配GPU内存。
# 假设输入数据是形状为(1, 3, 224, 224)的图像数据,数据类型为float32
input_shape = (1, 3, 224, 224)
input_data = np.random.randn(*input_shape).astype(np.float32)
# 为输入数据分配GPU内存
input_buffer = cuda.mem_alloc(input_data.nbytes)
cuda.memcpy_htod(input_buffer, input_data)
4. 准备输出内存
根据模型的输出要求,分配输出内存。
# 获取输出张量的形状和数据类型
output_shape = (1, 1000) # 根据实际模型输出形状调整
output_dtype = np.float32
# 为输出数据分配GPU内存
output_data = np.empty(output_shape, dtype=output_dtype)
output_buffer = cuda.mem_alloc(output_data.nbytes)
5. 执行推理
将输入数据传递给模型,执行推理,并将输出结果存储到输出内存中。
# 创建绑定缓冲区列表,包含输入和输出缓冲区
bindings = [int(input_buffer), int(output_buffer)]
# 执行推理
context.execute_v2(bindings)
6. 处理输出结果
将输出结果从GPU内存复制到CPU内存,并进行后续处理,如打印结果或进行评估。
# 将输出结果从GPU复制到CPU
cuda.memcpy_dtoh(output_data, output_buffer)
# 处理输出结果,例如打印类别概率最高的类别
predicted_class = np.argmax(output_data, axis=1)
print("Predicted Class:", predicted_class)
在实际应用中,需要根据具体的模型和任务对代码进行调整,还可以添加错误处理、性能优化等功能,以提高推理的稳定性和效率。
tensorrt C++ 部署推理
以下是在C++中使用TensorRT进行模型部署推理的详细步骤,包含了从加载模型引擎到执行推理以及处理结果的完整流程:
1. 包含必要的头文件和链接相关库
在C++代码中,首先需要包含TensorRT相关的头文件以及CUDA的头文件(因为TensorRT依赖于CUDA进行GPU加速),同时要确保在编译时链接对应的库文件。
#include <iostream>
#include <fstream>
#include <vector>
#include <NvInfer.h>
#include <cuda_runtime.h>
// 引入必要的命名空间
using namespace nvinfer1;
using namespace std;
2. 加载TensorRT引擎
从磁盘上保存的 .engine
文件中加载TensorRT引擎,这是执行推理的核心组件,以下是一个加载引擎的函数示例:
// 函数用于从文件中读取并反序列化TensorRT引擎
ICudaEngine* loadEngine(const string& engineFilePath) {
ifstream file(engineFilePath, ios::binary);
if (!file) {
cerr << "Failed to open engine file: " << engineFilePath << endl;
return nullptr;
}
file.seekg(0, ios::end);
size_t size = file.tellg();
file.seekg(0, ios::beg);
char* buffer = new char[size];
file.read(buffer, size);
file.close();
IRuntime* runtime = createInferRuntime(gLogger);
ICudaEngine* engine = runtime->deserializeCudaEngine(buffer, size);
delete[] buffer;
return engine;
}
3. 准备输入输出数据结构
需要根据模型的输入输出要求,定义相应的数据结构,包括在GPU上分配内存来存储数据,并创建对应的CUDA流等。
// 结构体用于管理输入输出缓冲区
struct BufferManager {
vector<void*> deviceBuffers;
vector<void*> hostBuffers;
int numBuffers;
BufferManager(int numBuffers_) : numBuffers(numBuffers_) {
deviceBuffers.resize(numBuffers);
hostBuffers.resize(numBuffers);
}
// 在GPU和CPU上分配内存
void allocateBuffers(ICudaEngine* engine) {
for (int i = 0; i < numBuffers; ++i) {
Dims dims = engine->getBindingDimensions(i);
size_t size = getSizeByDim(dims) * sizeof(float);
cudaMalloc(&deviceBuffers[i], size);
hostBuffers[i] = new float[getSizeByDim(dims)];
}
}
// 释放内存
void freeBuffers() {
for (int i = 0; i < numBuffers; ++i) {
cudaFree(deviceBuffers[i]);
delete[] hostBuffers[i];
}
}
private:
// 根据维度计算元素个数
int getSizeByDim(const Dims& dims) {
int size = 1;
for (int i = 0; i < dims.nbDims; ++i) {
size *= dims.d[i];
}
return size;
}
};
4. 执行推理
创建执行上下文,将输入数据从CPU复制到GPU,然后执行推理操作,最后将输出结果从GPU复制回CPU。
// 执行TensorRT推理的函数
void doInference(ICudaEngine* engine, BufferManager& buffers) {
IExecutionContext* context = engine->createExecutionContext();
if (!context) {
cerr << "Failed to create execution context" << endl;
return;
}
// 将输入数据从CPU复制到GPU
for (int i = 0; i < engine->getNbBindings(); ++i) {
if (engine->bindingIsInput(i)) {
cudaMemcpy(buffers.deviceBuffers[i], buffers.hostBuffers[i],
getSizeByDim(engine->getBindingDimensions(i)) * sizeof(float),
cudaMemcpyHostToDevice);
}
}
// 执行推理
context->executeV2(buffers.deviceBuffers.data());
// 将输出结果从GPU复制回CPU
for (int i = 0; i < engine->getNbBindings(); ++i) {
if (!engine->bindingIsInput(i)) {
cudaMemcpy(buffers.hostBuffers[i], buffers.deviceBuffers[i],
getSizeByDim(engine->getBindingDimensions(i)) * sizeof(float),
cudaMemcpyDeviceToHost);
}
}
context->destroy();
}
5. 主函数整合及调用
在主函数中,调用上述的各个函数来完成整个TensorRT模型的部署推理流程,示例如下:
int main() {
// 加载TensorRT引擎
string engineFilePath = "model.engine";
ICudaEngine* engine = loadEngine(engineFilePath);
if (!engine) {
return -1;
}
// 创建缓冲区管理器并分配内存
BufferManager buffers(engine->getNbBindings());
buffers.allocateBuffers(engine);
// 假设这里简单地填充输入数据(实际中根据具体模型输入要求填充)
fillInputData(buffers.hostBuffers[0]);
// 执行推理
doInference(engine, buffers);
// 处理输出结果(根据具体任务处理)
processOutputData(buffers.hostBuffers[engine->getNbBindings() - 1]);
// 释放内存
buffers.freeBuffers();
engine->destroy();
return 0;
}
在上述代码中:
fillInputData
函数用于根据模型的实际输入要求,向hostBuffers
中对应的输入缓冲区填充合适的数据,这部分代码需要根据具体模型的输入维度、数据类型等进行编写。processOutputData
函数则是按照具体的任务类型(比如分类任务查看预测类别、回归任务分析预测数值等)对输出缓冲区中的数据进行处理,同样需要依据具体应用场景来实现。
另外,在实际编译运行这段代码时,需要正确配置编译环境,包含TensorRT库和CUDA库的路径以及链接相应的库文件,不同的开发环境(如Visual Studio、GCC等)配置方式会有所不同。
TensorRT C++部署
onnx模型转engine
- 基于C++代码生成engine:构建
IBuilder
、INetworkDefinition
、nvonnxparser::IParser
等对象,通过parser
解析onnx文件,利用builder
和配置对象IBuilderConfig
创建推理引擎ICudaEngine
,最后将engine
序列化并保存到本地文件。 - 基于trtexec.exe命令行生成:可使用
trtexec.exe
命令行工具,指定--onnx
参数为onnx文件路径,--saveEngine
参数为保存engine文件路径,实现将onnx模型转换为engine。
读取本地模型
使用 std::ifstream
以二进制方式打开engine文件,将文件内容读取到内存中。可以定义一个字符指针和文件大小变量,通过移动文件指针获取文件大小,再将文件指针移回文件开始处,读取文件数据到字符指针指向的内存区域。
创建推理引擎
创建 nvinfer1::IRuntime
对象,通过 createInferRuntime
函数实现。然后使用 runtime
的 deserializeCudaEngine
函数,传入之前读取的engine文件数据和文件大小,创建推理引擎 ICudaEngine
。
创建推理上下文
通过推理引擎 ICudaEngine
的 createExecutionContext
函数创建推理上下文 IExecutionContext
,该上下文用于执行推理操作。
创建GPU显存缓冲区
根据模型的输入输出信息,计算所需的显存大小。使用 cudaMalloc
函数在GPU上分配输入和输出缓冲区的显存空间。将分配的显存指针与模型的输入输出绑定,可通过 IExecutionContext
的 setBindingAddress
函数实现。
配置输入数据
将输入数据从主机内存复制到GPU显存的输入缓冲区中,可使用 cudaMemcpy
函数实现。设置输入数据的维度、数据类型等信息,确保与模型的输入要求匹配。
模型推理
使用 IExecutionContext
的 execute
或 executeV2
函数执行推理操作,传入输入输出缓冲区的绑定信息,让模型在GPU上进行推理计算。
获得输出数据
将推理结果从GPU显存的输出缓冲区复制到主机内存中,使用 cudaMemcpy
函数实现。对输出数据进行后处理,如解析检测结果、进行分类判断、绘制检测框等,根据具体的模型和应用需求进行相应的处理。