1. 程式人生 > >YOLO v3基於ROS應用記錄

YOLO v3基於ROS應用記錄

“有時候,就要敢於背上超出自己預料的包袱,真的努力後,你會發現自己要比想象的優秀很多。”

 

하루하루가 지나간다

2019.01

願在別人眼裡算不上夢想的夢想 成真~

 

言歸正傳,記錄下之前在ROS下跑yolov3的歷程吧:

感覺現在視覺感知領域用yolo的比faster-RCNN多很多了,畢竟在無人駕駛圈子,感知從不能僅靠一個視覺感測器來實現了,所以在實用方面,單一的視覺演算法精度從80%提高到90%,其實際用處並不算大:一方面,可以通過其他感測器比如Lidar來彌補,另一方面在決策規劃層的應用效果也是一樣的,無論怎樣,它都是一個障礙物,需要躲避。所以在yolo版本不斷更新、既保持了處理效率的同時,也提高了檢測精度,yolo的優勢也就凸顯出來了!

 

先來看看yolo v1~v3這幾個版本的特點吧~

 

YOLO v1:

論文名稱:You only look once unified real-time object detection
論文連結

YOLO:其實是把物體檢測(object detection)問題看作是目標區域預測和類別預測的迴歸問題,只用一個卷積神經網路結構就可以從輸入影象直接預測bounding box和類別概率。

優點:1、YOLO的速度非常快。在Titan X GPU上的速度是45 fps(frames per second),加速版的YOLO差不多是150fps。2、YOLO是基於影象的全域性資訊進行預測的。這一點和基於sliding window以及region proposal等檢測演算法不一樣,與Fast R-CNN相比,YOLO在誤檢測(將背景檢測為物體)方面的錯誤率能降低一半多。3、YOLO可以學到物體的generalizable representations。可以理解為泛化能力強。4、準確率高,有實驗證明。

缺點1、位置精確性差,對於小目標物體以及物體比較密集的也檢測不好,比如一群小鳥。2、YOLO雖然可以降低將背景檢測為物體的概率,但同時導致召回率較低。

 

結構:

演算法:

首先把輸入影象劃分成S*S的格子,然後對每個格子都預測B個bounding boxes,每個bounding box都包含5個預測值:x,y,w,h和confidence。x,y就是bounding box的中心座標,與grid cell對齊(即相對於當前grid cell的偏移值),使得範圍變成0到1;w和h進行歸一化(分別除以影象的w和h,這樣最後的w和h就在0到1範圍);另外每個格子都預測C個假定類別的概率。在本文中作者取S=7,B=2,C=20(因為PASCAL VOC有20個類別),所以最後一共有7*7*30個tensor

此時,可以發現,預測的每個bounding box都對應一個confidence score,如果grid cell裡面沒有object,confidence就是0,如果有,則confidence score等於預測的box和ground truth的IOU值。所以如何判斷一個grid cell中是否包含object呢?答案是:如果一個object的ground truth的中心點座標在一個grid cell中,那麼這個grid cell就是包含這個object,也就是說這個object的預測就由該grid cell負責。每個grid cell都預測C個類別概率,表示一個grid cell在包含object的條件下屬於某個類別的概率,注意grid cell和bounding box的區別,類別概率是針對grid cell的。

但是,這個方格(grid cell)在包含object的條件下屬於某個類別的概率,也就是這個乘法到底是怎麼算的呢?每個bounding box的confidence和每個類別的score相乘,得到每個bounding box屬於哪一類的confidence score,即得到每個bounding box屬於哪一類的confidence score。也就是說最後會得到20*(7*7*2)=20*98的score矩陣,括號裡面是bounding box的數量,20代表類別。接下來的操作都是20個類別輪流進行:在某個類別中(即矩陣的某一行),將得分少於閾值(0.2)的設定為0,然後再按得分從高到低排序。最後再用NMS演算法去掉重複率較大的bounding box(NMS:針對某一類別,選擇得分最大的bounding box,然後計算它和其它bounding box的IOU值,如果IOU大於0.5,說明重複率較大,該得分設為0,如果不大於0.5,則不改;這樣一輪後,再選擇剩下的score裡面最大的那個bounding box,然後計算該bounding box和其它bounding box的IOU,重複以上過程直到最後)。最後每個bounding box的20個score取最大的score,如果這個score大於0,那麼這個bounding box就是這個socre對應的類別(矩陣的行),如果小於0,說明這個bounding box裡面沒有物體,跳過即可。

網路方面主要採用GoogLeNet,卷積層主要用來提取特徵,全連線層主要用來預測類別概率和座標。最後的輸出是7*7*30,這個30前面也解釋過了,7*7是grid cell的數量。這裡注意下實現的細節可能人人都不大一樣,比如對inception的改動,最後幾層的全連線層的改動等等,但是重點在於最後一層的輸出是7*7*30。另外兩個小細節:1、作者先在ImageNet資料集上預訓練網路,而且網路只採用fig3的前面20個卷積層,輸入是224*224大小的影象。然後在檢測的時候再加上隨機初始化的4個卷積層和2個全連線層,同時輸入改為更高解析度的448*448。2、Relu層改為pRelu,即當x<0時,啟用值是0.1*x,而不是傳統的0。

損失函式方面,作者採用sum-squared error的方式把localization error(bounding box的座標誤差)和classificaton error整合在一起。在loss function中,前面兩行表示localization error(即座標誤差),第一行是box中心座標(x,y)的預測,第二行為寬和高的預測。這裡注意用寬和高的開根號代替原來的寬和高,這樣做主要是因為相同的寬和高誤差對於小的目標精度影響比大的目標要大。舉個例子,原來w=10,h=20,預測出來w=8,h=22,跟原來w=3,h=5,預測出來w1,h=7相比,其實前者的誤差要比後者小,但是如果不加開根號,那麼損失都是一樣:4+4=8,但是加上根號後,變成0.15和0.7。第三、四行表示bounding box的confidence損失,就像前面所說的,分成grid cell包含與不包含object兩種情況。這裡注意下因為每個grid cell包含兩個bounding box,所以只有當ground truth 和該網格中的某個bounding box的IOU值最大的時候,才計算這項。第五行表示預測類別的誤差,注意前面的係數只有在grid cell包含object的時候才為1。

 

流程:

訓練的時候:輸入N個影象,每個影象包含M個object,每個object包含4個座標(x,y,w,h)和1個label。然後通過網路得到7*7*30大小的三維矩陣。每個1*30的向量前5個元素表示第一個bounding box的4個座標和1個confidence,第6到10元素表示第二個bounding box的4個座標和1個confidence。最後20個表示這個grid cell所屬類別。注意這30個都是預測的結果。然後就可以計算損失函式的第一、二 、五行。至於第二三行,confidence可以根據ground truth和預測的bounding box計算出的IOU和是否有object的0,1值相乘得到。真實的confidence是0或1值,即有object則為1,沒有object則為0。 這樣就能計算出loss function的值了。

測試的時候:輸入一張影象,跑到網路的末端得到7*7*30的三維矩陣,這裡雖然沒有計算IOU,但是由訓練好的權重已經直接計算出了bounding box的confidence。然後再跟預測的類別概率相乘就得到每個bounding box屬於哪一類的概率。
 

YOLO v2:

論文名稱: YOLOv2:Better,Faster,Stronger
論文連結:https://arxiv.org/abs/1612.08242

首先YOLO有兩個缺點:一個缺點在於定位不準確,另一個缺點在於和基於region proposal的方法相比召回率較低。因此YOLOv2主要是要在這兩方面做提升。另外YOLOv2並不是通過加深或加寬網路達到效果提升,反而是簡化了網路。大概看一下YOLOv2的表現:YOLOv2演算法在VOC 2007資料集上的表現為67 FPS時,MAP為76.8,在40FPS時,MAP為78.6.

那好,就來看看YOLOv2是怎麼來進行改進v1,變得Better,Faster,Stronger?

1、Batch Normalization(添加了BN層)
BN(Batch Normalization)層簡單講就是對網路的每一層的輸入都做了歸一化,這樣網路就不需要每層都去學資料的分佈,收斂會快點。原來的YOLO演算法(採用的是GoogleNet網路提取特徵)是沒有BN層的,因此在YOLOv2中作者為每個卷積層都添加了BN層。另外由於BN可以規範模型,所以本文加入BN後就把dropout去掉了。實驗證明添加了BN層可以提高2%的mAP。

2、High Resolution Classifier(修改了預訓練的步驟,解決了模型從分類模型切換到檢測模型時還要適應影象解析度的改變)
首先fine-tuning的作用不言而喻,現在基本跑個classification或detection的模型都不會從隨機初始化所有引數開始,所以一般都是用預訓練的網路來finetuning自己的網路,而且預訓練的網路基本上都是在ImageNet資料集上跑的,一方面資料量大,另一方面訓練時間久,而且這樣的網路都可以在相應的github上找到。
原來的YOLO網路在預訓練的時候採用的是224*224的輸入(這是因為一般預訓練的分類模型都是在ImageNet資料集上進行的),然後在detection的時候採用448*448的輸入,這會導致從分類模型切換到檢測模型的時候,模型還要適應影象解析度的改變。而YOLOv2則將預訓練分成兩步:先用224*224的輸入從頭開始訓練網路,大概160個epoch(表示將所有訓練資料迴圈跑160次),然後再將輸入調整到448*448,再訓練10個epoch。注意這兩步都是在ImageNet資料集上操作。最後再在檢測的資料集上fine-tuning,也就是detection的時候用448*448的影象作為輸入就可以順利過渡了。作者的實驗表明這樣可以提高几乎4%的MAP。

3、Convolutional With Anchor Boxes(去掉了全連線層和最後一個池化層;縮減網路輸入影象尺寸;引入anchor boxes)
原來的YOLO是利用全連線層直接預測bounding box的座標,而YOLOv2借鑑了Faster R-CNN的思想,引入anchor。首先將原網路的全連線層和最後一個pooling層去掉,使得最後的卷積層可以有更高解析度的特徵;然後縮減網路,用416*416大小的輸入代替原來448*448。這樣做的原因在於希望得到的特徵圖都有奇數大小的寬和高,奇數大小的寬和高會使得每個特徵圖在劃分cell的時候就只有一個center cell(比如可以劃分成7*7或9*9個cell,center cell只有一個,如果劃分成8*8或10*10的,center cell就有4個)。為什麼希望只有一個center cell呢?因為大的object一般會佔據影象的中心,所以希望用一個center cell去預測,而不是4個center cell去預測。網路最終將416*416的輸入變成13*13大小的feature map輸出,也就是縮小比例為32。我們知道原來的YOLO演算法將輸入影象分成7*7的網格,每個網格預測兩個bounding box,因此一共只有98個box,但是在YOLOv2通過引入anchor boxes,預測的box數量超過了1千(以輸出feature map大小為13*13為例,每個grid cell有9個anchor box的話,一共就是13*13*9=1521個,當然由後面第4點可知,最終每個grid cell選擇5個anchor box)。順便提一下在Faster RCNN在輸入大小為1000*600時的boxes數量大概是6000,在SSD300中boxes數量是8732。顯然增加box數量是為了提高object的定位準確率。
作者的實驗證明:雖然加入anchor使得MAP值下降了一點(69.5降到69.2),但是提高了recall(81%提高到88%)。

4、Dimension Clusters
我們知道在Faster R-CNN中anchor box的大小和比例是按經驗設定的,然後網路會在訓練過程中調整anchor box的尺寸。但是如果一開始就能選擇到合適尺寸的anchor box,那肯定可以幫助網路越好地預測detection。所以作者採用k-means的方式對訓練集的bounding boxes做聚類,試圖找到合適的anchor box。另外作者發現如果採用標準的k-means(即用歐式距離來衡量差異),在box的尺寸比較大的時候其誤差也更大,而我們希望的是誤差和box的尺寸沒有太大關係。所以通過IOU定義瞭如下的距離函式,使得誤差和box的大小無關:

在分析了聚類的結果並平衡了模型複雜度與recall值,作者選擇了K=5,這也就是Figure2中右邊的示意圖是選出來的5個box的大小,這裡紫色和黑色也是分別表示兩個不同的資料集,可以看出其基本形狀是類似的。而且發現聚類的結果和手動設定的anchor box大小差別顯著。聚類的結果中多是高瘦的box,而矮胖的box數量較少。
5、Multi-Scale Training
為了讓YOLOv2模型更加robust,作者引入了Muinti-Scale Training,簡單講就是在訓練時輸入影象的size是動態變化的,注意這一步是在檢測資料集上fine tune時候採用的,不要跟前面在Imagenet資料集上的兩步預訓練分類模型混淆。具體來講,在訓練網路時,每訓練10個batch(文中是10個batch,個人認為會不會是筆誤,不應該是10個epoch?),網路就會隨機選擇另一種size的輸入。那麼輸入影象的size的變化範圍要怎麼定呢?前面我們知道本文網路本來的輸入是416*416,最後會輸出13*13的feature map,也就是說downsample的factor是32,因此作者採用32的倍數作為輸入的size,具體來講文中作者採用從{320,352,…,608}的輸入尺寸。這種網路訓練方式使得相同網路可以對不同解析度的影象做detection。雖然在輸入size較大時,訓練速度較慢,但同時在輸入size較小時,訓練速度較快,而multi-scale training又可以提高準確率,因此算是準確率和速度都取得一個不錯的平衡。通過multi-scale training的檢測模型,在測試的時候,輸入影象在尺寸變化範圍較大的情況下也能取得mAP和FPS的平衡。

總結:High Resolution Classifier的提升非常明顯(近4%),另外通過結合dimension prior+localtion prediction這兩種方式引入anchor也能帶來近5%mAP的提升

那以上是將模型精度變的更好,那怎麼讓模型變的更快呢?

 

1、Darknet-19
在YOLO v1中,作者採用的訓練網路是基於GooleNet,這裡作者將GooleNet和VGG16做了簡單的對比,GooleNet在計算複雜度上要優於VGG16(8.25 billion operation VS 30.69 billion operation),但是前者在ImageNet上的top-5準確率要稍低於後者(88% VS 90%)。而在YOLO v2中,作者採用了新的分類模型作為基礎網路,那就是Darknet-19。

2、Training for Classification
這裡的2和3部分在前面有提到,就是訓練處理的小trick。這裡的training for classification都是在ImageNet上進行預訓練,主要分兩步:1、從頭開始訓練Darknet-19,資料集是ImageNet,訓練160個epoch,輸入影象的大小是224*224,初始學習率為0.1。另外在訓練的時候採用了標準的資料增加方式比如隨機裁剪,旋轉以及色度,亮度的調整等。2、再fine-tuning 網路,這時候採用448*448的輸入,引數的除了epoch和learning rate改變外,其他都沒變,這裡learning rate改為0.001,並訓練10個epoch。結果表明fine-tuning後的top-1準確率為76.5%,top-5準確率為93.3%,而如果按照原來的訓練方式,Darknet-19的top-1準確率是72.9%,top-5準確率為91.2%。因此可以看出第1,2兩步分別從網路結構和訓練方式兩方面入手提高了主網路的分類準確率。

3、Training for Detection
在前面第2步之後,就開始把網路移植到detection,並開始基於檢測的資料再進行fine-tuning。首先把最後一個卷積層去掉,然後新增3個3*3的卷積層,每個卷積層有1024個filter,而且每個後面都連線一個1*1的卷積層,1*1卷積的filter個數根據需要檢測的類來定。比如對於VOC資料,由於每個grid cell我們需要預測5個box,每個box有5個座標值和20個類別值,所以每個grid cell有125個filter(與YOLOv1不同,在YOLOv1中每個grid cell有30個filter,還記得那個7*7*30的矩陣嗎,而且在YOLOv1中,類別概率是由grid cell來預測的,也就是說一個grid cell對應的兩個box的類別概率是一樣的,但是在YOLOv2中,類別概率是屬於box的,每個box對應一個類別概率,而不是由grid cell決定,因此這邊每個box對應25個預測值(5個座標加20個類別值),而在YOLOv1中一個grid cell的兩個box的20個類別值是一樣的)。另外作者還提到將最後一個3*3*512的卷積層和倒數第二個卷積層相連。最後作者在檢測資料集上fine tune這個預訓練模型160個epoch,學習率採用0.001,並且在第60和90epoch的時候將學習率除以10,weight decay採用0.0005。


YOLO v3:

論文名稱:YOLOv3: An Incremental Improvement
論文地址:https://pjreddie.com/media/files/papers/YOLOv3.pdf

YOLO演算法的基本思想是:首先通過特徵提取網路對輸入影象提取特徵,得到一定size的feature map,比如13*13,然後將輸入影象分成13*13個grid cell,接著如果ground truth中某個object的中心座標落在哪個grid cell中,那麼就由該grid cell來預測該object,因為每個grid cell都會預測固定數量的bounding box(YOLO v1中是2個,YOLO v2中是5個,YOLO v3中是3個,這幾個bounding box的初始size是不一樣的),那麼這幾個bounding box中最終是由哪一個來預測該object?答案是:這幾個bounding box中只有和ground truth的IOU最大的bounding box才是用來預測該object的。可以看出預測得到的輸出feature map有兩個維度是提取到的特徵的維度,比如13*13,還有一個維度(深度)是B*(5+C),注:YOLO v1中是(B*5+C),其中B表示每個grid cell預測的bounding box的數量,C表示bounding box的類別數(沒有背景類,所以對於VOC資料集是20),5表示4個座標資訊和一個置信度(objectness score)。

bounding box的座標預測方式還是延續了YOLO v2的做法,類別預測方面主要是將原來的單標籤分類改進為多標籤分類,因此網路結構上就將原來用於單標籤多分類的softmax層換成用於多標籤多分類的邏輯迴歸層。首先說明一下為什麼要做這樣的修改,原來分類網路中的softmax層都是假設一張影象或一個object只屬於一個類別,但是在一些複雜場景下,一個object可能屬於多個類,比如你的類別中有woman和person這兩個類,那麼如果一張影象中有一個woman,那麼你檢測的結果中類別標籤就要同時有woman和person兩個類,這就是多標籤分類,需要用邏輯迴歸層來對每個類別做二分類。邏輯迴歸層主要用到sigmoid函式,該函式可以將輸入約束在0到1的範圍內,因此當一張影象經過特徵提取後的某一類輸出經過sigmoid函式約束後如果大於0.5,就表示屬於該類。
YOLO v3採用多個scale融合的方式做預測。在多個scale的feature map上做檢測,對於小目標的檢測效果提升還是比較明顯的。前面提到過在YOLO v3中每個grid cell預測3個bounding box,看起來比YOLO v2中每個grid cell預測5個bounding box要少,其實不是!因為YOLO v3採用了多個scale的特徵融合,所以boundign box的數量要比之前多很多,以輸入影象為416*416為例:(13*13+26*26+52*52)*3和13*13*5相比哪個更多應該很清晰了。關於bounding box的初始尺寸還是採用YOLO v2中的k-means聚類的方式來做。

網路結構(Darknet-53)一方面基本採用全卷積(YOLO v2中採用pooling層做feature map的sample,這裡都換成卷積層來做了),另一方面引入了residual結構(YOLO v2中還是類似VGG那樣直筒型的網路結構,層數太多訓起來會有梯度問題,所以Darknet-19也就19層,因此得益於ResNet的residual結構,訓深層網路難度大大減小,因此這裡可以將網路做到53層,精度提升比較明顯)。Darknet-53只是特徵提取層,原始碼中只使用了pooling層前面的卷積層來提取特徵,因此multi-scale的特徵融合和預測支路並沒有在該網路結構中體現。具體資訊可以看原始碼:https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg。預測支路採用的也是全卷積的結構,其中最後一個卷積層的卷積核個數是255,是針對COCO資料集的80類:3*(80+4+1)=255,3表示一個grid cell包含3個bounding box,4表示框的4個座標資訊,1表示objectness score。模型訓練方面還是採用原來YOLO v2中的multi-scale training。  
 

以上借鑑了部落格專家AI之路的部落格,在此表示欽佩和感謝!

理解了這幾個版本的原理以後,就可以實戰操作了

https://pjreddie.com/darknet/yolo/

網路訓練以及執行,官方的教程已經給的很清楚了,在此不在贅述~

接下來,說一說移植ROS實際應用的事:

兩種方法:

1.我自己整理的github,直接down下來即可:https://github.com/I-am-Unique/catkin_darknet

  注:①使用的時候因為我是將一個工作空間的src一起上傳了,下載下來以後,將darknet_ros和detecting_test兩個程式包移到你的catkin_ws/src目錄下,然後進入darknet_ros ,有一個darknet.zip的壓縮包,將其解壓到當前目錄下即可,直接後退到你的catkin_ws/目錄下,catkin_make 即可。

mkidr -p catkin_ws/src
cd catkin_ws/src
git clone  https://github.com/I-am-Unique/catkin_darknet
cd ..
catkin_make 

    ②在catkin_ws/src/darknet_ros/darknet_ros/yolo_network_config/weights目錄下應該放你用yolo訓練的權重檔案,或者直接下載預訓練好的權重放這裡即可。

            ③在catkin_darknet/src/darknet_ros/darknet_ros/config/目錄下,ros.yaml檔案定義了該工程訂閱的topic以及它檢測完成以後將結果釋出出去的topic,見下圖:

     可以看出,這個yolo_ros節點訂閱了/camera/image話題作為輸入,釋出三個話題作為輸出,分別是:

     /darknet_ros/found_object 物體類別名稱。

     /darknet_ros/bounding_boxes 框資訊。

     /darknet_ros/detection_image 檢測結果圖片。

   ④git上,src目錄下還有一個程式包detecting_test是測試用的,裡面有一個將本地圖片讀取釋出在topic:/camera/image上和訂閱檢測結果的/darknet_ros/detection_image的可執行程式,具體編譯通過以後,使用兩個終端分別執行以下命令:

    rosrun detecting_test image_publisher test.jpg

                rosrun detecting_test image_subscriber    

2.參考ROS官方的原始碼:https://github.com/leggedrobotics/darknet_ros

  注:① git clone https://github.com/leggedrobotics/darknet_ros.git 之後,你會發現,darknet資料夾是空的,需要自己手動安裝,因為它是@連結到darknet官網的,但是一定注意在去官網:

https://github.com/pjreddie/darknet/tree/508381b37fe75e0e1a01bcb2941cb0b31eb0e4c9下載darknet時,一定不能使用git clone,要使用壓縮包下載下來,然後進行提取替換剛才那個空的darknet資料夾,直接後退catkin_make,不要在darknet資料夾裡make。

    ②如果不使用壓縮包,會一直編譯出問題。

  

(∩_∩)

各自加油~