目标检测---SSD

参考自SSD算法理论
Pytorch搭建孪生神经网络
先一点点记录零碎的东西,后面积累到一定量了再总结一下
在这里插入图片描述
在这里插入图片描述

第一个预测特征层,scale为21的尺度,他会有1:1,2:1,1:2这三个比例,对于根号下2145的scale,只有1:1的比例,所以一共有4个default boxes.
第二个预测特征层,scale为45的尺度,他会有1:1,2:1,1:2,3:1,1:3这5个比例,对于根号下45
99的scale,只有1:1的比例,所以一共有6个default boxes.
其余的预测特征层是一样的道理。
在这里插入图片描述
在这6个特征图上,总共会生成8732个default boxes.
在这里插入图片描述选取了6个feature map中的两个,feature map1对应4个default boxes ,其中1:1为中间黄色框,1:2和2:1为中间两个蓝色框,外面那个黄色框为根号下21 * 45,比例为1:1.
feature map4对应6个default boxes.其中1:1为中间黄色框,1:2和2:1为中间两个蓝色框,1:3和3:1为中间两个红色框。外面那个黄色框为根号下153*207,比例为1:1.
如果把个feature map全部绘制在图片上,那么基本可以覆盖图片上所有出现的目标了。
在这里插入图片描述对于每个给定的位置,会生成k个default box,对于每个default box 会计算c个类别分数和4个坐标偏移量。那么就需要(c + 4) * k个卷积核进行卷积处理,那么对于一个m * n的feature map而言,就会生成一个(c + 4) * k * m * n个输出值。
在这里插入图片描述

其中,c* k个值用来预测目标类别,4 * k个值用来预测边界框回归参数。
这里的c包括背景类别,也就是会预测背景的概率的。
每个default box对应计算4个偏移量:中心坐标x,y的偏移量,w,h宽度和高度的偏移量。
在这里插入图片描述训练模型时,
正样本选取的两个准则:
1、对于每个ground truth box,去匹配与它IOU值最大的那个default box
2、对于任意的default box,它只要与任意的一个ground truth的IOU值大于0.5,那么也认为它是正样本。
在这里插入图片描述
负样本的选取。
如果把匹配剩下的所有default box当成负样本,那么负样本的数量也太大了。这样就会造成样本数量不平衡问题。我们的措施:计算每个default box的highest confidence loss,并且按照从高到底进行排序。
为什么计算confidence loss:这个值越大,那么网络将这个负样本预测为目标的概率会更大,这是我们所不能容忍的。
于是,计算出confidence loss的值后,取一些confidence loss最大的样本作为负样本。具体取多少呢?使得总体负样本比正样本的比例为3:1即可。
这种负样本的选取方法在论文中称为Hard negative mining
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
SSD框架大概:
输入是一个300 * 300的图片,先通过一个VGG16的前置网络部分,通过Conv5_3layer后,还有一个pooling层,但是这个pooling层由原来的池化核大小2 * 2步距为2,变为池化核大小3 * 3步距为1.这样他是不会改变我们特征矩阵的大小的,后面又接了一系列的卷积层,见上图的下面部分。
通过上面的卷积能够得到我们的6个特征层(上图已经标注了),然后分别在这6个特征层上去预测不同尺度以及比例的目标,预测完之后再通过非极大值抑制算法,以及,滤除我们小概率的目标,就能够得到最终的预测结果。

代码的原理图:
在这里插入图片描述

在这里插入图片描述

SSD算法会在这6个特征层上进行预测。
继续补充 下面参考自Pytorch 搭建自己的SSD目标检测平台
在这里插入图片描述
在这里插入图片描述
因为经多次卷积池化后,小物体的特征容易消失,所以这里采用不同尺寸的网格(越细密的网格可以延缓小物体特征消失的现象,结合一下池化均一代表就好理解一些)
一般来讲,38 * 38和1919的网格用来检测小物体,55,33,11的网格用来检测大的物体。
在这里插入图片描述在这里插入图片描述
对于SSD目标检测网络来讲,它的输入图片shape是https://github.com/bubbliiiing/ssd-pytorch/archive/refs/heads/master.zip300 * 300 * 3 的。所以我们输入一张图片的时候,需要对它resize一下。另外,这里用到了VGG用来特征提取,但是对VGG进行了一些改变,后面还增加了4个卷积层。通过这样一个主干特征提取网络,SSD可以对输入的300 * 300的图片进行不断的特征提取(提取了6个特征层),利用这些特征层就可以进行下一步的操作,获得框的位置了。
在这里插入图片描述
上面的特征层,比如第一个特征层38 * 38 ,就是把图片分割成38 * 38 的网格,然后这样对图片进行目标检测。
在这里插入图片描述
为啥要分割成那么多的网格呢?因为以每个网格为中心,会生成许多的先验框(那么就是每个网格都对应几个先验框,整张图来看就是密密麻麻的先验框),后面对这些先验框进行调整选择,最后真正选中的只有几个。
上数第三张图中(懒得标序号了,就这么叫吧),def:boxes后面对应的数字就是每个网格所对应的先验框的个数,所以第一个特征层每个网格对应4个先验框,那么第一个特征层对应38 * 38 * 4个先验框,其它特征层也是同理,最后六个特征层加起来,总共是8732个先验框。那么我们就相当于调整了8732个先验框,然后判断这调整的8732个先验框里面有没有我们需要的物体,如果有的话我们就把它标出来。当然我们得到的框有一些是重合的,那么我们就需要对这些框的得分和重合情况进行判断,利用非极大抑制的方法,找到我们需要的框,并且标出它们所属的种类。这就是SSD算法的检测过程。

这里用到了VGG作为backbone,下面的代码进行到了到这里,得到了19 * 19 * 1024的特征层:
在这里插入图片描述

代码:

import torch.nn as nn

base = [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'C', 512, 512, 512, 'M',
            512, 512, 512]

'''
该代码用于获得VGG主干特征提取网络的输出。
输入变量i代表的是输入图片的通道数,通常为3。

一般来讲,输入图像为(300, 300, 3),随着base的循环,特征层变化如下:
300,300,3 -> 300,300,64 -> 300,300,64 -> 150,150,64 -> 150,150,128 -> 150,150,128 -> 75,75,128 -> 75,75,256 -> 75,75,256 -> 75,75,256 
-> 38,38,256 -> 38,38,512 -> 38,38,512 -> 38,38,512 -> 19,19,512 ->  19,19,512 ->  19,19,512 -> 19,19,512
到base结束,我们获得了一个19,19,512的特征层

之后进行pool5、conv6、conv7。
'''
def vgg(i):
    layers = []
    in_channels = i
    for v in base:
        if v == 'M':
            layers += [nn.MaxPool2d(kernel_size=2, stride=2)]
        elif v == 'C':
            layers += [nn.MaxPool2d(kernel_size=2, stride=2, ceil_mode=True)]
        else:
            conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1)
            layers += [conv2d, nn.ReLU(inplace=True)]
            in_channels = v
    pool5 = nn.MaxPool2d(kernel_size=3, stride=1, padding=1)
    conv6 = nn.Conv2d(512, 1024, kernel_size=3, padding=6, dilation=6)
    conv7 = nn.Conv2d(1024, 1024, kernel_size=1)
    layers += [pool5, conv6,
               nn.ReLU(inplace=True), conv7, nn.ReLU(inplace=True)]
    return layers

i是通道数,一般等于3,M和C是加入最大池化层,C模式下,ceil_mode = True 是指池化的时候,把不足的边给保留下来,单独另算,这样会增加池化后的特征层尺度:
在这里插入图片描述在这里插入图片描述

数字是经过卷积后的通道数。
在这里插入图片描述
上图,红色框代表最大池化层,白色框代表卷积层。

在这里插入图片描述而从第三个特征层到最后特征层的实现,是由ssd.py中的下面的代码完成的:

def add_extras(i, backbone_name):
    layers = []
    in_channels = i
    
    if backbone_name=='vgg':
        # Block 6
        # 19,19,1024 -> 10,10,512
        layers += [nn.Conv2d(in_channels, 256, kernel_size=1, stride=1)]
        layers += [nn.Conv2d(256, 512, kernel_size=3, stride=2, padding=1)]

        # Block 7
        # 10,10,512 -> 5,5,256
        layers += [nn.Conv2d(512, 128, kernel_size=1, stride=1)]
        layers += [nn.Conv2d(128, 256, kernel_size=3, stride=2, padding=1)]

        # Block 8
        # 5,5,256 -> 3,3,256
        layers += [nn.Conv2d(256, 128, kernel_size=1, stride=1)]
        layers += [nn.Conv2d(128, 256, kernel_size=3, stride=1)]
        
        # Block 9
        # 3,3,256 -> 1,1,256
        layers += [nn.Conv2d(256, 128, kernel_size=1, stride=1)]
        layers += [nn.Conv2d(128, 256, kernel_size=3, stride=1)]

这里之所以判断是不是VGG,是因为代码中除了提供VGG作为主干特征提取网络外,还添加了mobilenetv2作为ssd的主干特征提取网络,作为轻量级ssd的实现,可通过设置train.py和ssd.py中的backbone进行主干变换。
在这里插入图片描述
而num_priors指的是该特征层每一个特征点所拥有的先验框数量。上述提到的六个特征层,每个特征层的每个特征点对应的先验框数量分别为4、6、6、6、4、4。
回归预测的通道数:num_priors * 4代表每个先验框的调整参数
分类预测的通道数:num_priors * num_classes代表对每个先验框预测的种类(种类包括背景,数量+1)
如果预测某个类的概率很高,那么这个先验框的内部很可能包含物体。
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值