YOLO v1:You Only Look Once: Unified, Real-Time Object Detection
YOLO v2:YOLO9000:Better,Faster,Stronger
YOLO v3:YOLOv3: An Incremental Improvement
近幾年來,目標檢測算法取得了很大的突破。比較流行的算法可以分為兩類,壹類是基於Region Proposal的R-CNN系算法(R-CNN,Fast R-CNN, Faster R-CNN),它們是two-stage的,需要先使用啟發式方法(selective search)或者CNN網絡(RPN)產生Region Proposal,然後再在Region Proposal上做分類與回歸。而另壹類是Yolo,SSD這類one-stage算法,其僅僅使用壹個CNN網絡直接預測不同目標的類別與位置。第壹類方法是準確度高壹些,但是速度慢,但是第二類算法是速度快,但是準確性要低壹些。這裏我們談的是Yolo-v1版本算法,其性能是差於後來的SSD算法的,但是Yolo後來也繼續進行改進,產生了Yolo9000、YOLO v3算法。
傳統方法常采用滑動窗口法,滑動窗口的目標檢測算法思路非常簡單,它將檢測問題轉化為了圖像分類問題。其基本原理就是采用不同大小和比例(寬高比)的窗口在整張圖片上以壹定的步長進行滑動,然後對這些窗口對應的區域做圖像分類,這樣就可以實現對整張圖片的檢測了,如 DPM 就是采用這種思路。但是這個方法有致命的缺點,就是妳並不知道要檢測的目標大小是什麽規模,所以妳要設置不同大小和比例的窗口去滑動,而且還要選取合適的步長。但是這樣會產生很多的子區域,並且都要經過分類器去做預測,這需要很大的計算量,所以妳的分類器不能太復雜,因為要保證速度。解決思路之壹就是減少要分類的子區域,這就是R-CNN的壹個改進策略,其采用了 selective search 方法來找到最有可能包含目標的子區域(Region Proposal),其實可以看成采用啟發式方法過濾掉很多子區域,這會提升效率。
如果妳使用的是CNN分類器,那麽滑動窗口是非常耗時的。但是結合卷積運算的特點,我們可以使用CNN實現更高效的滑動窗口方法。這裏要介紹的是壹種全卷積的方法,簡單來說就是網絡中用卷積層代替了全連接層,如圖所示。輸入圖片大小是16x16,經過壹系列卷積操作,提取了2x2的特征圖,但是這個2x2的圖上每個元素都是和原圖是壹壹對應的,如圖上藍色的格子對應藍色的區域,這不就是相當於在原圖上做大小為14x14的窗口滑動,且步長為2,***產生4個字區域。最終輸出的通道數為4,可以看成4個類別的預測概率值,這樣壹次CNN計算就可以實現窗口滑動的所有子區域的分類預測。這其實是overfeat算法的思路。之所可以CNN可以實現這樣的效果是因為卷積操作的特性,就是圖片的空間位置信息的不變性,盡管卷積過程中圖片大小減少,但是位置對應關系還是保存的。這個思路也被R-CNN借鑒,從而誕生了Fast R-cNN算法。
上面盡管可以減少滑動窗口的計算量,但是只是針對壹個固定大小與步長的窗口,這是遠遠不夠的。Yolo算法很好的解決了這個問題,它不再是窗口滑動了,而是直接將原始圖片分割成互不重合的小方塊,然後通過卷積最後生產這樣大小的特征圖,基於上面的分析,可以認為特征圖的每個元素也是對應原始圖片的壹個小方塊,然後用每個元素來可以預測那些中心點在該小方格內的目標,這就是Yolo算法的樸素思想。
整體來看,Yolo算法采用壹個單獨的CNN模型實現end-to-end的目標檢測,整個系統如圖所示:首先將輸入圖片resize到448x448,然後送入CNN網絡,最後處理網絡預測結果得到檢測的目標。相比R-CNN算法,其是壹個統壹的框架,其速度更快,而且Yolo的訓練過程也是end-to-end的。
具體來說,Yolo的CNN網絡將輸入的圖片分割成 網格,然後每個單元格負責去檢測那些中心點落在該格子內的目標,如圖所示,可以看到狗這個目標的中心落在左下角壹個單元格內,那麽該單元格負責預測這個狗。每個單元格會預測B個邊界框(bounding box)以及邊界框的 置信度 (confidence score)。所謂置信度其實包含兩個方面,壹是這個邊界框含有目標的可能性大小,二是這個邊界框的準確度。前者記為 ,當該邊界框是背景時(即不包含目標),此時 。而當該邊界框包含目標時, 。邊界框的準確度可以用預測框與實際框(ground truth)的 IOU (intersection over union,交並比)來表征,記為 IOU 。因此置信度可以定義為 。
很多人可能將Yolo的置信度看成邊界框是否含有目標的概率,但是其實它是兩個因子的乘積,預測框的準確度也反映在裏面。邊界框的大小與位置可以用4個值來表征:(x,y,h,w),其中(x,y)是邊界框的中心坐標,而w和h是邊界框的寬與高。還有壹點要註意,中心坐標的預測值(x,y)是相對於每個單元格左上角坐標點的偏移值,並且單位是相對於單元格大小的,單元格的坐標定義如圖所示。而邊界框的w和h預測值是相對於整個圖片的寬與高的比例,這樣理論上4個元素的大小應該在[0,1]範圍。這樣,每個邊界框的預測值實際上包含5個元素:(x,y,w,h,c),其中前4個表征邊界框的大小與位置,而最後壹個值是置信度。
值得註意的是,不管壹個單元格預測多少個邊界框,其只預測壹組類別概率值,這是Yolo算法的壹個缺點,在後來的改進版本中,Yolo9000是把類別概率預測值與邊界框是綁定在壹起的。同時,我們可以計算出各個邊界框類別置信度(class-specificconfidence scores):
邊界框類別置信度表征的是該邊界框中目標屬於各個類別的可能性大小以及邊界框匹配目標的好壞。後面會說,壹般會根據類別置信度來過濾網絡的預測框。
總結壹下,每個單元格需要預測 個值。如果將輸入圖片劃分為 網格,那麽最終預測值為 大小的張量。整個模型的預測值結構如下圖所示。對於PASCALVOC數據,其***有20個類別,如果使用S=7,B=2,那麽最終的預測結果就是 大小的張量。在下面的網絡結構中我們會詳細講述每個單元格的預測值的分布位置。
Yolo采用卷積網絡來提取特征,然後使用全連接層來得到預測值。網絡結構參考GooLeNet模型,包含24個卷積層和2個全連接層,如圖所示。對於卷積層,主要使用1x1卷積來做channle reduction,然後緊跟3x3卷積。對於卷積層和全連接層,采用Leaky ReLU激活函數:max(x,0)。但是最後壹層卻采用線性激活函數。除了上面這個結構,文章還提出了壹個輕量級版本Fast Yolo,其僅使用9個卷積層,並且卷積層中使用更少的卷積核。
可以看到網絡的最後輸出為 大小的張量。這和前面的討論是壹致的。這個張量所代表的具體含義如圖所示。對於每壹個單元格,前20個元素是類別概率值,然後2個元素是邊界框置信度,兩者相乘可以得到類別置信度,最後8個元素是邊界框的(x,y,w,h)。大家可能會感到奇怪,對於邊界框為什麽把置信度c和(x,y,w,h)都分開排列,而不是按照(x,y,w,h,c)這樣排列,其實純粹是為了計算方便,因為實際上這30個元素都是對應壹個單元格,其排列是可以任意的。但是分離排布,可以方便地提取每壹個部分。這裏來解釋壹下,首先網絡的預測值是壹個二維張量P,其shape為 。
采用切片,那麽 就是類別概率部分; 是置信度部分; 是邊界框的預測結果。這樣,提取每個部分是非常方便的,這會方面後面的訓練及預測時的計算。
在訓練之前,先在ImageNet上進行了預訓練,其預訓練的分類模型采用圖中前20個卷積層,然後添加壹個average-pool層和全連接層。預訓練之後,在預訓練得到的20層卷積層之上加上隨機初始化的4個卷積層和2個全連接層。由於檢測任務壹般需要更高清的圖片,所以將網絡的輸入從224x224增加到了448x448。整個網絡的流程如下圖所示:
損失函數計算如下:
其中第壹項是邊界框中心坐標的誤差項, 指的是第i個單元格存在目標,且該單元格中的第j個邊界框負責預測該目標。第二項是邊界框的高與寬的誤差項。第三項是包含目標的邊界框的置信度誤差項。第四項是不包含目標的邊界框的置信度誤差項。而最後壹項是包含目標的單元格的分類誤差項, 指的是第i個單元格存在目標。
在說明Yolo算法的預測過程之前,這裏先介紹壹下非極大值抑制算法(non maximum suppression, NMS),這個算法不單單是針對Yolo算法的,而是所有的檢測算法中都會用到。NMS算法主要解決的是壹個目標被多次檢測的問題,如圖中人臉檢測,可以看到人臉被多次檢測,但是其實我們希望最後僅僅輸出其中壹個最好的預測框,比如對於美女,只想要紅色那個檢測結果。那麽可以采用NMS算法來實現這樣的效果:首先從所有的檢測框中找到置信度最大的那個框,然後挨個計算其與剩余框的IOU,如果其值大於壹定閾值(重合度過高),那麽就將該框剔除;然後對剩余的檢測框重復上述過程,直到處理完所有的檢測框。
下面就來分析Yolo的預測過程,這裏我們不考慮batch,認為只是預測壹張輸入圖片。根據前面的分析,最終的網絡輸出是 ,但是我們可以將其分割成三個部分:類別概率部分為 ,置信度部分為 ,而邊界框部分為 (對於這部分不要忘記根據原始圖片計算出其真實值)。然後將前兩項相乘可以得到 類別置信度值為 ,這裏總***預測了 邊界框。
所有的準備數據已經得到了,那麽先說第壹種策略來得到檢測框的結果。首先,對於每個預測框根據類別置信度選取置信度最大的那個類別作為其預測標簽,經過這層處理我們得到各個預測框的預測類別及對應的置信度值,其大小都是[7,7,2]。壹般情況下,會設置置信度閾值,就是將置信度小於該閾值的box過濾掉,所以經過這層處理,剩余的是置信度比較高的預測框。最後再對這些預測框使用NMS算法,最後留下來的就是檢測結果。壹個值得註意的點是NMS是對所有預測框壹視同仁,還是區分每個類別,分別使用NMS。Ng在deeplearning.ai中講應該區分每個類別分別使用NMS,但是看了很多實現,其實還是同等對待所有的框,可能是不同類別的目標出現在相同位置這種概率很低吧。
上面的預測方法應該非常簡單明了,但是對於Yolo算法,其卻采用了另外壹個不同的處理思路(至少從C源碼看是這樣的),其區別就是先使用NMS,然後再確定各個box的類別。其基本過程如圖所示。對於98個boxes,首先將小於置信度閾值的值歸0,然後分類別地對置信度值采用NMS,這裏NMS處理結果不是剔除,而是將其置信度值歸為0。最後才是確定各個box的類別,當其置信度值不為0時才做出檢測結果輸出。這個策略不是很直接,但是貌似Yolo源碼就是這樣做的。Yolo論文裏面說NMS算法對Yolo的性能是影響很大的,所以可能這種策略對Yolo更好。
總結壹下Yolo的優缺點。首先是優點,Yolo采用壹個CNN網絡來實現檢測,是單管道策略,其訓練與預測都是end-to-end,所以Yolo算法比較簡潔且速度快。第二點由於Yolo是對整張圖片做卷積,所以其在檢測目標有更大的視野,它不容易對背景誤判。另外,Yolo的泛化能力強,在做遷移時,模型魯棒性高。
Yolo的缺點,首先Yolo各個單元格僅僅預測兩個邊界框,而且屬於壹個類別。對於小物體,Yolo的表現會不如人意。這方面的改進可以看SSD,其采用多尺度單元格。也可以看Faster R-CNN,其采用了anchor boxes。Yolo對於在物體的寬高比方面泛化率低,就是無法定位不尋常比例的物體。當然Yolo的定位不準確也是很大的問題。
參考鏈接
YOLO算法的原理與實現
/developer/article/1058057