Caffe SSD Ubuntu16.04 訓練自己的數據集

總的來說蒿叠,Caffe 是一個比較難上手的框架。這次嘗試訓練 Caffe 框架下 SSD 模型的訓練是我第一次使用 Caffe 框架蚣常。下面就說一說我踩過的幾個坑市咽,希望能夠幫助到大家。

1 編譯 Caffe 框架

這一步是我認為使用 Caffe 框架的最大障礙抵蚊,編譯不停出錯施绎。最后我不得不放棄轉而使用 Docker 解決 Caffe 的編譯安裝問題。下面寫出 Docker 的安裝以及拉取所需鏡像的方法贞绳。

Docker 安裝

安裝過程我是參照的 Docker 官方的安裝指引谷醉,傳送門在這里。
我使用的安裝命令為(Ubuntu 16.04 LTS):

# 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個小時,下載在這里躺酒。

尋找并拉取合適的鏡像

我是在 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

按照順序依次執(zhí)行就可以了裸违,如果你遇到問題掖桦,請參照 nvidia-docker 安裝官方指引。

拉取鏡像:

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 下存放的文件了

2 制作自己的數據集

這一步才是重點越庇,下面我們將處理自己的使用的數據集罩锐。

標注數據集

因為我們將要把數據集制作為 pascal voc 格式的數據集,因此我們需要將標注信息存放在 xml 文件中卤唉。推薦使用 LabelImg 這個開源軟件標注涩惑。其優(yōu)點是圖形化界面,并且自動生成 xml 文件搬味,省去了很多轉化的步驟境氢,傳送門在這里。
怎樣使用其實非常簡單碰纬,在這里略過不講萍聊。如果你不會使用,請善用搜索悦析。無法解決的話可以留言詢問寿桨。

制作 Pascal Voc 格式數據集

在主機的交換目錄下創(chuàng)建名為 VOCdevkit 的文件夾用于存放圖片等內容。

交換目錄就是剛剛創(chuàng)建容器時候指定的主機目錄强戴,例如我的交換目錄為 /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

創(chuàng)建完成后將 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 文本骑歹。

修改 create_list.sh 與 create_data.sh 并生成 LMDB files

首先在 Caffe 根目錄下的 data 目錄內創(chuàng)建一個名為 VOC2007 的目錄预烙,然后執(zhí)行下列命令:

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
# 如果你目錄嚴格按照我上面提供的命令創(chuàng)建的話扁掸,那么下面 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 保存退出

都修改好后執(zhí)行腳本生成 LMDB

# 進入 Caffe 根目錄
cd /opt/caffe
./data/VOC2007/create_list.sh
./data/VOC2007/create_data.sh

3 修改 ssd_pascal.py 并開始訓練

終于進行到最后一步了牺蹄,這一步相對來說很簡單。

# Caffe root dir
cd /opt/caffe
vim example/ssd/ssd_pascal.py
# 82 行修改 LMDB 文件位置信息
# 上一步執(zhí)行 *.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 放在一開始指定的交換區(qū)
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
Train Log

結語

至此,恭喜你可以使用 Caffe 框架訓練屬于自己的 SSD 模型了潜慎, SSD 的官方實現(xiàn)的 github 地址為 鏈接捡多。如果你有任何問題,可以求助于 github 討論區(qū)或者留言問詢铐炫。

另外垒手,歡迎關注我。最近會寫很多關于目標檢測方面的文章倒信,包括一些論文翻譯以及論文解讀科贬。還可能寫一點算法實現(xiàn)的內容。

謝謝閱讀鳖悠。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末榜掌,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子乘综,更是在濱河造成了極大的恐慌憎账,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卡辰,死亡現(xiàn)場離奇詭異胞皱,居然都是意外死亡,警方通過查閱死者的電腦和手機看政,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門朴恳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人允蚣,你說我怎么就攤上這事于颖。” “怎么了嚷兔?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵森渐,是天一觀的道長。 經常有香客問我冒晰,道長同衣,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任壶运,我火速辦了婚禮耐齐,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己埠况,他們只是感情好耸携,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著辕翰,像睡著了一般夺衍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喜命,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天沟沙,我揣著相機與錄音,去河邊找鬼壁榕。 笑死矛紫,一個胖子當著我的面吹牛,可吹牛的內容都是我干的牌里。 我是一名探鬼主播含衔,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼二庵!你這毒婦竟也來了贪染?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤催享,失蹤者是張志新(化名)和其女友劉穎杭隙,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體因妙,經...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡痰憎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了攀涵。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铣耘。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖以故,靈堂內的尸體忽然破棺而出蜗细,到底是詐尸還是另有隱情,我是刑警寧澤怒详,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布炉媒,位于F島的核電站,受9級特大地震影響昆烁,放射性物質發(fā)生泄漏吊骤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一静尼、第九天 我趴在偏房一處隱蔽的房頂上張望白粉。 院中可真熱鬧传泊,春花似錦、人聲如沸鸭巴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奕扣。三九已至,卻和暖如春掌敬,著一層夾襖步出監(jiān)牢的瞬間惯豆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工奔害, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留楷兽,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓华临,卻偏偏與公主長得像芯杀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子雅潭,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內容