Back_To_Home..

Tensorrt

TensorRT

TensorRT是可以在NVIDIA各种GPU硬件平台下运行的一个C++推理框架。我们利用Pytorch、TF或者其他框架训练好的模型,可以转化为TensorRT的格式,然后利用TensorRT推理引擎去运行我们这个模型,从而提升这个模型在英伟达GPU上运行的速度。

在GPU服务器上部署的话,TensorRT是首选!

TensorRT是硬件相关的,不同的硬件不同的优化。TensorRT是半开源的,除核心部分外基本都开源了。

TensorRT模型转换

  1. TF-TRT,TensorRT集成在TensorFlow中
  2. ONNX2TensorRT,即ONNX转换trt的工具
    /usr/src/tensorrt/bin/trtexec --onnx=yolov7-tiny.onnx --saveEngine=yolov7-tiny-nms.trt --fp16
    
  3. 手动构造模型结构,然后手动将权重信息挪过去,非常灵活但是时间成本略高,有大佬已经尝试过了.

Project

https://github.com/wang-xinyu/pytorchx Implement popular deep learning networks in pytorch, used by

PyTorch与TensorRT

模型转换方式:PyTorch → ONNX → TensorRT C++API

理解TensorRT Runtimes

在TensorRT Runtime环境中运行模型,就是直接使用TensorRT;

TensorRT/5. Understanding TensorRT Runtimes.ipynb at main · NVIDIA/TensorRT

Python API

TensorRT/4. Using PyTorch through ONNX.ipynb at 87f3394404ff9f9ec92c906cd4c39b5562aea42e · NVIDIA/TensorRT

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

Triton Inference Server

Nvidia 官方镜像

nvidia官方提供了tensorrt以及包含tensorrt的流行深度学习环境的镜像,例如tensorflow、paddlepaddle、torch

推荐pytorch镜像

PyTorch Release Notes :: NVIDIA Deep Learning Frameworks Documentation

查看nvidia的pytorch镜像包含的库版本

NVIDIA TensorRT

Git History

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版本适配

To build the TensorRT-OSS components, you will first need the following software packages.

TensorRT GA build675

System Packages

Optional Packages

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,根据使用的平台和需求选择合适的版本。

pip install onnxruntime
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和相关依赖

pip install nvidia-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;
}

在上述代码中:

另外,在实际编译运行这段代码时,需要正确配置编译环境,包含TensorRT库和CUDA库的路径以及链接相应的库文件,不同的开发环境(如Visual Studio、GCC等)配置方式会有所不同。

TensorRT C++部署

onnx模型转engine

读取本地模型

使用 std::ifstream以二进制方式打开engine文件,将文件内容读取到内存中。可以定义一个字符指针和文件大小变量,通过移动文件指针获取文件大小,再将文件指针移回文件开始处,读取文件数据到字符指针指向的内存区域。

创建推理引擎

创建 nvinfer1::IRuntime对象,通过 createInferRuntime函数实现。然后使用 runtimedeserializeCudaEngine函数,传入之前读取的engine文件数据和文件大小,创建推理引擎 ICudaEngine

创建推理上下文

通过推理引擎 ICudaEnginecreateExecutionContext函数创建推理上下文 IExecutionContext,该上下文用于执行推理操作。

创建GPU显存缓冲区

根据模型的输入输出信息,计算所需的显存大小。使用 cudaMalloc函数在GPU上分配输入和输出缓冲区的显存空间。将分配的显存指针与模型的输入输出绑定,可通过 IExecutionContextsetBindingAddress函数实现。

配置输入数据

将输入数据从主机内存复制到GPU显存的输入缓冲区中,可使用 cudaMemcpy函数实现。设置输入数据的维度、数据类型等信息,确保与模型的输入要求匹配。

模型推理

使用 IExecutionContextexecuteexecuteV2函数执行推理操作,传入输入输出缓冲区的绑定信息,让模型在GPU上进行推理计算。

获得输出数据

将推理结果从GPU显存的输出缓冲区复制到主机内存中,使用 cudaMemcpy函数实现。对输出数据进行后处理,如解析检测结果、进行分类判断、绘制检测框等,根据具体的模型和应用需求进行相应的处理。

tensorrt step transform tutorials

© 2025 ai-charlie   •  Powered by Soopr   •  Theme  Moonwalk