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

2.修改box解码

3.获得输出

4.转onnx[可以直接从这部分看]

5.ONNX推理

6.engine推理

 7.FPS测试


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')

输出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

 

 推理结果


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:

如果是视频推理就运行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

 详细使用请看readme.md

代码: 

https://github.com/YINYIPENG-EN/SSD_tensorRT_pytorch.git

权重:

链接:https://pan.baidu.com/s/1YAF_cruDG254ZZD5InPsbg 
提取码:yypn

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
SSD(Single Shot MultiBox Detector)是一种目标检测算法,其主要思想是将目标检测任务化为一个回归问题和一个分类问题,并通过一个单一的CNN网络同时解决这两个问题。 SSD代码实现主要包括以下几个步骤: 1. 数据准备:首先需要准备用于目标检测的数据集,包括训练集和测试集。每个图像需要标注目标的位置和类别,并化为特定的数据格式,如VOC或COCO格式。 2. 构建模型:使用深度学习框架(如TensorFlow、PyTorch等)创建SSD模型。SSD模型由一个基础的卷积神经网络(如VGG、ResNet等)和几个额外的卷积层和预测层组成。 3. 数据预处理:对输入的图像进行预处理,使其适应SSD模型的输入要求。预处理包括图像的缩放、裁剪、归一化等操作。 4. 模型训练:使用训练集对SSD模型进行训练。训练过程中主要包括前向传播(计算损失函数)和反向传播(更新模型参数)。 5. 目标检测:使用训练好的SSD模型对测试集或新的图像进行目标检测。首先对图像进行预处理,然后通过前向传播计算预测框和类别得分。根据设定的阈值和非极大值抑制算法,筛选出预测框中得分较高的目标,并抑制重叠的框。 6. 评估性能:使用评价指标(如精确率、召回率、平均精确率均值mAP等)对SSD模型的性能进行评估,衡量其目标检测的准确性和鲁棒性。 7. 模型优化:根据评估结果,对SSD模型进行优化,如调整超参数、更改网络结构、引入数据增强等方法,提升模型的性能。 综上所述,SSD目标检测代码的实现主要包括数据准备、模型构建、数据预处理、模型训练、目标检测、性能评估和模型优化等步骤。通过这些步骤,可以实现一个高效准确的SSD目标检测系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱吃肉的鹏

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

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

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

打赏作者

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

抵扣说明:

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

余额充值