總的來講,Caffe 是一個比較難上手的框架。此次嘗試訓練 Caffe 框架下 SSD 模型的訓練是我第一次使用 Caffe 框架。下面就說一說我踩過的幾個坑,但願可以幫助到你們。python
這一步是我認爲使用 Caffe 框架的最大障礙,編譯不停出錯。最後我不得不放棄轉而使用 Docker 解決 Caffe 的編譯安裝問題。下面寫出 Docker 的安裝以及拉取所需鏡像的方法。linux
安裝過程我是參照的 Docker 官方的安裝指引,傳送門在這裏。 我使用的安裝命令爲(Ubuntu 16.04 LTS
):git
# If you have installed older version of docker, removing it by using command as follows
sudo apt-get remove docker docker-engine docker.io
# Docker installation
# 1. Update the apt package index
sudo apt-get update
# 2. Install packages to allow apt to use a repository over HTTPS
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
# 3. Add Docker’s official GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 4. Add apt repository
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable"
# 5. INSTALL DOCKER CE
sudo apt-get update
sudo apt-get install docker-ce
# 6. Verify that Docker CE is installed correctly by running the hello-world image
sudo docker run hello-world
複製代碼
以上就是 Docker 的安裝過程,安裝完成 Docker 後,下一步就是尋找合適的鏡像並拉取。若是你對 Docker 的使用並不熟悉,推薦你閱讀《第一本Docker書》的前4章,閱讀時間大約在2個小時,下載在這裏。github
我是在 Docker Hub 直接搜索我須要的鏡像的,個人需求是 python2, gpu 版本的 Caffe 而且是 SSD 分支。 在 Bing.com 搜索「docker hub」,打開以後搜索關鍵詞 「caffe ssd」。 算法
我所使用的鏡像連接 在這裏。 若是你想在 Docker 中使用 GPU 加速,那麼你還必須安裝 nvidia-docker,不過幸虧安裝特別簡單。過程以下:# If you have nvidia-docker 1.0 installed: we need to remove it and all existing GPU containers
docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f
sudo apt-get purge -y nvidia-docker
# Add the package repositories
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | \
sudo apt-key add -
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \
sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update
# Install nvidia-docker2 and reload the Docker daemon configuration
sudo apt-get install -y nvidia-docker2
sudo pkill -SIGHUP dockerd
# Test nvidia-smi with the latest official CUDA image
docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi
複製代碼
按照順序依次執行就能夠了,若是你遇到問題,請參照 nvidia-docker 安裝官方指引。docker
拉取鏡像:ubuntu
sudo nvidia-docker run -it --name xxx -v /your/path/to/swap place(host):/your/path/to/swap place narumi/caffe-ssd-gpu /bin/bash
# -it 指定這個 Docker 容器是可交互的,不可少
# --name 指定 Docker 容器的名稱,方便之後使用 。將 xxx 替換爲你想要的名稱,例如我指定的名稱爲 caffe_ssd_gpu_py2。
# -v 指定掛載目錄到容器中,這樣方便容器與宿主機進行文件交換。 「:」前爲宿主機目錄,後爲容器內目錄
# narumi/caffe-ssd-gpu 指定我拉取的鏡像名
# /bin/bash 命令使得容器開啓後自動爲我打開終端
# 我本身使用的命令以下,供你們參考
sudo nvidia-docker run -it --name caffe_ssd_gpu_py2 -v /home/ubuntu/work/docker_swap:/home/swap narumi/caffe_ssd_gpu /bin/bash
# 這樣,我在容器中訪問 /home/swap 時就能看到我主機 /home/ubuntu/work/docker_swap 下存放的文件了
複製代碼
這一步纔是重點,下面咱們將處理本身的使用的數據集。vim
由於咱們將要把數據集製做爲 pascal voc 格式的數據集,所以咱們須要將標註信息存放在 xml 文件中。推薦使用 LabelImg 這個開源軟件標註。其優勢是圖形化界面,而且自動生成 xml 文件,省去了不少轉化的步驟,傳送門在這裏。 怎樣使用其實很是簡單,在這裏略過不講。若是你不會使用,請善用搜索。沒法解決的話能夠留言詢問。bash
在主機的交換目錄下建立名爲 VOCdevkit 的文件夾用於存放圖片等內容。框架
交換目錄就是剛剛建立容器時候指定的主機目錄,例如個人交換目錄爲 /home/ubuntu/work/docker_swap
具體命令以下:
cd /home/ubuntu/work/docker_swap
mkdir VOCdevkit
cd VOCdevkit
mkdir VOC2007
cd VOC2007
mkdir Annotations # 存放 xml 文件
mkdir JPEGImages # 存放 jpg 文件
mkdir ImageSets
cd ImageSets
mkdir Main
複製代碼
建立完成後將 jpg 和 xml 文件放入對應目錄下。而後使用 python 腳本劃分一下訓練集和測試集。我把我使用的腳本貼在這裏:
import os
import random
# 下面兩個目錄改爲本身的目錄
xmlfilepath=r'/your/path/to/xmls'
saveBasePath=r"your/path/to/save/VOCdevkit"
trainval_percent=0.9 # 劃分訓練集和驗證集的比例
train_percent=0.9 # trainval 中 訓練集所佔比例
total_xml = os.listdir(xmlfilepath)
num=len(total_xml)
list=range(num)
tv=int(num*trainval_percent)
tr=int(tv*train_percent)
trainval= random.sample(list,tv)
train=random.sample(trainval,tr)
print("train and val size",tv)
print("traub suze",tr)
ftrainval = open(os.path.join(saveBasePath,'VOC2007/ImageSets/Main/trainval.txt'), 'w')
ftest = open(os.path.join(saveBasePath,'VOC2007/ImageSets/Main/test.txt'), 'w')
ftrain = open(os.path.join(saveBasePath,'VOC2007/ImageSets/Main/train.txt'), 'w')
fval = open(os.path.join(saveBasePath,'VOC2007/ImageSets/Main/val.txt'), 'w')
for i in list:
name=total_xml[i][:-4]+'\n'
if i in trainval:
ftrainval.write(name)
if i in train:
ftrain.write(name)
else:
fval.write(name)
else:
ftest.write(name)
ftrainval.close()
ftrain.close()
fval.close()
ftest .close()
複製代碼
運行完畢後 VOCdevkit/VOC2007/ImageSets 下應該有4個 txt 文本。
首先在 Caffe 根目錄下的 data 目錄內建立一個名爲 VOC2007 的目錄,而後執行下列命令:
cd /opt/caffe/data
mkdir VOC2007
cp VOC0712/create_* VOC2007/
cp VOC0712/labelmap_voc.prototxt VOC2007/
cd VOC2007
# 修改 label map
vim labelmap_voc.prototxt
# 若是提示沒有vim,使用 sudo apt-get install vim 安裝一下
複製代碼
# labelmap_voc.prototxt 中內容修改成本身須要的內容
item {
name: "none_of_the_above"
label: 0
display_name: "background"
}
item {
name: "label1" # label 爲你本身數據集裏label的名稱,替換便可
label: 1
display_name: "label1"
}
item {
name: "label2"
label: 2
display_name: "label2"
}
item {
name: "label3"
label: 3
display_name: "label3"
}
item {
name: "label4"
label: 4
display_name: "label4"
}
...
# 修改好後 :wq 保存
複製代碼
# 修改 create_list.sh
vim create_list.sh
複製代碼
#!/bin/bash
# 若是你目錄嚴格按照我上面提供的命令建立的話,那麼下面 root_dir 等不用修改,直接用個人就行
# 若是你自定義了目錄名,須要根據本身的定義修改
root_dir=/opt/caffe/data/VOCdevkit
sub_dir=ImageSets/Main
bash_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
for dataset in trainval test
do
dst_file=$bash_dir/$dataset.txt
if [ -f $dst_file ]
then
rm -f $dst_file
fi
for name in VOC2007
do
# 注意下面這一段須要註釋掉
# if [[ $dataset == "test" && $name == "VOC2007" ]]
# then
# continue
# fi
echo "Create list for $name $dataset..."
dataset_file=$root_dir/$name/$sub_dir/$dataset.txt
img_file=$bash_dir/$dataset"_img.txt"
cp $dataset_file $img_file
sed -i "s/^/$name\/JPEGImages\//g" $img_file
sed -i "s/$/.jpg/g" $img_file
label_file=$bash_dir/$dataset"_label.txt"
cp $dataset_file $label_file
sed -i "s/^/$name\/Annotations\//g" $label_file
sed -i "s/$/.xml/g" $label_file
paste -d' ' $img_file $label_file >> $dst_file
rm -f $label_file
rm -f $img_file
done
# Generate image name and size infomation.
if [ $dataset == "test" ]
then
$bash_dir/../../build/tools/get_image_size $root_dir $dst_file $bash_dir/$dataset"_name_size.txt"
fi
# Shuffle trainval file.
if [ $dataset == "trainval" ]
then
rand_file=$dst_file.random
cat $dst_file | perl -MList::Util=shuffle -e 'print shuffle(<STDIN>);' > $rand_file
mv $rand_file $dst_file
fi
done
# 修改好 :wq 保存退出
複製代碼
# 修改 create_data.sh
vim create_data.sh
複製代碼
# 一樣,若是你嚴格按照個人命令定義了目錄名,就不須要修改
# 若是你修改了我上述命令中的目錄名,須要你改的地方有 root_dir, data_root_dir, dataset_name, mapfile
cur_dir=$(cd $( dirname ${BASH_SOURCE[0]} ) && pwd )
root_dir="/opt/caffe"
cd $root_dir
redo=1
data_root_dir="/opt/caffe/data/VOCdevkit"
dataset_name="VOC2007"
mapfile="/opt/caffe/data/$dataset_name/labelmap_voc.prototxt"
anno_type="detection"
db="lmdb"
min_dim=0
max_dim=0
width=0
height=0
extra_cmd="--encode-type=jpg --encoded"
if [ $redo ]
then
extra_cmd="$extra_cmd --redo"
fi
for subset in test trainval
do
python $root_dir/scripts/create_annoset.py --anno-type=$anno_type --label-map-file=$mapfile --min-dim=$min_dim --max-dim=$max_dim --resize-width=$width --resize-height=$height --check-label $extra_cmd $data_root_dir $root_dir/data/$dataset_name/$subset.txt $data_root_dir/$dataset_name/$db/$dataset_name"_"$subset"_"$db examples/$dataset_name
done
# 修改好後 :wq 保存退出
複製代碼
都修改好後執行腳本生成 LMDB
# 進入 Caffe 根目錄
cd /opt/caffe
./data/VOC2007/create_list.sh
./data/VOC2007/create_data.sh
複製代碼
終於進行到最後一步了,這一步相對來講很簡單。
# Caffe root dir
cd /opt/caffe
vim example/ssd/ssd_pascal.py
複製代碼
# 82 行修改 LMDB 文件位置信息
# 上一步執行 *.sh 文件時候輸出了 LMDB file 的存放位置
# The database file for training data. Created by data/VOC0712/create_data.sh
train_data = "/opt/caffe/data/VOCdevkit/VOC2007/lmdb/VOC2007_trainval_lmdb"
# The database file for testing data. Created by data/VOC0712/create_data.sh
test_data = "/opt/caffe/data/VOCdevkit/VOC2007/lmdb/VOC2007_test_lmdb"
# 258 行修改必要信息
# Stores the test image names and sizes. Created by data/VOC0712/create_list.sh
name_size_file = "data/VOC2007/test_name_size.txt"
# The pretrained model. We use the Fully convolutional reduced (atrous) VGGNet.
pretrain_model = "models/VGGNet/VGG_ILSVRC_16_layers_fc_reduced.caffemodel"
# Stores LabelMapItem.
label_map_file = "data/VOC2007/labelmap_voc.prototxt"
# MultiBoxLoss parameters.
num_classes = 5 # 修改成你要的分類數+1。例如我是4分類,我寫了 4+1=5
# 332 行修改 GPU 信息
gpus = "0,1" # 你要開啓幾個 GPU 加速就寫幾個,編號從0開始。我用兩張 1080TI 就寫了 0,1
# 337 修改 batch size 大小
batch_size = 32 # 這個數字大小看你顯存大小填寫, 容許範圍內越大越好
# 359 行修改測試集圖片數
num_test_image = 1000 # 根據你測試集圖片數實際填寫
# 圖片數爲 $caffe_root/data/VOCdevkit/VOC2007/ImageSets/Main/test.txt 的行數
# 修改好後 :wq 保存退出
複製代碼
下一步就是訓練,可是開始以前,先下載預訓練的 base model 放入對應位置,減小訓練時間並提升效率 爲了方便你們下載,我上傳到百度網盤了,密碼: ip6v。若是你想本身下載,去 github 下載,連接。
# 下載好的 caffe model 放在一開始指定的交換區
cd /opt/caffe/model
mkdir VGGNet
cp /home/swap/VGG_ILSVRC_16_layers_fc_reduced.caffemodel /ope/caffe/model/
複製代碼
cd /opt/caffe
python example/ssd/ssd_pascal.py
複製代碼
至此,恭喜你可使用 Caffe 框架訓練屬於本身的 SSD 模型了, SSD 的官方實現的 github 地址爲 連接。若是你有任何問題,能夠求助於 github 討論區或者留言問詢。
另外,歡迎關注我。最近會寫不少關於目標檢測方面的文章,包括一些論文翻譯以及論文解讀。還可能寫一點算法實現的內容。
謝謝閱讀。