參考文章: 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。
-------------------------------------------------------結束語--------------------------------------------------------------------------
雖然這樣一篇博客看上去好像也沒有多少東西,但是卻凝結了我和很多人長時間努力的汗水,晝夜奮戰搞出來的。我相信,在看過我這篇文章之前,應該是沒有多少中文資料的,要么只言片語,要么誤導性的。后面也許會有一些博客陸續出來,那基本上也是的小伙伴們寫的,但是基本上比較簡單,言簡意賅,不會像我這么細致寫出來。
為什么要做這樣一件工作呢?辛苦搞出來的東西,就這樣送人了,而且還要再花時間寫這么長的博文,打這么多的字。我希望后來人能少走一些彎路,不像我這么痛苦;同時,我希望看過我博客的人也能秉承奉獻的精神,把自己的工作公開出來,避免別人少走彎路,這既是對自己工作的總結和梳理,也是對后來人的極大幫助!搞學術不能閉門造車,希望我等共勉!