深度学习目标检测系列论文阅读和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的卷积层修改进行特征提取,网络结构如下:

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

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

Conv8_1 Conv2d(1024, 256, kernel_size=(1, 1), stride=(1, 1)) 19  
Conv8_2 Conv2d(256, 512, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)) 10 Y
Conv9_1 Conv2d(512, 128, kernel_size=(1, 1), stride=(1, 1))    
Conv9_2 Conv2d(128, 256, kernel_size=(3, 3), stride=(2, 2),padding=(1, 1)) 5 Y
Conv10_1 Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))    
Conv10_2 Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1)) 3 Y
Conv11_1 Conv2d(256, 128, kernel_size=(1, 1), stride=(1, 1))    
Conv11_2 Conv2d(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具有相当的参数:

model Top-1Acc. Top-5Acc. #Params
VGG-16 71.93% 90.67% 14.7M
ResNet-50 76.0% 93.0% 26M
EfficientNet-B4 83.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代码实现

Guess you like

Origin blog.csdn.net/IEEE_FELLOW/article/details/104241995