之前在簡書的文章,搬遷過來 ^-^
先放大神的論文和原始碼鎮樓:
SSD Github: https://github.com/weiliu89/caffe
請選擇分支 SSD
SSD paper: arxiv.org/abs/1512.02…
對於SSD來說,最有新意的就是它的多尺度特徵,而整個程式碼中調整頻度最高的應該是它的Prior_box,我們就從這些方面來分享一下我自己的理解。
##多尺度 先說一下多尺度特徵。在之前的Faster-RCNN中,特徵向量都是從最後一層的Feature Maps上得到的,對於這種單一的特徵層而言,感受野是十分有限的,沒有完全利用好前面幾級的特徵網路。在SSD中,作者從CONV4_3開始,利用多級Feature Maps的組合作為分類和迴歸的依據,達到了論文中提到的多尺度的效果。 借用論文中的一張圖來說明,作者是拿YOLO和SSD做的對比:
可以看出SSD 的特徵是從不同的卷積層提取出來(上圖紅線),進行組合再進行迴歸和分類,而YOLO只有一層,在YOLO之後的版本中也借鑑了 SSD的這種多尺度的思想來提高mAp。也就是說,SSD就是Faster-RCNN和YOLO中做了一次的分類和檢測過程放在不同的影像大小上做了多次。
##Prior_box
知道了SSD的特徵是從不同尺度上提取的,那麼論文中所說的8732 BOXES又是怎麼來的呢?用下面這張表來告訴你。
name | Out_size | Prior_box_num | Total_num |
---|---|---|---|
conv4-3 | 38x38 | 4 | 5776 |
fc7 | 19x19 | 6 | 2166 |
conv5-2 | 10x10 | 6 | 600 |
conv7-2 | 5x5 | 6 | 150 |
conv8-2 | 3x3 | 6 | 36 |
conv9-2 | 1x1 | 4 | 4 |
8732 |
和 Faster-RCNN一樣,SSD也是特徵圖上的每一個點對應一組預選框。然後每一層中每一個點對應的prior box的個數,是由PriorBox這一層的配置檔案決定的。拿conv4-3對應的priorbox來說,caffe的模型配置檔案如下:
那麼SSD是怎麼生成對應的四個priorbox的呢? 框的生成過程大概分為下面三種方式:
-
先以 min_size為寬高生成一個框。
-
如果存在max_size則用sqrt(min_size_ * max_size_),生成一個框。
-
然後根據 aspect_ratio,再去生成。如上面的配置檔案,aspect_ratio=2,那麼會自動的再新增一個aspect_ratiod = 1/2,然後根據下面的計算方法:
分別生成兩個框,一個對應 ar = 2 一個對應 ar= 1/2。
直觀點說,就是min_size和max_size會分別生成一個正方形的框,aspect_ratio引數會生成2個長方形的框。所以輸出框的個數 :
prior_box_num = count(min_size)*1+count(max_size)*1+count(aspect_ratio)*2。
PS: min_size是必須要有的引數,否則不會進入對應的框的生成過程。論文跟實際程式碼是有一些出入的,Git上也有人在討論這個,基本都選擇無視論文。。。
這裡還有一個比較關鍵的引數,就是step,在conv4-3中設定為8,這個又是怎麼來的呢?還是用一個表來看一下:
name | Out_size | Cal_scale | Real_scale |
---|---|---|---|
conv4-3 | 38x38 | 7.8 | 8 |
fc7 | 19x19 | 15.78 | 16 |
conv5-2 | 10x10 | 30 | 32 |
conv7-2 | 5x5 | 60 | 64 |
conv8-2 | 3x3 | 100 | 100 |
conv9-2 | 1x1 | 300 | 300 |
Cal_scale = 300/out_size 實際就是 原圖與特徵圖 大小的比值,比如conv4-3 width = 38 ,輸入的大小為300,那麼scale=7.8,所以這裡設定的step=8。程式碼中實現如下:
這一部分的計算過程可以在 prior_box_layer.cpp的Forward_cpu中看到。
##特徵的表出形式 如果你看了SSD的網路結構會發現,每一個 convXXXX_mbox_loc 或者 convXXXX_mbox_conf後面都會跟一個permute+flatten layer,如下圖:
這是在幹什麼呢? 使用CAFFE的同學都知道 ,CAFFE的資料結構是 NCHW的形式(N:樣本個數, C:通道數,H:高,W:寬),而SSD的 XX_conf 和 XX_loc層的輸出,是用通道來儲存特徵向量的,所以這裡需要將通道數調整到最後,也就是 permute所做的事情,通過該層後,資料的順序被換成了 NHWC,再通過 flatten拉成一列。
這裡還有還是要說一下 XX_LOC 和 XX_CONF 層的輸出通道的規則,XX_LOC層是用來回歸框的,所以需要4個座標資訊,而XX_CONF是用來做分類的,所以需要class_num個資訊,同時每個點會有多個prior_box ,我們令 K = count(prior_box),那麼相應的XX_LOC的輸出的通道個數應為4*K,而XX_CONF的輸出通道個數應為 class_num*K,作為驗證,我們還是看一下針對於VOC的模型的引數設定,
還是看conv4_3,這一層對應了4個prior_box ,VOC的分類個數是21(20個分類+1個背景),所以對應的conv4_3_norm_mbox_loc 的num output = 16 = 4*4 ,而 conf的 num_output = 84 = 21*4。所以,如果針對的是自己的訓練集,一定要記著修改 XX_CONF的輸出通道數。
其實簡單點理解,就是SSD的最後幾層的輸出資訊都是儲存在Channel這一維度的,而一個LOC+CONF+PRIOR的模組可以認為等效於一個 Faster-rcnn的最後的迴歸+分類過程,通過將這些子模組的特徵拼接起來,得到一組特徵向量,達到提取多尺度特徵的目的(多個F-RCNN同時工作於同一圖片的不同尺度上)。
#####再看一下為什麼一個特徵點要對應幾個prior_box。 原圖中的某一個片區域,在經過幾層的提取後,會抽象成特徵圖上的一個點,那麼多對於多個prior_box而言,他們對應的都是同一組資訊,那麼多個prior_box的意義是什麼呢?看下圖:
該圖只是示意作用,我假設某一層的特徵輸出中的一個點,在原圖中的感受野剛好是上圖左上角的區域,可以看出卡片遮擋了筆的部分特徵(橙色和藍色的框是我標註上去的,2根藍線是示意作用,可以忽略),如果沒有多個prioro_box的時候,這種場景就無法正確分類,要麼認為是卡片,要麼認為是筆。這時候多個Prior_box的價值就來了,因為多個框都會輸出自己的座標迴歸和分類,它們會去關注自己對應的特徵,然後不同的框給出不同的分類得分,個人覺得有點類似於一個Attention的結構。
##最後還是要提一下SSD的資料增強 SSD的資料增強有很多,隨機的剪裁,放縮,亮度,飽和度的調整,等等。引數也基本是見名知意的,所以最好自己跟著程式碼看一下比較有效。如果自己需要做資料增強不妨學習一下他的用法。 這裡推薦一個 GIT: https://github.com/eric612/MobileNet-SSD-windows
這個GIT的SSD版本是可以在 WINDOWS上跑的,這樣就能用宇宙最強IDE——VS一步一步的跟著看圖片的變化了。