SSD目标检测网络tensorRT推理【附代码】

简介: 笔记

终于更新了,本篇是实现了SSD的tensorrt 推理【python版】。YOLOv4以及YOLOv5C++版的tensorrt推理可以看我之前的文章。


SSD代码我这里是在b站up主Bubbliiiing的pytorch版SSD的基础上进行的实现。


环境说明


windows10


cuda10.2


cudnn8.2.1


pytorch1.7


tensorrt8.2.5.1


python 3.7


显卡:NVIDIA 1650 4G(比较拉跨)


注:linux下我还没有试,可能有些代码需要改,而且trt的版本也会受影响


首先说一下网络输出部分我修改了哪些。


【其实这部分不看也可以,可以直接跳过1,2,3节,直接从转onnx开始看】


再转onnx之前需要修改一些地方,我们知道SSD有6个输出,代码中的输出output是包含了loc【位置】,conf【每个先验框对应类的置信度】,先验框数量。在代码中分train模式和test模式【这两个模式好像在后面的版本中进行了整合】。


train模式下的输出为:

output = (
                 loc.view(loc.size(0), -1, 4),
                 conf.view(conf.size(0), -1, self.num_classes),
                 self.priors
             )

test模式下的输出为:

if self.phase == "test":
            output = self.detect.forward(
                loc.view(loc.size(0), -1, 4),  # (batch_size, num_anchors,4) = (batch_size, num_anchors,(x,y,w,h))
                self.softmax(conf.view(conf.size(0), -1, self.num_classes)),  # (batch_size,num_anchors, num_classes)
                self.priors              
            )

因此我们首先要做的是修改一些输出,至于为什么要修改呢,是因为如果我导ONNX的时候进行检测的时候发现没有任何检测结果,分析内部数据的时候发现在num_classes以及对应的conf维度上无结果,因此当我以train模式导出onnx模式并在外面采用detect时发现是可以的。【反正就是我的个人经验得来的,大家用就可以了】


1.修改ouputput


所以需要将output改为以下【在nets/ssd.py】:

        if self.phase == "test":
            output = self.detect.forward(
                loc.view(loc.size(0), -1, 4),  # (batch_size, num_anchors,4) = (batch_size, num_anchors,(x,y,w,h))
                self.softmax(conf.view(conf.size(0), -1, self.num_classes)),  # (batch_size,num_anchors, num_classes)
                self.priors              
            )
        else:
            # output = (
            #     loc.view(loc.size(0), -1, 4),
            #     conf.view(conf.size(0), -1, self.num_classes),
            #     self.priors
            # )
            loc = torch.tensor(loc.view(loc.size(0), -1, 4)),
            conf = torch.tensor(conf.view(conf.size(0), -1, self.num_classes)),
            self.priors = torch.tensor(self.priors)
        #return output
        return loc, conf, self.priors

2.修改box解码


接下来再去nets/ssd_layers.py中的Dectect中的forward()中添加一行:

prior_data = prior_data.cpu()

也就是把先验框放在cpu上,不然在box解码的时候会出问题。


3.获得输出


然后在工程文件中的ssd.py中的检测部分添加下面的代码:

因为我们的输出现在有三个,所以要将其送入detect中【如果你是pytorch1.5以后版本要加上forward不然会报错】

            preds = self.net(photo)
            if self.onnx:
                loc = preds[0]
                conf = preds[1]
                priors = preds[2]
                preds = self.detect.forward(loc, nn.Softmax(dim=-1).forward(conf), priors)

4.转onnx


运行以下代码,仅需要修改torch权重路径即可以及类别:

python torch2onnx.py

torch转onnx模型我这里以及写好了,如果你是自己的数据集,需要修改num_classes,ckpt中torch的模型,output_namse和input_names是输出以及输入结点,因为有三个输出,所以是三个结点名字。最终导出的模型会保存在model_data文件下。


这里导出onnx有两个模式,你可以选择是否开启simplity,如果开启改功能可以更详细的看清楚每个卷积输出的尺寸大小,而且也会对onnx模型进行一定的优化。

import onnx
from nets.ssd import get_ssd
import torch
from utils.config import Config
from onnxsim import simplify
import numpy as np
Simplity = False
output_path = "model_data/ssd.onnx"
num_classes = 21
model = get_ssd('train', num_classes)
model_dict = model.state_dict()
device = torch.device('cuda')
ckpt = torch.load('./model_data/ssd_weights.pth',map_location=device)
ckpt = {k: v for k, v in ckpt.items() if np.shape(model_dict[k]) == np.shape(ckpt[k])}
model_dict.update(ckpt)
model.load_state_dict(model_dict)
model.eval()
model.to(device)
x = torch.zeros(1, 3, 300, 300).to(device)
output_names = ["output0","output1", "output2"]
input_names = ["images"]
torch.onnx.export(model, x, output_path, verbose=True, input_names=input_names,
                  output_names=output_names, do_constant_folding=True, opset_version=12)
if Simplity:
    onnx_model = onnx.load(output_path)  # load onnx model
    model_simp, check = simplify(onnx_model)
    assert check, "Simplified ONNX model could not be validated"
    onnx.save(model_simp, output_path)
    print('finished exporting onnx')

10.png

输出onnx部分图


5.ONNX推理


在推理前,你需要进入ssd.py中修改以下部分,将ONNX设置为True:


_defaults = {
    "model_path"    : 'model_data/ssd.onnx', # 权重路径
    "classes_path"  : 'model_data/voc_classes.txt',
    "confidence"    : 0.5,
    "nms_iou"       : 0.45,
    "cuda"          : True,
}


#---------------------------------------------------#
#   初始化SSD
#---------------------------------------------------#
def __init__(self, input_shape=None, ONNX=False, TRT=False, **kwargs):
    if input_shape is None:
        input_shape = [300, 300]  # 支持300和512大小
    self.input_shape = input_shape
    self.onnx = ONNX  # 是否开启onnx推理
    self.engine = TRT  # 是否开启trt推理
    self.__dict__.update(self._defaults)
    self.class_names = self._get_class()
    self.generate()

注意:输入大小以及要和你onnx、engine输入大小一致!【我这里没做动态输入】


然后运行predict.py即可。


ONNX推理这部分代码是参考了YOLOV5中的方式,代码如下:

import torch
import torch.nn as nn
import numpy as np
class DetectMultiBackend(nn.Module):
    def __init__(self, weights, device=torch.device('cpu'), fp16=False):
        super(DetectMultiBackend, self).__init__()
        cuda = torch.cuda.is_available()
        if weights.split('.')[-1] == "onnx":
            import onnxruntime
            providers = ['CUDAExecutionProvider', 'CPUExecutionProvider'] if cuda else ['CPUExecutionProvider']
            session = onnxruntime.InferenceSession(weights, None)
            output_names = [x.name for x in session.get_outputs()]
            print("Output_names: ", output_names)
            meta = session.get_modelmeta().custom_metadata_map  # metadata
        self.__dict__.update(locals())
    def forward(self, im):
        global y
        if self.weights.split('.')[-1] == 'onnx':
            im = im.cpu().numpy()
            y = self.session.run(self.output_names, {self.session.get_inputs()[0].name: im})
        if isinstance(y, (list, tuple)):  # 多输出
            return self.from_numpy(y[0]) if len(y) == 1 else [self.from_numpy(x) for x in y]
        else:
            return self.from_numpy(y)
    def from_numpy(self, x):
        return torch.from_numpy(x).to(self.device) if isinstance(x, np.ndarray) else x


推理结果

11.jpeg

6.engine推理


将ssd.py中的model_path路径设置为engine路径,如下:


_defaults = {
    "model_path"    : 'model_data/ssd.engine',
    "classes_path"  : 'model_data/voc_classes.txt',
    "confidence"    : 0.5,
    "nms_iou"       : 0.45,
    "cuda"          : True,
}

将TRT功能打开,设置为True,同时注意网络输入尺寸:


def __init__(self, input_shape=None, ONNX=False, TRT=True, **kwargs):
    if input_shape is None:
        input_shape = [300, 300]
    self.input_shape = input_shape
    self.onnx = ONNX
    self.engine = TRT
    self.__dict__.update(self._defaults)
    self.class_names = self._get_class()
    self.generate()


运行predict.py,输入图像路径进行推理:


10/26/2022-17:38:05] [TRT] [I] [MemUsageChange] Init CUDA: CPU +421, GPU +0, now: CPU 5526, GPU 896 (MiB)

[10/26/2022-17:38:07] [TRT] [I] Loaded engine size: 131 MiB

[10/26/2022-17:38:09] [TRT] [I] [MemUsageChange] Init cuBLAS/cuBLASLt: CPU +326, GPU +70, now: CPU 5998, GPU 1097 (MiB)

[10/26/2022-17:38:10] [TRT] [I] [MemUsageChange] Init cuDNN: CPU +108, GPU +88, now: CPU 6106, GPU 1185 (MiB)

[10/26/2022-17:38:10] [TRT] [I] [MemUsageChange] TensorRT-managed allocation in engine deserialization: CPU +0, GPU +131, now: CPU 0, GPU 131 (MiB)

[10/26/2022-17:38:10] [TRT] [I] [MemUsageChange] Init cuBLAS/cuBLASLt: CPU +0, GPU +10, now: CPU 5983, GPU 1177 (MiB)

[10/26/2022-17:38:10] [TRT] [I] [MemUsageChange] Init cuDNN: CPU +0, GPU +8, now: CPU 5983, GPU 1185 (MiB)

[10/26/2022-17:38:10] [TRT] [I] [MemUsageChange] TensorRT-managed allocation in IExecutionContext creation: CPU +0, GPU +168, now: CPU 0, GPU 299 (MiB)

model_data/ssd.engine model, anchors, and classes loaded.

Input image filename:


12.jpeg

如果是视频推理就运行video.py即可



7.FPS测试


如果是用torch模型进行测试,将FPS_test.py中的TORCH设置为True,然后在ssd.py中输入权重路径,然后运行FPS_test.py即可。


如果是用onnx或者engine模型进行测试,将FPS_test.py中的TORCH设置为False,然后在ssd.py中输入权重路径,然后运行FPS_test.py即可。


如果输入大小为300 * 300,在我的显卡上FPS如下,可以看到其实提升还是可以的,提升了十几帧:


NVIDIA 1650 4G

# engine:300*300 41FPS

# onnx:300*300 4FPS

# torch:300*300 28FPS


相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
目录
相关文章
|
1月前
|
编解码 缓存 计算机视觉
改进的yolov5目标检测-yolov5替换骨干网络-yolo剪枝(TensorRT及NCNN部署)-1
改进的yolov5目标检测-yolov5替换骨干网络-yolo剪枝(TensorRT及NCNN部署)-1
167 0
|
1月前
|
算法 PyTorch 计算机视觉
改进的yolov5目标检测-yolov5替换骨干网络-yolo剪枝(TensorRT及NCNN部署)-2
改进的yolov5目标检测-yolov5替换骨干网络-yolo剪枝(TensorRT及NCNN部署)-2
132 1
改进的yolov5目标检测-yolov5替换骨干网络-yolo剪枝(TensorRT及NCNN部署)-2
|
1月前
|
机器学习/深度学习 算法 计算机视觉
[YOLOv8/YOLOv7/YOLOv5系列算法改进NO.5]改进特征融合网络PANET为BIFPN(更新添加小目标检测层yaml)
本文介绍了改进YOLOv5以解决处理复杂背景时可能出现的错漏检问题。
145 5
|
2天前
|
机器学习/深度学习 算法 计算机视觉
没有公式,不要代码,让你理解 RCNN:目标检测中的区域卷积神经网络
没有公式,不要代码,让你理解 RCNN:目标检测中的区域卷积神经网络
5 0
没有公式,不要代码,让你理解 RCNN:目标检测中的区域卷积神经网络
|
30天前
|
机器学习/深度学习 编解码 算法
YOLOv8改进 | 主干网络 | 增加网络结构增强小目标检测能力【独家创新——附结构图】
YOLOv8在小目标检测上存在挑战,因卷积导致信息丢失。本文教程将原网络结构替换为更适合小目标检测的backbone,并提供结构图。通过讲解原理和手把手教学,指导如何修改代码,提供完整代码实现,适合新手实践。文章探讨了大特征图对小目标检测的重要性,如细节保留、定位精度、特征丰富度和上下文信息,并介绍了FPN等方法。YOLOv8流程包括预处理、特征提取、融合和检测。修改后的网络结构增加了上采样和concatenate步骤,以利用更大特征图检测小目标。完整代码和修改后的结构图可在文中链接获取。
|
1月前
|
计算机视觉
YOLOv5改进 | 主干篇 | 低照度图像增强网络SCINet改进黑暗目标检测(全网独家首发)
YOLOv5改进 | 主干篇 | 低照度图像增强网络SCINet改进黑暗目标检测(全网独家首发)
140 3
|
1月前
|
机器学习/深度学习 计算机视觉
YOLOv8改进 | 主干篇 | 低照度图像增强网络SCINet改进黑暗目标检测(全网独家首发)
YOLOv8改进 | 主干篇 | 低照度图像增强网络SCINet改进黑暗目标检测(全网独家首发)
171 2
|
1月前
|
机器学习/深度学习 测试技术 计算机视觉
YOLOv5改进 | 主干篇 | 低照度增强网络Retinexformer改进黑夜目标检测 (2023.11最新成果,全网独家首发)
YOLOv5改进 | 主干篇 | 低照度增强网络Retinexformer改进黑夜目标检测 (2023.11最新成果,全网独家首发)
96 0
|
1月前
|
机器学习/深度学习 测试技术 计算机视觉
YOLOv8改进 | 主干篇 | 低照度增强网络Retinexformer改进黑夜目标检测 (2023.11最新成果,独家首发)
YOLOv8改进 | 主干篇 | 低照度增强网络Retinexformer改进黑夜目标检测 (2023.11最新成果,独家首发)
166 0
|
1月前
|
消息中间件 Java Linux
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜
2024年最全BATJ真题突击:Java基础+JVM+分布式高并发+网络编程+Linux(1),2024年最新意外的惊喜

热门文章

最新文章