從零開始實現SSD目標檢測(pytorch)(一)


從零開始實現SSD目標檢測(pytorch)

特別說明:

  1. 本系列文章是Pytorch目標檢測手冊的翻譯+總結
  2. 知其然知其所以然,光看論文不夠,得親自實現

第一章 相關概念概述

1.1 檢測框表示

邊界寬(bounding box)是包圍一個物體(objective)的框,用來表示這個物體的位置、形狀、大小等信息。不是最小外接矩形,僅僅是一個轉動角度為 0 的框。如下圖1-1所示:

表示框的方法有很多(不贅述),圖1-1的方式為框的邊界四個極值坐標\(x_{min},y_{min},x_{max},y_{max}\)

但是這樣做的有點缺點

  • 知道\(x_{min},y_{min},x_{max},y_{max}\),我們無法知道這個目標的更多信息(比例),必須畫出來才有感官
  • 如果沒有圖像的寬高,像素值毫無用處(其實還是無法知道比例信息)

改進方式如下圖1-2所示

1-2使用比例的方式,很直觀的知道目標更多的信息

但是還有一個缺點

  • 直接看這個信息,我們不知道目標長寬信息(當然你自己可以另外計算)
  • 也不知道中心位置(相對於邊界,我們更關心中心)

再次改進的方式如下圖1-3所示:

\(c_x,c_y,w,h\),中心點+寬高的方式,滿足視覺信息最大化。

1.2 交並比

如何用來判斷一個框檢測的好與壞?

  1. 使用交集\(A \cap B\)

直接使用交集的大小去判斷好壞,大尺度和小尺度不對等

比如:A和B大小都為100,交集50. C和D大小都為10,交集5.

如何說明這兩組哪個好壞?

通過上述的例子,我們發現少了一個比例問題。。。

  1. 使用 \(\frac{A{\cap}B}{A{\cup}B}\)交集除以並集

完全解決上訴問題

注意:這里還存在一個關於LOSS的問題,具體可參考GIOU

第二章 基礎網絡

注釋:這里實現的是SSD300,並非SSD512.

2.1 基礎網絡

當前都是使用特征提取的基礎架構,VGG、ResNet、DenseNet。。。等,原作者使用VGG-16架構作為基礎網絡。如下圖2-1所示

原始VGG-16是基於ImageNet訓練的,參數優異。但是為了符合我們的設計,需要對基礎網絡做一定程度的修改。

  • 原始輸入300*300圖像
  • 對於池化后非整數feature map進行像上取整,比如con3_3==>>75*75,進行池化之后的大小為38 * 38,而不是37 * 37。
  • 修改maxpooling_5==>>(size=3 * 3,stride=1),作用是不再將feature map的大小減半。
  • 我們不需要全連接層,對 FC6 和 FC7 進行修改為conv6和conv7。直接去掉FC8

FC層修改為CONV層

說明:原作者對此進行了詳細的描述,筆者這里默認大家有基礎網絡架構。

全連接和卷積的區別之處在於reshape!!!

也就是說卷積之后進行reshape(nuns,1)和全連接結果一樣

總結:

  • 大家不妨試一下,對VGG的最后全連接改為卷積,然后最后做一個reshape看看效果。
  • 想一下效果一樣的,CNN就是學習參數,與最后的形式無關

2.2 附加網絡

我們當前已經學會如何從FC轉換為CONV,以下對VGG-16進行轉換。

VGG-16輸入圖像為224 * 224 * 3,那么conv5_3的輸出也就是7 * 7 * 512。

  • FC6的輸入是7 * 7 * 512的一行向量,輸出是4096 * 1的向量,那么參數數量為:4096 * 7 * 7 * 512,也就等於kernel=512 * 7 * 7 * 4046
  • FC7的輸入4096 * 1,輸出為4096 * 1,也就等於kernel = 4096 * 1 * 1 * 4096

雖然轉化到卷積了,但是channel太大了,512都挺大,別說4096了。。。

作者對其maxpooling和卷積進行了調整,如下圖2-5所示

個人對其原因進行分析

  1. 按照轉換,Conv6的輸入是7 * 7的feature,卷積也是7 * 7的大小。不符合常理,feature太小,卷積核太大。
  2. Conv7的channel=4096,那么深的通道完全沒有必要(網絡沒大到那種地步)
  3. 考慮到后面金字塔采樣(FPN),feature得有層次感

我們添加了額外的四個卷積塊,feature map的變化是通過卷積的stride=2來實現的,並非maxpooling

注意是每隔一個stride=1之后接一個stride=2,這樣做的感覺是過度一下

就像maxpooling不能緊接着來maxpooling一樣

第三章 先驗框設計

3.1 引言

在介紹先驗框(prior box)的設計之前,我們先確認一下目的:檢測出目標種類位置

我們先從種類檢測入手:

以VGG-16為例,輸出是1000 * 1.代表1000個種類的預測

我們事先把標簽(label)編碼成one-hot形式,訓練結果接一個softmax,loss就可以使用交叉熵返回梯度。

沒毛病,完全可以

問題一:一個圖像中有兩個目標呢?

我們可以讓輸出為1000 * 2,其他情況類似直接為1000 * nums

問題二:如何知道輸出對應哪個呢?

假設輸入圖像為5 * 5,我們就讓輸出為5 * 5 * 1000,這樣就可以知道每個像素屬於什么label

好像這是語義分割了。。。

我們再從位置入手:

我們之前定義了bounding box的形式, $ c_x, c_y, w, h $

跟着上面VGG-16的思路走,一個目標就輸出4 * 1即可

問題三:同種類一樣,存在多個目標呢?

類似種類,輸出4 * 1 * nums即可

問題四:同種類一樣,確定對應的種類?

假設輸入圖像為5 * 5,我們就讓輸出為5 * 5 * 2,注意這里沒必要輸出 \(c_x, c_y\) ,因為每個像素也就知道位置了

問題五:一個像素點多個目標問題?

這種情況很少很少,可以輸出5 * 5 * nums * 2,nums是一個冗余,極少情況的冗余

這里就不上圖了,自己想一下即可明白,而且后面會有SSD的分類說明!

3.2 先驗框設計

3.1節中,我們知道直接輸出每個像素點的框W和H即可,為什么現在又來個先驗框呢?

注意:這種方式是完全可行的,大家可以移步EASTAdvanceEAST

我們先來說直接輸出W和H的缺點

  • 深度學習學到的東西是有能力限制的,而不是說什么都可以學到(理想情況可以),比如ResNet多加了一個原始數據(ResNet模塊)就能更好的學習?
  • 按理說RPN模塊應該過時了,為什么現在還有很多人使用?比如SiamMask目標跟蹤領域
  • W和H的波動幅度都很大,直接去學習較為困難,我們能不能降低學習代價

直接拋出一個話題,對比下圖3-1兩種學習目標,哪個更容易學習?

毋容置疑,肯定第二章方式更容易學習,具體如何設計,且看SSD大神作者的方案

以這里為出發點,我們稱事先定義的bounding box為先驗框

  • 我們在conv4_3, conv7, conv8_2, conv9_2, conv10_2, conv11_2.這些feature map上定義先驗框 ,采用FPN(特征金字塔)進行多尺度檢測的方案。
  • 定義一個參數 \(s\)\(w * h = s^2\),先驗框最大存在於Conv4_3上,設置為0.1,也就是圖像的10%大小。
  • 為了進一步降低學習成本,定義不同比例的先驗框。1:1、1:2、2:1等,其中每一層額外加一個1:1的框,其他的框面積都是s,而這個額外的框\(s = \sqrt{s_k*s_{k+1}}\) ,這樣做的目的是為了銜接上下兩個feature
Feature Map From Feature Map Dimensions Prior Scale Aspect Ratios Number of Priors per Position Total Number of Priors on this Feature Map
conv4_3 38, 38 0.1 1:1, 2:1, 1:2 + an extra prior 4 5776
conv7 19, 19 0.2 1:1, 2:1, 1:2, 3:1, 1:3 + an extra prior 6 2166
conv8_2 10, 10 0.375 1:1, 2:1, 1:2, 3:1, 1:3 + an extra prior 6 600
conv9_2 5, 5 0.55 1:1, 2:1, 1:2, 3:1, 1:3 + an extra prior 6 150
conv10_2 3, 3 0.725 1:1, 2:1, 1:2 + an extra prior 4 36
conv11_2 1, 1 0.9 1:1, 2:1, 1:2 + an extra prior 4 4
Grand Total 8732 priors

3.3 先驗框可視化

我們先看一下定義s之后的基本公式:

\[w * h = s^2 \]

\[\frac{w}{h}=a \]

\[w = s * \sqrt{a} \]

\[h = \frac{s}{\sqrt{a}} \]

下面對conv9_2進行可視化:

con9_2總共有6個框,五個框面積是0.55,一個框面積是0.63

注意:文章全部參數都是歸一化之后的,請移步bounding box定義查看

大於邊界直接裁斷,其余feature map照部就搬即可,不再贅述

3.4 學習參數定義

3.1節,我們對比了學習代價問題,具體的學習參數這節進行詳細說明

觀察上圖3-3,是先驗框和實際框之間的偏差圖,具體我們學習哪些參數?

下面直接看作者的設計:

按照3.1節說的,為什么不直接定義成\(c_x - c_{x1}\) (帽子用x1代替),而作者定義了除以\(w_{x1}\)

因為較大的先驗對應較小的偏差時,這個偏差很難學習,會導致大目標邊界不准確。

下圖3-5情況一和情況二哪個學習的更好?沒有歸一化,根本無法使用

第四章 網絡輸出定義

3.1節中已經進行了初步說明,作者的思路也是按照這個進行的

種類的輸出,這個多定義個背景(background)類,也就是nums = nums + 1,當然也可以沒有背景類,那么負樣本就無法進行學習,會大大降低網絡的魯棒性!!!

如上圖4-1所示

  • 位置輸出:采用3 * 3的卷積輸出4個通道(這里沒考慮單個像素多個目標情況),對應之前的(g_c_x, g_c_y, g_w, g_h)
  • 種類輸出:采用3*3的卷積輸出nums個通道(這里沒考慮單個像素多個目標情況),對應之前VGG-16的輸出。

下面以具體的Con9_2為例,如下圖4-2所示:

定位的24 = 4 * 6===>>4:輸出參數,6:先驗框的數量

種類的6*n_class===>>同上

更為具體的如下圖5-4所示:

作者還怕讀者不懂,特意舉了個例子,假設就兩個種類:貓和狗,一個背景

網絡輸出如下圖5-5

將位置和種類對應上,輸出一個完整的矩陣,如下圖5-6所示:

第五章 LOSS設計

5.1 目標框匹配

在計算LOSS之前,我們得先把框對應上(輸出框和目標框的對應關系)

  • 假設一張圖有N個目標,那就需要找到N和8732(這是疊加了輸出的偏差信息)個框的交集

  • 為每個目標匹配一個最大交集的框

  • 如果一個先驗框與目標的交集小於0.5,那么就當這個先驗框為負樣本

  • 如果一個先驗框和目標的交集大於0.5,那么這個先驗框為正樣本,並且最大的交集為其匹配的種類

  • 匹配的label都存在種類的標簽

插曲: 有些人搞不懂正樣本負樣本的概念

正樣本:這個先驗框匹配到目標框,並且用於計算位置種類的LOSS,其中位置loss為了更好的框住目標,種類loss為了提高置信度。

負樣本:這個先驗框沒有匹配到目標框,或者匹配到的目標框小於閾值(0.5等),並且位置不用計算loss(因為background沒有框位置),種類需要計算loss,提高背景那一類的置信度。

上圖5-25-3給出了具體的例子,容易理解不在贅述。

5.2 LOSS計算

定位LOSS

定位的loss使用SmoothL1,沒什么好說的

種類LOSS

現在遇到一個問題,我們直接使用交叉熵的LOSS的話,8732個框,基本百分之八十都是負樣本,而負樣本學習的結果是使當前的框種類置信度更高(背景)。

這樣導致的結果就是類間不平衡情況,導致背景檢測很准確,可能部分目標都會檢測成背景

作者采用的解決方案是:舍棄簡單的負樣本,重點學習困難的負樣本

假設綠色為負樣本框(實際背景不存在框),情況一相較於情況二更容易學習,那么我們舍棄情況一,使用情況二進行訓練。

總的LOSS

其中a也是一個可以作為學習的參數(筆者沒試過),原文中a=1

第六章 非極大值抑制

這一章比較簡單,直接看后面代碼即可


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM