目標檢測 | 讓YOLO算法告訴你迴歸網絡的能力

0.簡介

這段時間YOLO系列算法很火,尤其是YOLOv3,很多大牛都復現了其在各種平臺下的實現(tensorflow,pytorch,caffe…)。主要還是因爲YOLOv3算法結合了很多有用的trick,兼顧了速度和精度。但究其本質仍然是迴歸算法,所以我們在這裏先詳細介紹下YOLOv1的實現細節,好了解迴歸算法的特性。網上有很多教程,但質量參差不齊,而且本人閱讀起來很多細節比較難理解。因此還是打算總結一下,加深印象。後續還會寫v2和v3的實現細節。我們主要分爲三部分來介紹,算法整體結構+算法前向計算+算法訓練過程。總體來說,YOLOv1算法的特點爲算法結構簡單,但具體實現細節比較多。

1.YOLO算法結構圖

YOLO是利用一個簡單的迴歸網絡實現目標檢測分類算法。直接放一張圖來說明YOLO算法結構之簡單。下圖不是YOLO論文中的原始結構,而是用一個24層的網絡替代了論文中原作者用的GoogleNet作爲YOLO檢測算法的特徵提取器(我們可以用任意的分類網絡替代,比如MobileNet,SqueezeNet,ShuffleNet等)。

那麼有人會問這麼簡單的網絡是怎麼實現目標檢測和分類的呢?其所有的祕密就在如下所示的相對複雜的損失函數中(所以損失函數纔是深度學習任務的核心嘛,我一直這麼認爲)。詳細細節我們下面展開討論。(下述代碼爲網絡最後一層的輸出層,和損失函數層)

layer {
  name: "reg_reshape"
  type: "Reshape"
  bottom: "conv_reg"
  top: "regression"
  reshape_param {
    axis: 1
    shape {
      dim: 1470
    }
  }
}

layer {
  name: "det_loss"
  type: "DetectionLoss"
  bottom: "result"
  bottom: "label"
  top: "det_loss"
  loss_weight: 1
  detection_loss_param {
    side: 7
    num_class: 20
    num_object: 2
    object_scale: 1.0
    noobject_scale: 0.5
    class_scale: 1.0
    coord_scale: 5.0
    sqrt: true
    constriant: true
  }
}

2.利用YOLO作前向測試

(1)簡介

YOLO的前向測試爲:將一張圖片送入YOLO網絡後網絡輸出檢測結果的整體流程。從上述的代碼結構中我們可以發現,網絡最終的輸出是一個1470維的向量,接下來我們將基於該向量來展開討論這1470維向量和最終輸出的檢測分類結果之間的關係。

首先我們先把輸出維度切分成1470 =(7*7)*(5+5+20)。這裏用一張網上很常見的圖來輔助解釋這個過程。

先解釋下上述的(7*7)*(5+5+20)中各個數字的含義:在解釋之前首先區分兩個概念:cell(紅色框) 和 bounding box(黃色框)。

先介紹cell,本網絡輸出49個cell(和最後的特徵圖大小對等)。

  • (7*7):表示將圖片分成49個等大小的cell格子(如圖紅色格子所示)。

對於每個cell而言,網絡輸出2個bounding box,每個bounding box都有一個confidence。(注意confidence和confidence score的區別,因爲我發現很多博客的介紹中沒有嚴格區分。)

  • (5+5):每一個5對應一個bounding box(黃色框),其中前4個值表示boundxing box位置(x,y,w,h),後1個值表示當前框的置信度confidence。
  • 20:代表了類別的概率,作者使用的數據集含有20個類別,因此該值爲20,該值可以根據自己的數據類別進行定製。需要注意的是我們發現圖中有兩個黃色框(bounding box)但確只有一組概率值,因爲概率值是針對於紅色框(cell)而言的,即不論一個cell有多少個bounding box,它最終都只有一組類概率。
2.開始介紹測試過程:
  • 1.計算每個bounding box的confidence score
    在得到了1470維向量後,一共49個cell,我們對每一個cell的30維向量來分析,因爲上面提到每個cell有兩個bounding box卻只有1組類概率(20個),那麼爲了使每個bounding box都能得到一個類概率,因此我們要對每個bounding box作如下的乘法操作。

    Pr(Object)乘以IOU(bounding box和標籤的IOU)就是上面提到的confidence,而Pr(Class|Object)就是上面提到的類概率。最終的乘積就是最終當前bounding box的confidence score(注意和confidence顯然不是一個意思)。上式中乘以IOU,說明了IOU越大那麼confidence score也越大,所以最終的confidence score結合了座標最優和類別最優。上述公式中左邊兩項的乘法操作示意圖如下。

最終對49個cell分別做上述操作後,可以得到如下圖所示的98個bounding box且每一個box都會有一個對應的20維度的confidence score。下圖中的每一列黃色的表示一個bounding box,下圖中的每一行黑色的表示一種類別。

  • 2.基於閾值刪除低分box
    在該過程中,以每一個類別爲單位,將confidence score低於0.2的值直接置爲零(相當於刪除)。僞代碼如下圖所示,閾值是一個超參數。代碼中的i,j參照上圖。相當於對於當前類別,將confidence score小於閾值的box過濾掉,剩下的分數高的box。

  • 3.利用NMS算法去重
    利用非極大值抑制法,進一步剔除質量較差的框。非極大值抑制(Non-Maximum Suppression,NMS),顧名思義就是抑制不是極大值的元素,可以理解爲局部最大搜索,保留局部最好的框,不好的如同閾值篩選一樣置爲零。示意圖如下(來自網絡)

3.訓練過程詳系介紹

要了解訓練過程主要就是了解兩個要點:標籤和損失函數。

  • 1.標籤

要介紹訓練過程,對標籤的理解是必須的。所以接下來介紹一下YOLO算法訓練時候的標籤。這有助於更好的理解YOLO的訓練原理。在目標檢測任務中我們輸入的標籤往往如下所示:第一個字段代表的是圖片名,最後四個字段代表的是目標框位置(有多種表達方式,如相對於左上角的百分比,絕對位置等),第二個字段代表的是當前目標框中目標類別。第一行代表圖1中有兩個目標。YOLO也不例外,其標籤也與之類似(當然具體示代碼而定)。

0000001.jpg 1 72 79 232 273  2  67 59 155 161
0000002.jpg 2 67 59 155 161

通過上述給定的標籤我們可以計算得到損失函數會用到的ground truth部分。在介紹損失函數的過程中會詳細說到需要通過標籤計算,並用於損失函數構建的部分。

  • 2.損失函數

YOLO算法通過迴歸的方式實現目標檢測和分類。
因此YOLO算法的損失函數分爲兩大部分:定位誤差和分類誤差。
其中分類誤差又分爲:置信度誤差和類概率誤差。
其中置信度誤差又分爲:含有物體的置信度誤差和不含物體的置信度誤差。
所以最終其損失函數如下圖所示。


從圖中的紅色框我們可以發現,我們對每一個bounding box都要計算定位誤差和置信度誤差,而計算類概率誤差是針對於每個cell的。
從圖中的藍色框我們可以發現,我們對每一個包含有目標的bounding box都會計算定位誤差,置信度誤差,且對包含目標的cell計算類概率誤差。但是對於不包含目標的bounding box我們只需要計算置信度誤差。(至於什麼叫做bounding box是否包含目標,cell是否包含目標,見如下注釋)。

1.怎麼判斷cell或bounding box是否包含目標?
每一個目標位置就相當於我們輸入的標籤信息,有了該信息後,也就知道每個目標的中心座標的位置。所以目標的中心座標坐落於某個cell中,那麼我們就說當前cell包含目標,隨之也就說當前cell的兩個bounding box也包含目標。我們發現只要cell包含了目標,其對應的兩個bounding box一定就隨之被認爲也包含目標。所以在正常的描述中我們就不怎麼將bounding box是否包含目標,我們只說cell是否包含目標。且這種情況下我們認爲該目標由當前cell負責預測。

需要標籤計算些什麼呢?
如上圖的損失函數所示,一共有四組誤差:每個bounding box的定位誤差,cell包含物體時的confidence誤差,cell不包含物體時的confidence誤差,cell的類概率誤差。所以與之對應的我們需要通過標籤得到四個對應的ground truth,如下圖所示,右邊圖中的四個選項都是可以通過輸入標籤計算得到的。

最後介紹一些小細節:

    side: 7                 #將原始圖片分成7*7的cell
    num_class: 20           #當前數據集一共有20個類別的目標
    num_object: 2           #每個cell都會有兩個bounding box
    object_scale: 1.0 #計算損失函數時,包含object的bounding box的confidence loss係數
  noobject_scale: 0.5#計算損失函數時,不包含object的bounding box的confidence loss係數
                    #(因爲不包含的多,所以講係數設小防止其loss過大,影響整體訓練)
    class_scale: 1.0       #計算損失函數時,每個cell所預測的類別概率誤差係數
    coord_scale: 5.0       #計算損失函數時,每個bounding box的座標誤差係數
 sqrt: true             #原始box的寬高(w,h)進行均方根轉換     
 constriant: true       #

下面兩段源碼說明了下述的兩個參數的含義,constriant表示將原始bounding box的中心點座標(x,y)進行與grid cell對齊。sqrt表示將原始box的寬高(w,h)進行均方根轉換。轉換原因如下:

這裏注意用寬和高的開根號代替原來的寬和高,這樣做主要是因爲相同的寬和高誤差對於小的目標精度影響比大的目標要大。舉個例子,原來w=10,h=20,預測出來w=8,h=22,跟原來w=3,h=5,預測出來w1,h=7相比,其實前者的誤差要比後者小,但是如果不加開根號,那麼損失都是一樣:4+4=8,但是加上根號後,變成0.15和0.7。 (來自網絡)

        if (constriant_) {
          box[0] = (j % side_ + box[0]) / side_;
          box[1] = (j / side_ + box[1]) / side_;
        }
        if (sqrt_) {
          box[2] = pow(box[2], 2);
          box[3] = pow(box[3], 2);
        }

YOLO算法的細節比較多~若是有講錯沒講清楚的歡迎指出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章