Caffe學習總結(更新中)
主要參考一個大牛的caffe學習系列博客:http://www.cnblogs.com/denny402/tag/caffe/
這里說明一下:下面的測試分別在我自己的電腦和服務器上進行(服務器速度快),
1. 關於caffe的簡介
caffe是一個清晰而高效的深度學習框架,純粹的C++/CUDA架構,支持命令行、Python和MATLAB接口,可以在CPU和GPU直接無縫切換;
caffe的主要優勢:
(1)CPU與GPU的無縫切換;
(2)模型與優化都是通過配置文件來設置,無需代碼;
簡潔好用不多說。
2. Caffe環境的搭建,參考另一篇博客:Ubuntu14.04安裝caffe。
3. Caffe總體框架介紹
主要參考:深度學習框架Caffe源碼解析;
若想詳細了解caffe源碼,參考官方教程Tutorial Documentation或者看《caffe官方教程中譯本》。
還有一個博客寫的很好,caffe源碼解析:http://www.cnblogs.com/louyihang-loves-baiyan/
Caffe主要由Blob,Layer,Net 和 Solver這幾個部分組成。
1)Blob
主要用來表示網絡中的數據,包括訓練數據,網絡各層自身的參數(包括權值、偏置以及它們的梯度),網絡之間傳遞的數據都是通過 Blob 來實現的,同時 Blob 數據也支持在 CPU 與 GPU 上存儲,能夠在兩者之間做同步。
2)Layer
是對神經網絡中各種層的一個抽象,包括我們熟知的卷積層和下采樣層,還有全連接層和各種激活函數層等等。同時每種 Layer 都實現了前向傳播和反向傳播,並通過 Blob 來傳遞數據。
3) Net
是對整個網絡的表示,由各種 Layer 前后連接組合而成,也是我們所構建的網絡模型。
4) Solver
定義了針對 Net 網絡模型的求解方法,記錄網絡的訓練過程,保存網絡模型參數,中斷並恢復網絡的訓練過程。自定義 Solver 能夠實現不同的網絡求解方式。
完整巧妙不多說。
4. 總流程(重點)
完成一個簡單的自己的網絡模型訓練預測,主要包含幾個步驟:(以一個實例介紹)
1) 准備數據;
例1,學習系列博客上的數據,后面會用到,這里簡單說明一下:
可以從這里下載:http://pan.baidu.com/s/1nuqlTnN
共有500張圖片,分為大巴車、恐龍、大象、鮮花和馬五個類,每個類100張。編號分別以3,4,5,6,7開頭,各為一類。從其中每類選出20張作為測試,其余80張作為訓練。
因此最終訓練圖片400張,測試圖片100張,共5類。我將圖片放在caffe根目錄下的 data文件夾下面。即訓練圖片目錄:data/re/train/ ,測試圖片目錄: data/re/test/。
2) 數據格式處理,也就是把我們jpg,jpeg,png,tif等格式的圖片(可能存在大小不一致的問題),處理轉換成caffe中能夠運行的db(leveldb/lmdb)文件。
參考學習系列博客 Caffe學習系列(11):圖像數據轉換成db(leveldb/lmdb)文件 講的非常詳細。
總結一下,在caffe中,作者為我們提供了這樣一個文件:
convert_imageset.cpp,存放在根目錄下的tools文件夾下。編譯之后,生成對應的可執行文件放在 buile/tools/ 下面,這個文件的作用就是用於將圖片文件轉換成caffe框架中能直接使用的db文件。
該文件的使用格式:
convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME
四個參數:
FLAGS: 圖片參數組,后面詳細介紹;
ROOTFOLDER/: 圖片存放的絕對路徑,從linux系統根目錄開始;
LISTFILE: 圖片文件列表清單,一般為一個txt文件,一行一張圖片;
DB_NAME: 最終生成的db文件存放目錄。
其中第二個和第四個目錄是自己決定的,不多說;
先說第三個,所謂圖片列表清單,也叫標簽文件,一般該文件存放圖片文件路徑,以及該圖片的標簽(屬於哪個類);一般來說,標簽文件有兩個,一個描述訓練集合-train.txt,一個描述測試集合-test.txt,(可能還有描述驗證的val.txt),
例1中測試圖片轉換后的標簽文件格式(test.txt)如下:
那怎么由圖片生成標簽文件呢?
當然圖片很少的時候,直接手動輸入就好了,但圖片很多的情況,就需要用腳本文件來自動生成了。之前對腳本的編寫不是很熟,有個快速上手的文章:
http://www.cnblogs.com/handsomecui/p/5869361.html(其實非常簡單( ̄▽ ̄))
對於例1,在examples下面創建一個myfile的文件夾,來用存放配置文件和腳本文件。然后編寫一個腳本create_filelist.sh,用來生成train.txt和test.txt清單文件。腳本(/examples/myfile/create_filelist.sh)編寫如下:
#!/usr/bin/env sh DATA=data/re/ MY=examples/myfile echo "Create train.txt..." rm -rf $MY/train.txt for i in 3 4 5 6 7 do find $DATA/train -name $i*.jpg | cut -d '/' -f4-5 | sed "s/$/ $i/">>$MY/train.txt done echo "Create test.txt..." rm -rf $MY/test.txt for i in 3 4 5 6 7 do find $DATA/test -name $i*.jpg | cut -d '/' -f4-5 | sed "s/$/ $i/">>$MY/test.txt done echo "All done"
生成的.txt文件,就可以作為第三個參數,直接使用了。
最后了解一下第一個參數:即FLAGS這個參數組,有些什么內容:
-gray: 是否以灰度圖的方式打開圖片。程序調用opencv庫中的imread()函數來打開圖片,默認為false
-shuffle: 是否隨機打亂圖片順序。默認為false
-backend:需要轉換成的db文件格式,可選為leveldb或lmdb,默認為lmdb
-resize_width/resize_height: 改變圖片的大小。在運行中,要求所有圖片的尺寸一致,因此需要改變圖片大小。 程序調用opencv庫的resize()函數來對圖片放大縮小,默認為0,不改變
-check_size: 檢查所有的數據是否有相同的尺寸。默認為false,不檢查
-encoded: 是否將原圖片編碼放入最終的數據中,默認為false
-encode_type: 與前一個參數對應,將圖片編碼為哪一個格式:‘png','jpg'......
繼續看例1,於是將訓練數據轉換成lmdb文件命令如下:
注:由於圖片大小不一,因此這里統一轉換成256*256大小。
類似的,將測試數據轉換成lmdb文件命令如下:
當然,由於參數比較多,依然可以寫腳本(examples/myfile/create_lmdb.sh):
#!/usr/bin/env sh MY=examples/myfile echo "Create train lmdb.." rm -rf $MY/img_train_lmdb build/tools/convert_imageset \ --shuffle \ --resize_height=256 \ --resize_width=256 \ /home/xxx/caffe/data/re/ \ $MY/train.txt \ $MY/img_train_lmdb echo "Create test lmdb.." rm -rf $MY/img_test_lmdb build/tools/convert_imageset \ --shuffle \ --resize_width=256 \ --resize_height=256 \ /home/xxx/caffe/data/re/ \ $MY/test.txt \ $MY/img_test_lmdb echo "All Done.."
這里的xxx是你具體的路徑。
運行成功后,會在 examples/myfile下面生成兩個文件夾img_train_lmdb和img_test_lmdb,分別用於保存圖片轉換后的lmdb文件。
3) 計算均值並保存
圖片減去均值再訓練,會提高訓練速度和精度。因此,一般都會有這個操作。
caffe程序提供了一個計算均值的文件compute_image_mean.cpp,我們直接使用就可以了。
對於例1,命令如下:
1 sudo build/tools/compute_image_mean examples/myfile/img_train_lmdb examples/myfile/mean.binaryproto
compute_image_mean帶兩個參數,第一個參數是lmdb訓練數據位置,第二個參數設定均值文件的名字及保存路徑。
運行成功后,會在 examples/myfile/ 下面生成一個mean.binaryproto的均值文件。
4) 創建模型
在caffe中是通過.prototxt配置文件來定義網絡模型。
例如,可以打開caffe自帶的手寫數字庫MNIST例子的網絡結構文件:
sudo vim examples/mnist/lenet_train_test.prototxt
這個網絡模型是非常有名的LeNet模型,網絡結構如下圖。
可以看到各個網絡層是如何定義的:
l 數據層(也叫輸入層)
layer { name: "mnist" //表示層名 type: "Data" //表示層的類型 top: "data" top: "label" include { phase: TRAIN //表示僅在訓練階段起作用 } transform_param { scale: 0.00390625 //將圖像像素值歸一化 } data_param { source: "examples/mnist/mnist_train_lmdb" //數據來源 batch_size: 64 //訓練時每個迭代的輸入樣本數量 backend: LMDB //數據類型 } }
關於數據層及參數的編寫,參看學習系列(2):Caffe學習系列(2):數據層及參數
簡單介紹一下其中參數:
name: 表示該層的名稱,可隨意取
type: 層類型,如果是Data,表示數據來源於LevelDB或LMDB。根據數據的來源不同,數據層的類型也不同(后面會詳細闡述)。一般在練習的時候,我們都是采 用的LevelDB或LMDB數據,因此層類型設置為Data。
top或bottom: 每一層用bottom來輸入數據,用top來輸出數據。如果只有top沒有bottom,則此層只有輸出,沒有輸入。反之亦然。如果有多個 top或多個bottom,表示有多個blobs數據的輸入和輸出。
data 與 label: 在數據層中,至少有一個命名為data的top。如果有第二個top,一般命名為label。 這種(data,label)配對是分類模型所必需的。
include: 一般訓練的時候和測試的時候,模型的層是不一樣的。該層(layer)是屬於訓練階段的層,還是屬於測試階段的層,需要用include來指定。如果沒有include參數,則表示該層既在訓練模型中,又在測試模型中。
Transformations: 數據的預處理,可以將數據變換到定義的范圍內。如設置scale為0.00390625,實際上就是1/255, 即將輸入數據由0-255歸一化到0-1之間。
l 視覺層
視覺層包括Convolution, Pooling, Local Response Normalization (LRN), im2col等層。
關於視覺層及參數的編寫,參看:Caffe學習系列(3):視覺層(Vision Layers)及參數
仍然以mnist為例,
mnist卷積層定義如下:
layer { name: "conv1" type: "Convolution" bottom: "data" //輸入是data top: "conv1" //輸出是卷積特征 param { lr_mult: 1 //權重參數w的學習率倍數 } param { lr_mult: 2 //偏置參數b的學習率倍數 } convolution_param { num_output: 20 kernel_size: 5 stride: 1 weight_filler { //權重參數w的初始化方案,使用xavier算法 type: "xavier" } bias_filler { type: "constant" //偏置參數b初始化化為常數,一般為0 } } }
簡單介紹一下其中參數:
層類型:Convolution
lr_mult: 學習率的系數,最終的學習率是這個數乘以solver.prototxt配置文件中的base_lr。如果有兩個lr_mult, 則第一個表示權值的學習率,第二個表示偏置項的學習率。一般偏置項的學習率是權值學習率的兩倍。
在后面的convolution_param中,我們可以設定卷積層的特有參數。
必須設置的參數:
num_output: 卷積核(filter)的個數
kernel_size: 卷積核的大小。如果卷積核的長和寬不等,需要用kernel_h和kernel_w分別設定
其它參數:
stride: 卷積核的步長,默認為1。也可以用stride_h和stride_w來設置。
pad: 擴充邊緣,默認為0,不擴充。 擴充的時候是左右、上下對稱的,比如卷積核的大小為5*5,那么pad設置為2,則四個邊緣都擴充2個像素,即寬度和高度都擴充了4個像素,這樣卷積運算之后的特征圖就不會變小。也可以通過pad_h和pad_w來分別設定。
weight_filler: 權值初始化。 默認為“constant",值全為0,很多時候我們用"xavier"算法來進行初始化,也可以設置為”gaussian"
bias_filler: 偏置項的初始化。一般設置為"constant",值全為0。
bias_term: 是否開啟偏置項,默認為true, 開啟
group: 分組,默認為1組。如果大於1,我們限制卷積的連接操作在一個子集內。如果我們根據圖像的通道來分組,那么第i個輸出分組只能與第i個輸入分組進行連接。
輸入:n*c0*w0*h0
輸出:n*c1*w1*h1
其中,c1就是參數中的num_output,生成的特征圖個數
w1=(w0+2*pad-kernel_size)/stride+1;
h1=(h0+2*pad-kernel_size)/stride+1;
如果設置stride為1,前后兩次卷積部分存在重疊。如果設置pad=(kernel_size-1)/2,則運算后,寬度和高度不變。
mnist的Pooling層,也叫池化層,為了減少運算量和數據維度而設置的一種層。
LeNet總共有兩個,其中一個定義如下:
layer { name: "pool1" type: "Pooling" bottom: "conv1" top: "pool1" pooling_param { pool: MAX kernel_size: 2 stride: 2 } }
層類型:Pooling
必須設置的參數:
kernel_size: 池化的核大小。也可以用kernel_h和kernel_w分別設定。
其它參數:
pool: 池化方法,默認為MAX。目前可用的方法有MAX, AVE, 或STOCHASTIC
pad: 和卷積層的pad的一樣,進行邊緣擴充。默認為0
stride: 池化的步長,默認為1。一般我們設置為2,即不重疊。也可以用stride_h和stride_w來設置。
還有Local Response Normalization (LRN)層和im2col層等,在LeNet中沒有定義,可以參看AlexNet或GoogLenet等更復雜的模型。
l 激活層
關於激活層及參數的編寫,參看Caffe學習系列(4):激活層(Activiation Layers)及參數
在LeNet中,用到的激活函數是ReLU / Rectified-Linear and Leaky-ReLU,
ReLU是目前使用最多的激活函數,主要因為其收斂更快,並且能保持同樣效果。標准的ReLU函數為max(x, 0),當x>0時,輸出x; 當x<=0時,輸出0。
f(x)=max(x,0)。
定義如下:
layer { name: "relu1" type: "ReLU" bottom: "ip1" top: "ip1" }
層類型:ReLU
可選參數:
negative_slope:默認為0. 對標准的ReLU函數進行變化,如果設置了這個值,那么數據為負數時,就不再設置為0,而是用原始數據乘以negative_slope。
RELU層支持in-place計算,這意味着bottom的輸出和輸入相同以避免內存的消耗。
l 其他層
包括:softmax_loss層,Inner Product層,accuracy層,reshape層和dropout層等,參看Caffe學習系列(5):其它常用層及參數,不多說。
l 關於模型編寫的一個總結:
Caffe學習系列(6):Blob,Layer and Net以及對應配置文件的編寫
一個完整網絡例子:
第一層:name為mnist, type為Data,沒有輸入(bottom),只有兩個輸出(top),一個為data,一個為label
第二層:name為ip,type為InnerProduct, 輸入數據data, 輸出數據ip
第三層:name為loss, type為SoftmaxWithLoss,有兩個輸入,一個為ip,一個為label,有一個輸出loss,沒有畫出來。
對應的配置文件prototxt就可以這樣寫:
name: "LogReg" layer { name: "mnist" type: "Data" top: "data" top: "label" data_param { source: "input_leveldb" batch_size: 64 } } layer { name: "ip" type: "InnerProduct" bottom: "data" top: "ip" inner_product_param { num_output: 2 } } layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip" bottom: "label" top: "loss" }
5) 編寫配置文件
主要參看Caffe學習系列(7):solver及其配置 和Caffe學習系列(8):solver優化方法
先看一下mnist中配置文件(一般是..solver.prototxt文件):
1 sudo vim examples/mnist/lenet_solver.prototxt
配置文件如下:
sudo vim examples/mnist/lenet_solver.prototxt 配置文件如下: # The train/test net protocol buffer definition net: "examples/mnist/lenet_train_test.prototxt" //設置深度網絡模型,就是介紹的模型。 # test_iter specifies how many forward passes the test should carry out. # In the case of MNIST, we have test batch size 100 and 100 test iterations, # covering the full 10,000 testing images. test_iter: 100 //這個要與test layer中的batch_size結合起來理解。mnist數據中測試樣本總數為10000,一次性執行全部數據效率很低,因此我們將測試數據分成幾個批次來執行,每個批次的數量就是batch_size。假設我們設置batch_size為100,則需要迭代100次才能將10000個數據全部執行完。因此test_iter設置為100。執行完一次全部數據,稱之為一個epoch # Carry out testing every 500 training iterations. test_interval: 500 // 測試間隔。也就是每訓練500次,才進行一次測試。 # The base learning rate, momentum and the weight decay of the network. base_lr: 0.01 momentum: 0.9 //上一次梯度更新的權重 weight_decay: 0.0005 //權重衰減項,防止過擬合的一個參數。 # The learning rate policy lr_policy: "inv" gamma: 0.0001 power: 0.75 //這幾個參數用於學習率的設置。只要是梯度下降法來求解優化,都會有一個學習率,也叫步長。base_lr用於設置基礎學習率,在迭代的過程中,可以對基礎學習率進行調整。怎么樣進行調整,就是調整的策略,由lr_policy來設置。 # Display every 100 iterations display: 100 //每100次迭代顯示一次 # The maximum number of iterations max_iter: 10000 //最大迭代次數。這個數設置太小,會導致沒有收斂,精確度很低。設置太大,會導致震盪,浪費時間。 # snapshot intermediate results snapshot: 5000 snapshot_prefix: "examples/mnist/lenet" //快照。將訓練出來的model和solver狀態進行保存,snapshot用於設置訓練多少次后進行保存,默認為0,不保存。snapshot_prefix設置保存路徑。 # solver mode: CPU or GPU solver_mode: GPU //設置運行模式。默認為GPU,如果你沒有GPU,則需要改成CPU,否則會出錯。
其實英文注釋將參數的意思說的很清楚,我自己也在后面注釋了,具體詳細的解釋還是參看前面提到的兩個鏈接。
優化的方法不是很清楚,這里不多說。
繼續看例1,簡單一點,直接用程序自帶的caffenet模型,位置在 models/bvlc_reference_caffenet/文件夾下, 將需要的兩個配置文件,復制到myfile文件夾內:
sudo cp models/bvlc_reference_caffenet/solver.prototxt examples/myfile/ sudo cp models/bvlc_reference_caffenet/train_val.prototxt examples/myfile/
修改其中的solver.prototxt
1 sudo vi examples/myfile/solver.prototxt
修改如下:
sudo cp models/bvlc_reference_caffenet/solver.prototxt examples/myfile/ sudo cp models/bvlc_reference_caffenet/train_val.prototxt examples/myfile/ 修改其中的solver.prototxt sudo vi examples/myfile/solver.prototxt 修改如下: net: "examples/myfile/train_val.prototxt" test_iter: 2 test_interval: 50 base_lr: 0.001 lr_policy: "step" gamma: 0.1 stepsize: 100 display: 20 max_iter: 500 momentum: 0.9 weight_decay: 0.005 solver_mode: GPU
說明:100個測試數據,batch_size為50,因此test_iter設置為2,就能全cover了。在訓練過程中,調整學習率,逐步變小。
修改train_val.protxt,只需要修改兩個階段的data層就可以了,其它可以不用管。
name: "CaffeNet" layer { name: "data" type: "Data" top: "data" top: "label" include { phase: TRAIN } transform_param { mirror: true crop_size: 227 mean_file: "examples/myfile/mean.binaryproto" } data_param { source: "examples/myfile/img_train_lmdb" batch_size: 256 backend: LMDB } } layer { name: "data" type: "Data" top: "data" top: "label" include { phase: TEST } transform_param { mirror: false crop_size: 227 mean_file: "examples/myfile/mean.binaryproto" } data_param { source: "examples/myfile/img_test_lmdb" batch_size: 50 backend: LMDB } }
實際上就是修改兩個data layer的mean_file和source這兩個地方,其它都沒有變化 。
6) 訓練和測試
caffe的運行提供三種接口:c++接口(命令行)、python接口和matlab接口。
這里選擇命令行,參看Caffe學習系列(10):命令行解析。
回到例1,命令很簡單:
1 sudo build/tools/caffe train -solver examples/myfile/solver.prototxt
運行時間和最后的精確度,會根據機器配置,參數設置的不同而不同。gpu+cudnn的配置運行500次,大約8分鍾,精度為95%。
其他的例子,參看Caffe學習系列(9):運行caffe自帶的兩個簡單例子
運行結果大致(我自己機子上跑的結果)如下:
mnist:
精度99%左右。
cifar10:
精度75%左右。
7) 用訓練好的model用到自己數據上
參看Caffe學習系列(23):如何將別人訓練好的model用到自己的數據上
l 下載caffemodel
參看:Model-Zoo或model zoo documentation;
下載地方:https://github.com/BVLC/caffe/tree/master/models
簡單點,直接運行腳本來下載:
sudo ./scripts/download_model_binary.py <dirname>
<dirname>可以是:
models/bvlc_reference_caffenet
models/bvlc_alexnet
models/bvlc_reference_rcnn_ilsvrc13
models/bvlc_googlenet
例2,這里我們以Alexnet為例,
sudo ./scripts/download_model_binary.py models/bvlc_alexnet
下載后,可以在models/bvlc_alexnet/下看到.caffemodel文件。
l 小插曲,先做一個簡單的測試,單圖的測試,也就是分類。
參考Caffe學習系列(20):用訓練好的caffemodel來進行分類
在caffe根目錄下的 examples/cpp-classification/ 文件夾下面,有個classification.cpp文件,就是用來分類的。
使用該文件的格式如下:
./build/examples/cpp_classification/classification.bin \
網絡結構文件:xx/xx/deploy.prototxt \
訓練的模型文件:xx/xx/xx.caffemodel \
訓練的圖像的均值文件:xx/xx/xx.binaryproto \
類別名稱標簽文件:xx/xx/synset_words.txt \
待測試圖像:xx/xx/xx.jpg
在例2中,即AlexNet模型中,
網絡結構文件:models/bvlc_alexnet/deploy.prototxt
訓練的模型文件:models/bvlc_alexnet/bvlc_alexnet.caffemodel
訓練的圖像的均值文件:
直接用腳本下載:sudo sh ./data/ilsvrc12/get_ilsvrc_aux.sh
下載后的文件是data/ilsvrc12/imagenet_mean.binaryproto
類別名稱標簽文件:
在調用腳本文件下載均值的時候,這個文件也一並下載好了。
文件:data/ilsvrc12/synset_words.txt
待測試圖像:
就以自帶的測試圖片吧:examples/images/cat.jpg
於是完整的命令是:
./build/examples/cpp_classification/classification.bin \ models/bvlc_alexnet/deploy.prototxt \ models/bvlc_alexnet/bvlc_alexnet.caffemodel \ data/ilsvrc12/imagenet_mean.binaryproto \ data/ilsvrc12/synset_words.txt \ examples/images/cat.jpg
運行結果如下:
(不要在意細節= =)
換VGG16試試(在服務器上跑):
(好吧,結果還是好奇怪。。)
l 繼續,第二步依然是准備數據並處理啦,前面提過了,不在贅述。
還是用例1的數據。
l 接下來就是測試
依舊跟前面說的一樣,要修改.prototxt文件:
先拷貝一份到myfile,順便把.caffemodel也拷貝過來吧:
cp models/bvlc_alexnet/bvlc_alexnet.caffemodel examples/myfile/ cp models/bvlc_alexnet/train_val.prototxt examples/myfile/ vim examples/myfile/train_val.prototxt
還是修改兩個data layer的mean_file和source這兩個地方,其它都沒有變化 。
接着按照命令行(C++)的測試命令格式,(不清楚的話參考博客)輸入測試命令就好了:
1 ./build/tools/caffe test -model examples/myfile/train_val.prototxt -weights examples/myfile/bvlc_alexnet.caffemodel -gpu 0 -iterations 1000
這個測試在服務器上完成的,結果如下:
額,這個結果很僵。。
應該是數據集的問題,用服務器上處理好的數據集吧,
(服務器上的數據是imagenet12百度雲下載鏈接:https://pan.baidu.com/s/1pLt83J9(該測試數據集較大)
修改train_val.prototxt:
再輸入測試命令:
1 ./build/tools/caffe test -model examples/myfile/train_val.prototxt -weights examples/myfile/bvlc_alexnet.caffemodel -gpu 0 -iterations 1000
換個VGG試試:
按前面步驟(服務器上沒有_train_val.prototxt文件,我自己找了一個,復制到examples/myfile/,按前面的修改),測試:
運行結果如下:
更新中。。。
8) 額外的例子
更新中。。。
注意事項:
1.在caffe中運行的所有程序,都必須在根目錄下進行,否則會有下面類似的報錯:
例子:報not found的錯。
2.遇到問題網上相關文章很多,多看,多問,多學。
更新中。。。