1. 程式人生 > >使用SSD模型檢測教學場景下的“舉手”目標

使用SSD模型檢測教學場景下的“舉手”目標

由於專案需求,最近花了約三週的時間,嘗試在我們自己的教學場景資料集上,完成SSD目標檢測模型的測試,檢測目標只有一個類別:舉手(Handraising)。實際上,專案中已經存在可以完成舉手目標檢測的方案R-FCN,所以目的是為了驗證SSD是否會有檢測效果和檢測速度的提升,這裡簡要記錄一下整個流程,儘管之後在測試資料集上,SSD的檢全率和準確率並不比R-FCN更好。

一、背景介紹:

SSDSingle Shot MultiBox Detector)是2016出來的一篇目標檢測的文章,實際比R-FCN稍早一些。在該文中,作者指出他們提出的新方法比之前的目標檢測方法都要好(準確率、檢測速度兩個方面都有優勢
),結果統計如下圖

截止到SSD釋出,它應該是最優的目標檢測演算法,之後同年公佈的R-FCN也表現出幾乎相同的檢測準確率,但並未與SSD作比較。到了現在,物體檢測方面最優的方法應該要算YOLO v2,下面是近些年出現的一些物體檢測演算法列表:

PS:總結主要來自部落格

DPM(時間 2008

Adiscriminatively trained, multiscale, deformable part model

OverFeat(時間 2013

OverFeat:Integrated Recognition, Localization and Detection using Convolutional Networks

SPP-Net(時間 2015

Spatial PyramidPooling in Deep Convolutional Networks for Visual Recognition

DeepID-Net(時間 2014

DeepID-Net:Deformable Deep Convolutional Neural Networks for Object Detection

RCNN(時間 2014

Rich featurehierarchies for accurate object detection and semantic segmentation

Fast RCNN(時間 2015

Fast R-CNN

Faster RCNN(時間 2015

Faster R-CNN towards real-time object detection with region proposalnetworks

R-FCN(時間 2016

R-FCN Object Detection via Region-based Fully Convolutional Networks

Yolo(時間 2016

You Only Look Once - Unified, Real-Time Object Detection

SSD(時間 2016

SSD Single Shot MultiBox Detector

Yolo v2(時間 2016

YOLO9000 - Better, Faster, Stronger

Mask R-CNN(時間 2017

Mask R-CNN

……

可以看到,物體檢測演算法層出不窮,讓人應接不暇,這裡只選取SSD物體檢測演算法來進行總結,理論分析部分不再討論,詳細記述模型使用過程。

二、SSD配置及除錯步驟:

原始碼網址:https://github.com/weiliu89/caffe/tree/ssd

其實github上已經給出了詳細使用步驟,這裡再重複一遍,同時就自己遇到的一些問題給出解決辦法。

這裡按照該網址提供的步驟來記錄

1Installation

2Preparation

3Train/Eval

4Models

1Installation

1)首先是下載原始碼並安裝,選擇將其放在自己的某個資料夾下

git clone https://github.com/weiliu89/caffe.git

cd caffe

git checkout ssd

(出現分支則說明copy-check成功...作者caffe目錄下有三個分支fcn/master/ssd, 利用git checkout來切換分支,否則只有master目錄下的檔案)

2)之後需要編譯原始碼

# Modify Makefile.config according to your Caffe installation.

cp Makefile.config.example Makefile.config 

#這裡需要根據電腦具體的配置修改Makefile.config

make -j8

# Make sure to include $CAFFE_ROOT/python to your PYTHONPATH.

make py

make test -j8

# (Optional)

make runtest -j8

編譯過程中,只要配置好了caffe所需要的檔案,一般不會出現什麼問題

2Preparation

1)下載已經訓練好的VGGNet16模型,fullyconvolutional reduced (atrous) VGGNet,確認將其放在$CAFFE_ROOT/models/VGGNet/ 目錄下

網址中提供的連結不可用,這裡使用網上搜到的預訓練模型

連結:http://pan.baidu.com/s/1miDE9h2

密碼:0hf2

可見SSD是基於VGGNet16的物體檢測演算法,如果該model已經掛了,就自己搜尋吧~

2)下載VOC2007VOC2012資料集,並將其放在 $HOME/data/ 目錄下,也可以修改之,但同時記得修改之後的指令碼中出現的該目錄的引用

# Download the data.

cd $HOME/data

wget http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar

wgethttp://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar

wget http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar

# 下載完畢

# Extract the data.

tar -xvf VOCtrainval_11-May-2012.tar

tar -xvf VOCtrainval_06-Nov-2007.tar

tar -xvf VOCtest_06-Nov-2007.tar

# 解壓完畢

3)使用資料集,建立LMDB(Lightning Memory-Mapped Database)檔案

cd $CAFFE_ROOT

# Create the trainval.txt, test.txt, and test_name_size.txt indata/VOC0712/

./data/VOC0712/create_list.sh

# 如果修改了資料集的位置,需要修改 create_list.sh 中相應的地址

執行結果:

/home/zhouhuayi/SSD/caffe$ ./data/VOC0712/create_list.sh

Create list for VOC2007 trainval...

Create list for VOC2012 trainval...

Create list for VOC2007 test...

I0122 14:30:12.952246 25110 get_image_size.cpp:61] A total of 4952 images.

I0122 14:30:14.953508 25110 get_image_size.cpp:100] Processed 1000 files.

I0122 14:30:16.875109 25110 get_image_size.cpp:100] Processed 2000 files.

I0122 14:30:18.889964 25110 get_image_size.cpp:100] Processed 3000 files.

I0122 14:30:20.848163 25110 get_image_size.cpp:100] Processed 4000 files.

I0122 14:30:22.774821 25110 get_image_size.cpp:105] Processed 4952 files.

create_list.sh指令碼將VOC2007VOC2012中的圖片混合到了一起,得到的trainval.txt,test.txt, test_name_size.txt三個txt文字中,前兩個分別是訓練集、測試集中圖片的名稱,test_name_size.txt則是測試集圖片名稱和對應的長寬

# You can modify the parameters in create_data.sh if needed.

# It will create lmdb files for trainval and test with encoded originalimage:

#   -$HOME/data/VOCdevkit/VOC0712/lmdb/VOC0712_trainval_lmdb

#   -$HOME/data/VOCdevkit/VOC0712/lmdb/VOC0712_test_lmdb

# and make soft links at examples/VOC0712/

./data/VOC0712/create_data.sh

create_data.sh才正式生成lmdb檔案

這一步總是出現問題,報錯如下:

Traceback (most recent call last):

File"/home/zhouhuayi/caffe/scripts/create_annoset.py", line 7, in<module>

from caffe.proto importcaffe_pb2

ImportError: No module named caffe.proto

Traceback (most recent call last):

File"/home/zhouhuayi/caffe/scripts/create_annoset.py", line 7, in<module>

from caffe.proto importcaffe_pb2

ImportError: No module named caffe.proto

上網查詢解決方法,發現問題是沒有把caffe中的和python相關的內容的路徑新增到python的編譯路徑中。

第一種解決方案是修改 .bashrc 檔案,但在我這邊實際沒有用,出現類似問題的不妨嘗試一下

$ cat .bashrc

$ echo "export PYTHONPATH=/home/zhouhuayi/caffe/python">>.bashrc

# .bashrc 檔案中追加python相關路徑

$ source ~/.bashrc

#儲存上述修改

之後執行仍舊出現上述bug,該方案不能解決問題

第二種解決方案是,修改出現問題的 .py檔案,每一次都明確指定python的相關路徑,使用python呼叫caffe時,在相應的.py檔案的最前面加入以下四句:

caffe_root = '/home/zhouhuayi/SSD/caffe/'

import sys

sys.path.insert(0, caffe_root + 'python')

import caffe

問題解決,不過使用該方法,所有呼叫caffe框架的.py檔案中都要包含這四行,略顯麻煩。

這裡修改的是 /caffe/scripts/create_annoset.py 檔案

構建LMDB資料成功後,介面輸出的結果:

/home/zhouhuayi/caffe$ ./data/VOC0712/create_data.sh

I0118 13:58:33.651304 18763 convert_annoset.cpp:122] A total of 4952images.

I0118 13:58:33.670905 18763 db_lmdb.cpp:35] Opened lmdb/data/zhouhuayi/VOC/VOCdevkit/VOC0712/lmdb/VOC0712_test_lmdb

I0118 13:58:40.737097 18763 convert_annoset.cpp:195] Processed 1000 files.

I0118 13:58:47.773864 18763 convert_annoset.cpp:195] Processed 2000 files.

I0118 13:58:54.553117 18763 convert_annoset.cpp:195] Processed 3000 files.

I0118 13:59:02.140028 18763 convert_annoset.cpp:195] Processed 4000 files.

I0118 13:59:08.677724 18763 convert_annoset.cpp:201] Processed 4952 files.

/home/zhouhuayi/caffe/build/tools/convert_annoset --anno_type=detection--label_type=xml--label_map_file=/home/zhouhuayi/caffe/data/VOC0712/../../data/VOC0712/labelmap_voc.prototxt--check_label=True --min_dim=0 --max_dim=0 --resize_height=0 --resize_width=0--backend=lmdb --shuffle=False --check_size=False --encode_type=jpg--encoded=True --gray=False /data/zhouhuayi/VOC/VOCdevkit//home/zhouhuayi/caffe/data/VOC0712/../../data/VOC0712/test.txt/data/zhouhuayi/VOC/VOCdevkit/VOC0712/lmdb/VOC0712_test_lmdb

I0118 13:59:09.883656 18802 convert_annoset.cpp:122] A total of 16551images.

I0118 13:59:09.884400 18802 db_lmdb.cpp:35] Opened lmdb/data/zhouhuayi/VOC/VOCdevkit/VOC0712/lmdb/VOC0712_trainval_lmdb

I0118 13:59:31.119462 18802 convert_annoset.cpp:195] Processed 1000 files.

I0118 13:59:52.780809 18802 convert_annoset.cpp:195] Processed 2000 files.

I0118 14:00:13.609648 18802 convert_annoset.cpp:195] Processed 3000 files.

I0118 14:00:34.121955 18802 convert_annoset.cpp:195] Processed 4000 files.

I0118 14:00:54.767715 18802 convert_annoset.cpp:195] Processed 5000 files.

I0118 14:01:15.330397 18802 convert_annoset.cpp:195] Processed 6000 files.

I0118 14:01:36.558912 18802 convert_annoset.cpp:195] Processed 7000 files.

I0118 14:01:57.138043 18802 convert_annoset.cpp:195] Processed 8000 files.

I0118 14:02:18.016297 18802 convert_annoset.cpp:195] Processed 9000 files.

I0118 14:02:39.436408 18802 convert_annoset.cpp:195] Processed 10000files.

I0118 14:03:00.065383 18802 convert_annoset.cpp:195] Processed 11000files.

I0118 14:03:21.318958 18802 convert_annoset.cpp:195] Processed 12000files.

I0118 14:03:41.989552 18802 convert_annoset.cpp:195] Processed 13000files.

I0118 14:04:03.185189 18802 convert_annoset.cpp:195] Processed 14000files.

I0118 14:04:24.233533 18802 convert_annoset.cpp:195] Processed 15000files.

I0118 14:04:45.259112 18802 convert_annoset.cpp:195] Processed 16000files.

I0118 14:04:57.101454 18802 convert_annoset.cpp:201] Processed 16551files.

/home/zhouhuayi/caffe/build/tools/convert_annoset --anno_type=detection--label_type=xml--label_map_file=/home/zhouhuayi/caffe/data/VOC0712/../../data/VOC0712/labelmap_voc.prototxt--check_label=True --min_dim=0 --max_dim=0 --resize_height=0 --resize_width=0--backend=lmdb --shuffle=False --check_size=False --encode_type=jpg--encoded=True --gray=False /data/zhouhuayi/VOC/VOCdevkit//home/zhouhuayi/caffe/data/VOC0712/../../data/VOC0712/trainval.txt/data/zhouhuayi/VOC/VOCdevkit/VOC0712/lmdb/VOC0712_trainval_lmdb

3Train/Eval

1)開始訓練

# It will create model definition files and save snapshot models in:

#   -$CAFFE_ROOT/models/VGGNet/VOC0712/SSD_300x300/

# and job file, log file, and the python script in:

#   -$CAFFE_ROOT/jobs/VGGNet/VOC0712/SSD_300x300/

# and save temporary evaluation results in:

#   -$HOME/data/VOCdevkit/results/VOC2007/SSD_300x300/

# It should reach 77.* mAP at 120k iterations.

python examples/ssd/ssd_pascal_300x300.py

經過120000次的訓練之後,我們可以得到所需要的帶有超引數的訓練model

執行時出現了下面的錯誤:

Check failed: error == cudaSuccess (10 vs. 0)  invalid device ordinal

這是由於GPU數量不匹配造成的,如果訓練自己的資料,那麼我們只需要將solver.prototxt檔案中的device_id項改為自己的GPU塊數,一塊就是0,兩塊就是1,以此類推。SSD配置時的例子是將訓練語句整合成一個python檔案ssd_pascal.py,所以需要改此程式碼。

解決方法:

ssd_pascal.py檔案中第332gpus = "0123"GPU選擇改為gpus = "0",後面的123都刪掉,再次訓練即可。由於我訓練時的119伺服器上有兩塊GPU,所以將gpus = "0123"修改為gpus ="1"gpus = "0",問題解決。

這個訓練過程十分漫長,我的GPUK40c,相較作者文中使用的TitanX十分老舊,於是迭代120k次花費了近4~5天的時間,後來驗證得到的model確實達到了文中所說的精度。之後才覺得完全沒必要這樣做,費時又無用,還不如直接準備自己的資料集,上手訓練測試,方便快捷。

2)模型測試

# If you would like to test a model you trained, you can do:

python examples/ssd/score_ssd_pascal_300x300.py

這裡就是單純測試model在測試集上的mAPmean Average Precision),如果訓練階段已經加入了test網路,這一步驟實際已經完成了。

也可以檢視單張圖片的檢測效果,需要修改引數,甚至可以用來測試批量圖片

python examples/ssd/ssd_detect.py

4Models

作者訓練好的3個數據集上的模型

PASCAL VOC models

COCO models

ILSVRC models

到這裡,完成了SSD的配置和除錯工作,在此基礎上,可以開始訓練自己的資料集。

三、使用SSD訓練自己的舉手資料集:

遵循上面的步驟,這裡可以直接開始準備自己的資料集。

假設已經在某個資料夾下按照VOC資料集的格式放置了自己舉手資料集


JPEGImages資料夾下是原始圖片幀,這裡都是從視訊中擷取的圖片,尺寸均為1080x1920

Annotations-only-handraising資料夾下是xml標籤資料,是人工提前將舉手物件標記出來的矩形框,也就是GroundTruth,格式模仿VOC資料集

ImageSets中是各類的txt文字,主要包括檢測物件的訓練集(trainval.txt)、測試集(test.txt),剩下的train,txtval.txt好像沒有單獨使用,二者之和就是trainval.txt。由於我只是用SSD檢測舉手,也就是隻有一個類,所以只有這四個txt文字

lmdb資料夾下是之後生成的lmdb檔案


準備好資料,開始修改編輯create_list.sh


以上畫線部分是需要修改的地方。前兩個和資料集的位置有關,不再解釋,後兩個是我有遇到問題的地方。

由於trainval.txttest.txt文字中,每一行結尾都是回車符’\r’,以致生成的圖片名稱、標籤名稱一一對應資料格式出問題,從字尾名開始總是換行,所以需要加上 ’\r’將其替換掉

原語句始:sed -i "s/$/.jpg/g" $img_file

現修改為:sed -i "s/\r$/.jpg/g"$img_file

再順利執行下列命令

./data/HR119/create_list.sh

接著是生成對應的lmdb資料

依舊是隻需要修改資料集的位置。

值得一提的是,這裡還要修改labelmap_voc.prototxt檔案,VOC資料集中共有20個類,加上backgroundnum_classes一共是21,而我只有舉手一個檢測物件,num_classes2labelmap_voc.prototxt修改成下列格式


再順利執行下列命令,就可以完成lmdb資料的製作

./data/HR119/create_data.sh

接著,進入訓練階段,需要修改的是examples/ssd/ssd_pascal.py檔案。

主要注意以下幾個部分:

1、各種訓練集、測試集的位置。VOC0712都要換成自己的資料集名稱,記得在對應位置提前新建好資料夾,我的都修改成HR119

2、 resize的尺寸。文中提供的有300x300和512x512兩種


如果是512,還要新增額外新增一層卷積層,如下就是需要多新增的卷積層conv6_1


如果不確定新增的是否正確,作者提供的訓練好的SSD512模型中也有修改好的py指令碼,可自行下載

3、檢測物件類別數。這裡的數量需要+1,因為有個類別是background


4、 根據GPU的塊數和是否空閒,修改引數gpus


5、batchsizebase_lr根據實際調整,一般GPU爆存要調小塊大小,loss崩掉(增大為nan)要適當調小學習率


其中,如果訓練階段GPU爆存,可以將測試階段去掉

6、 修改設定solver.prototxt。這裡是在指令碼中調整

以上,主要就是調整迭代次數’max_iter’、暫時儲存節點’snapshot’(可以在意外停止訓練時,從儲存的最新狀態繼續恢復訓練),其他引數基本不需要修改

終於可以開始訓練自己的資料集了,執行以下命令,之後就是耐心的等待了~

python examples/ssd/ssd_HR119_512x512.py


四、問題總結和思考:

一開始,我使用的是SSD300,但是訓練之後的效果非常差,舉手物件的錯檢、漏檢非常多。

比如下面這張圖片391_0041_Student.jpg,人工數的方法GroundTruth21,使用迭代40000次的SSD300模型檢測,並將DetectionBoxingThreshold設定為0.25,結果正檢數量只有5,漏檢數達到16

有嘗試過將Threshold調低一些,但是相應的錯檢率就會上升,說到底還是SSD300模型不能有效區分舉手和非舉手。

仍舊是圖片391_0041_Student.jpg,使用迭代40000次的SSD512模型檢測,並將DetectionBoxingThreshold設定為0.25,結果正檢數量是20,漏檢數為1,錯檢數為3

其中,錯檢中有一個是老師舉手,該干擾不可避免,另外兩個誤檢和一個漏檢就是模型自身的缺陷。之後在測試資料集上,發現SSD512的漏檢率勉強能接受,但錯檢率太高,這是致命缺陷,將Threshold設定為0.3,在犧牲檢全率的情況下,正檢率提升仍舊不明顯。

圖中,正檢數量是17,漏檢數為4,錯檢數為2

下圖是R-FCN的檢測結果:

雖然單張的檢測結果優勢不明顯,但全方位統計之後,在我們檢測舉手的場景下,R-FCNSSD有很大優勢。

總結起來就是,在我們的應用場景下,檢測效果SSD300 < SSD512< R-FCN

再將各階段的嘗試總結如下:

第一階段

沒有改變resize的大小,保持預設的300x300,設定迭代次數為40000次,lr經過調整,比原文稍小loss才會收斂,結果訓練得到的模型效果十分差勁,這樣也就再次浪費了23天的時間。經過分析,VOC資料集中的圖片,尺寸大都是300x500左右,且清晰度較高,要檢測的物體相對整個圖片所佔比例也較大(大物體檢測),而我們自己的資料集圖片,是從視訊檔案中擷取的幀,大小均為1080x1920,清晰度較低,舉手作為檢測物件,符合小物體檢測特徵,所以猜測是以上的資料集缺陷,導致檢測效果不理想。於是修改網路結構,在原始的網路結構上,新增新的卷積層,訓練階段使用較大的resize值。

第二階段

resize的調大為512x512,依舊設定迭代次數為40000次,同樣lr經過調整比300x300的還要小一些才能正常訓練。其中還出現了GPU視訊記憶體不足的問題,自然是因為resize增大,輸入的圖片更大,所以需要將batchsize相應調小些,必要時,可以設定成1。期間,在test階段又出現了GPU爆存的情況,所以test_batch_size也應調小一些,必要時可以設定訓練階段不進行test。這樣,又經過23天的時間的訓練,得到的新模型效果大大改善,和RFCN的檢測效果相對比,表現甚至基本接近,但後來統計舉手檢全率、準確率,效果還是有差距的。

SSD512情況,依舊存在一些問題:

1test的準確率(mAP)非常低。可能是因為每個DetectionBounding的置信度偏低,導致計算出的AveragePrecision偏低

2、模型的Threshold需要設定的非常低才能使用。由於每個DetectionBounding的置信度偏低,也可以說舉手沒有被有效區分出來,所以實際設定中Threshold調整為0.25左右才能有較好效果,這與RFCN0.8的閾值相去甚遠

3、對於拍手、打手勢等一些易與舉手混淆的物件的剔除不太理想。這個問題也出現在RFCN的模型中,實際上這個問題也是影響檢測準確率的關鍵因素

後來,通過增加迭代次數至80000,檢測效果依舊沒有得到提升。

第三階段

繼續探索將resize的調大為1024x1024,訓練效果是否會進一步提升。由於圖片實在過大,此次batchsize必須設定為1,且不能在訓練階段進行測試,為了儘可能準確,迭代次數增大為80000次,lr同樣變得更小,訓練完成耗費了34天的時間。在訓練過程中生成的中間結果(40000迭代),可以暫時用來檢測效果是否有提升。結果顯示,相比512x512的三個問題,有以下改變:

1test的準確率(mAP)無法測試。儘管將test_batch_size的大小設定成1GPU卻還總是爆存,原因尚不明確

2、模型的Threshold顯著提升。這裡檢測到的降序排名靠前的DetectionBounding的置信度很高,甚至將threshold設定為0.8,仍舊能區分得到大部分舉手物件,但結果不比512x512的模型更好,可能是訓練次數還不夠的原因

3、對於拍手、打手勢等一些易與舉手混淆的物件,這裡表現的也更差勁了,不確定是否也是因為訓練次數不夠

可惜的是,之後80000次迭代完成後,模型反而更差勁了,可能是發生了過擬合,至此才放棄了SSD替代R-FCN來檢測舉手的想法。

以上,儘管嘗試SSD沒有得到更好的結果,但整個過程還是學到了很多,也為之後的實驗積累了經驗,遂記錄之。

2018291803

PS:不要是用Microsoft Edge瀏覽器線上編輯部落格,儘量使用Google Chorme!!!