1. 程式人生 > >FCN網路的訓練——以SIFT-Flow 資料集為例

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.prototxttrainval.prototxt中的fc6fc7分別替換為其他名稱,例如:fc6_newfc7_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.prototxttrainval.prototxt中的fc6fc7替換為其他名稱.

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。

-------------------------------------------------------結束語--------------------------------------------------------------------------

雖然這樣一篇部落格看上去好像也沒有多少東西,但是卻凝結了我和很多人長時間努力的汗水,晝夜奮戰搞出來的。我相信,在看過我這篇文章之前,應該是沒有多少中文資料的,要麼隻言片語,要麼誤導性的。後面也許會有一些部落格陸續出來,那基本上也是的小夥伴們寫的,但是基本上比較簡單,言簡意賅,不會像我這麼細緻寫出來。

為什麼要做這樣一件工作呢?辛苦搞出來的東西,就這樣送人了,而且還要再花時間寫這麼長的博文,打這麼多的字。我希望後來人能少走一些彎路,不像我這麼痛苦;同時,我希望看過我部落格的人也能秉承奉獻的精神,把自己的工作公開出來,避免別人少走彎路,這既是對自己工作的總結和梳理,也是對後來人的極大幫助!搞學術不能閉門造車,希望我等共勉!