1. 程式人生 > >SSD詳解 + default box生成過程

SSD詳解 + default box生成過程

在mxnet上面看李沐大神的視訊,自己看了SSD的paper裡面還是有些一知半解的東西,於是就用篇部落格記錄下來。文章中的圖和部分見解都來自於網路有些錯誤的圖已經修正,如有侵權,聯絡我刪除。

先放一張SSD演算法的模型圖。SSD採用不用卷積層的feature map進行綜合,將VGG16的最後兩層全連線改為卷積層並額外增加四個卷積層來達到構造網路。對於這5個不同尺度的卷積層輸出的feature map,作者分別採用不同的3x3的卷積核進行卷積,一個輸出分類的置信度(condfidence),一個輸出迴歸的localization。下面先放一張作者構造的程式碼:


作者文中多次提到的default boxes和aspect ratios,那麼他們是什麼意思呢?其實就是每一個box相對於其對應的 feature map cell 的位置是固定的。 在每一個 feature map cell 中,我們要 predict 得到的 box 與 default box 之間的 offsets,以及每一個 box 中包含物體的 score(每一個類別概率都要計算出)。 
因此,對於一個位置上的 kk 個boxes 中的每一個 box,我們需要計算出 cc 個類,每一個類的 score,還有這個 box 相對於 它的預設 box 的 4 個偏移值(offsets)。於是,在 feature map 中的每一個 feature map cell 上,就需要有 (c+4)×k(c+4)×k 個 filters。對於一張 m×nm×n 大小的 feature map,即會產生 (c+4)×k×m×n(c+4)×k×m×n 個輸出結果。

 

在訓練的過程中,SSD與two stage的方法最大的區別是,SSD 訓練影象中的 groundtruth 需要賦予到那些固定輸出的 boxes 上。在前面也已經提到了,SSD 輸出的是事先定義好的,一系列固定大小的 bounding boxes。

最後會得到(38*38*4 + 19*19*6 + 10*10*6 + 5*5*6 + 3*3*4 + 1*1*4)= 8732個default box。

訓練中還有一個東西:prior box,是指實際中選擇的default box(每一個feature map cell 不是k個default box都取)。也就是說default box是一種概念,prior box則是實際的選取。訓練中一張完整的圖片送進網路獲得各個feature map,對於正樣本訓練來說,需要先將prior box與ground truth box做匹配,匹配成功說明這個prior box所包含的是個目標,但離完整目標的ground truth box還有段距離,訓練的目的是保證default box的分類confidence的同時將prior box儘可能迴歸到ground truth box。 舉個列子:假設一個訓練樣本中有2個ground truth box,所有的feature map中獲取的prior box一共有8732個。那個可能分別有10、20個prior box能分別與這2個ground truth box匹配上。訓練的損失包含定位損失和迴歸損失兩部分。

 

 

重點:

一直很難理解上述的8732個框和每個feature map每個畫素生成6個框,paper裡面提到:ratio={1,2,3,1/2,1/3},但是看了程式碼發現其實這5種ratio是作者計算出來的。作者在ssd_pascal.py這個指令碼中提到的計算如下,程式碼在:https://github.com/weiliu89/caffe/blob/ssd/examples/ssd/ssd_pascal_orig.py

#coding:utf-8
import math

min_dim = 300   #######維度
# conv4_3 ==> 38 x 38
# fc7 ==> 19 x 19
# conv6_2 ==> 10 x 10
# conv7_2 ==> 5 x 5
# conv8_2 ==> 3 x 3
# conv9_2 ==> 1 x 1
mbox_source_layers = ['conv4_3', 'fc7', 'conv6_2', 'conv7_2', 'conv8_2', 'conv9_2'] #####prior_box來源層,可以更改。很多改進都是基於此處的調整。
# in percent %
min_ratio = 20 ####這裡即是論文中所說的Smin=0.2,Smax=0.9的初始值,經過下面的運算即可得到min_sizes,max_sizes。具體如何計算以及兩者代表什麼,請關注我的部落格SSD詳解。這裡產生很多改進。
max_ratio = 90
####math.floor()函式表示:求一個最接近它的整數,它的值小於或等於這個浮點數。
step = int(math.floor((max_ratio - min_ratio) / (len(mbox_source_layers) - 2)))####取一個間距步長,即在下面for迴圈給ratio取值時起一個間距作用。可以用一個具體的數值代替,這裡等於17。
min_sizes = []  ###經過以下運算得到min_sizes和max_sizes。
max_sizes = []
for ratio in xrange(min_ratio, max_ratio + 1, step):  ####從min_ratio至max_ratio+1每隔step=17取一個值賦值給ratio。注意xrange函式的作用。
########min_sizes.append()函式即把括號內部每次得到的值依次給了min_sizes。
  min_sizes.append(min_dim * ratio / 100.)
  print(min_sizes)
  max_sizes.append(min_dim * (ratio + step) / 100.)
min_sizes = [min_dim * 10 / 100.] + min_sizes
max_sizes = [min_dim * 20 / 100.] + max_sizes
steps = [8, 16, 32, 64, 100, 300]  ###這一步要仔細理解,即計算卷積層產生的prior_box距離原圖的步長,先驗框中心點的座標會乘以step,相當於從feature map位置映射回原圖位置,比如conv4_3輸出特徵圖大小為38*38,而輸入的圖片為300*300,所以38*8約等於300,所以對映步長為8。這是針對300*300的訓練圖片。
aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]]

print(min_sizes)
print(max_sizes)

我們可以得到:建議大家跑一下這段程式碼就能明白。

/usr/bin/python /Users/bin/Desktop/data/xuelang/ssd.py
[60.0]
[60.0, 111.0]
[60.0, 111.0, 162.0]
[60.0, 111.0, 162.0, 213.0]
[60.0, 111.0, 162.0, 213.0, 264.0]
[30.0, 60.0, 111.0, 162.0, 213.0, 264.0]
[60.0, 111.0, 162.0, 213.0, 264.0, 315.0]

Process finished with exit code 0

首先根據以上程式碼,六個特徵層共產生6組min_sizes和max_sizes。另外min_dim=300,ratio取20到90即min_ratio=20,max_ratio=90。

        然後根據程式碼的計算公式,我們還需要step,注意是step不是steps,兩者的作用不一樣,在程式碼中有博主的註釋。這裡計算後step=(max_ratio-min_ratio)/(len(mbox_source_layers)-2)=(90-20)/(6-2)=17。要說這個step的作用,其實就是取一個間隔,全文看完你就應該明白了。其實這裡用了一個複雜的公式說白了就是顯得程式碼高大上一點。

        然後就開始計算min_sizes和max_sizes了,首先定義陣列min_sizes[]和max_sizes[]用來存放計算結果,沒有初始化說明預設為0,。然後計算conv4_3產生的min_sizes和max_sizes。根據程式碼中的公式計算:min_sizes=[min_dim*10/100]+min_sizes和max_sizes=[min_dim*20/100]+max_sizes得到結果為min_sizes=[300*10/100]+0=30,而max_sizes=[300*20/100]+0=60。這樣conv4_3的計算公式被計算分別為30和60。這裡為什麼要先計算下面兩行產生conv4_3的結果而不是使用上面兩行公式產生博主也沒有搞明白,歡迎指教。

        然後根據公式min_sizes.append(min_dim*ratio/100)和公式max_sizes.append(min_dim*(ratio+step)/100)來計算剩下5層的min_size和max_sizes。這裡需要用到ratio和step,我們前面講了step=17,根據程式碼for ratio in xrange(min_ratio, max_ratio+1, step)(這句的意思我們在程式碼中有註釋,即在min_ratio和max_ratio之間即20-90之間以step=17為間隔產生一組資料賦值給ratio),最終ratio=[20,37,54,71,88]。所以對於剩餘5層所產生的min_sizes和max_sizes分別為:

fc7:min_sizes=min_dim*ratio/100=300*20/100=60,max_sizes=min_dim*(ratio+step)/100=300*(20+17)/100=111;

conv6_2:min_sizes=min_dim*ratio/100=300*37/100=111,max_sizes=min_dim*(ratio+step)/100=300*(37+17)/100=162;

conv7_2:min_sizes=min_dim*ratio/100=300*54/100=162,max_sizes=min_dim*(ratio+step)/100=300*(54+17)/100=213;

conv8_2:min_sizes=min_dim*ratio/100=300*71/100=213,max_sizes=min_dim*(ratio+step)/100=300*(71+17)/100=264;

conv9_2:min_sizes=min_dim*ratio/100=300*88/100=213,max_sizes=min_dim*(ratio+step)/100=300*(88+17)/100=315;

 

文中程式碼顯示,給出的長寬比為

aspect_ratios = [[2], [2, 3], [2, 3], [2, 3], [2], [2]] 

這裡並不是paper中所給出的ar={1,2,3,1/2,1/3},這個比例是計算出來的。

        首先我們要知道,我們在前面也講了,每層的特徵圖的每個中心點分別會產生4、6、6、6、4、4個預設框,但我們要知道為什麼是這幾個預設框,這裡就和aspect_ratios有關係了。

        在SSD中6層卷積層的每個特徵圖的每個中心點會產生2個不同大小的正方形預設框,另外每設定一個aspect_ratio則會增加兩個長方形預設框,而文中程式碼對於6層的aspect_ratio個數分別為1、2、2、2、1、1,所以這也就是為什麼會產生4、6、6、6、4、4個預設框了。例如conv4_3預設生成兩個不同大小的正方形預設框,另外又有一個aspect_ratio=2產生了兩個長方形預設框,所以總共有4個。再如fc7,預設生成兩個正方形預設框,另外又有aspect_ratio=[2,3],所以又生成了4個不同的長方形預設框,共有6個不同大小的預設框。

        接著我們再講這些產生的預設框的大小計算。這裡參考paper中的計算公式,我們可以知道,對於產生的正方形的預設框,一大一小共兩個,其邊長計算公式為:小邊長=min_size,而大邊長=sqrt(min_size*max_size)。對於產生的長方形預設框,我們需要計算它的高(height)和寬(width),其中,height=1/sqrt(aspect_ratio)*min_size,width=sqrt(aspect_ratio)*min_size,對其高和寬翻轉後得到另一個面積相同但寬高相互置換的長方形。如圖所示:

根據以上分析,我們可以計算6層中每個特徵圖的每個中心點所產生的預設框的大小,分別如下:

conv4_3:小正方形邊長=min_size=30,大正方形邊長=sqrt(min_size*max_size)=sprt(30*60)=42.42;

                長方形的寬=sqrt(aspect_ratio)*min_size=sqrt(2)*30,高=1/sqrt(aspect_ratio)*min_size=30/sqrt(2),寬高比剛好為2:1;

                    將以上寬高旋轉90度產生另一個長方形,寬高比變為1:2。

 

fc7:小正方形邊長=min_size=60,大正方形邊長=sqrt(min_size*max_size)=sprt(60*111)=81.6;

                   第1組長方形的寬=sqrt(aspect_ratio)*min_size=sqrt(2)*60,高=1/sqrt(aspect_ratio)*min_size=60/sqrt(2),寬高比剛好為2:1;

                              將以上寬高旋轉90度產生另一個長方形,寬高比變為1:2。

                   第2組長方形的寬=sqrt(aspect_ratio)*min_size=sqrt(3)*60,高=1/sqrt(aspect_ratio)*min_size=60/sqrt(3),寬高比剛好為3:1;

                              將以上寬高旋轉90度產生另一個長方形,寬高比變為1:3。

 

conv6_2:小正方形邊長=min_size=111,大正方形邊長=sqrt(min_size*max_size)=sprt(111*162);

                   第1組長方形的寬=sqrt(aspect_ratio)*min_size=sqrt(2)*111,高=1/sqrt(aspect_ratio)*min_size=111/sqrt(2),寬高比剛好為2:1;

                              將以上寬高旋轉90度產生另一個長方形,寬高比變為1:2。

                   第2組長方形的寬=sqrt(aspect_ratio)*min_size=sqrt(3)*111,高=1/sqrt(aspect_ratio)*min_size=111/sqrt(3),寬高比剛好為3:1;

                              將以上寬高旋轉90度產生另一個長方形,寬高比變為1:3。

        conv7_2、conv8_2、conv9_2我們這裡就不再計算了,相信大家看完以上應該明白瞭如何計算,具體實現的步驟請大家參考指令碼prior_box_layer.cpp。這就是我們先驗框的計算方式。

 

部分內容應用來自:https://blog.csdn.net/xunan003/article/details/79186162

https://www.cnblogs.com/xuanyuyt/p/7447111.html