本文系做者原創,轉載請註明出處:http://www.javashuo.com/article/p-btcodynk-dr.htmlhtml
yolo是當前目標檢測最頂級的算法之一,v1版本是2016年提出來的,v2是2017年提出來的,v3是2018年提出的。算法
官網地址:https://pjreddie.com/darknet/yolo/網絡
說它最牛掰,有兩點:架構
一是由於它採用深層卷積神經網絡,吸取了當前不少經典卷積神經網絡架構的優秀思想,在位置檢測和對象的識別方面,性能
性能達到最優(準確率很是高的狀況下還能達到實時檢測)。this
二是由於做者還將代碼開源了。真心爲做者這種大公無私的心胸點贊。spa
美中不足的是:做者雖然將代碼開源,可是在論文介紹架構原理的時候比較模糊,特別是對一些重要改進,基本上是一筆帶過。3d
如今在網絡上有不少關於YOLO原理的講解,我的感受講得不是很清楚,總感受有點知其然而不知其因此然。好比:orm
yolo是在什麼背景下出現的?htm
v1是在哪一個經典網絡架構上發展起來的?解決了什麼問題?又存在什麼問題?
v2針對v1的缺點作了哪些改進?改進後效果怎麼樣?又存在什麼問題?
v3針對v2的缺點作了哪些改進?
這些問題不搞清楚,我以爲對yolo就談不上真正的理解。廢話很少說,下面就來介紹yolo的技術演進。
問題1:yolo v1是在什麼背景下出現的?
yolo v1是在 R-CNN 基礎上發展起來的。
R-CNN(region proposals + cnn),採用卷積神經網絡進行目標檢測的開山之做。
主要思想:對輸入圖片採用selective search 搜索查詢算法,提取出大約 2000 我的眼感興趣的候選邊框,而後每一個邊框都經過一個獨立的
卷積神經網絡進行預測輸出,後面再加上一個SVM(支持向量機)預測分類。
優勢:位置檢測與對象分類準確率很是高。
缺點:運算量大,檢測速度很是慢,在 GPU 加持下一幀檢測時間要 13s 左右,這在工程應用上是不可接受的。
形成速度慢的緣由主要有 2 個:
> 用搜索查詢算法提取 2000 個候選邊框
> 每一個候選邊框都採用一個獨立的CNN通道,這意味着卷積核的參數和全鏈接的參數都是不同的,總的參數個數是很是恐怖的。
針對這個缺點,yolo v1作了哪些改進呢。
上面的圖片是官網論文給出的結構圖,可是我的感受畫得不太好,它容易讓人誤認爲對輸入圖片進行網格化處理,每一個網格化的小窗口是 7*7。
真實狀況是,由最終輸出是 7*7*30 大小表明的物理含義是:你能夠把輸入圖片當作是通過了網格化(grid cell 7*7),每一個網格化後的小窗口經過 CNN 預測出 1*30。
這裏可能有點很差理解。總之一句話,在真正網絡架構流程中,沒有對輸入圖片進行任何網格化處理。
v1 改進點:
> Backbone: googLeNet22 (採用googLeNet22層卷積結構)
> 輸入圖片只處理一次(yolo名稱的由來),經過多個卷積層提取不一樣的特徵,每次卷積的時候共享卷積核參數。
輸入圖片只處理一次:表示輸入圖片只在第一次卷積的時候做爲輸入進行卷積運算,第一次卷積後的輸出做爲第二次卷積的輸入,經過多個卷積層遞進的方式來提取不一樣的特徵。
而且這些特徵經過的CNN通道都是相同的,從而能夠共享卷積核參數。
> 每一個 3*3 卷積核前面引入了 1*1 卷積核,做用有兩個,一是提取更豐富的特徵,二是減小卷積核的參數個數。
你們可能對減小卷積核參數個數的做用比較難以理解,這裏舉個例子。
好比輸入圖片大小是 56*56*256 最終轉化的目標大小是 28*28*512。
直接卷積:56*56*256 & 3*3*256*512 -> 56*56*512 & pooling -> 28*28*512
卷積核參數:3*3*256*512 = 1179648
引入 1*1 卷積:56*56*256 & 1*1*256*128 -> 56*56*128 & 3*3*128*512 -> 56*56*512 & pooling -> 28*28*512
卷積核參數:1*1*256*128 + 3*3*128*512 = 622592
通過改進後,圖片檢測速度很是快,基本上能夠達到實時。可是缺點是位置檢測準確度低,而且不能檢測出小對象物體。
針對 v1 的缺點,v2作了哪些改進呢?
v2 改進點:
> Backbone:darknet19 (採用darknet 19層卷積結構)
> 輸入圖片批歸一化處理(BN),做用是下降不是重要特徵的重要性。
這句話可能聽得有點暈,舉個例子,
你的樣本里有兩個特徵列,一個特徵列的數值在[1,10]範圍內,另外一個特徵列的數值在[1000,10000]範圍內,
可是真實狀況是,你的這兩個特徵列重要性多是同樣的,只不過你拿到的數據就是這樣的,咱們知道,
卷積神經網絡訓練,實質就是一系列數值運算的過程,若是你將這兩個特徵列直接經過卷積神經網絡進行訓練,
那最終生成的模型準確率確定是不高的,因此須要進行歸一化處理,將數值歸一化到[0,1]範圍內,
從而是損失能量在收斂的時候更加平穩。
> 採用passthrough算法,解決池化信息丟失的問題,增長細粒度特徵的檢測(小對象)。
passthrough 算法主要是爲了解決 pooling(池化)的缺點,不論是最大值池化,仍是平均值池化,都有一個很明顯的問題,
就是會形成信息丟失,passthrough 主要思想是在池化以前,將輸入信息進行拆分,一拆爲四,通過拆分後的大小就和池化後的輸出大小相同,
而後疊加,疊加後的結果主要就是維度變化,這樣就能解決池化會形成信息丟失的問題。
> 去掉全鏈接(FC),將輸入圖片拉伸(resize)到不一樣尺寸而後經過卷積神經網絡,這樣就獲得了多尺寸的輸出,從而能提高對不一樣大小對象的預測準確度。
全鏈接其實就是矩陣乘法運算,矩陣乘法有一個前提,矩陣 A 的列必須與矩陣 B 的行個數相同,不然是不能進行矩陣乘法運算的。
全鏈接的參數大小是固定的,那麼你的輸入大小天然就固定了,這樣就沒法實現多尺寸的輸出,因此這裏去掉了全鏈接層。
通過改進後,精度提高明顯,特別是對小對象的檢測,缺點是對小對象檢測準確度不高。
針對 v2 的缺點,v3 又作了哪些改進呢?
v3 改進點:
上面是 v3 的結構圖,是我跟蹤代碼,查找資料,繪製這張圖真心不容易,正確性絕對有保證,你們若是以爲這張圖對你理解 yolo 有幫助,麻煩點個贊。
輸入大小這裏是416*416,輸出13*13,26*26,52*52,這裏通常要求輸入圖片大小是 32 的倍數,由於整個卷積神經網絡會將圖片縮小32倍,16倍,8倍,
這裏取最大公倍數32。 123 = 3*(邊框座標 4 + 置信度 1 + 類對象 36)。
> Backbone: darknet53 (採用darknet 53層卷積結構,實際是52層卷積,去掉了全鏈接層)
能夠看出v3的卷積層數是v2的2.8倍,有個潛在的共識:增長模型準確率的一個直接作法是增長網絡的深度和厚度,
(深度是指卷積層數,厚度指卷積核的維度或者是種類數),這裏做用天然就是提高精度了。
> 用卷積取代池化
以前咱們提到過池化的問題,會形成信息丟失,這裏用卷積來實現池化的功能(使圖片大小縮小2倍),同時不會形成信息的明顯丟失。
> 採用殘差網絡(resnet)防止梯度消失
梯度消失或者梯度爆炸是在深層的卷積神經網絡中才有可能出現的,梯度的計算是經過鏈式求導獲得的,隨着網絡層的增長,鏈式求導項就會愈來愈長,
由於在每一層卷積後的輸出都作了歸一化處理,因此梯度只會愈來愈小,有可能爲0,而0在後面模塊運算中都爲0,這樣致使的直接後果是:
損失能量在收斂到某一階段後就中止收斂,最後生成模型的精度天然就不高。而這裏採用殘差網絡就能防止梯度消失,v3結構圖裏左下角就是殘差網絡的結構圖。
殘差網絡的思想:每次卷積後的輸出當作殘差,將卷積前的輸入與殘差融合,做爲整個輸出,即便殘差爲0,整個輸出也不會爲0。
從殘差網絡結構圖能夠看出,每一個3*3卷積核前都引入了1*1卷積,這裏沿用了v1的思想。
從v3的結構圖能夠看出,darknet53網絡骨架裏大量的引入了殘差網絡的思想。
> 使用空間金字塔池化網絡算法(sppnet spatial pyramid pooling)實現多尺寸的輸出
空間金字塔池化網絡算法主要思想:不一樣尺寸的輸入經過sppnet模塊後生成一個固定尺寸的輸出。
在v3結構圖裏,有兩個地方用到這個思想:
一個是 13*13*512 通過 1*1 卷積,改變特徵維度,變成 13*13*256,通過上採樣(upsample,這裏採用相鄰像素插入算法),
改變特徵尺寸,變成 26*26*256,而後與 26*26*512 疊加,生成 26*26*768。
另外一個是 26*26*256 與 52*52*256 疊加後生成 52*52*384。
v3的多尺寸輸出與v2的多尺寸輸出有本質不一樣,v2多尺寸輸出是對輸入圖片拉伸到不一樣的尺寸,而後經過卷積神經網絡獲得不一樣的輸出,
可是這樣就存在一個圖片失真的問題,由於你是對圖片進行的拉伸處理。而v3經過sppnet實現的多尺寸輸出,就能有效避免圖片失真的問題。
> 13*13*123,26*26*123,52*52*123物理意義
這裏用v1版本論文圖片來解釋,物理意義:
表示將輸入圖片網格化,有 13*13,26*26,52*52 大小,每一個網格化的小窗口(grid cell)預測 3 個邊框(bounding box),
每一個邊框包含 4 個位置座標,1個置信度,36個對象種類。
123 = 3 *(4 + 1 + 36)
這裏就存在一個問題:預測輸出如此之多,直接用於損失能量的計算,運算量豈不是很恐怖?
確實是這樣,因此這裏先通過下面的兩個步驟的處理:
(1)每一個小窗口只取置信度最大的邊框,由於yolo規定,只能有一個真實的對象中心座標屬於每一個小窗口。
這樣,就得出 13*13*3*41 => 13*13*41,26*26*3*41 => 26*26*41,52*52*3*41 => 52*52*41
13*13*41,26*26*41,52*52*41 表示一個真實對象有不少預測重疊邊框,好比說上面圖裏屬於狗的預測邊框很是多,
可是咱們只須要預測最準確的邊框,去掉其餘屬於狗的重疊邊框。
(2)採用NMS(非極大值抑制算法)去除重疊邊框。
這樣 13*13*41,26*26*41,52*52*41 => N*41 (N表示不一樣對象預測數目,好比說上面圖理想狀況下,N = 3)。
> 損失能量(採用交叉熵)
損失能量的計算是v1版本提出來的,這裏放到了v3來講,有3個改進點:
(1)將位置檢測與對象識別做爲一個總體,進行訓練預測,這從損失能量的計算能夠直接反應出來。
(2)位置的寬度和高度先開根號,與歸一化的做用相同,下降不是重要特徵的重要性。
(3)增長權重參數 ,當邊框預測出含有對象時,增大它的權重值,當邊框預測出不含有對象時,減少它的權重值,這樣就能使損失能量計算更準確。
不要讓懶惰佔據你的大腦,不要讓妥協拖垮了你的人生。青春就是一張票,能不能遇上時代的快車,你的步伐就掌握在你的腳下。