1. 程式人生 > >caffe安裝,編譯(包括CUDA和cuDNN的安裝),並訓練,測試自己的資料(caffe使用教程)

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: lmdbleveldb (note: leveldb requires snappy)
  • 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版本,然後解壓縮,並且放到相應的資料夾下,最後修改一下許可權

<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*
網上的不少教程一般給到上面這些就沒有了,我安裝的時候,僅僅進行上面的步驟,在make  test的時候會報錯的。錯誤資訊大致如下:

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%。