yolo系列之yolo v3【深度解析】
版權申明:轉載和引用圖片,都必須經過書面同意。獲得留言同意即可
本文使用圖片多為本人所畫,需要高清圖片可以留言聯系我,先點贊后取圖
這篇博文比較推薦的yolo v3代碼是qwe的keras版本,復現比較容易,代碼相對來說比較容易理解。同學們可以結合代碼和博文共同理解v3的精髓。
github地址:https://github.com/qqwweee/keras-yolo3
前言
前言就是嘮嘮嗑,想直接看干貨可以跳過前言,直接看Yolo v3。
yolo_v3是我最近一段時間主攻的算法,寫下博客,以作分享交流。
看過yolov3論文的應該都知道,這篇論文寫得很隨意,很多亮點都被作者都是草草描述。很多騷年入手yolo算法都是從v3才開始,這是不可能掌握yolo精髓的,因為v3很多東西是保留v2甚至v1的東西,而且v3的論文寫得很隨心。想深入了解yolo_v3算法,是有必要先了解v1和v2的。以下是我關於v1和v2算法解析所寫的文章:
v1算法解析:《yolo系列之yolo v1》
v2算法解析:《yolo系列之yolo v2》
yolo_v3作為yolo系列目前最新的算法,對之前的算法既有保留又有改進。先分析一下yolo_v3上保留的東西:
- “分而治之”,從yolo_v1開始,yolo算法就是通過划分單元格來做檢測,只是划分的數量不一樣。
- 采用"leaky ReLU"作為激活函數。
- 端到端進行訓練。一個loss function搞定訓練,只需關注輸入端和輸出端。
- 從yolo_v2開始,yolo就用batch normalization作為正則化、加速收斂和避免過擬合的方法,把BN層和leaky relu層接到每一層卷積層之后。
- 多尺度訓練。在速度和准確率之間tradeoff。想速度快點,可以犧牲准確率;想准確率高點兒,可以犧牲一點速度。
yolo每一代的提升很大一部分決定於backbone網絡的提升,從v2的darknet-19到v3的darknet-53。yolo_v3還提供替換backbone——tiny darknet。要想性能牛叉,backbone可以用Darknet-53,要想輕量高速,可以用tiny-darknet。總之,yolo就是天生“靈活”,所以特別適合作為工程算法。
當然,yolo_v3在之前的算法上保留的點不可能只有上述幾點。由於本文章主要針對yolo_v3進行剖析,不便跑題,下面切入正題。
YOLO v3
網上關於yolo v3算法分析的文章一大堆,但大部分看着都不爽,為什么呢?因為他們沒有這個玩意兒:
yolo系列里面,作者只在v1的論文里給出了結構圖,而v2和v3的論文里都沒有結構圖,這使得讀者對后兩代yolo結構的理解變得比較難。but,對於yolo學習者來說,腦子里沒有一個清晰的結構圖,就別說自己懂yolo了。上圖是我根據官方代碼和官方論文以及模型結構可視化工具等經過好幾個小時畫出來的,修訂過幾個版本。所以,上圖的准確性是可以保證的。
這里推薦的模型結構可視化工具是:Netron
netron方便好用,可以直觀看到yolo_v3的實際計算結構,精細到卷積層。But,要進一步在人性化的角度分析v3的結構圖,還需要結合論文和代碼。對此,我是下了不少功夫。
上圖表示了yolo_v3整個yolo_body的結構,沒有包括把輸出解析整理成咱要的[box, class, score]。對於把輸出張量包裝成[box, class, score]那種形式,還需要寫一些腳本,但這已經在神經網絡結構之外了(我后面會詳細解釋這波操作)。
為了讓yolo_v3結構圖更好理解,我對圖1做一些補充解釋:
DBL: 如圖1左下角所示,也就是代碼中的Darknetconv2d_BN_Leaky,是yolo_v3的基本組件。就是卷積+BN+Leaky relu。對於v3來說,BN和leaky relu已經是和卷積層不可分離的部分了(最后一層卷積除外),共同構成了最小組件。
resn:n代表數字,有res1,res2, … ,res8等等,表示這個res_block里含有多少個res_unit。這是yolo_v3的大組件,yolo_v3開始借鑒了ResNet的殘差結構,使用這種結構可以讓網絡結構更深(從v2的darknet-19上升到v3的darknet-53,前者沒有殘差結構)。對於res_block的解釋,可以在圖1的右下角直觀看到,其基本組件也是DBL。
concat:張量拼接。將darknet中間層和后面的某一層的上采樣進行拼接。拼接的操作和殘差層add的操作是不一樣的,拼接會擴充張量的維度,而add只是直接相加不會導致張量維度的改變。
我們可以借鑒netron來分析網絡層,整個yolo_v3_body包含252層,組成如下:
根據表0可以得出,對於代碼層面的layers數量一共有252層,包括add層23層(主要用於res_block的構成,每個res_unit需要一個add層,一共有1+2+8+8+4=23層)。除此之外,BN層和LeakyReLU層數量完全一樣(72層),在網絡結構中的表現為:每一層BN后面都會接一層LeakyReLU。卷積層一共有75層,其中有72層后面都會接BN+LeakyReLU的組合構成基本組件DBL。看結構圖,可以發現上采樣和concat都有2次,和表格分析中對應上。每個res_block都會用上一個零填充,一共有5個res_block。
1. backbone
整個v3結構里面,是沒有池化層和全連接層的。前向傳播過程中,張量的尺寸變換是通過改變卷積核的步長來實現的,比如stride=(2, 2),這就等於將圖像邊長縮小了一半(即面積縮小到原來的1/4)。在yolo_v2中,要經歷5次縮小,會將特征圖縮小到原輸入尺寸的1/251/2^51/25,即1/32。輸入為416x416,則輸出為13x13(416/32=13)。
yolo_v3也和v2一樣,backbone都會將輸出特征圖縮小到輸入的1/32。所以,通常都要求輸入圖片是32的倍數。可以對比v2和v3的backbone看看:(DarkNet-19 與 DarkNet-53)
yolo_v2中對於前向過程中張量尺寸變換,都是通過最大池化來進行,一共有5次。而v3是通過卷積核增大步長來進行,也是5次。(darknet-53最后面有一個全局平均池化,在yolo-v3里面沒有這一層,所以張量維度變化只考慮前面那5次)。
這也是416x416輸入得到13x13輸出的原因。從圖2可以看出,darknet-19是不存在殘差結構(resblock,從resnet上借鑒過來)的,和VGG是同類型的backbone(屬於上一代CNN結構),而darknet-53是可以和resnet-152正面剛的backbone,看下表:
從上表也可以看出,darknet-19在速度上仍然占據很大的優勢。其實在其他細節也可以看出(比如bounding box prior采用k=9),yolo_v3並沒有那么追求速度,而是在保證實時性(fps>36)的基礎上追求performance。不過前面也說了,你要想更快,還有一個tiny-darknet作為backbone可以替代darknet-53,在官方代碼里用一行代碼就可以實現切換backbone。搭用tiny-darknet的yolo,也就是tiny-yolo在輕量和高速兩個特點上,顯然是state of the art級別,tiny-darknet是和squeezeNet正面剛的網絡,詳情可以看下表:
所以,有了yolo v3,就真的用不着yolo v2了,更用不着yolo v1了。這也是[yolo官方網站](https://pjreddie.com/darknet/),在v3出來以后,就沒提供v1和v2代碼下載鏈接的原因了。
2. Output
對於圖1而言,更值得關注的是輸出張量:
yolo v3輸出了3個不同尺度的feature map,如上圖所示的y1, y2, y3。這也是v3論文中提到的為數不多的改進點:predictions across scales
這個借鑒了FPN(feature pyramid networks),采用多尺度來對不同size的目標進行檢測,越精細的grid cell就可以檢測出越精細的物體。
y1,y2和y3的深度都是255,邊長的規律是13:26:52
對於COCO類別而言,有80個種類,所以每個box應該對每個種類都輸出一個概率。
yolo v3設定的是每個網格單元預測3個box,所以每個box需要有(x, y, w, h, confidence)五個基本參數,然后還要有80個類別的概率。所以3*(5 + 80) = 255。這個255就是這么來的。(還記得yolo v1的輸出張量嗎? 7x7x30,只能識別20類物體,而且每個cell只能預測2個box,和v3比起來就像老人機和iphoneX一樣)
v3用上采樣的方法來實現這種多尺度的feature map,可以結合圖1和圖2右邊來看,圖1中concat連接的兩個張量是具有一樣尺度的(兩處拼接分別是26x26尺度拼接和52x52尺度拼接,通過(2, 2)上采樣來保證concat拼接的張量尺度相同)。作者並沒有像SSD那樣直接采用backbone中間層的處理結果作為feature map的輸出,而是和后面網絡層的上采樣結果進行一個拼接之后的處理結果作為feature map。為什么這么做呢? 我感覺是有點玄學在里面,一方面避免和其他算法做法重合,另一方面這也許是試驗之后並且結果證明更好的選擇,再者有可能就是因為這么做比較節省模型size的。這點的數學原理不用去管,知道作者是這么做的就對了。
3. some tricks
上文把yolo_v3的結構討論了一下,下文將對yolo v3的若干細節進行剖析。
Bounding Box Prediction
b-box預測手段是v3論文中提到的又一個亮點。先回憶一下v2的b-box預測:想借鑒faster R-CNN RPN中的anchor機制,但不屑於手動設定anchor prior(模板框),於是用維度聚類的方法來確定anchor box prior(模板框),最后發現聚類之后確定的prior在k=5也能夠又不錯的表現,於是就選用k=5。后來呢,v2又嫌棄anchor機制線性回歸的不穩定性(因為回歸的offset可以使box偏移到圖片的任何地方),所以v2最后選用了自己的方法:直接預測相對位置。預測出b-box中心點相對於網格單元左上角的相對坐標。
yolo v2直接predict出(txt_xtx, tyt_yty, twt_wtw, tht_hth, tot_oto),並不像RPN中anchor機制那樣去遍歷每一個pixel。可以從上面的公式看出,b-box的位置大小和confidence都可以通過(txt_xtx, tyt_yty, twt_wtw, tht_hth, tot_oto)計算得來,v2相當直接predict出了b-box的位置大小和confidence。box寬和高的預測是受prior影響的,對於v2而言,b-box prior數為5,在論文中並沒有說明拋棄anchor機制之后是否拋棄了聚類得到的prior(沒看代碼,所以我不能確定),如果prior數繼續為5,那么v2需要對不同prior預測出twt_wtw和