思想

其實出發點我覺得不難想到,主要基於以下幾點設計出來了SSD的結構:

  1. 相對於Faster RCNN家族的兩階段檢測,想一個網路最後直接出兩個head,一個算bbox們的分類loss,一個算bbox相對於標準位置(也就是所謂anchor)的位置偏差loss。而不是像Faster RNN那樣RPN算一次loss(分類回歸都要算),ROI Pooling後還要再算一次loss。因此提出一階段模型。
  2. 既然希望只算bbox相對於標準位置上anchor的一次偏差loss,就需要定義標準anchor的(x0,y0,w,h),也就自然想到用Faster RCNN的anchor,即從feature map上每個位置出n個不同scale不同aspect ratio的anchor。但faster rcnn是將每個scale和每個aspect ratio都組合,比如3x3=9個anchors,每個位置會出9個anchor會顯得太冗餘了。因此ssd在anchor設定這塊減少了數量。
  3. 既然一個網路就要學到bbox位置偏移量,如果是faster rcnn那類只在一種scale上的feature map想檢測出各種大小的ground truth框有點困難了,引入類似FPN的結構,在淺層和深層等不同大小的feature map上都進行預測,這樣能利用不同大小的感受野,檢測到原圖上大小尺寸不同的物體。

這張圖能把我說的第2,3點講清楚了。

原圖(a)中有一個較小的目標(籃框),和一個較大的目標(紅框)。

原圖經過多層卷積後,可以在某層上得到8x8的feature map,即圖(b),也可以在稍後面的層得到4x4的feature map,即圖(c)。

針對圖(b),每個位置上出4(或6)個,圖中是出4個不同scale和aspect ratio的anchors。比如ratio_anchor=2x2,也就是在8x8和4x4兩種feature map上anchor size是相同的,都是2x2,那麼由於8x8層對應的感受野比4x4的小,因此2x2的anchor對應到原圖上則是較小的一塊區域。那麼,原圖中較大的目標(紅框表示的物體)在8x8 feature map很可能沒有anchor能檢測到它,但藍框物體是可以在8x8 feature map上檢測到的,即圖(b)中的2個藍虛線框所表示的anchors。對應的,4x4 feature map的感受野更大,它上面就可能有對應的anchor能檢測到原圖中較大物體,即圖(c)中的紅虛線框所表示的anchor。這樣一來,原圖中一大一小兩個物體就在不同scale的feature map上被檢測出來了。

這就是為什麼SSD要利用「直筒」網路的不同scale的feature maps,每個都出classifier head和regression head來做檢測的原因。

Faster RCNN結構
SSD結構

backbone

原文中的圖例使用的是VGG。

先看原始的VGG網路結構,如下圖:

原始VGG

作為SSD的backbone時,只用到了conv5以前的層。pool5開始到後面幾個FC層都去掉了。

去掉原始的一些層之後,作者又加入了新層,如下圖所示。

conv5-3之後的三層:pool5,conv6和conv7都是新增的。這裡pool5的stride=1並沒有做downsampling,保持了conv5-3的output size。

按論文中作者設定的輸入圖片大小為(B,3,300,300),那麼conv5-3之後得到(B,512,19,19),pool5後仍然為(B,512,19,19)。

pool5後接了一個3x3帶dilation的卷積層conv6,又接一個1x1的conv7。這兩層的輸出都相同,為(B,1024,19,19)。

  1. 空洞卷積的作用是擴大感受野。為了能不丟失解析度,且仍然擴大感受野,可以使用空洞卷積。在檢測、分割任務中十分有用。一方面感受野大了可以檢測分割大目標(對conv7及其之後的層有幫助),另一方面解析度高了可以精確定位目標。
  2. 1x1卷積在這裡的作用是進一步增加非線性。

extra layers

這些層為了逐步得到multi scale的feature而存在。具體請看下圖:

從conv8到conv11共四層,出4種不同scale的feature map。

conv8-1的輸入也就是conv7的輸出,shape=[B,1024,19,19]

-------------------------------------------------------------------------------------------

經過conv8-1,ksize=1,filter num=256,輸出[B,256,19,19](1x1降低channel數)

經過conv8-2,ksize=3,filter num=512,stride=2,padding=1,輸出[B,512,10,10]

-------------------------------------------------------------------------------------------

經過conv9-1,ksize=1,filter num=128,輸出[B,128,10,10] (1x1降低channel數)

經過conv9-2,ksize=3,filter num=256,stride=2,padding=1,輸出[B,256,5,5]

-------------------------------------------------------------------------------------------

經過conv10-1,ksize=1,filter num=128,輸出[B,128,5,5]

經過conv10-2,ksize=3,filter num=256,stride=1,輸出[B,256,3,3]

-------------------------------------------------------------------------------------------

經過conv11-1,ksize=1,filter num=128,輸出[B,128,3,3]

經過conv11-2,ksize=3,filter num=256,stride=1,輸出[B,256,1,1]

-------------------------------------------------------------------------------------------

以上就得到了extra feature layers,對應於原論文中圖例的結構。

multibox

從上一步得到[B,512,10,10],[B,256,5,5],[B,256,3,3],[B,256,1,1]四種feature map,再加上conv4-3[B,512,38,38]和conv7[B,1024,19,19]兩層較淺的特徵(為了檢測小目標)。

這六個feature map,每個位置上都要出4或6個anchors,每個anchor要輸出n_classes個prob(cls_head),和4個bbox偏移量(reg_head)。

假如anchor_num=4, cls_head的卷積層out_channel=(anchor_num)x(n_classes),reg_head的卷積層out_channel=(anchor_num)x4。這些卷積的kernel size和padding都相同,ksize=3,padding=1。最終得到:

cls_head = [B, (anchor_num)x(n_classes), 38/19/10/5/3/1, 38/19/10/5/3/1]

reg_head = [B, (anchor_num)x4, 38/19/10/5/3/1, 38/19/10/5/3/1]

multi_box_cfg = [4,6,6,6,4,4],也就是這6個feature map上每個位置放置的anchor數不同的。有的層每個位置放4個anchor,有的feature map的每個位置放6個anchor。

那麼,總的detection bbox數=38x38x4+19x19x6+10x10x6+5x5x6+3x3x4+1x1x4=8732

參考代碼

https://github.com/amdegroot/ssd.pytorch/blob/master/ssd.py?

github.com


推薦閱讀:

相关文章