轉自https://blog.csdn.net/watermelon1123/article/details/82083522
前些日子因工程需求,須要將yolov3從基於darknet轉化爲基於Caffe框架,過程當中踩了一些坑,特在此記錄一下。git
想要轉化爲Caffe框架,就要先了解yolov3的網絡結構,以下圖。github
若是有運行過darknet應該會很熟悉,這是darknet運行成功後打印log信息,這裏麪包含了yolo網絡結構的一些信息。yolov3與v2相比,網絡結構中加入了殘差(shortcut層),而且引入了上採樣(upsample層),併爲了將採樣後的特徵圖進行融合引入了拼接(route層),最後融合的特徵圖以三個不一樣的大小13*13*75,26*26*75,52*52*75輸入給yolo層最後獲得目標的位置及分類信息,加上卷積層convolution,這些即是yolov3的網絡基本構造。所以只要咱們若是在Caffe中找到對應的層按照相應的進行構造就可以使用Caffe實現yolov3了。網絡
卷積層不說,yolov3中的shortcut層能夠用eltwise替代,route層能夠用concat替代,而upsample層和yolo層則須要本身實現,並添加到Caffe中便可。upsample層主要完成了上採樣的工做,這裏不細說。本文主要講一下yolo層如何實現,上圖中的YOLO Detection即爲yolo層的所在位置,接收三種不一樣大小的特徵圖,並完成對特徵圖的解析,獲得物體的位置和類別信息。因此其實yolo層主要起到了解析特徵並輸出檢測結果的做用,這一過程咱們徹底能夠在外部實現而無需加入到網絡結構當中,也就是說咱們無需將實現的yolo層加入到Caffe當中去。框架
經過上圖(我本身花的靈魂解析圖,湊活看吧),能夠解釋yolo層如何獲得檢測目標的位置和分類。Yolo層的input是一個13*13*N的特徵圖,其中13*13若是有看過yolov1的論文做者有給出過解釋,其實就是圖像被分紅了13*13個grid cell,而每一個grid中是一個長度爲N的張量,其中的數據是這樣分佈的,前4個位置分別爲x,y,w,h,用於計算目標框的位置;第5個位置爲置信度值Pr(object)*IOU,代表了該位置的目標框包含目標的置信度;第5個位置日後則爲該box包含物體類別的條件機率Pr(class|object),從class1~class n,n爲你所需檢測類別數。這樣(x,y,w,h)+ Pr(object)*IOU + n*Pr(class|object)構成了box1的全部信息,而一個grid cell中含有3個這樣的boxes,這就是輸入到yolo層的特徵圖的直觀解釋。在yolo層進行檢測的時候,首先斷定每一個box的包含物體的置信度值即p的值是否大於設定閾值thresh,若是大於該閾值則認爲這個box中含有物體,讀取位置信息(x,y,w,h)與對應的anchor box的信息計算獲得物體框的實際位置。以後針對於每一個含有物體的box,根據其類別機率斷定其類別所屬,再對同一類別的目標框進行非極大值抑制NMS,即獲得最終結果。函數
以上即爲yolo層所實現的檢測過程簡要介紹,具體的過程如何計算還須要看官們仔細看一下代碼和論文,固然此過程不包括訓練的前向和反向過程,僅包含推理。所以咱們轉換到Caffe框架下的yolov3也僅能實現推理過程,具體的訓練還須要經過darknet來完成。工具
下面這部分將着重講一下如何實現從darknet向yolov3的轉換,首先這一過程要感謝chenyingpeng提供的代碼,博客在這裏。測試
1.加入upsample層並編譯Caffe編碼
upsample層的代碼在這裏,密碼bwrd。spa
其中的upsample_layer.hpp放入include/caffe/layers下面;upsample_layer.cpp與upsample_layer.cu放在src/caffe/layers下面。.net
修改相應的caffe.proto文件,src/caffe/proto/caffe.proto中的LayerParameter的最後一行加入加入:
message LayerParameter {
.....
optional UpsampleParameter upsample_param = 149;
}
注意149爲新層的ID號,該ID號請根據我的的caffe.proto文件指定便可。
而後再caffe.proto中添加upsample層的參數:
message UpsampleParameter{
optional int32 scale = 1 [default = 1];
}
緊接着從新編譯Caffe,這樣就完成了在Caffe中添加upsample層。更多信息請參考caffe中添加新層教程。
上面說過轉換到Caffe後只包含推理過程,所以咱們須要將訓練好的模型(.cfg)和權重文件(.weights)轉換到對應Caffe下的.proto和.caffemodel,代碼能夠借鑑github上的模型轉換工具。注意該工具須要pytorch支持請自行安裝。且該工具應用於Yolov2,由於咱們在Caffe中加入了相應的upsample層而且yolov3和v2的網絡結構有變化,所以須要替換相應的darknet2caffe.py,代碼在這裏,密碼:i6y2。
至此咱們的準備工做就結束了,這樣經過Caffe咱們就能獲得相應的blobs,這些blobs裏包含的信息和darknet輸入給yolo層的信息是同樣的。咱們只須要經過yolo layer將blobs的信息進行解析就可以獲得目標的位置和類別信息。由於私人緣由,這部分代碼不能開放,可是能夠參考chenyingpeng的代碼,在這裏。經測試是一樣可用的,只須要注意由於咱們的yolo layer的檢測過程是在Caffe外部實現的,所以yolo layer層的相應信息做者以硬編碼的形式加入到代碼中,使用的時候須要根據我的yolo layer的參數進行修改(好比我測試的時候yolo_layer.cpp中的函數get_detections中的類別數目沒有修改就發生了難以言表的結果...)。
yolov3從darknet轉Caffe的整個過程就結束了,其中關於yolov3的原理並無詳細解釋特別多,本文主要着重於和轉到Caffe框架相關的內容,具體yolov3的原理性文章推薦你們看這篇,裏面關於yolov1~v3講解的很詳細(來自一羣還在上大一的學生的論文解讀,不由讓人感嘆長江後浪推前浪,前浪我已GG)。關於yolov3的訓練代碼,推薦你們去看darknet的源碼,尤爲是關於Yolo layer的代碼,裏面有許多做者文章裏沒有講清楚的內容,感興趣的能夠仔細鑽研一下。
本人才疏學淺,本文僅是最近工程實踐中的一點成果,若有錯誤還望指正。