yolov8实战第三天——yolov8TensorRT部署(python推理)(保姆教学)

本文是YOLOv8实战系列的第三篇,主要讲解如何使用TensorRT对训练好的yolov8模型进行优化部署。首先介绍了TensorRT的作用和适用场景,然后详细阐述了TensorRT的安装步骤,接着将pt模型转换为onnx格式,并进行了模型验证。最后,通过Python脚本展示了TensorRT推理过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇中我们使用自己的数据集训练了一个yolov8检测模型,best.py。

yolov8实战第一天——yolov8部署并训练自己的数据集(保姆式教程)-CSDN博客

yolov8实战第二天——yolov8训练结果分析(保姆式解读)-CSDN博客

接下要对best.py进行TensorRT优化并部署。

TensorRT是一种高性能深度学习推理优化器和运行时加速库,可以为深度学习应用提供低延迟、高吞吐率的部署推理。

TensorRT可用于对超大规模数据中心、嵌入式平台或自动驾驶平台进行推理加速。

TensorRT现已能支持TensorFlow、Caffe、Mxnet、Pytorch等几乎所有的深度学习框架,将TensorRT和NVIDIA的GPU结合起来,能在几乎所有的框架中进行快速和高效的部署推理。

一般的深度学习项目,训练时为了加快速度,会使用多GPU分布式训练。但在部署推理时,为了降低成本,往往使用单个GPU机器甚至嵌入式平台(比如 NVIDIA Jetson)进行部署,部署端也要有与训练时相同的深度学习环境,如caffe,TensorFlow等。

由于训练的网络模型可能会很大(比如,inception,resnet等),参数很多,而且部署端的机器性能存在差异,就会导致推理速度慢,延迟高。这对于那些高实时性的应用场合是致命的,比如自动驾驶要求实时目标检测,目标追踪等。

为了提高部署推理的速度,出现了很多模型优化的方法,如:模型压缩、剪枝、量化、知识蒸馏等,这些一般都是在训练阶段实现优化。

而TensorRT 则是对训练好的模型进行优化,通过优化网络计算图提高模型效率。

一、安装TensorRT  

Log in | NVIDIA Developer

下载TensorRT 。

我下载的是8.6里画黑线的那个。 

将 TensorRT-8.6.1.6\include中头文件 copy 到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.7\include
将TensorRT-8.6.1.6\lib 中所有lib文件 copy 到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.7\lib\x64
将TensorRT-8.6.1.6\lib 中所有dll文件copy 到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.7\bin

在python文件夹中找到适合自己的。

pip install tensorrt-8.6.1-cp310-none-win_amd64.whl

 至此TensorRT安装完成。

这里可能会报错:

FileNotFoundError: Could not find: cublasLt64_12.dll. Is it on your PATH?

解决办法:C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8\bin

将这个文件 cublasLt64_11.dll 复制一份,并保存为 cublasLt64_12.dll

 二、pt转onnx:

GitHub - triple-Mu/YOLOv8-TensorRT: YOLOv8 using TensorRT accelerate !

参考着这个,下载,安装环境后。

安装onnx:

pip install onnx -i https://pypi.tuna.tsinghua.edu.cn/simple 
pip install onnxsim -i https://pypi.tuna.tsinghua.edu.cn/simple 
pip install onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/simple 

生成onnx: 

python export-det.py --weights yolov8n.pt --iou-thres 0.65 --conf-thres 0.25 --topk 100 --opset 11 --sim --input-shape 1 3 640 640 --device cuda:0

使用上篇文章中的老鼠模型做了测试: 

onnx的测试代码:

import onnxruntime as rt
import numpy as np
import cv2
import  matplotlib.pyplot as plt
 
 
def nms(pred, conf_thres, iou_thres): 
    conf = pred[..., 4] > conf_thres
    box = pred[conf == True] 
    cls_conf = box[..., 5:]
    cls = []
    for i in range(len(cls_conf)):
        cls.append(int(np.argmax(cls_conf[i])))
    total_cls = list(set(cls))  
    output_box = []  
    for i in range(len(total_cls)):
        clss = total_cls[i] 
        cls_box = []
        for j in range(len(cls)):
            if cls[j] == clss:
                box[j][5] = clss
                cls_box.append(box[j][:6])
        cls_box = np.array(cls_box)
        box_conf = cls_box[..., 4]  
        box_conf_sort = np.argsort(box_conf) 
        max_conf_box = cls_box[box_conf_sort[len(box_conf) - 1]]
        output_box.append(max_conf_box) 
        cls_box = np.delete(cls_box, 0, 0) 
        while len(cls_box) > 0:
            max_conf_box = output_box[len(output_box) - 1]  
            del_index = []
            for j in range(len(cls_box)):
                current_box = cls_box[j]  
                interArea = getInter(max_conf_box, current_box)  
                iou = getIou(max_conf_box, current_box, interArea)  
                if iou > iou_thres:
                    del_index.append(j)  
            cls_box = np.delete(cls_box, del_index, 0)  
            if len(cls_box) > 0:
                output_box.append(cls_box[0])
                cls_box = np.delete(cls_box, 0, 0)
    return output_box
 
 
def getIou(box1, box2, inter_area):
    box1_area = box1[2] * box1[3]
    box2_area = box2[2] * box2[3]
    union = box1_area + box2_area - inter_area
    iou = inter_area / union
    return iou
 
 
def getInter(box1, box2):
    box1_x1, box1_y1, box1_x2, box1_y2 = box1[0] - box1[2] / 2, box1[1] - box1[3] / 2, \
                                         box1[0] + box1[2] / 2, box1[1] + box1[3] / 2
    box2_x1, box2_y1, box2_x2, box2_y2 = box2[0] - box2[2] / 2, box2[1] - box1[3] / 2, \
                                         box2[0] + box2[2] / 2, box2[1] + box2[3] / 2
    if box1_x1 > box2_x2 or box1_x2 < box2_x1:
        return 0
    if box1_y1 > box2_y2 or box1_y2 < box2_y1:
        return 0
    x_list = [box1_x1, box1_x2, box2_x1, box2_x2]
    x_list = np.sort(x_list)
    x_inter = x_list[2] - x_list[1]
    y_list = [box1_y1, box1_y2, box2_y1, box2_y2]
    y_list = np.sort(y_list)
    y_inter = y_list[2] - y_list[1]
    inter = x_inter * y_inter
    return inter
 
 
def draw(img, xscale, yscale, pred):
    img_ = img.copy()
    if len(pred):
        for detect in pred:
            detect = [int((detect[0] - detect[2] / 2) * xscale), int((detect[1] - detect[3] / 2) * yscale),
                      int((detect[0]+detect[2] / 2) * xscale), int((detect[1]+detect[3] / 2) * yscale)]
            img_ = cv2.rectangle(img, (detect[0], detect[1]), (detect[2], detect[3]), (0, 255, 0), 1)
    return img_
 
 
if __name__ == '__main__':
    height, width = 640, 640
    img0 = cv2.imread('mouse-4-6-0004.jpg')
    x_scale = img0.shape[1] / width
    y_scale = img0.shape[0] / height
    img = img0 / 255.
    img = cv2.resize(img, (width, height))
    img = np.transpose(img, (2, 0, 1))
    data = np.expand_dims(img, axis=0)
    sess = rt.InferenceSession('best.onnx')
    input_name = sess.get_inputs()[0].name
    label_name = sess.get_outputs()[0].name
    pred = sess.run([label_name], {input_name: data.astype(np.float32)})[0]
    pred = np.squeeze(pred)
    pred = np.transpose(pred, (1, 0))
    pred_class = pred[..., 4:]
    pred_conf = np.max(pred_class, axis=-1)
    pred = np.insert(pred, 4, pred_conf, axis=-1)
    result = nms(pred, 0.3, 0.45)
    ret_img = draw(img0, x_scale, y_scale, result)
    ret_img = ret_img[:, :, ::-1]
    plt.imshow(ret_img)
    plt.show()

三、TensorRT部署

导出engine模型:

python build.py --weights yolov8n.onnx --iou-thres 0.65 --conf-thres 0.25 --topk 100 --fp16 --device cuda:0

等待一会,engine成功导出。 

 

1.使用python脚本进行推理:

python infer-det.py --engine yolov8n.engine --imgs data --show --out-dir outputs --out-dir outputs --device cuda:0

infer-det.py: 

from models import TRTModule  # isort:skip
import argparse
from pathlib import Path

import cv2
import torch

from config import CLASSES, COLORS
from models.torch_utils import det_postprocess
from models.utils import blob, letterbox, path_to_list


def main(args: argparse.Namespace) -> None:
    device = torch.device(args.device)
    Engine = TRTModule(args.engine, device)
    H, W = Engine.inp_info[0].shape[-2:]

    # set desired output names order
    Engine.set_desired(['num_dets', 'bboxes', 'scores', 'labels'])

    images = path_to_list(args.imgs)
    save_path = Path(args.out_dir)

    if not args.show and not save_path.exists():
        save_path.mkdir(parents=True, exist_ok=True)

    for image in images:
        save_image = save_path / image.name
        bgr = cv2.imread(str(image))
        draw = bgr.copy()
        bgr, ratio, dwdh = letterbox(bgr, (W, H))
        rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
        tensor = blob(rgb, return_seg=False)
        dwdh = torch.asarray(dwdh * 2, dtype=torch.float32, device=device)
        tensor = torch.asarray(tensor, device=device)
        # inference
        data = Engine(tensor)

        bboxes, scores, labels = det_postprocess(data)
        if bboxes.numel() == 0:
            # if no bounding box
            print(f'{image}: no object!')
            continue
        bboxes -= dwdh
        bboxes /= ratio

        for (bbox, score, label) in zip(bboxes, scores, labels):
            bbox = bbox.round().int().tolist()
            cls_id = int(label)
            cls = CLASSES[cls_id]
            color = COLORS[cls]
            cv2.rectangle(draw, bbox[:2], bbox[2:], color, 2)
            cv2.putText(draw,
                        f'{cls}:{score:.3f}', (bbox[0], bbox[1] - 2),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.75, [225, 255, 255],
                        thickness=2)
        if args.show:
            cv2.imshow('result', draw)
            cv2.waitKey(0)
        else:
            cv2.imwrite(str(save_image), draw)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument('--engine', type=str, help='Engine file')
    parser.add_argument('--imgs', type=str, help='Images file')
    parser.add_argument('--show',
                        action='store_true',
                        help='Show the detection results')
    parser.add_argument('--out-dir',
                        type=str,
                        default='./output',
                        help='Path to output file')
    parser.add_argument('--device',
                        type=str,
                        default='cuda:0',
                        help='TensorRT infer device')
    args = parser.parse_args()
    return args


if __name__ == '__main__':
    args = parse_args()
    main(args)

2.加入计算推理时间版本:

from models import TRTModule  # isort:skip
import argparse
from pathlib import Path
import time  # Import time module to measure inference time

import cv2
import torch

from config import CLASSES, COLORS
from models.torch_utils import det_postprocess
from models.utils import blob, letterbox, path_to_list


def main(args: argparse.Namespace) -> None:
    device = torch.device(args.device)
    Engine = TRTModule(args.engine, device)
    H, W = Engine.inp_info[0].shape[-2:]

    # set desired output names order
    Engine.set_desired(['num_dets', 'bboxes', 'scores', 'labels'])

    images = path_to_list(args.imgs)
    save_path = Path(args.out_dir)

    if not args.show and not save_path.exists():
        save_path.mkdir(parents=True, exist_ok=True)

    for image in images:
        save_image = save_path / image.name
        bgr = cv2.imread(str(image))
        draw = bgr.copy()
        bgr, ratio, dwdh = letterbox(bgr, (W, H))
        rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
        tensor = blob(rgb, return_seg=False)
        dwdh = torch.asarray(dwdh * 2, dtype=torch.float32, device=device)
        tensor = torch.asarray(tensor, device=device)

        start_time = time.time()  # Start time for inference
        # inference
        data = Engine(tensor)
        end_time = time.time()  # End time for inference

        inference_time = end_time - start_time  # Calculate inference time
        print(f'Inference time for {image}: {inference_time:.3f} seconds')

        bboxes, scores, labels = det_postprocess(data)
        if bboxes.numel() == 0:
            # if no bounding box
            print(f'{image}: no object!')
            continue
        bboxes -= dwdh
        bboxes /= ratio

        for (bbox, score, label) in zip(bboxes, scores, labels):
            bbox = bbox.round().int().tolist()
            cls_id = int(label)
            cls = CLASSES[cls_id]
            color = COLORS[cls]
            cv2.rectangle(draw, bbox[:2], bbox[2:], color, 2)
            cv2.putText(draw,
                        f'{cls}:{score:.3f}', (bbox[0], bbox[1] - 2),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.75, [225, 255, 255],
                        thickness=2)
        if args.show:
            cv2.imshow('result', draw)
            cv2.waitKey(0)
        else:
            cv2.imwrite(str(save_image), draw)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument('--engine', type=str, help='Engine file')
    parser.add_argument('--imgs', type=str, help='Images file')
    parser.add_argument('--show',
                        action='store_true',
                        help='Show the detection results')
    parser.add_argument('--out-dir',
                        type=str,
                        default='./output',
                        help='Path to output file')
    parser.add_argument('--device',
                        type=str,
                        default='cuda:0',
                        help='TensorRT infer device')
    args = parser.parse_args()
    return args


if __name__ == '__main__':
    args = parse_args()
    main(args)

 第一张图片由于加载模型耽误时间,以第二张图片检测耗时为准。

yolov8m的tensorrt  推理时间是0.004s,非常快。

四、对比yolov8m.pt

import cv2
from ultralytics import YOLO
import os

# 载入 YOLOv8 模型
model_path = 'yolov8m.pt'
model = YOLO(model_path)

# 指定输入文件夹和输出文件夹
input_folder = 'data'
output_folder = 'output1'

# 确保输出文件夹存在
if not os.path.exists(output_folder):
    os.makedirs(output_folder)

# 遍历输入文件夹中的所有文件
for filename in os.listdir(input_folder):
    if filename.lower().endswith(('.png', '.jpg', '.jpeg')):  # 只处理图像文件
        image_path = os.path.join(input_folder, filename)
        image = cv2.imread(image_path)  # 读取图像

        # 对图像运行 YOLOv8 推理
        results = model(image)

        # 在图像上可视化结果
        annotated_image = results[0].plot()  # 假设plot返回带标注的图像

        # 构建输出图像的路径
        output_image_path = os.path.join(output_folder, filename)

        # 保存带有标注的图像
        cv2.imwrite(output_image_path, annotated_image)
        print(f'Processed and saved: {output_image_path}')

1.速度对比 

 推理速度在100ms,tensorrt 后速度提升非常多。

2.检测质量对比(左tensorrt,右原版)

 

3.对比总结 :

检测后置信度会差1个点左右,速度快非常多。

### YOLOv11与TensorRTPython中的集成 目前可获得的信息主要集中在YOLO系列较早版本如YOLOv5与TensorRT的集成方法[^1]。对于YOLOv11的具体实现细节尚未广泛公开,因此无法提供针对该特定版本的确切指导。然而,可以推测YOLOv11与TensorRT的集成过程可能遵循类似的模式。 #### 安装依赖库 为了能够在Python环境中使用YOLOv11配合TensorRT进行部署或优化,首先需要确保安装了必要的软件包和工具链。这通常包括但不限于CUDA Toolkit、cuDNN以及TensorRT本身。这些组件可以通过NVIDIA官方渠道获取并按照说明完成设置[^4]。 #### 导出模型文件 假设已经训练好了YOLOv11权重文件(例如`.pt`),下一步就是将其转换成适合TensorRT加载的形式。此操作一般通过导出ONNX格式或其他兼容格式来达成。参考以往经验,可能会存在专门用于此类目的脚本,比如: ```bash python export.py --weights yolov11.pt --include onnx ``` 上述命令会将PyTorch格式(`.pt`)YOLOv11模型转化为ONNX格式以便后续处理[^3]。 #### 构建TensorRT引擎 一旦拥有了合适的中间表示形式(如ONNX),就可以利用TensorRT API创建高效的推理引擎。这一阶段涉及编写一段Python代码片段调用相关API读取ONNX定义,并据此生成序列化后的计划文件(plan file)供实际应用中快速加载执行。下面给出一个简化版的例子: ```python import tensorrt as trt from polygraphy.backend.trt import CreateConfig, EngineFromNetwork, NetworkFromOnnxPath # 加载 ONNX 文件路径 onnx_file_path = "path_to_your/yolov11.onnx" # 创建 TensorRT 运行环境 TRT_LOGGER = trt.Logger(trt.Logger.WARNING) config = CreateConfig() engine_builder = EngineFromNetwork(NetworkFromOnnxPath(onnx_file_path), config=config) with engine_builder() as engine: with open("yolov11.engine", "wb") as f: f.write(engine.serialize()) ``` 这段程序负责解析给定的ONNX描述并将之编译成为适用于目标硬件平台的最佳性能配置下的TensorRT引擎实例。 #### 执行推断任务 最后,在应用程序层面只需要简单地加载之前保存下来的引擎文件即可开始图像检测等工作流。这里展示了一个基本框架示意如何初始化Session对象进而发起预测请求: ```python import pycuda.driver as cuda import pycuda.autoinit import numpy as np import cv2 import tensorrt as trt class HostDeviceMem(object): def __init__(self, host_mem, device_mem): self.host = host_mem self.device = device_mem def allocate_buffers(engine): inputs = [] outputs = [] bindings = [] stream = cuda.Stream() for binding in engine: size = trt.volume(engine.get_binding_shape(binding)) * engine.max_batch_size dtype = trt.nptype(engine.get_binding_dtype(binding)) # Allocate host and device buffers host_mem = cuda.pagelocked_empty(size, dtype) device_mem = cuda.mem_alloc(host_mem.nbytes) # Append the device buffer to device bindings. bindings.append(int(device_mem)) # Append to the appropriate list. if engine.binding_is_input(binding): inputs.append(HostDeviceMem(host_mem, device_mem)) else: outputs.append(HostDeviceMem(host_mem, device_mem)) return inputs, outputs, bindings, stream def do_inference(context, bindings, inputs, outputs, stream, batch_size=1): # Transfer input data to the GPU. [cuda.memcpy_htod_async(inp.device, inp.host, stream) for inp in inputs] # Run inference. context.execute_async(batch_size=batch_size, bindings=bindings, stream_handle=stream.handle) # Transfer predictions back from the GPU. [cuda.memcpy_dtoh_async(out.host, out.device, stream) for out in outputs] # Synchronize the stream stream.synchronize() return [out.host for out in outputs] if __name__ == '__main__': TRT_LOGGER = trt.Logger(trt.Logger.WARNING) runtime = trt.Runtime(TRT_LOGGER) with open("yolov11.engine", 'rb') as f: serialized_engine = f.read() engine = runtime.deserialize_cuda_engine(serialized_engine) context = engine.create_execution_context() image = cv2.imread('test.jpg') img_resized = preprocess(image) # 预处理函数需自行定义 inputs, outputs, bindings, stream = allocate_buffers(engine) np.copyto(inputs[0].host, img_resized.ravel()) detections = do_inference(context, bindings=bindings, inputs=inputs, outputs=outputs, stream=stream)[0] postprocess(detections) # 后处理逻辑同样由开发者自定义 ``` 以上即为大致流程概述;需要注意的是具体实施过程中还需考虑更多因素,像输入预处理方式的选择、输出解码策略的设计等均会影响最终效果表现。
评论 45
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学术菜鸟小晨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值