深度学习目标检测系列论文阅读和Pytorch实现(二)——SSD论文阅读

比较老的论文,但是是one stage的代表。

  • 主要贡献

1)提出SSD检测架构。具有比YOLO更快更准的单阶段检测器,具有和较慢的Faster RCNN一样的准确率。

2)SSD的核心思想是使用一组固定的default bounding box通过小的卷积核作用于特征图上对类别和边框的偏移(offset)进行预测。

3)为了获得高的检测准确率,在不同尺度特征图上进行预测。

4)简单的端到端(end to end)训练并获得高的准确率,提升了speed和accuracy两者的折中(trade-off)。

5)在PASCAL VOC,COCO和ILSVRC数据集上和近期的state of the art算法作出了timing和accuracy的对比。

  • SSD架构

  • Multi-scale feature maps for detection

    在截断的主干网络后添加卷积层。不同于YOLO、FasterRCNN等,这些网络仅仅使用了最后一层特征图进行预测,SSD充分利用了主干网络提取特征形成的多尺度卷积特征图,在不同特征图上分别预测。

论文给出SSD300结构如下:

使用VGG16的卷积层作为骨干网络提取特征并且在多尺度特征图上进行预测。

为了细致分析其结构,首先看VGG16网络结构:

我们使用上图D结构的VGG16的卷积层修改进行特征提取,网络结构如下:

idxoperationfeature map sizeprediction
0Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))300 
1ReLU(inplace=True)  
2Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
3ReLU(inplace=True),  
4MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)150 
5Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
6ReLU(inplace=True)  
7Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
8ReLU(inplace=True)  
9MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)75 
10Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
11ReLU(inplace=True),  
12Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
13ReLU(inplace=True)  
14Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
15ReLU(inplace=True)  
16MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=True)38 
17Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
18ReLU(inplace=True)  
19Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1),padding=(1, 1))  
20ReLU(inplace=True)  
21Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) Y
22ReLU(inplace=True),  
23MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)19 
24Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
25ReLU(inplace=True),  
26Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
27ReLU(inplace=True),  
28Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))  
29ReLU(inplace=True),  
30MaxPool2d(kernel_size=3, stride=1, padding=1, dilation=1, ceil_mode=False)  
31Conv2d(512, 1024, kernel_size=(3, 3), stride=(1, 1), padding=(6, 6), dilation=(6, 6))  
32ReLU(inplace=True)  
33Conv2d(1024, 1024, kernel_size=(1, 1), stride=(1, 1)) Y
34ReLU(inplace=True)19 

在上面的基础上,SSD增加了额外层进行特征提取和预测,增加的额外层结构如下:

Conv8_1Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1))19 
Conv8_2Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))10Y
Conv9_1Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1))  
Conv9_2Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2),padding=(1, 1))5Y
Conv10_1Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))  
Conv10_2Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1))3Y
Conv11_1Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))  
Conv11_2Conv2d(128, 256, kernel_size=(3, 3),stride=(1, 1))1

Y

【注意】论文中的SSD添加的额外层画的和具体实现有所不同,我们看一下作者在自己的Caffe源码上的实现:

# Add extra layers on top of a "base" network (e.g. VGGNet or Inception).
def AddExtraLayers(net, use_batchnorm=True, lr_mult=1):
    use_relu = True

    # Add additional convolutional layers.
    # 19 x 19
    from_layer = net.keys()[-1]

    # TODO(weiliu89): Construct the name using the last layer to avoid duplication.
    # 10 x 10
    out_layer = "conv6_1"
    ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 256, 1, 0, 1,
        lr_mult=lr_mult)

    from_layer = out_layer
    out_layer = "conv6_2"
    ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 512, 3, 1, 2,
        lr_mult=lr_mult)

    # 5 x 5
    from_layer = out_layer
    out_layer = "conv7_1"
    ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 128, 1, 0, 1,
      lr_mult=lr_mult)

    from_layer = out_layer
    out_layer = "conv7_2"
    ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 256, 3, 1, 2,
      lr_mult=lr_mult)

    # 3 x 3
    from_layer = out_layer
    out_layer = "conv8_1"
    ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 128, 1, 0, 1,
      lr_mult=lr_mult)

    from_layer = out_layer
    out_layer = "conv8_2"
    ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 256, 3, 0, 1,
      lr_mult=lr_mult)

    # 1 x 1
    from_layer = out_layer
    out_layer = "conv9_1"
    ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 128, 1, 0, 1,
      lr_mult=lr_mult)

    from_layer = out_layer
    out_layer = "conv9_2"
    ConvBNLayer(net, from_layer, out_layer, use_batchnorm, use_relu, 256, 3, 0, 1,
      lr_mult=lr_mult)

    return net

[注意]可以看到在代码中的"conv8_2"、"conv9_2"实际上是没有做stride=2,pading=1的卷积的,这一点和论文结构图下方的文字相符:

事实上,Conv10_2和Conv11_2的卷积操并没有进行跨步stride=2,应该都是5x5x256。猜测特征图太小可能并不利于检测,因为损失的细节可能比较多,毕竟在YOLO v1和FasterRCNN中最后都是7x7的特征图,这里虽然最后两个预测特征图都是5x5,但是通过不同scale的default box依然可以检测不同尺度的目标。真是情况可能还是以实验结果为准,这里我们还是以caffe源码为准。不过值得一提是,在SSD512中,最后的一层预测特征图是1x1x256.

具体细节可以参看原论文。

这里我们可以修改VGG主干网络成19年出的EfficientNet。

EfficientNet-b4和Vgg16具有相当的参数:

modelTop-1Acc. Top-5Acc.#Params
VGG-1671.93% 90.67%14.7M
ResNet-5076.0% 93.0%26M
EfficientNet-B483.0% 96.3%19M

其中VGG16我们仅仅计算卷积层参数大约:138M - [(1000×4096+1000)+ (4096×4096+4096)+(224/2^5)^2 *512 *4096]/10^6=14.7M

借用EfficientDet论文中的图,可以看到P3相当于SSD300中Conv4_3。鉴于EfficientNet本身结构比较深,上图一直到P5都是EfficientNet主干网络。仿照SSD300结构,我们还是在最后添加类似Conv9-Conv11共6层,保持SSD300结构一致性。

下一章节介绍SSD的Pytorch代码实现

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值