深度學習目標檢測系列論文閱讀和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代碼實現

發佈了12 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章