目標檢測|YOLOv2原理與實現(附YOLOv3)

碼字不易,歡迎給個贊!

歡迎交流與轉載,文章會同步發布在公眾號:機器學習演算法全棧工程師(Jeemy110)


前期文章:

小白將:目標檢測|YOLO原理與實現?

zhuanlan.zhihu.com圖標小白將:目標檢測|SSD原理與實現?

zhuanlan.zhihu.com圖標小白將:綜述|基於深度學習的目標檢測(一)?

zhuanlan.zhihu.com圖標

前言

在前面的一篇文章中,我們詳細介紹了YOLOv1的原理以及實現過程。這篇文章接著介紹YOLOv2的原理以及實現,YOLOv2的論文全名為YOLO9000: Better, Faster, Stronger,它斬獲了CVPR 2017 Best Paper Honorable Mention。在這篇文章中,作者首先在YOLOv1的基礎上提出了改進的YOLOv2,然後提出了一種檢測與分類聯合訓練方法,使用這種聯合訓練方法在COCO檢測數據集和ImageNet分類數據集上訓練出了YOLO9000模型,其可以檢測超過9000多類物體。所以,這篇文章其實包含兩個模型:YOLOv2和YOLO9000,不過後者是在前者基礎上提出的,兩者模型主體結構是一致的。YOLOv2相比YOLOv1做了很多方面的改進,這也使得YOLOv2的mAP有顯著的提升,並且YOLOv2的速度依然很快,保持著自己作為one-stage方法的優勢,YOLOv2和Faster R-CNN, SSD等模型的對比如圖1所示。這裡將首先介紹YOLOv2的改進策略,並給出YOLOv2的TensorFlow實現過程,然後介紹YOLO9000的訓練方法。近期,YOLOv3也放出來了,YOLOv3也在YOLOv2的基礎上做了一部分改進,我們在最後也會簡單談談YOLOv3所做的改進工作。

圖1:YOLOv2與其它模型在VOC 2007數據集上的效果對比

YOLOv2的改進策略

YOLOv1雖然檢測速度很快,但是在檢測精度上卻不如R-CNN系檢測方法,YOLOv1在物體定位方面(localization)不夠準確,並且召回率(recall)較低。YOLOv2共提出了幾種改進策略來提升YOLO模型的定位準確度和召回率,從而提高mAP,YOLOv2在改進中遵循一個原則:保持檢測速度,這也是YOLO模型的一大優勢。YOLOv2的改進策略如圖2所示,可以看出,大部分的改進方法都可以比較顯著提升模型的mAP。下面詳細介紹各個改進策略。

圖2:YOLOv2相比YOLOv1的改進策略

Batch Normalization

Batch Normalization可以提升模型收斂速度,而且可以起到一定正則化效果,降低模型的過擬合。在YOLOv2中,每個卷積層後面都添加了Batch Normalization層,並且不再使用droput。使用Batch Normalization後,YOLOv2的mAP提升了2.4%。

High Resolution Classifier

目前大部分的檢測模型都會在先在ImageNet分類數據集上預訓練模型的主體部分(CNN特徵提取器),由於歷史原因,ImageNet分類模型基本採用大小為 224	imes224 的圖片作為輸入,解析度相對較低,不利於檢測模型。所以YOLOv1在採用 224	imes224 分類模型預訓練後,將解析度增加至 448	imes448 ,並使用這個高解析度在檢測數據集上finetune。但是直接切換解析度,檢測模型可能難以快速適應高解析度。所以YOLOv2增加了在ImageNet數據集上使用 448	imes448 輸入來finetune分類網路這一中間過程(10 epochs),這可以使得模型在檢測數據集上finetune之前已經適用高解析度輸入。使用高解析度分類器後,YOLOv2的mAP提升了約4%。

Convolutional With Anchor Boxes

在YOLOv1中,輸入圖片最終被劃分為 7	imes7 網格,每個單元格預測2個邊界框。YOLOv1最後採用的是全連接層直接對邊界框進行預測,其中邊界框的寬與高是相對整張圖片大小的,而由於各個圖片中存在不同尺度和長寬比(scales and ratios)的物體,YOLOv1在訓練過程中學習適應不同物體的形狀是比較困難的,這也導致YOLOv1在精確定位方面表現較差。YOLOv2借鑒了Faster R-CNN中RPN網路的先驗框(anchor boxes,prior boxes,SSD也採用了先驗框)策略。RPN對CNN特徵提取器得到的特徵圖(feature map)進行卷積來預測每個位置的邊界框以及置信度(是否含有物體),並且各個位置設置不同尺度和比例的先驗框,所以RPN預測的是邊界框相對於先驗框的offsets值(其實是transform值,詳細見Faster R_CNN論文),採用先驗框使得模型更容易學習。所以YOLOv2移除了YOLOv1中的全連接層而採用了卷積和anchor boxes來預測邊界框。為了使檢測所用的特徵圖解析度更高,移除其中的一個pool層。在檢測模型中,YOLOv2不是採用 448	imes448 圖片作為輸入,而是採用 416	imes416 大小。因為YOLOv2模型下採樣的總步長為 32 ,對於 416	imes416 大小的圖片,最終得到的特徵圖大小為 13	imes13 ,維度是奇數,這樣特徵圖恰好只有一個中心位置。對於一些大物體,它們中心點往往落入圖片中心位置,此時使用特徵圖的一個中心點去預測這些物體的邊界框相對容易些。所以在YOLOv2設計中要保證最終的特徵圖有奇數個位置。對於YOLOv1,每個cell都預測2個boxes,每個boxes包含5個值: (x, y, w, h, c) ,前4個值是邊界框位置與大小,最後一個值是置信度(confidence scores,包含兩部分:含有物體的概率以及預測框與ground truth的IOU)。但是每個cell只預測一套分類概率值(class predictions,其實是置信度下的條件概率值),供2個boxes共享。YOLOv2使用了anchor boxes之後,每個位置的各個anchor box都單獨預測一套分類概率值,這和SSD比較類似(但SSD沒有預測置信度,而是把background作為一個類別來處理)。

使用anchor boxes之後,YOLOv2的mAP有稍微下降(這裡下降的原因,我猜想是YOLOv2雖然使用了anchor boxes,但是依然採用YOLOv1的訓練方法)。YOLOv1隻能預測98個邊界框( 7	imes7	imes2 ),而YOLOv2使用anchor boxes之後可以預測上千個邊界框( 13	imes13	imes	ext{num_anchors} )。所以使用anchor boxes之後,YOLOv2的召回率大大提升,由原來的81%升至88%。

Dimension Clusters

在Faster R-CNN和SSD中,先驗框的維度(長和寬)都是手動設定的,帶有一定的主觀性。如果選取的先驗框維度比較合適,那麼模型更容易學習,從而做出更好的預測。因此,YOLOv2採用k-means聚類方法對訓練集中的邊界框做了聚類分析。因為設置先驗框的主要目的是為了使得預測框與ground truth的IOU更好,所以聚類分析時選用box與聚類中心box之間的IOU值作為距離指標:

d(box, centroid) = 1 - IOU(box, centroid)

圖3為在VOC和COCO數據集上的聚類分析結果,隨著聚類中心數目的增加,平均IOU值(各個邊界框與聚類中心的IOU的平均值)是增加的,但是綜合考慮模型複雜度和召回率,作者最終選取5個聚類中心作為先驗框,其相對於圖片的大小如右邊圖所示。對於兩個數據集,5個先驗框的width和height如下所示(來源:YOLO源碼的cfg文件):

COCO: (0.57273, 0.677385), (1.87446, 2.06253), (3.33843, 5.47434), (7.88282, 3.52778), (9.77052, 9.16828)

VOC: (1.3221, 1.73145), (3.19275, 4.00944), (5.05587, 8.09892), (9.47112, 4.84053), (11.2364, 10.0071)

但是這裡先驗框的大小具體指什麼作者並沒有說明,但肯定不是像素點,從代碼實現上看,應該是相對於預測的特徵圖大小( 13	imes13 )。對比兩個數據集,也可以看到COCO數據集上的物體相對小點。這個策略作者並沒有單獨做實驗,但是作者對比了採用聚類分析得到的先驗框與手動設置的先驗框在平均IOU上的差異,發現前者的平均IOU值更高,因此模型更容易訓練學習。

圖3:數據集VOC和COCO上的邊界框聚類分析結果

New Network: Darknet-19

YOLOv2採用了一個新的基礎模型(特徵提取器),稱為Darknet-19,包括19個卷積層和5個maxpooling層,如圖4所示。Darknet-19與VGG16模型設計原則是一致的,主要採用 3	imes3 卷積,採用 2	imes2 的maxpooling層之後,特徵圖維度降低2倍,而同時將特徵圖的channles增加兩倍。與NIN(Network in Network)類似,Darknet-19最終採用global avgpooling做預測,並且在 3	imes3 卷積之間使用 1	imes1 卷積來壓縮特徵圖channles以降低模型計算量和參數。Darknet-19每個卷積層後面同樣使用了batch norm層以加快收斂速度,降低模型過擬合。在ImageNet分類數據集上,Darknet-19的top-1準確度為72.9%,top-5準確度為91.2%,但是模型參數相對小一些。使用Darknet-19之後,YOLOv2的mAP值沒有顯著提升,但是計算量卻可以減少約33%。

圖4:Darknet-19模型結構

Direct location prediction

前面講到,YOLOv2借鑒RPN網路使用anchor boxes來預測邊界框相對先驗框的offsets。邊界框的實際中心位置 (x,y) ,需要根據預測的坐標偏移值 (t_x, t_y) ,先驗框的尺度 (w_a, h_a) 以及中心坐標 (x_a, y_a) (特徵圖每個位置的中心點)來計算:

x = (t_x	imes w_a)-x_a

y=(t_y	imes h_a) - y_a

但是上面的公式是無約束的,預測的邊界框很容易向任何方向偏移,如當 t_x=1 時邊界框將向右偏移先驗框的一個寬度大小,而當 t_x=-1 時邊界框將向左偏移先驗框的一個寬度大小,因此每個位置預測的邊界框可以落在圖片任何位置,這導致模型的不穩定性,在訓練時需要很長時間來預測出正確的offsets。所以,YOLOv2棄用了這種預測方式,而是沿用YOLOv1的方法,就是預測邊界框中心點相對於對應cell左上角位置的相對偏移值,為了將邊界框中心點約束在當前cell中,使用sigmoid函數處理偏移值,這樣預測的偏移值在(0,1)範圍內(每個cell的尺度看做1)。總結來看,根據邊界框預測的4個offsets t_x, t_y, t_w, t_h ,可以按如下公式計算出邊界框實際位置和大小:

b_x = sigma (t_x)+c_x

b_y = sigma (t_y) + c_y

b_w = p_we^{t_w}

b_h = p_he^{t_h}

其中 (c_x, x_y) 為cell的左上角坐標,如圖5所示,在計算時每個cell的尺度為1,所以當前cell的左上角坐標為 (1,1) 。由於sigmoid函數的處理,邊界框的中心位置會約束在當前cell內部,防止偏移過多。而 p_wp_h 是先驗框的寬度與長度,前面說過它們的值也是相對於特徵圖大小的,在特徵圖中每個cell的長和寬均為1。這裡記特徵圖的大小為 (W, H) (在文中是 (13, 13) ),這樣我們可以將邊界框相對於整張圖片的位置和大小計算出來(4個值均在0和1之間):

b_x = (sigma (t_x)+c_x)/W

 b_y = (sigma (t_y) + c_y)/H

b_w = p_we^{t_w}/W

 b_h = p_he^{t_h}/H

如果再將上面的4個值分別乘以圖片的寬度和長度(像素點值)就可以得到邊界框的最終位置和大小了。這就是YOLOv2邊界框的整個解碼過程。約束了邊界框的位置預測值使得模型更容易穩定訓練,結合聚類分析得到先驗框與這種預測方法,YOLOv2的mAP值提升了約5%。

圖5:邊界框位置與大小的計算示例圖

Fine-Grained Features

YOLOv2的輸入圖片大小為 416	imes416 ,經過5次maxpooling之後得到 13	imes13 大小的特徵圖,並以此特徵圖採用卷積做預測。 13	imes13 大小的特徵圖對檢測大物體是足夠了,但是對於小物體還需要更精細的特徵圖(Fine-Grained Features)。因此SSD使用了多尺度的特徵圖來分別檢測不同大小的物體,前面更精細的特徵圖可以用來預測小物體。YOLOv2提出了一種passthrough層來利用更精細的特徵圖。YOLOv2所利用的Fine-Grained Features是 26	imes26 大小的特徵圖(最後一個maxpooling層的輸入),對於Darknet-19模型來說就是大小為 26	imes26	imes512 的特徵圖。passthrough層與ResNet網路的shortcut類似,以前面更高解析度的特徵圖為輸入,然後將其連接到後面的低解析度特徵圖上。前面的特徵圖維度是後面的特徵圖的2倍,passthrough層抽取前面層的每個 2	imes2 的局部區域,然後將其轉化為channel維度,對於 26	imes26	imes512 的特徵圖,經passthrough層處理之後就變成了 13	imes13	imes2048 的新特徵圖(特徵圖大小降低4倍,而channles增加4倍,圖6為一個實例),這樣就可以與後面的 13	imes13	imes1024 特徵圖連接在一起形成 13	imes13	imes3072 大小的特徵圖,然後在此特徵圖基礎上卷積做預測。在YOLO的C源碼中,passthrough層稱為reorg layer。在TensorFlow中,可以使用tf.extract_image_patches或者tf.space_to_depth來實現passthrough層:

out = tf.extract_image_patches(in, [1, stride, stride, 1], [1, stride, stride, 1], [1,1,1,1], padding="VALID")// or use tf.space_to_depthout = tf.space_to_depth(in, 2)

圖6:passthrough層實例

另外,作者在後期的實現中借鑒了ResNet網路,不是直接對高分辨特徵圖處理,而是增加了一個中間卷積層,先採用64個 1	imes1 卷積核進行卷積,然後再進行passthrough處理,這樣 26	imes26	imes512 的特徵圖得到 13	imes13	imes256 的特徵圖。這算是實現上的一個小細節。使用Fine-Grained Features之後YOLOv2的性能有1%的提升。

Multi-Scale Training

由於YOLOv2模型中只有卷積層和池化層,所以YOLOv2的輸入可以不限於 416	imes416 大小的圖片。為了增強模型的魯棒性,YOLOv2採用了多尺度輸入訓練策略,具體來說就是在訓練過程中每間隔一定的iterations之後改變模型的輸入圖片大小。由於YOLOv2的下採樣總步長為32,輸入圖片大小選擇一系列為32倍數的值: {320, 352,..., 608} ,輸入圖片最小為 320	imes320 ,此時對應的特徵圖大小為 10	imes10 (不是奇數了,確實有點尷尬),而輸入圖片最大為 608	imes608 ,對應的特徵圖大小為 19	imes19 。在訓練過程,每隔10個iterations隨機選擇一種輸入圖片大小,然後只需要修改對最後檢測層的處理就可以重新訓練。

圖7:Multi-Scale Training

採用Multi-Scale Training策略,YOLOv2可以適應不同大小的圖片,並且預測出很好的結果。在測試時,YOLOv2可以採用不同大小的圖片作為輸入,在VOC 2007數據集上的效果如下圖所示。可以看到採用較小解析度時,YOLOv2的mAP值略低,但是速度更快,而採用高分辨輸入時,mAP值更高,但是速度略有下降,對於 544	imes544 ,mAP高達78.6%。注意,這只是測試時輸入圖片大小不同,而實際上用的是同一個模型(採用Multi-Scale Training訓練)。

圖8:YOLOv2在VOC 2007數據集上的性能對比

總結來看,雖然YOLOv2做了很多改進,但是大部分都是借鑒其它論文的一些技巧,如Faster R-CNN的anchor boxes,YOLOv2採用anchor boxes和卷積做預測,這基本上與SSD模型(單尺度特徵圖的SSD)非常類似了,而且SSD也是借鑒了Faster R-CNN的RPN網路。從某種意義上來說,YOLOv2和SSD這兩個one-stage模型與RPN網路本質上無異,只不過RPN不做類別的預測,只是簡單地區分物體與背景。在two-stage方法中,RPN起到的作用是給出region proposals,其實就是作出粗糙的檢測,所以另外增加了一個stage,即採用R-CNN網路來進一步提升檢測的準確度(包括給出類別預測)。而對於one-stage方法,它們想要一步到位,直接採用「RPN」網路作出精確的預測,要因此要在網路設計上做很多的tricks。YOLOv2的一大創新是採用Multi-Scale Training策略,這樣同一個模型其實就可以適應多種大小的圖片了。

YOLOv2的訓練

YOLOv2的訓練主要包括三個階段。第一階段就是先在ImageNet分類數據集上預訓練Darknet-19,此時模型輸入為 224	imes224 ,共訓練160個epochs。然後第二階段將網路的輸入調整為 448	imes448 ,繼續在ImageNet數據集上finetune分類模型,訓練10個epochs,此時分類模型的top-1準確度為76.5%,而top-5準確度為93.3%。第三個階段就是修改Darknet-19分類模型為檢測模型,並在檢測數據集上繼續finetune網路。網路修改包括(網路結構可視化):移除最後一個卷積層、global avgpooling層以及softmax層,並且新增了三個 3	imes3	imes2014卷積層,同時增加了一個passthrough層,最後使用 1	imes1 卷積層輸出預測結果,輸出的channels數為: 	ext{num_anchors}	imes(5+	ext{num_classes}) ,和訓練採用的數據集有關係。由於anchors數為5,對於VOC數據集輸出的channels數就是125,而對於COCO數據集則為425。這裡以VOC數據集為例,最終的預測矩陣為 T (shape為 (	ext{batch_size}, 13, 13, 125) ),可以先將其reshape為 (	ext{batch_size}, 13, 13, 5, 25) ,其中 T[:, :, :, :, 0:4] 為邊界框的位置和大小 (t_x, t_y, t_w, t_h)T[:, :, :, :, 4] 為邊界框的置信度,而 T[:, :, :, :, 5:] 為類別預測值。

圖9:YOLOv2訓練的三個階段

圖10:YOLOv2結構示意圖

YOLOv2的網路結構以及訓練參數我們都知道了,但是貌似少了點東西。仔細一想,原來作者並沒有給出YOLOv2的訓練過程的兩個最重要方面,即先驗框匹配(樣本選擇)以及訓練的損失函數,難怪Ng說YOLO論文很難懂,沒有這兩方面的說明我們確實不知道YOLOv2到底是怎麼訓練起來的。不過默認按照YOLOv1的處理方式也是可以處理,我看了YOLO在TensorFlow上的實現darkflow(見yolov2/train.py),發現它就是如此處理的:和YOLOv1一樣,對於訓練圖片中的ground truth,若其中心點落在某個cell內,那麼該cell內的5個先驗框所對應的邊界框負責預測它,具體是哪個邊界框預測它,需要在訓練中確定,即由那個與ground truth的IOU最大的邊界框預測它,而剩餘的4個邊界框不與該ground truth匹配。YOLOv2同樣需要假定每個cell至多含有一個grounth truth,而在實際上基本不會出現多於1個的情況。與ground truth匹配的先驗框計算坐標誤差、置信度誤差(此時target為1)以及分類誤差,而其它的邊界框只計算置信度誤差(此時target為0)。YOLOv2和YOLOv1的損失函數一樣,為均方差函數。

但是我看了YOLOv2的源碼(訓練樣本處理與loss計算都包含在文件region_layer.c中,YOLO源碼沒有任何注釋,反正我看了是直搖頭),並且參考國外的blog以及allanzelener/YAD2K(Ng深度學習教程所參考的那個Keras實現)上的實現,發現YOLOv2的處理比原來的v1版本更加複雜。先給出loss計算公式:

我們來一點點解釋,首先 W, H 分別指的是特徵圖( 13	imes13 )的寬與高,而 A 指的是先驗框數目(這裡是5),各個 lambda 值是各個loss部分的權重係數。第一項loss是計算background的置信度誤差,但是哪些預測框來預測背景呢,需要先計算各個預測框和所有ground truth的IOU值,並且取最大值Max_IOU,如果該值小於一定的閾值(YOLOv2使用的是0.6),那麼這個預測框就標記為background,需要計算noobj的置信度誤差。第二項是計算先驗框與預測寬的坐標誤差,但是只在前12800個iterations間計算,我覺得這項應該是在訓練前期使預測框快速學習到先驗框的形狀。第三大項計算與某個ground truth匹配的預測框各部分loss值,包括坐標誤差、置信度誤差以及分類誤差。先說一下匹配原則,對於某個ground truth,首先要確定其中心點要落在哪個cell上,然後計算這個cell的5個先驗框與ground truth的IOU值(YOLOv2中bias_match=1),計算IOU值時不考慮坐標,只考慮形狀,所以先將先驗框與ground truth的中心點都偏移到同一位置(原點),然後計算出對應的IOU值,IOU值最大的那個先驗框與ground truth匹配,對應的預測框用來預測這個ground truth。在計算obj置信度時,在YOLOv1中target=1,而YOLOv2增加了一個控制參數rescore,當其為1時,target取預測框與ground truth的真實IOU值。對於那些沒有與ground truth匹配的先驗框(與預測框對應),除去那些Max_IOU低於閾值的,其它的就全部忽略,不計算任何誤差。這點在YOLOv3論文中也有相關說明:YOLO中一個ground truth只會與一個先驗框匹配(IOU值最好的),對於那些IOU值超過一定閾值的先驗框,其預測結果就忽略了。這和SSD與RPN網路的處理方式有很大不同,因為它們可以將一個ground truth分配給多個先驗框。儘管YOLOv2和YOLOv1計算loss處理上有不同,但都是採用均方差來計算loss。另外需要注意的一點是,在計算boxes的 wh 誤差時,YOLOv1中採用的是平方根以降低boxes的大小對誤差的影響,而YOLOv2是直接計算,但是根據ground truth的大小對權重係數進行修正:l.coord_scale * (2 - truth.w*truth.h)(這裡w和h都歸一化到(0,1)),這樣對於尺度較小的boxes其權重係數會更大一些,可以放大誤差,起到和YOLOv1計算平方根相似的效果(參考YOLO v2 損失函數源碼分析)。

// box誤差函數,計算梯度float delta_region_box(box truth, float *x, float *biases, int n, int index, int i, int j, int w, int h, float *delta, float scale, int stride){ box pred = get_region_box(x, biases, n, index, i, j, w, h, stride); float iou = box_iou(pred, truth); // 計算ground truth的offsets值 float tx = (truth.x*w - i); float ty = (truth.y*h - j); float tw = log(truth.w*w / biases[2*n]); float th = log(truth.h*h / biases[2*n + 1]); delta[index + 0*stride] = scale * (tx - x[index + 0*stride]); delta[index + 1*stride] = scale * (ty - x[index + 1*stride]); delta[index + 2*stride] = scale * (tw - x[index + 2*stride]); delta[index + 3*stride] = scale * (th - x[index + 3*stride]); return iou;}

最終的YOLOv2模型在速度上比YOLOv1還快(採用了計算量更少的Darknet-19模型),而且模型的準確度比YOLOv1有顯著提升,詳情見paper。

YOLOv2在TensorFlow上實現

這裡參考YOLOv2在Keras上的復現(見yhcc/yolo2),使用TensorFlow實現YOLOv2在COCO數據集上的test過程。首先是定義YOLOv2的主體網路結構Darknet-19:

def darknet(images, n_last_channels=425): """Darknet19 for YOLOv2""" net = conv2d(images, 32, 3, 1, name="conv1") net = maxpool(net, name="pool1") net = conv2d(net, 64, 3, 1, name="conv2") net = maxpool(net, name="pool2") net = conv2d(net, 128, 3, 1, name="conv3_1") net = conv2d(net, 64, 1, name="conv3_2") net = conv2d(net, 128, 3, 1, name="conv3_3") net = maxpool(net, name="pool3") net = conv2d(net, 256, 3, 1, name="conv4_1") net = conv2d(net, 128, 1, name="conv4_2") net = conv2d(net, 256, 3, 1, name="conv4_3") net = maxpool(net, name="pool4") net = conv2d(net, 512, 3, 1, name="conv5_1") net = conv2d(net, 256, 1, name="conv5_2") net = conv2d(net, 512, 3, 1, name="conv5_3") net = conv2d(net, 256, 1, name="conv5_4") net = conv2d(net, 512, 3, 1, name="conv5_5") shortcut = net net = maxpool(net, name="pool5") net = conv2d(net, 1024, 3, 1, name="conv6_1") net = conv2d(net, 512, 1, name="conv6_2") net = conv2d(net, 1024, 3, 1, name="conv6_3") net = conv2d(net, 512, 1, name="conv6_4") net = conv2d(net, 1024, 3, 1, name="conv6_5") # --------- net = conv2d(net, 1024, 3, 1, name="conv7_1") net = conv2d(net, 1024, 3, 1, name="conv7_2") # shortcut shortcut = conv2d(shortcut, 64, 1, name="conv_shortcut") shortcut = reorg(shortcut, 2) net = tf.concat([shortcut, net], axis=-1) net = conv2d(net, 1024, 3, 1, name="conv8") # detection layer net = conv2d(net, n_last_channels, 1, batch_normalize=0, activation=None, use_bias=True, name="conv_dec") return net

然後實現對Darknet-19模型輸出的解碼:

def decode(detection_feat, feat_sizes=(13, 13), num_classes=80, anchors=None): """decode from the detection feature""" H, W = feat_sizes num_anchors = len(anchors) detetion_results = tf.reshape(detection_feat, [-1, H * W, num_anchors, num_classes + 5]) bbox_xy = tf.nn.sigmoid(detetion_results[:, :, :, 0:2]) bbox_wh = tf.exp(detetion_results[:, :, :, 2:4]) obj_probs = tf.nn.sigmoid(detetion_results[:, :, :, 4]) class_probs = tf.nn.softmax(detetion_results[:, :, :, 5:]) anchors = tf.constant(anchors, dtype=tf.float32) height_ind = tf.range(H, dtype=tf.float32) width_ind = tf.range(W, dtype=tf.float32) x_offset, y_offset = tf.meshgrid(height_ind, width_ind) x_offset = tf.reshape(x_offset, [1, -1, 1]) y_offset = tf.reshape(y_offset, [1, -1, 1]) # decode bbox_x = (bbox_xy[:, :, :, 0] + x_offset) / W bbox_y = (bbox_xy[:, :, :, 1] + y_offset) / H bbox_w = bbox_wh[:, :, :, 0] * anchors[:, 0] / W * 0.5 bbox_h = bbox_wh[:, :, :, 1] * anchors[:, 1] / H * 0.5 bboxes = tf.stack([bbox_x - bbox_w, bbox_y - bbox_h, bbox_x + bbox_w, bbox_y + bbox_h], axis=3) return bboxes, obj_probs, class_probs

我將YOLOv2的官方訓練權重文件轉換了TensorFlow的checkpoint文件(下載鏈接),具體的測試demo都放在我的GitHub上了,感興趣的可以去下載測試一下,至於train的實現就自己折騰吧,相對會棘手點。

圖11:YOLOv2在自然圖片上的測試

YOLO9000

YOLO9000是在YOLOv2的基礎上提出的一種可以檢測超過9000個類別的模型,其主要貢獻點在於提出了一種分類和檢測的聯合訓練策略。眾多周知,檢測數據集的標註要比分類數據集打標籤繁瑣的多,所以ImageNet分類數據集比VOC等檢測數據集高出幾個數量級。在YOLO中,邊界框的預測其實並不依賴於物體的標籤,所以YOLO可以實現在分類和檢測數據集上的聯合訓練。對於檢測數據集,可以用來學習預測物體的邊界框、置信度以及為物體分類,而對於分類數據集可以僅用來學習分類,但是其可以大大擴充模型所能檢測的物體種類。

作者選擇在COCO和ImageNet數據集上進行聯合訓練,但是遇到的第一問題是兩者的類別並不是完全互斥的,比如"Norfolk terrier"明顯屬於"dog",所以作者提出了一種層級分類方法(Hierarchical classification),主要思路是根據各個類別之間的從屬關係(根據WordNet)建立一種樹結構WordTree,結合COCO和ImageNet建立的WordTree如下圖所示:

圖12:基於COCO和ImageNet數據集建立的WordTree

WordTree中的根節點為"physical object",每個節點的子節點都屬於同一子類,可以對它們進行softmax處理。在給出某個類別的預測概率時,需要找到其所在的位置,遍歷這個path,然後計算path上各個節點的概率之積。

圖13:ImageNet與WordTree預測的對比

在訓練時,如果是檢測樣本,按照YOLOv2的loss計算誤差,而對於分類樣本,只計算分類誤差。在預測時,YOLOv2給出的置信度就是 Pr(physical space object) ,同時會給出邊界框位置以及一個樹狀概率圖。在這個概率圖中找到概率最高的路徑,當達到某一個閾值時停止,就用當前節點表示預測的類別。

通過聯合訓練策略,YOLO9000可以快速檢測出超過9000個類別的物體,總體mAP值為19,7%。我覺得這是作者在這篇論文作出的最大的貢獻,因為YOLOv2的改進策略亮點並不是很突出,但是YOLO9000算是開創之舉。

YOLOv3

近期,YOLOv3發布了,但是正如作者所說,這僅僅是他們近一年的一個工作報告(TECH REPORT),不算是一個完整的paper,因為他們實際上是把其它論文的一些工作在YOLO上嘗試了一下。相比YOLOv2,我覺得YOLOv3最大的變化包括兩點:使用殘差模型和採用FPN架構。YOLOv3的特徵提取器是一個殘差模型,因為包含53個卷積層,所以稱為Darknet-53,從網路結構上看,相比Darknet-19網路使用了殘差單元,所以可以構建得更深。另外一個點是採用FPN架構(Feature Pyramid Networks for Object Detection)來實現多尺度檢測。YOLOv3採用了3個尺度的特徵圖(當輸入為 416	imes416 時): (13	imes13)(26	imes26)(52	imes52) ,VOC數據集上的YOLOv3網路結構如圖15所示,其中紅色部分為各個尺度特徵圖的檢測結果。YOLOv3每個位置使用3個先驗框,所以使用k-means得到9個先驗框,並將其劃分到3個尺度特徵圖上,尺度更大的特徵圖使用更小的先驗框,和SSD類似。

圖14:YOLOv3所用的Darknet-53模型

圖15 YOLOv3網路結構示意圖(VOC數據集)

YOLOv3與其它檢測模型的對比如下圖所示,可以看到在速度上YOLOv3完勝其它方法,雖然AP值並不是最好的(如果比較AP-0.5,YOLOv3優勢更明顯)。

圖16:YOLOv3在COCO測試集與其它檢測演算法對比圖

小結

從YOLO的三代變革中可以看到,在目標檢測領域比較好的策略包含:設置先驗框,採用全卷積做預測,採用殘差網路,採用多尺度特徵圖做預測。期待未來有更好的策略出現。本人水平有限,文中難免出現錯誤,歡迎指正!

參考

  1. Darknet官網.
  2. thtrieu/darkflow.
  3. You Only Look Once: Unified, Real-Time Object Detection.
  4. YOLO9000: Better, Faster, Stronger.
  5. YOLOv3: An Incremental Improvement.
  6. yhcc/yolo2.
  7. pytorch-yolo2.
  8. Training Object Detection (YOLOv2) from scratch using Cyclic Learning Rates.
  9. allanzelener/YAD2K.
  10. marvis/pytorch-yolo3.

碼字不易,歡迎給個贊!

歡迎交流與轉載,文章會同步發布在公眾號:機器學習演算法全棧工程師(Jeemy110)

推薦閱讀:

一些圖像處理模塊
CVPR2018 | 直接建模視覺智能體?讓「小狗」動起來~
[計算機論文速遞] 2018-03-23
淺談深度學習的技術原理及其在計算機視覺的應用
brox近期論文

TAG:計算機視覺 | 目標檢測 | 深度學習DeepLearning |