1. 程式人生 > >Yolov3轉化Caffe框架詳解

Yolov3轉化Caffe框架詳解

    前些日子因工程需求,需要將yolov3從基於darknet轉化為基於Caffe框架,過程中踩了一些坑,特在此記錄一下。

1.Yolov3的網路結構

想要轉化為Caffe框架,就要先了解yolov3的網路結構,如下圖。

    如果有執行過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來完成。

2.如何實現

    下面這部分將著重講一下如何實現從darknet向yolov3的轉換,首先這一過程要感謝chenyingpeng提供的程式碼,部落格在這裡

1.加入upsample層並編譯Caffe

upsample層的程式碼在這裡,密碼bwrd。

其中的upsample_layer.hpp放入include/caffe/layers下面;upsample_layer.cpp與upsample_layer.cu放在src/caffe/layers下面。

修改相應的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的程式碼,裡面有許多作者文章裡沒有講清楚的內容,感興趣的可以仔細鑽研一下。

   本人才疏學淺,本文僅是最近工程實踐中的一點成果,如有錯誤還望指正。