FCN網路的訓練——以SIFT-Flow 資料集為例
參考文章: http://blog.csdn.net/u013059662/article/details/52770198
caffe的安裝配置,以及fcn的使用在我前邊的文章當中都已經提及到了,這邊不會再細講。在下邊的內容當中,我們來看看如何使用別人提供的資料集來訓練自己的模型!在這篇文章之後,我計劃還要再寫如何fine-tune和製作自己的資料集,以及用自己的資料集fine-tune。
(一)資料準備(以SIFT-Flow 資料集為例)
下載資料集: http://pan.baidu.com/s/1dFxaAtj ,並解壓至/fcn.berkeleyvision.org/data/
下,並將資料夾名重新命名為sift-flow
/fcn.berkeleyvision.org/data/下本來就有一個資料夾叫sift-flow,千萬不要覆蓋。同時,這些原本就存在的資料夾裡的東西還要全部複製到新解壓的sift-flow資料夾下邊。你可以先把原本的sift-slow重新命名為sitf-flow_1,然後再解壓複製!
(二) 下載預訓練模型
下載VGG-16的預訓練模型放至/fcn.berkeleyvision.org/ilsvrc-nets/
目錄下,並重命名為vgg16-fcn.caffemodel
。
下載地址: http://pan.baidu.com/s/1gfeF4wN
(三)原始碼修改
1. prototxt檔案修改
進入siftflow-fcn32s
資料夾下,將test.prototxt
和trainval.prototxt
中的fc6
和fc7
分別替換為其他名稱,例如:fc6_new
和fc7_new
。
原因是我們下載的預訓練模型VGG-16原模型中包含有fc6和fc7這兩個全連線層,而在prototxt中,使我們新新增的卷積層,在模型載入時,如果名稱一樣,而結構資料不同,便會報錯。如果改名之後,原來的fc6/7則會被忽略,而使用我們新的層。
2. caffe path的加入
由於FCN程式碼和caffe程式碼是獨立的資料夾,因此,須將caffe的Python介面加入到path中去。這裡有兩種方案,一種是在所有程式碼中出現import caffe
1 import sys 2 sys.path.append('caffe根目錄/python')
另一種一勞永逸的方法是:在終端或者bashrc中將介面加入到PYTHONPATH
中:
export PYTHONPATH=caffe根目錄/python:$PYTHONPATH
(四)訓練
1 $ cd cd siftflow-fcn32s/ 2 $ python solve.py
這裡會遇見幾個問題:
(1)No module named surgery,score
原因是下載的fcn原始碼解壓根目錄下有兩個檔案:surgery.py和score.py。這兩個檔案是下載下來就自帶的,並不是caffe自帶的,也不是前邊我安裝caffe時需要配置的。由於我是在/fcn根目錄/siftflow-fcn32s/這個資料夾下執行的,會導致找不到這兩個檔案。所以,解決方案就是:
cp surgery.py score.py ./siftflow-fcn32s/
將surgery.py和score.py拷貝到siftflow-fcn32s下。
(2)ImportError: No module named setproctitle
解決方案是:安裝setproctitle! sudo pip install setproctitle
(3)IndexError: list index out of range
解決方案:修改GPU編號為0號GPU
(4)No modulw named siftflow_layers
解決方案:瘋了,幹錯把根目錄下邊的所有.py檔案全拷貝到siftflow-fcn32s裡邊去吧。
好了,現在可以開始訓練了!看看訓練過程:
由於損失loss很大,我也不知道什麼時候能收斂,所以先放一放,等跑出結果來我再過來更新!
-------------------------------------------------------------2017.1.12更新----------------------------------------------------------------------
經過半個月的折騰和討論,現在可以確定原先的參考文章是有問題的,這個網路沒有辦法收斂。下面更正幾條:
1. 一般情況下不要直接修改test.prototxt和trainval.prototxt,而是執行net.py這個指令碼,執行完成後也不要將test.prototxt
和trainval.prototxt
中的fc6
和fc7
替換為其他名稱.
2. 這是重點,沒有收斂的根源在這裡!修改solve.py:
1 import sys 2 import caffe 3 import surgery, score 4 5 import numpy as np 6 import os 7 8 import setproctitle 9 setproctitle.setproctitle(os.path.basename(os.getcwd())) 10 11 vgg_weights = '../ilsvrc-nets/vgg16-fcn.caffemodel' 12 vgg_proto = '../ilsvrc-nets/VGG_ILSVRC_16_layers_deploy.prototxt' 13 # init 14 #caffe.set_device(int(sys.argv[1])) 15 caffe.set_device(0) 16 caffe.set_mode_gpu() 17 18 #solver = caffe.SGDSolver('solver.prototxt') 19 #solver.net.copy_from(weights) 20 solver = caffe.SGDSolver('solver.prototxt') 21 vgg_net = caffe.Net(vgg_proto, vgg_weights, caffe.TRAIN) 22 surgery.transplant(solver.net, vgg_net) 23 del vgg_net 24 25 # surgeries 26 interp_layers = [k for k in solver.net.params.keys() if 'up' in k] 27 surgery.interp(solver.net, interp_layers) 28 29 # scoring 30 test = np.loadtxt('../data/sift-flow/test.txt', dtype=str) 31 32 for _ in range(50): 33 solver.step(2000) 34 # N.B. metrics on the semantic labels are off b.c. of missing classes; 35 # score manually from the histogram instead for proper evaluation 36 score.seg_tests(solver, False, test, layer='score_sem', gt='sem') 37 score.seg_tests(solver, False, test, layer='score_geo', gt='geo')
可以對比一下之前的solve.py,發現區別在這:
紅色框出來的是原先的內容,被我註釋掉了,換成了下面四行!為什麼要這樣做呢?我們來看看這個transplant函式的定義吧:
所以,原先的方法僅僅是從vgg-16模型中拷貝引數,但是並沒有改造原先的網路,這才是不收斂的根源啊!
3.修改solver.prototxt:
1 train_net: "trainval.prototxt" 2 test_net: "test.prototxt" 3 test_iter: 200 4 # make test net, but don't invoke it from the solver itself 5 test_interval: 999999999 6 display: 20 7 average_loss: 20 8 lr_policy: "fixed" 9 # lr for unnormalized softmax 10 base_lr: 1e-10 11 # high momentum 12 momentum: 0.99 13 # no gradient accumulation 14 iter_size: 1 15 max_iter: 300000 16 weight_decay: 0.0005 17 snapshot:4000 18 snapshot_prefix:"snapshot/train" 19 test_initialization: false
這裡我們增加了 snapshot:4000 snapshot_prefix:"snapshot/train" ,每4000次我們會在當前目錄下的snapshot下儲存一下模型(需要提前建立snapshot目錄)。
4. 有的人可能會出現out of memory錯誤。這裡有兩種判斷:(1)根本沒法迭代,那麼你就要把batchsize設定小一點了。預設是 iter_size: 1 (solver.prototxt),另外在siftflow_layers.py中 top[0].reshape(1, *self.data.shape) 這裡預設也是1,batchsize = iter_size* 1。如果已經是最小了,即這兩個地方都是1了,如果你還是out of memory,那麼要麼更換好的硬體(GPU),要麼resize 資料集到更小的尺寸。(2)如果先提示“Begin seg tests”,然後out of memory,那麼是在執行score.py時記憶體溢位了,這時還是上面的兩種解決方案。
好了,上面是我這段時間研究後的補充,感謝小夥伴@踏雪霏鴻 ,最初是他發現了這裡的錯誤,最終幫助大家解決了問題。下面我們就可以重新開始訓練模型了!
1 python solve.py
這一次收斂速度會非常快,而且loss會降到很可觀的數字。
這裡我沒有一直跑下去,因為我準備用voc資料集,所以跑了一會就去跑voc資料集了。但是,這次是有小夥伴做過測試的,效果可以,所以基本上siftflow32s的訓練步驟就是這些了!需要訓練siftflow-fcn32s的朋友可以按這個走,然後用訓練得到的32s的模型去訓練16s的模型,最後用16s的模型去訓練8s的。
5. 最後,由於從32s->16s和16s->8s不需要重新構造卷積層,所以上面第二點提到的註釋和替換的那部分就不需要了,直接用solver.net.copy_from(weights)就可以了!
其二,deploy.prototxt的第一層(input層),維度要和輸入圖片大小對應,如下圖:
因為siftflow資料集是256*256的,要一一對應。如果發現要訓練的模型下沒有deploy.prototxt這個檔案,可以從test.prototxt或者trainval.prototxt複製,然後刪除最後一層loss層:
再新增一層輸入層,這個輸入層可以從voc-fcn8s這個資料夾裡的deploy.protottx裡邊複製,內容如下(注意根據輸入圖片的尺寸修改):
其三,很多人要用自己的資料集訓練fcn,那怎麼做呢?
是這樣的,我們可以先用fcn32s的模型(已經訓練好的)和自己的資料集再訓練一遍得到新的fcn32s,這其實有專業術語——fine-tune.
然後用上面訓練好的fcn32s和自己的資料集,訓練出fcn16s。最後,再用上一步的fcn16s和自己的資料集訓練出fcn8s。
-------------------------------------------------------結束語--------------------------------------------------------------------------
雖然這樣一篇部落格看上去好像也沒有多少東西,但是卻凝結了我和很多人長時間努力的汗水,晝夜奮戰搞出來的。我相信,在看過我這篇文章之前,應該是沒有多少中文資料的,要麼隻言片語,要麼誤導性的。後面也許會有一些部落格陸續出來,那基本上也是的小夥伴們寫的,但是基本上比較簡單,言簡意賅,不會像我這麼細緻寫出來。
為什麼要做這樣一件工作呢?辛苦搞出來的東西,就這樣送人了,而且還要再花時間寫這麼長的博文,打這麼多的字。我希望後來人能少走一些彎路,不像我這麼痛苦;同時,我希望看過我部落格的人也能秉承奉獻的精神,把自己的工作公開出來,避免別人少走彎路,這既是對自己工作的總結和梳理,也是對後來人的極大幫助!搞學術不能閉門造車,希望我等共勉!