caffe安裝,編譯(包括CUDA和cuDNN的安裝),並訓練,測試自己的資料(caffe使用教程)
caffe是一個非常清晰且高效的深度學習框架,目前有著不少的使用者,也漸漸的形成了自己的社群,社群上可以討論相關的問題。
我從開始看深度學習的相關內容到能夠用caffe訓練測試自己的資料,看了不少網站,教程和部落格,也走了不少彎路,在此把整個流程梳理和總結一遍,以期望可以可以僅僅通過這一篇文章就可以輕鬆的用caffe訓練自己的資料,體驗深度學習的樂趣。(本文中包含了很多的連結,連結中有我看過的詳細的教程和講解,以方便大家理解)
1.安裝和配置caffe(包括安裝CUDA)
caffe的安裝和編譯,官網上已經寫的非常的詳細了。官網上的安裝連結 : 連結 只要按照官網的說明一步步的進行,一般都是不會出什麼問題的。並且,官網給出來ubuntu,Mac,和Windows環境下安裝和配置的連結。條件允許的話,還是儘量在Ubuntu下安裝和配置吧,對於不熟悉Ubuntu的人來說可能比較麻煩,但是我感覺,在linux下安裝,編譯,包括後面的使用都是比較方便的。
這裡需要提前說明的一點是,在官方給的安裝文件裡,下面幾個是可以選擇安裝的
- OpenCV >= 2.4 including 3.0
-
IO libraries:
lmdb
,leveldb
(note: leveldb requiressnappy
) - cuDNN for GPU acceleration (v5)
其中,cuDnn是專門針對CUDA的加速套件,可以為在GPU上跑深度學習的程式進行加速,所以這個還是安裝一下吧,為以後跑程式能省些時間。
2. cuDNN的安裝
這裡單獨強調一下cuDNN的安裝(網上給的很多教程有問題,會導致後面的編譯失敗),cuDNN的安裝需要在CUDA安裝完成後進行。
cuDnn需要從Nvidia的官網上下載,並且需要先註冊才行,註冊的時候會讓你填寫一些調查問卷,註冊完成後,選擇正確的版本進行下載。我這裡下載的是:Download cuDNN v5 (May 27, 2016), for CUDA 8.0 cuDNN v5 Library for Linux版本,然後解壓縮,並且放到相應的資料夾下,最後修改一下許可權
網上的不少教程一般給到上面這些就沒有了,我安裝的時候,僅僅進行上面的步驟,在make test的時候會報錯的。錯誤資訊大致如下:<span style="background-color: rgb(240, 240, 240);">tar xvzf cudnn-8.0-linux-x64-v4.tgz #這裡要注意你下載的版本,需要解壓你下載的對應版本的檔案</span>解壓後的資料夾名字是cuda sudo cp cuda/include/cudnn.h /usr/local/cuda/include sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64 sudo chmod a+r /usr/local/cuda/include/cudnn.h /usr/local/cuda/lib64/libcudnn*
CXX/LD -o .build_release/tools/upgrade_net_proto_text.bin
.build_release/lib/libcaffe.so: undefined reference to caffe::curandGetErrorString(curandStatus)
.build_release/lib/libcaffe.so: undefined reference to caffe::BaseConvolutionLayer::weight_gpu_gemm(double const*, double const*, double*)
.build_release/lib/libcaffe.so: undefined reference to caffe::BaseConvolutionLayer::forward_gpu_bias(double*, double const*)
找了好多資料,最後在stackoverflaw上找到了解決辦法,我這裡把連結貼出來:連結
其實方法就是,在/usr/local/cuda/lib64 目錄下,執行如下操作(網上的大神說,複製過程破壞了軟連線,所以要自己手動建立一下軟連線)
sudo rm libcudnn.so.5 libcudnn.so
sudo ln -sf libcudnn.so.5 libcudnn.so
sudo ln -sf libcudnn.so.5.1.3 libcudnn.so.5
sudo ldconfig /usr/local/cuda/lib64
此時,再重新編譯,執行 make all 然後,make test ,在然後make runtest 就可以了。
3.在MNIST上用caffe訓練LeNet網路
MNIST是一個手寫數字的資料集,LeNet 是一個比較簡單的深度學習網路模型。
這個過程,官方是給了教程的,附上鍊接:連結
但是當時我按著步驟一步步的執行,最後也能夠完全執行成功,但是程式是怎麼執行成功的,怎麼跑通的,原理是什麼,我還是搞不懂。
為了弄清楚程式具體的執行過程,我嘗試利用自己找的一些資料進行訓練和測試,以便加深對caffe和深度學習的理解。
4.訓練和測試自己的資料
這裡要感謝這篇部落格的給我的巨大幫助:點選開啟連結。雖然部落格寫的很好,但是還是有一些不詳細的地方。我在這篇部落格的基礎上進行完成和補充,儘量把整個過程描述的更加詳細。
1)準備資料
要想訓練自己的網路,首先要有圖片資料,前期在學習階段,為了節省時間,先從小的資料集開始訓練和學習比較合適,部落格的博主給了一個小資料集的連結,共500張圖片,分為大巴,恐龍,大象,鮮花和馬 五個類別,每個類別100張,資料集的下載連結為:http://pan.baidu.com/s/1nuqlTnN
編號為3,4,5,6,7開頭的,分別為一類,每一類的100張圖片,用80個作為訓練,20個作為測試,圖片放在caffe目錄下的data目錄下,即訓練圖片的目錄為data/re/train,測試圖片的目錄為data/re/test
2)圖片轉換為lmdb格式
caffe中,不能直接拿jpg,png之類的圖片檔案進行訓練,需要將圖片格式轉換為lmdb格式才可以使用。而且caffe提供了轉換格式所需要的工具,我們只需要寫幾行程式碼就能轉換完成。
在examples資料夾下新建一個myffle資料夾,這個檔案用來存放配置檔案,指令碼檔案和lmdb檔案,然後編寫一個指令碼檔案,create_filelist.sh用來生成train.txt和test.txt兩個文字檔案,這連個文字檔案包含圖片的列表清單。然後編輯這個指令碼檔案如下:
#!/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"
然後執行這個指令碼檔案
sudo sh examples/myfile/create_filelist.sh
然後就可以得到train.txt和test.txt檔案了,開啟看看基本上就知道這個檔案是幹啥的了吧。(就是為每個圖片做類別標記)
(其實,上述步驟的指令碼檔案如果看不懂或者覺得不方便,用c寫個檔案生成train.txt和test.txt也可以。我自己對指令碼檔案不太熟悉,所以這些操作我有時候會用c寫。)
接著在編寫一個指令碼檔案,呼叫convert_imageset命令來轉換資料格式。(就是把圖片格式的檔案轉換成了lmdb格式)
sudo vi 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.."
圖片的大小不一致,所以統一轉換成了256×256 的大小,執行成功後,會在examples/myfile下生成兩個檔案,img_train_lmdb和img_val_lmdb,這個兩個檔案就是圖片資料集轉換後的檔案。
3)計算均值並儲存
圖片減去均值再訓練,可以提高訓練速度和精度。因此,一般都會有這個操作。(比如非常有名的描述AlexNet的論文中,也有這一步預操作)
同樣的,減去均值的操作caffe也寫好了,直接呼叫就行了,即compute_image_mean.cpp檔案
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自帶的caffenet模型進行訓練,把該模型和相應的配置檔案都拷貝過來,操作如下:
sudo cp models/bvlc_reference_caffenet/solver.prototxt examples/myfile/
sudo cp models/bvlc_reference_caffenet/train_val.prototxt 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
# snapshot intermediate results
snapshot: 500
snapshot_prefix: "examples/myfile/results/my"
solver_mode: GPU
關於配置檔案引數不理解的地方,可以看連結:連結
然後,修改 train_val.protxt,修改成如下形式
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"
}
# mean pixel / channel-wise mean instead of mean image
# transform_param {
# crop_size: 227
# mean_value: 104
# mean_value: 117
# mean_value: 123
# mirror: true
# }
data_param {
source: "examples/myfile/img_train_lmdb"
batch_size: 50
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"
}
# mean pixel / channel-wise mean instead of mean image
# transform_param {
# crop_size: 227
# mean_value: 104
# mean_value: 117
# mean_value: 123
# mirror: false
# }
data_param {
source: "examples/myfile/img_test_lmdb"
batch_size: 50
backend: LMDB
}
}
layer {
name: "conv1"
type: "Convolution"
bottom: "data"
top: "conv1"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 96
kernel_size: 11
stride: 4
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "relu1"
type: "ReLU"
bottom: "conv1"
top: "conv1"
}
layer {
name: "pool1"
type: "Pooling"
bottom: "conv1"
top: "pool1"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm1"
type: "LRN"
bottom: "pool1"
top: "norm1"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv2"
type: "Convolution"
bottom: "norm1"
top: "conv2"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 2
kernel_size: 5
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu2"
type: "ReLU"
bottom: "conv2"
top: "conv2"
}
layer {
name: "pool2"
type: "Pooling"
bottom: "conv2"
top: "pool2"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "norm2"
type: "LRN"
bottom: "pool2"
top: "norm2"
lrn_param {
local_size: 5
alpha: 0.0001
beta: 0.75
}
}
layer {
name: "conv3"
type: "Convolution"
bottom: "norm2"
top: "conv3"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "relu3"
type: "ReLU"
bottom: "conv3"
top: "conv3"
}
layer {
name: "conv4"
type: "Convolution"
bottom: "conv3"
top: "conv4"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 384
pad: 1
kernel_size: 3
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu4"
type: "ReLU"
bottom: "conv4"
top: "conv4"
}
layer {
name: "conv5"
type: "Convolution"
bottom: "conv4"
top: "conv5"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
convolution_param {
num_output: 256
pad: 1
kernel_size: 3
group: 2
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu5"
type: "ReLU"
bottom: "conv5"
top: "conv5"
}
layer {
name: "pool5"
type: "Pooling"
bottom: "conv5"
top: "pool5"
pooling_param {
pool: MAX
kernel_size: 3
stride: 2
}
}
layer {
name: "fc6"
type: "InnerProduct"
bottom: "pool5"
top: "fc6"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu6"
type: "ReLU"
bottom: "fc6"
top: "fc6"
}
layer {
name: "drop6"
type: "Dropout"
bottom: "fc6"
top: "fc6"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc7"
type: "InnerProduct"
bottom: "fc6"
top: "fc7"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 4096
weight_filler {
type: "gaussian"
std: 0.005
}
bias_filler {
type: "constant"
value: 1
}
}
}
layer {
name: "relu7"
type: "ReLU"
bottom: "fc7"
top: "fc7"
}
layer {
name: "drop7"
type: "Dropout"
bottom: "fc7"
top: "fc7"
dropout_param {
dropout_ratio: 0.5
}
}
layer {
name: "fc8"
type: "InnerProduct"
bottom: "fc7"
top: "fc8"
param {
lr_mult: 1
decay_mult: 1
}
param {
lr_mult: 2
decay_mult: 0
}
inner_product_param {
num_output: 1000
weight_filler {
type: "gaussian"
std: 0.01
}
bias_filler {
type: "constant"
value: 0
}
}
}
layer {
name: "accuracy"
type: "Accuracy"
bottom: "fc8"
bottom: "label"
top: "accuracy"
include {
phase: TEST
}
}
layer {
name: "loss"
type: "SoftmaxWithLoss"
bottom: "fc8"
bottom: "label"
top: "loss"
}
這個檔案其實就是定義了整個網路的模型,每一個layer就是一層,你在每一層中定義好這是卷積層還是池化層,神經元有多少等等,具體的引數的含義可以看連結:點選開啟連結
5)訓練和測試
只需要直接輸入下面的命令就可以完成了。(這裡需要注意的是,訓練和測試整個的流程是一起執行的,因為上面的train_val.prototxt檔案把train和test的引數都設定好了,所以程式訓練完了以後就會直接進入測試資料的階段,其實也可以寫成兩個檔案分開執行。在solver.prototxt中,有一行為
snapshot_prefix: "examples/myfile/results/my"
這就表示,你訓練好的權值都在results資料夾下面儲存著,以後如果想要使用訓練好的模型,直接呼叫rusult下的檔案進行測試就可以。)
最後的輸出結果中,
Test net output #0:accuracy:0.95
這一行,就表示在測試資料上,用自己訓練的網路進行分類的正確率達到了95%。