摘要:
要把自己的模型進行移植,之前是後端的移植,最近前端也提了需求,前端一般都是用海思芯片(海思HI3516DV300),只支持caffe,所以爲了先測試時間得把tf的模型轉成caffemodel。這裏是將tf1.x轉爲caffemode,後續補全darknet轉爲caffemode
一、轉換環節的注意點:
BN層由於tensoflow和caffe有差異
在轉化的時候記住caffe的bn+scale層等於tensorflow的bn層次
-
卷積
tensorflow卷積參數的存儲方式是:
(kernel_h, kernel_w, input_channel, output_channel)
caffe卷積參數的存儲方式是:
(output_channel, input_channel, kernel_h, kernel_w)
卷積的存儲權重和偏差的位置:
權重存儲的位置爲:conv_params[conv][0]
偏差存儲的位置爲:conv_params[conv][1] -
全連接
tensorflow全連接參數的存儲方式是:
(input_dim, output_dim)
caffe全連接參數存儲方式是:
(output_dim, input_dim)
全連接的存儲權重和偏差的位置:
權重存儲的位置爲:conv_params[fc][0]
偏差存儲的位置爲:conv_params[fc][1] -
TF和caffe的卷積和池化操作在針對某些特徵圖尺寸取整或者填充機制上有所不同;因此,會導致輸出的特徵值不同;
-
TF的conv和pooling爲均是向下取整,而caffe的Conv是向下取整,而pooling爲向上取整;
在做tensorflow模型轉caffe模型時,遇到了幾個坑。其中之一就是caffe的padding方式和tensorflow的padding方式有很大的區別,導致 每一層的輸出都無法對齊,讓我一度懷疑轉換模型的代碼是錯的。
卷積操作輸出的形狀計算公式是這樣的:
output_shape = (image_shape-fileter_shape+2padding)/stride+1
因爲padding前面的係數是2,所以在padding時,一般是對稱地補,左/右各padding一列 或者 上下各padding一行。
那麼問題來了,如果stride是2,而括號裏算出來的值剛好是奇數怎麼辦?那就再偷偷摸摸補一列padding或者補一行padding。
解決辦法:
tensorflow/core/kernels/ops_util.cc
把60行的pad_top 改成 pad_bottom,把61行的pad_bottom改成pad_top
把65行的pad_left 改成 pad_right ,把66行的pad_right 改成 *pad_left
然後重新編譯一下,就可以讓tensorflow和caffe的padding方式保持一致了。
除了padding方式外,卷積層和fc層的通道順序也需要注意一下:
卷積層的通道順序:在caffe裏是[N,C,H,W],而tensorflow是[H,W,C,N]
fc層的通道順序:在caffe 裏是[c_in,c_out],而tensorflow是[c_out,c_in] -
TF和caffe在遇到非整除情況下,其進行卷積遍歷的位置不一樣,當計算需遍歷一行一列滿足尺寸要求時,TF優先遍歷填充方向在右下角的一行一列,而忽略左上的填充,而caffe是優先遍歷填充方向在左上角的一行一列,pytorch和caffe是一致
-
使用訓練好的tensorflow模型,轉換成caffemodel的時候,需要對tensorflow中接口設置和caffe中接口設置都非常瞭解,才能對其參數進行對應設置,舉個簡單的例子:在我們使用slim庫的時候,卷積層默認relu激活,caffe,並沒有默認relu層
二、驗證環節的注意點
這裏還有一個問題需要注意的是tensorflow的數據格式一般都是(N, H,W, C)的方式,而在caffe中是(N, C, H, W),opencv讀入圖片的格式爲(H, W, C),所以在傳入數據時需要使用transpose進行轉換。
img = cv2.imread(test_dir) # (H, W, C)
img = preprocess(img)
img = img.transpose((2, 0, 1)) # (C, H, W)