使用深度學習網路SSD識別自己定義的物體
最近在做一些使用深度學習網路SSD(single shot multibox detector)的Caffe版本進行物體識別的工作。具體的實現效果如下圖所示:
解釋一下其中的代號:
(1)poc(pocky,百奇餅乾)
(2)sti(stick,棉籤)
(3)pie(派)
(4)cla(clamp,塑料夾子)
(5)bit(biscuit,餅乾)
(6)cup(水杯)
(7)lit(light,手電筒)
(8)hap(happy,開心巴旦木。。。)
(9)in(instantnoodles,方便麵)
一共是9種物體,圖片中名字後面的數字是識別的置信度。可以看出識別的準確度是很高的,識別的速度達到了FPS58.2,已經滿足了實時性的要求。下面就來說一下如何實現吧。
主要內容:
-建立資料集
-配置Caffe
-訓練並檢驗SSD網路
-使用攝像頭識別物體
1. 建立資料集
如果要網路識別自己定義的物體,首先要建立自己的物體資料集。建立資料集時要注意資料的多樣性,如果要網路有很好的泛化效能,資料就要儘量包括所遇到的情況。比如說我們的資料集如下圖所示:
我將物體放於一個盒子中,並將其作為訓練集背景。這和我應用的場景有關,因為我測試的情況就是識別盒子中的物體,所以我將其背景固定下來。另外要注意資料的多樣性,我設定的變數有盒子的位置和角度變化,盒子內物體的位置和姿態的變化,最後還有不同方向的光照。一共記錄了300張圖片。
下一步就是要標註圖片了,這一步中要注意標註的質量和生成的標註檔案格式。我使用的標註軟體是labelme,但這個軟體生成的標註檔案XML和SSD使用的常用資料集VOC2007的標註檔案XML有著格式上的不同。所以,我後來在github上找到了一個可以將labelme標註檔案轉換為VOC標註檔案的專案labelme2VOC2007mat。git地址:
但是我發現還有其他的一些標註軟體可以直接生成VOC格式的標註檔案,比如:Label-Annotation-VOC-Pascal。git地址:https://github.com/manhcuogntin4/Label-Annotation-VOC-Pascal
標註是一個勞動密集型工作,需要一段時間的艱苦勞動。所以我和幾個實驗室的小夥伴一起花了一個小時完成了300張圖片的標註。標註的時候要注意每個物體的名稱和id號。
當我們獲得了圖片和對應的標註檔案後,我們就要開始整理資料了。按照VOC2007的格式來整理最好,因為SSD原始碼中的示例程式用的就是VOC2007的資料庫。所以,資料的格式是這樣的:
先在根目錄下建立自己的資料集資料夾:
cd
mkdir data
cd data
mkdir VOCdevkit
cd VOCdevkit
mkdir mydataset
然後在mydataset下面建立三個子資料夾:Annotations,imageSets和JPEGImages。第一個是標註資料夾,其中放置標註檔案XML。第二個是分類資料夾,其中還有一個資料夾Main,這個資料夾下放置訓練集和測試集的檔名文件,如test.txt和trainval.txt。第三個資料夾是圖片資料夾,放置訓練集圖片。如果使用我之前說的labelme2VOC2007mat,它裡面包含了自動生成這些檔案,更加方便。
2.配置Caffe
這部分開始就和深度學習框架Caffe有關係了。Caffe的輸入資料必須為LMDB格式,一方面可以將各種資料轉換為統一格式,另一方面還可以提高磁碟的IO利用率。我們上一步所做的資料集還要轉換格式。所以,我們先來看一下SSD在Caffe構架下的配置過程。
安裝Caffe依賴項:
sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install --no-install-recommends libboost-all-dev
sudo apt-get install libopenblas-dev liblapack-dev libatlas-base-dev
sudo apt-get install libgflags-dev libgoogle-glog-dev liblmdb-dev
sudo apt-get install git cmake build-essential
先從github上下載ssd原始碼
git clone https://github.com/weiliu89/caffe.git
cd caffe
git checkout ssd
後面要開始編譯原始碼。這裡可能會報一些細節的錯誤,以後再補充。
先拷貝Makefile.config
cp Makefile.config.example Makefile.config
再開啟Makefile.config,需要根據自己的需要調整一些使用Caffe時的選項。比如我選了:
USE_CUDNN := 1
將 INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include 改為
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include /usr/lib/x86_64-linux-gnu/hdf5/serial/include
將 LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib 改為
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu/hdf5/serial
完成這些後,我使用cmake進行編譯。在caffe根目錄下建立build,然後編譯。
mkdir build
cd build
cmake ..
make all -j8
make install
# (Optional)
make runtest -j8
通過後就可以進行下一步了。如果報錯,可能需要根據錯誤資訊安裝依賴,或者調整一些細節。
下面就要將我們做的資料集轉換為LMDB格式了,這裡我們使用了SSD一些自帶的程式功能。首先,在caffe目錄下的data資料夾中建立自己的資料資料夾/mydataset。這就是我們自己的資料分組檔案和LMDB生成檔案的所在地。然後將同樣是資料資料夾VOC0712中的create_list.sh,create_data.sh和labelmap_voc.prototxt拷貝過來。並修改其中的一些地方:
create_list.sh中,改for name in 後面的為mydataset
create_data.sh中,改為:
dataset_name="mydataset"
mapfile="$root_dir/data/$dataset_name/labelmap_voc.prototxt"
注意labelmap_voc.prototxt為標註對映檔案,我們要把裡面的內容改為自己設定的分類名稱和id號。這裡的id號要和標註時設定的物體id號相同。標註對映檔案如下圖所示:
現在可以使用
./data/mydataset/create_list.sh
對我們製作的資料集進行分組。分組完畢後,就可以開始轉換資料為LMDB格式了。
./data/mydataset/create_data.sh
如果成功生成LMDB就可以開始訓練了。
3.訓練並檢驗SSD網路
到這一步就比較簡單了,先改一下examples/ssd/ssd_pascal.py中的一些配置和地址。
1.改訓練集和測試集的地址:
# The database file for training data. Created by data/VOC0712/create_data.sh
train_data = "examples/mydataset/mydataset_trainval_lmdb" #mzm
# The database file for testing data. Created by data/VOC0712/create_data.sh
test_data = "examples/mydataset/mydataset_test_lmdb" #mzm
2.改一些儲存資料檔案的地址
# Directory which stores the model .prototxt file.
save_dir = "models/VGGNet/mydataset/{}".format(job_name) #mzm
# Directory which stores the snapshot of models.
snapshot_dir = "models/VGGNet/mydataset/{}".format(job_name) #mzm
# Directory which stores the job script and log file.
job_dir = "jobs/VGGNet/mydataset/{}".format(job_name) #mzm
# Directory which stores the detection results.
output_result_dir = "{}/data/VOCdevkit/results/mydataset/{}/Main".format(os.environ['HOME'], job_name) #mzm
3.改一些測試集大小檔案和標註對映檔案地址
# Stores the test image names and sizes. Created by data/VOC0712/create_list.sh
name_size_file = "data/mydataset/test_name_size.txt" #mzm
# 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/mydataset/labelmap_mydataset.prototxt" #mzm
4.改寫網路分類總數。這裡注意我在標註時把背景的id設為了9,所以在這裡把background_label_id也設定為9。
# MultiBoxLoss parameters.
num_classes = 10 #mzm
share_location = True
background_label_id=9 #mzm
5.根據自己的裝置情況,改gpu的使用數量。我只有一塊gpu,所以改為:
# Defining which GPUs to use.
gpus = "0"
gpulist = gpus.split(",")
num_gpus = len(gpulist)
完成以上改動後,就可以按照github上面的指導,使用
python examples/ssd/ssd_pascal.py
來訓練ssd網路了,訓練完畢後就是這樣。
4.使用攝像頭識別物體
這一步和上一步很相似,只要改動一下examples/ssd/ssd_pascal_webcam.py裡面的類別,對映和儲存地址就可以了。
num_classes = 10 #mzm
share_location = True
background_label_id=9 #mxm
# Stores LabelMapItem.
label_map_file = "data/mydataset/labelmap_mydataset.prototxt" #mzm
# Directory which stores the model .prototxt file.
save_dir = "models/VGGNet/mydataset1/{}_webcam".format(job_name) #mzm
# Directory which stores the snapshot of trained models.
snapshot_dir = "models/VGGNet/mydataset1/{}".format(job_name) #mzm
# Directory which stores the job script and log file.
job_dir = "jobs/VGGNet/mydataset1/{}_webcam".format(job_name) #mzm
然後執行
python examples/ssd/ssd_pascal_webcam.py
就可以看到攝像頭拍攝的識別結果了,如下圖所示:
大功告成!
當然,這只是實現了別人的演算法,關於SSD的原理結構以後再說吧。如果有什麼錯誤,還請大家指出。