SSD: Single Shot MultiBox Detector 【論文筆記-檢測】
部落格作者:講究的洋芋
随着筆者對算法了解的加深,本文會持續更新。如有謬誤,歡迎指正。
研究動機
現有的目标檢測方法大概的思想是這樣的:
- 猜想bounding box (就是産生proposals);
- 對bbox區域的圖像或者特征抽樣(如Faster R-CNN中的RoI Pooling);
- 施加高品質分類器。
但是這種流程的檢測方法要求計算量太大,很難實時應用。例如比較快的Faster R-CNN,處理速度是 7FPS。
本文貢獻
重點就是更快。
本文第一次提出,不需要重采樣bbox 區域的像素(或特征)就可以實作利用深度網絡進行目标檢測,而且得到與已有方法相似的準确率。VOC2007上具體性能如下:
模型 | FPS | mAP |
---|---|---|
SSD | 59 | 74.3% |
Faster R-CNN | 7 | 73.2% |
YOLO | 45 | 63.4% |
在速度上的提升來源于:消除了BBox proposals 以及後續的特征(或像素)采樣步驟。
文中提到,他們不是第一個這麼做的,但是通過加入一系列改進,比過去的嘗試獲得了顯著的準确率提升,改進主要包括(我認為就是調參,對了解思想不重要):
- 使用更小的卷積核來預測目标種類和bbox位置的“補償”(offset);
- 對不同長寬比的檢測使用獨立的預測器(卷積核);
- 在多層特征圖上施加卷積,實作多尺度的目标檢測。
方法
簡單了解就是Faster R-CNN中的RPN的變體。差別就是:RPN隻判斷前景還是背景的二分類問題,然後将PRN得到的proposals進一步處理得到檢測結果;而SSD直接判斷 C + 1 C+1 C+1( C C C類目标+背景)以及相對anchors的位置補償。直接得到檢測結果而不是proposals。
首先看網絡結構,主幹網絡以VGG-16為例:
從relu5_3之前都是标準的VGG16網絡,然後将Pool5從原本的 k e r n e l = 2 kernel=2 kernel=2, s t r i d e = 2 stride=2 stride=2 改為 k e r n e l = 3 kernel=3 kernel=3, s t r i d e = 1 stride=1 stride=1 的MaxPooling層。然後在fc6、fc7改為卷積層conv6、conv7。在conv7後面接連續四個{ 1 × 1 1\times{}1 1×1、 3 × 3 3\times{}3 3×3 } 的卷積塊,得到conv8、conv9、conv10、conv11。四個卷積塊有不同的stride,實作下采樣,不贅述。
然後取conv4_3、conv7、conv8、conv9、conv10、conv11七個層的特征圖進行多尺度目标檢測。每個特征圖對應不同的anchor 大小。anchor比例除了conv4_3是 [ 1 , 0.5 , 2 ] [1, 0.5, 2] [1,0.5,2],其他都是 [ 1 , 0.5 , 2 , 0.33 , 3 ] [1, 0.5, 2, 0.33, 3] [1,0.5,2,0.33,3]。
SSD中anchor的生成和Faster R-CNN(以及Mask R-CNN)有很大的差別,這也是我一開始很困惑的地方:
-
Faster R-CNN和Mask R-CNN的anchor生成機制
Faster R-CNN先确定多個尺度,例如 [ 12 8 2 , 25 6 2 , 51 2 2 ] [128^2, 256^2, 512^2] [1282,2562,5122]這三個尺度;然後确定長寬比,例如 [ 1 , 0.5 , 2 ] [1, 0.5, 2] [1,0.5,2];最後,尺度和長寬比兩兩配對,生成共 3 × 3 3\times 3 3×3 個anchors。也就是說anchor的總數為:
N a n c h o r s = N r a t i o s ∗ N s i z e s N_{anchors}=N_{ratios} * N_{sizes} Nanchors=Nratios∗Nsizes
-
SSD的anchor生成機制
SSD先根據任務确定anchors的最大和最小尺度,如: s m a x = 0.9 , s m i n = 0.2 s_{max}=0.9, s_{min}=0.2 smax=0.9,smin=0.2;conv4_3、conv7、conv8、conv9、conv10、conv11共 m = 7 m=7 m=7 個特征圖 ,通過以下公式确定每個feature map 對應的尺度:
s k = s m i n + s m a x − s m i n m − 1 ( k − 1 ) , s_k = s_{min} + \frac{s_{max}-s_{min}}{m-1}(k-1), sk=smin+m−1smax−smin(k−1),
論文中分母是 m − 1 m-1 m−1,代碼中實作的是 m − 2 m-2 m−2,先得到後面六層的尺度 [ 0.2 , 0.34 , 0.48 , 0.62 , 0.76 , 0.9 ] [0.2, 0.34, 0.48, 0.62, 0.76, 0.9] [0.2,0.34,0.48,0.62,0.76,0.9],然後加入conv4_3層對應的尺度 s m i n / 2 s_{min} / 2 smin/2,就有了 [ 0.1 , 0.2 , 0.34 , 0.48 , 0.62 , 0.76 , 0.9 ] [0.1, 0.2, 0.34, 0.48, 0.62, 0.76, 0.9] [0.1,0.2,0.34,0.48,0.62,0.76,0.9],文章和代碼都沒有解釋為什麼這麼做。
每個尺度生成不同長寬比( [ 1 , 0.5 , 2 , 0.33 , 3 ] [1, 0.5, 2, 0.33, 3] [1,0.5,2,0.33,3])的anchors,共 N r a t i o s = 5 N_{ratios}=5 Nratios=5個。然後再加入一個尺度為 s k s k + 1 \sqrt{s_ks_{k+1}} sksk+1
,比例為1的anchor1個。總共6個anchor。
我看的是Mxnet的SSD實作,用Mxnet的函數mx.contrib.symbol.MultiBoxPrior()函數來生成每個特征圖對應的anchors,以conv4_3為例:
#這隻是個示意代碼
achors = mx.contrib.symbol.MultiBoxPrior(conv4_3, size=[0.1,0.14],
ratios=[1, 0.5, 2, 0.33, 3]
step = 0.125)
我特意查了下這個函數的底層實作,基本流程時這樣的:
- 不管輸入的ratios參數什麼樣,固定ratio=1,生成各種sizes的anchors,共 N s i z e s N_{sizes} Nsizes個;
- 固定size=sizes[0],生成除ratios[0]以外不同比例的anchors,共 N r a t i o s − 1 N_{ratios}-1 Nratios−1個。
- 是以,隻有sizes[0]有各種比例的anchors,其他尺寸隻有正方形框,總共anchor數為: N a n c h o r s = N r a t i o s + N s i z e s − 1 N_{anchors}=N_{ratios} + N_{sizes}-1 Nanchors=Nratios+Nsizes−1
我困惑的是:Mxnet這種通用的深度學習架構,為什麼有這樣特别的生成anchor的方式?是SSD參考mxnet,還是反過來? 歡迎高手指點!
最後訓練的代價函數以及實驗結果有空再寫吧~