檢測算法實際同時作檢測(坐标回歸)和判别(分類),并且從直覺上講,後者依賴于前者。而YOLO v3是個多任務,同時完成這2項任務。這與我以前常見的任務不一樣,是個很好的學習案例。而在學習YOLOv3之前以及過程中,先後産生了許許多多的疑問。例如:
- 多個cell,多個anchor,在訓練時哪個與ground truth作比對?
- 為何需要有anchor box?anchor box是怎麼來的?
- IOU用于何處?
- NMS用于何處?
這些問題是相關聯的。其中的關鍵是弄清訓練所用的loss和label。為了了解loss和label,先了解網絡架構。
Architecture overview

對三層作監督,分别重點檢測大中小物體。
如果從未接觸過檢測算法,一定會對YOLOv3有别于其它CNN的諸多方面深表驚奇。驚奇可能意味着巧妙,也可能意味着不合理或者局限。在YOLOv3身上二者兼備。
Output and loss
需要監督的輸出層如下。The shape of the detection kernel is 1 x 1 x (B x (5 + C) ).
這裡有如下令人驚奇之處。
- 輸出格式。訓練,總是把預測輸出與ground truth作比對。我們通常碰到的任務,一個樣本通常隻(預測)輸出一個值,例如,分類中的類别,回歸中的實值。而YOLOv3的預測輸出N個結構(可以把它了解為c語言的struct),N= cell number * B個,其中B一個cell能預測的bounding box數目(即每一層的anchor數目,實作中一般是取為3)。cell number三層分别為13*13, 26*26, 52*52。結構中有3類成員:bounding box attributes (x, y, w, h),置信度,類别。總共有(5+C)個,5是4 bounding box attributes and one object confidence,C是類個數。
- feature map用途。通常CNN的feature map往往隻存在hidden層,最後在輸出層通過full connection層或者average polling層轉換為vector。而YOLOv3的feature map直接作為輸出。通常不同channel用于表示圖像的不同特征。而YOLOv3的不同channel不僅可以表達是圖像特征,例如人、車這種類别,還可以表達坐标和置信度。
很多情況下,我們不創新,并不是沒有想法,而是沒有想到可以這麼實作。有時,我們說,在網絡結構上改進餘地不大,但在怎麼使用網絡來作表征上,YOLOv3大大拓寬了我們的思路。
以上說過,YOLOv3的輸出結構中有3個,bbox attribute, object confidence, class score。loss就是以上三部分的權重和,其中bbox attribute (x, y, w, h)用MSE,後二者用交叉熵。
看起來比較簡單,裡面還是有些trick。
首先,output有cell number * B(對第一層是13*13*3)個這樣的結構,而ground truth才有目标個數(假定對一幅圖的最大檢測目标個數為20)個。13*13*3與20如何作MSE或者交叉熵?通常我們能想到的是,對13*13*3作某種grouping算法得出與ground truth相同shape的scalar或者tensor與ground truth比較。但YOLOv3的作法正好與此相反,是為20個找到13*13*3個格子中的20格,來安放ground truth,然後把其餘格子全部置0。如何找到這些格子呢?對于width和height這2個次元很容易,就是根據中心點重合。而對于depth(即B)這個次元,它是找到與gound truth的bbox有最大IOU的anchor box對應的格式。這裡第一處用到IOU。它有一個局限,一個格子隻能容納一個目标。假如一個人坐在一張椅子上,中心位置和寬高大小比例都相若,本來同屬于一個格子,那麼即使打标時打了2個标,在訓練時,也隻有一個标(樣本)會生效,後面一個樣本會覆寫前面一個樣本。從設計上就注定了訓練時的這種局限。不過,這不是什麼大問題,頂多就是浪費了一個訓練樣本而已。那麼在預測時,能否檢測出2個來?設計上是可以的,因為結構中包括了C個class score。但我看到的有的編碼實作都隻保留class score最大的那個,這是可以作微改進的。
其次,不知你有沒有注意到,object confidence有點奇怪,不僅算法奇怪,它的存在就很奇怪。BBOX可以用回歸,class可以用交叉熵,怎麼突然冒出個confidence?它是對目标從屬于哪個格子的confidence,還是對(x, y, w, h)的值的confidence?YOLOv1論文上有解釋,These confidence scores reflect how confident the model is that the box contains an object and also how accurate it thinks the box is that it predicts. Formally we define confidence as Po * IOU.
在計算confidence的loss時,對于正樣本(即該格子有目标),計交叉熵,這很正常。對于負樣本(即該格子沒有目标),隻有bbox與ground truth的IOU小于門檻值ignore threshold(通常取為0.5),才計交叉熵。這裡第一處用到threshold,第二處用到IOU。為啥對正負樣本差別對待呢?我的了解是,對于有目标的格子,如果預測出有目标的可能性低,即輸出值的Po小,不正常,需要作懲罰。而對于沒目标的格子,如果預測出有目标的可能性高,即輸出值的Po高,這有可能是正常的,因為目标的中心點不在這個格子,可能在附近的格子,是否需要懲罰要視情況而定。如果IOU小于門檻值,說明附近的格子沒有目标,Po高不正常,需要懲罰,即參與計算交叉熵。大于門檻值,說明附近的格子有目标,Po高正常,不需要處罰,即計算交叉熵時忽略。這一點與我看到的文章的說法以及同僚們的看法正好相反,而與我了解的keras代碼和pytorch代碼一緻。
這裡還有一個問題,上文中提到,對于沒有包含目标的格子,它的5+C全部置0,是以bbox也為0,那如何計算它與預測輸出的bbox的IOU呢?實際上,此處計算IOU時,它不是與本格子的ground truth的bbox計算IOU,而是與所有目标的bbox都計算IOU,并取最大值。YOLOv1論文上的這段話the confidence prediction represents the IOU between the predicted box and any ground truth box.證明了該解釋。
總結一下loss公式。
loss = bbox loss + confidence loss + class loss
bbox loss =
其中,bx, by, bw, bh是我們預測的x,y中心坐标,寬度和高度。lx, ly, lw, lh是ground truth的x,y中心坐标,寬度和高度。
代表該格子是否有目标,有為1,無為0。
confidence loss =
其中,同上,分别為object confidence的ground truth和預測輸出的機率,為二者的交叉熵,m為mask,即如上段中所說,正樣本恒為1,負樣本依條件而0或1。
class loss =
其中,
同上,C為類個數,p(c), q(c)分别為各類的ground truth和預測輸出的機率,KL同上。
盡管在道理上解釋得通,我覺得不太合理,一方面過于複雜,另一方面多了個超參ignore threshold。也許有簡潔的改進方案,object confidence這個loss可以反過來算,不是把預測輸出值與ground truth雙方統一到格子上,而是統一到目标(即最多20個)上來。
最後再提醒一下,一個沒有被ground truth占的格子,它不參與計算BBOX的loss和class的loss,但參與計算object confidence的loss。
預測
For an image of size 416 x 416, YOLO predicts ((52 x 52) + (26 x 26) + 13 x 13)) x 3 = 10647 bounding boxes. 這麼多個預測輸出如何縮減到目标個數呢?經過2道工序。
- object confidence threshold
過濾掉小于object confidence threshold的格子。這裡第二處用到threshold。
- NMS,即Non-maximum Suppression
大名鼎鼎的NMS的目的是消除掉不同格子中預測出的同一個目标。
一般介紹Non-Maximum Suppression的文章會這樣介紹NMS算法。首先,NMS計算出每一個bounding box的面積,然後根據score進行排序,把score最大的bounding box作為隊列中。(這裡的score = object confidence * class score, class score實際是指P(Class | Object), 是以P(Class) = P(Object) * P(Class | Object))接下來,計算其餘bounding box與目前最大score與box的IoU,去除IoU大于設定的nms threshold的bounding box。這裡第三處用到IOU和threshold。然後重複上面的過程,直至候選bounding box為空。
NMS就這麼簡單?有沒有忽略了什麼重要的東西?會不會發生這種情況,過于鄰近的N個目标中N-1個被無辜地過濾掉?為了防止這種可能,NMS在過濾時分類過濾的,即在同一個類的檢測輸出中執行上述算法。即便如此,仍然避免不了過于鄰近的N個同類目标中N-1個被無辜地過濾掉。這是另一個局限。
順便說一下,NMS僅用于預測,并未用于訓練。
anchor box
我們面臨2個疑問
- 為何需要有anchor box?
It might make sense to predict the width and the height of the bounding box, but in practice, that leads to unstable gradients during training.
是以,需要pre-defined default bounding box作為錨點。
bx,by,bw,bh是我們預測的x,y中心坐标,寬度和高度。 tx,ty,tw,th是網絡輸出的内容。 cx和cy是網格的左上角坐标。 pw和ph是anchor box的寬度和高度。
- anchor box是怎麼來的?
Instead of choosing priors by hand, we run k-means clustering on the training set bounding boxes to automatically find good priors.
k-means無論是算中心還是重新選擇cluster,都會用到距離,我們通常見到的距離是歐氏距離。但這裡是BBOX,BBOX間怎麼算距離呢?
d(box; centroid) = 1 - IOU(box; centroid)。這是第四處用到IOU。
YOLOv3整個思路讓人匪夷所思。正如它的名字,You Only Look Once,它不是先切割再分類。實際上,它是根據中心的一個格子來預測它的BBOX和分類,這怎麼可能呢?
編輯于 2019-12-07
「真誠贊賞,手留餘香」
如果這篇文章幫助到了你,你可以請作者喝一杯咖啡