1. 程式人生 > >You only look once系列

You only look once系列

YOLOV1

1、綜述

Yolov1是CVPR2016的論文,Yolov1的網路速度很快,可以實時處理圖片,達到45fps,還有一種改進的fast Yolo(減少了一些卷積層),可以達到155fps,精度也比較高,雖然會在定位上錯誤比較高,但是會在將背景預測為正樣本的情況較少。主要的思想就是將一幅圖片分成一個SxS grid,每個cell負責檢測落在其中的gt框內容,那麼只有當gt框的中心落在cell中,才算這個gt屬於這個cell。且不像二階段中有RPN階段,Yolov1是直接輸出最終得到的結果,結果維度為SxSx(B*5+C),其中SxS是cell的數量,B為每個cell中bbox的數量,C為類別數。在本輪文中使用的是C=20,B=2,S=7。

    

2、Details

首先,YOLOv1的網路是使用自己設計的darknet。使用的darknet網路最開始是在ImageNet上使用224x224(在進行目標檢測的時候是448x448)的圖進行預訓練,預訓練的任務是進行影象分類,大概訓練了一週時間,在ImageNet 2012 validation set達到了top-5 accuracy 80%的效果,堪比GoogleNet的模型達到的效果。

考慮到檢測的任務需要細粒度的資訊,所以將輸入圖片的解析度由224x224變成448x448。最終的預測結果是7x7x(5x2+20),其中5是1 class probabilities+ 4 bbox coordinates。需要將bbox的寬和長歸一化,即相對於image width and height的比例,取值範圍在0-1,bbox的中心座標x,y則是相對於cell左上座標的偏移,也是一個0-1範圍的值(見下面程式碼)。

#如下是gt框計算loss需要的對應的值的過程
cellx = 1. * w / S
celly = 1. * h / S
for obj in allobj:
    centerx = .5*(obj[1]+obj[3]) #xmin, xmax
    centery = .5*(obj[2]+obj[4]) #ymin, ymax
    cx = centerx / cellx
    cy = centery / celly
    # obj[1] is lttop_x, obj[2] is lttop_y, obj[3] is rigbot_x, obj[4] is rigbot_y 
    obj[3] = float(obj[3]-obj[1]) / w
    obj[4] = float(obj[4]-obj[2]) / h
    obj[3] = np.sqrt(obj[3])
    obj[4] = np.sqrt(obj[4])
    obj[1] = cx - np.floor(cx) # centerx
    obj[2] = cy - np.floor(cy) # centery
    obj += [int(np.floor(cy) * S + np.floor(cx))]

在這裡,最終的每一個obj為(None, centerx, centery, w, h, index),其中index是表示obj所在的cell,且是flatten後的index。但是這後面有一個處理(如下程式碼):

   # Calculate placeholders' values
    probs = np.zeros([S*S,C])
    confs = np.zeros([S*S,B])
    coord = np.zeros([S*S,B,4])
    proid = np.zeros([S*S,C])
    prear = np.zeros([S*S,4])
    for obj in allobj:
        probs[obj[5], :] = [0.] * C
        probs[obj[5], labels.index(obj[0])] = 1.
        proid[obj[5], :] = [1] * C
        coord[obj[5], :, :] = [obj[1:5]] * B
        prear[obj[5],0] = obj[1] - obj[3]**2 * .5 * S # xleft
        prear[obj[5],1] = obj[2] - obj[4]**2 * .5 * S # yup
        prear[obj[5],2] = obj[1] + obj[3]**2 * .5 * S # xright
        prear[obj[5],3] = obj[2] + obj[4]**2 * .5 * S # ybot
        confs[obj[5], :] = [1.] * B

在for迴圈中,obj[5]作為每個量的索引,因此,如果有兩個gt在同一個cell中,在迴圈中後面迴圈到的會把同樣cell的前一個gt的資訊覆蓋掉,所以,可以姑且認為yolov1比較自信的將所有的gt劃分在不同的cell中,否則就會有檢測不到的。(這一份程式碼是在github上有3k+的stars,而且不知道我理解的是否有誤)。

Loss function

根據Loss再來看其他的一些細節部分:

1)其中\lambda _{coord}=5\lambda_{noobj}=0.5,之所以需要這兩個權值:優化是使用平方和誤差loss函式(比較直接簡單),但是,它會將位置檢測貢獻的loss和分類貢獻的loss平等對待,即會將兩者的權值設為一致,這樣是不科學的。因為在每個圖中,許多的cell中不包含有目標,這其中的bbox的產生的loss容易對那些擁有目標的cell產生的loss產生影響。最終的效果就是導致模型的不穩定,導致訓練在早期出現分歧。(在這裡應該是會導致模型無法很好的收斂,來回動盪)。故採用兩個權值來引入關注。

2)(上圖)其中的長寬的loss,使用平方根的原因是為了減少大目標和小目標對loss的貢獻值的差距,比如一個大目標,長寬值都比較大,那麼直接與gt框的平方和會比較大,可以設想,將大目標和其對應bbox成比例縮小,雖然我們可以知道其對loss的貢獻應該是一樣的,但是通過w,h直接的平方和來計算,其結果相對於原來沒縮小的會減少。所以,用平方根,算是一個減少差距的小trick。

3)loss是直接使用x,y,w,h計算的,雖然這些都是相對值,不像二階段是直接計算偏移量。Yolov2也是沿用這樣的計算。

4)在yolov1中,每個cell中有兩個產生的bbox,但是每個gt只會對應一個正樣本,選擇與gt的IOU最高的那個bbox作為正樣本。

5)P為對bbox是否有目標的預測值,gt的這個標籤為最優IOU的值,即取gt與2個bbox的IOU值的最大值。這個loss是每個cell都會產生一個,當然得是包含有目標的cell。

6)其中的C是類別的probabilities。bbox是預測出來的,gt的則為1或0,因為有20個類,共20個值。C產生的loss有兩個式子,一個是有目標bbox產生的,一個無目標bbox產生,且權值不同。

YOLOv2

第二個版本相當於提出了兩個點,一個是對YOLOv1的改進,一個就是針對類別數提升到9000個類別,CVPR2016的在pascal和coco上的state-of-the-art,在VOC2007上有76.8的mAP,速度是67fps,如果是40fps的,則有78.6的mAP,比同時期的faster rcnn和SSD效果都要好,且速度更快。上述的是對YOLO的改進。而YOLO9000可以看作是一種訓練的方式,沒有著重在網路結構的修改,而是提出了一種訓練方法(YOLO9000的聯合訓練可以為分類的資料來預測位置)。

一下是一些零散的結構改進:

1)比較值得注意的一點是這個版本使用了anchor box。v2將v1中最後的FC層都去掉。具體做法是

  • 首先移除掉一個池化層,這樣可以獲得更高的解析度。
  • 將輸入由原來的448x448變為416x416,這樣做是為了在後面feature map中可以達到奇數個畫素點,416/32=13,最後是13x13,這樣會產生一個單獨的中心點。(這樣是基於一個設定,即如果有大目標的話,大目標的中心更可能傾向於在整個image的中心,那麼有cell可以更好的捕捉到這個點,如果是偶數的話,可能就落在周圍四個cell中的某一箇中。這樣的想法是有道理的,因為在影象到feature map的過程中,其實最中間的cell有一個比較大的感受野,可以捕捉到原始影象中很大中心區域。)

2)Batch Normalization:論文使用Batch Normalization作為正則化手段,這樣其他的一些正則化手段(dropout)就可以去掉了。做法是在所有的卷積層後都加上batch normalization,將會有2%的提升。

3)高解析度分類:現在大部分的目標檢測模型都會使用在ImageNet上預訓練的分類器,因此大多的輸入為256x256,YOLOv1則是先用224x224訓練分類器,目標檢測網路訓練的時候使用的是448x448的輸入。那麼在v2版本,就要在ImageNet上得到了預訓練的分類器後,先將輸入改為448x448後,再進行ImageNet上10個epoches的分類器訓練。最後進行目標檢測器的訓練調整。這樣的三個步驟會給mAP帶來4%的提升。

4)使用anchor boxes,可以將分類從空間位置中解耦出來,即可以將其是做相對單純的分類。使用anchor box,精度會有一定的下降。在v1中每幅圖僅僅產生7x7x2=98個bbox,若是使用anchor box,則會產生比較多的box。如果不使用anchor,那麼有69.5%的mAP,81%的召回率,如果使用了anchor,則有69.2%的mAP和88%的召回率。很明顯,這樣做是比較值得的。

5)anchor box的形狀,是使用聚類演算法來得到的,並且每個cell使用5個anchor box是比較好的效果和計算量的平衡。有一個值得注意的點,在使用聚類演算法時,其度量的方法是使用IOU來參與其中的聚類度量。

6)在v1中,會出現一個早期迭代模型不穩定的情況,不穩定的來源則是因為直接對box座標的預測,在v2中,是預測出相對於網格單元位置的位置座標。其實我理解也可以是某種意義上的偏移offset,因為實際上,網路對每個Anchor box預測五個coordinates,分別是t_x, t_y, t_w, t_y, t_o,如下圖所示的計算,其中C_x, C_y是cell相對於image左上的偏移。下面的Pr(object)是gt的label值。那麼相對於預測值來說,其代表的含義就是Pr(object)*IOU(b,object)。P_w, P_h是anchor box的長寬,t_x, t_y經sigmoid函式的約束,取值在0-1,這樣再加上偏移,會得到在cell中的位置b_x, b_y,這裡應該是指代的中心座標,這個點是無所謂,看具體的實現。中心座標是我們的轉換後的預測值,且是一個相對值。

這裡的,實際可以這麼認為,anchor box的作用是提供一個可參考的比較符合各個目標情況的長寬參考,那麼5個anchor box就有五個參考,我們預測也會有5個預測組,都是基於這五個參考分別進行預測長寬的offset,至於對於中心座標的預測,就是以cell的左上座標來做一個位置偏移預測,最終如下來計算我們要投入loss計算的(b_x, b_y, b_w, b_h)。這樣的方式給網路帶來5%的提升。

後面的套路和v1一樣,選取IOU最大的那個bbox作為正樣本,其餘的作為負樣本?