使用YOLOv3 訓練行人檢測模型

使用YOLO進行目標檢測在保證速度的前提下能有較好的準確性,這篇文章是在yolov3的代碼基礎上進行修改爲行人檢測,由原來的20個分類改爲識別person,使用tiny-yolo的進行訓練和檢測,最後能達到還不錯的效果。

1.數據集製作

收集圖片並標註,這是一件工作量較大的事情,所以我們直接使用了PASCAL VOC2007的數據集,抽取其中帶有行人的圖片。

具體過程如下:

1.1 創建相應的文件夾

  • 在yolo代碼裏的scripts/目錄下創建VOC_person文件夾,在其文件夾下創建Annotations、ImageSets、JPEGImages文件夾,在ImageSets文件夾下建立Main文件夾。這裏順便說下這三個文件夾的作用:

Annotations -- Annotations文件夾中存放的是xml格式的標籤文件,每一個xml文件都對應於JPEGImages文件夾中的一張圖片;

ImageSets -- ImageSets存放的是每一種類型的challenge對應的圖像數據。在ImageSets下有Main文件夾,存放的是圖像物體識別的數據,總共分爲20類;

JPEGImages -- JPEGImages文件夾中包含了PASCAL VOC所提供的所有的圖片信息,包括了訓練圖片和測試圖片。

1.2 拷貝相應的文件

  • 提取VOC2007/ImageSets/Main/文件夾裏的person_test.txt,person_trainval.txt,person_train.txt  person_val.txt 這四個文件到VOC_person/ImageSets/Main/目錄下;
  • 拷貝VOC2007/Annotations/ 目錄下的所有的文件到VOC_person/Annotations/ 目錄下;
  • 拷貝VOC2007/JPEGImages/ 目錄下的所有的文件到VOC_person/JPEGImages/ 目錄下;

1.3 生成訓練集,測試集和開發集文件

  • 我們需要生成VOC_person/ImageSets/Main/目錄下的test.txt,train.txt,trainval.txt,val.txt 這四個文件。

train.txt是訓練集,test.txt是測試集,val.txt是驗證集,trainval.txt是訓練和驗證集。VOC2007中,trainval大概是整個數據集的50%,test也大概是整個數據集的50%;train大概是trainval的50%,val大概是trainval的50%,當數據集較小時,可減少val和test的數量。

使用Matlab生成這四個txt文件,make_train_data.m 代碼如下:

%注意修改下面四個值  
xmlfilepath='E:\Image Sets\VOC_person\Annotations';  
txtsavepath='E:\Image Sets\VOC_person\ImageSets\Main\';  
trainval_percent=0.8;%trainval佔整個數據集的百分比,剩下部分就是test所佔百分比  
train_percent=0.8;%train佔trainval的百分比,剩下部分就是val所佔百分比  

xmlfile=dir(xmlfilepath);  
numOfxml=length(xmlfile)-2;%減去.和..  總的數據集大小  

trainval=sort(randperm(numOfxml,floor(numOfxml*trainval_percent)));  
test=sort(setdiff(1:numOfxml,trainval));  

trainvalsize=length(trainval);%trainval的大小  
train=sort(trainval(randperm(trainvalsize,floor(trainvalsize*train_percent))));  
val=sort(setdiff(trainval,train));  

ftrainval=fopen([txtsavepath 'trainval.txt'],'w');  
ftest=fopen([txtsavepath 'test.txt'],'w');  
ftrain=fopen([txtsavepath 'train.txt'],'w');  
fval=fopen([txtsavepath 'val.txt'],'w');  

for i=1:numOfxml  
    if ismember(i,trainval)  
        fprintf(ftrainval,'%s\n',xmlfile(i+2).name(1:end-4));  
        if ismember(i,train)  
            fprintf(ftrain,'%s\n',xmlfile(i+2).name(1:end-4));  
        else  
            fprintf(fval,'%s\n',xmlfile(i+2).name(1:end-4));  
        end  
    else  
        fprintf(ftest,'%s\n',xmlfile(i+2).name(1:end-4));  
    end  
end  
fclose(ftrainval);  
fclose(ftrain);  
fclose(fval);  
fclose(ftest);
  • 將數據轉換成YOLO訓練所需的標註各色,並生成訓練/驗證/測試圖片路徑文件

1)打開yolo代碼下的scripts目錄,拷貝voc_label.py文件並重命名爲person_voc_label.py 文件:

cp voc_label.py person_voc_label.py

2)修改person_voc_label.py文件:

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join

sets=['train', 'val', 'test']

classes = ["person"]


def convert(size, box):
    dw = 1./(size[0])
    dh = 1./(size[1])
    x = (box[0] + box[1])/2.0 - 1
    y = (box[2] + box[3])/2.0 - 1
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return (x,y,w,h)

def convert_annotation(image_id):
    in_file = open('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/Annotations/%s.xml'%(image_id))
    out_file = open('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/labels/%s.txt'%(image_id), 'w')
    tree=ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)

    for obj in root.iter('object'):
        difficult = obj.find('difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult)==1:
            continue
        cls_id = classes.index(cls)
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
        bb = convert((w,h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')

wd = getcwd()

for image_set in sets:
    if not os.path.exists('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/labels/'):
        os.makedirs('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/labels/')
    image_ids = open('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/ImageSets/Main/%s.txt'%(image_set)).read().strip().split()
    list_file = open('person_%s.txt'%(image_set), 'w')
    for image_id in image_ids:
        list_file.write('/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/JPEGImages/%s.jpg\n'%(image_id))
        convert_annotation(image_id)
    list_file.close()

os.system("cat person_train.txt person_val.txt > my_person_train.txt")

3)執行person_voc_label.py腳本。

發現在scripts/VOC_person目錄下生成label文件夾,裏面有每張圖片的標註文件(文件內容形如0 0.488888888889 0.289256198347 0.977777777778 0.429752066116),其中第一個數字表示類別標籤(0表示person,也即前面寫的classes的順序),後面四個數爲包圍框信息。

4)生成訓練文件

在上一步執行了person_voc_label.py腳本後,會在 scripts下多了person_train.txt、person_val.txt和person_test.txt這三個文件,這三個文件是數據集中圖片的路徑。由於yolo訓練只需要一個my_person_train.txt文件,文件中包含所有你想要訓練的圖片的路徑。因此,我們還要生成my_person_train.txt文件。

cat person_train.txt person_val.txt > my_person_train.txt

到這裏數據集製作算是製作完成了。

 

2.修改代碼訓練

(1)在data目錄下新建文件my_person_train.names,內容爲:

person

(2)在cfg目錄下新建 my_person_train.data文件,內容爲:

classes= 1
train  = /home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/my_person_train.txt
valid  = /home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt
names = data/my_person_train.names
backup = backup

(3)修改examples/yolo.c文件:

  • 檢測目標類別改成自己的數據集類別:
char *voc_names[] = {"person"};
  • train_yolo函數:
char *train_images = "/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/my_person_train.txt";
char *backup_directory = "/home/liguiyuan/deep_learning/project/darknet/backup/";

train_images應該指向我們剛得到的my_person_train.txt;backup_directory指向的路徑是訓練過程中生成的weights文件保存的路徑(可以在darknet-master下新建文件夾backup然後指向它)。這兩個路徑按自己系統修改即可。

  • validate_yolo函數:
    char *base = "results/comp4_det_test_";
    //list *plist = get_paths("data/voc.2007.test");
    list *plist = get_paths("/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt");
    //list *plist = get_paths("data/voc.2012.test");
  • validate_yolo_recall函數:
    char *base = "results/comp4_det_test_";
    list *plist = get_paths("/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt");
  • test_yolo函數:
//draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, 0, voc_names, alphabet, 20);
draw_detections(im, l.side*l.side*l.n, thresh, boxes, probs, 0, voc_names, alphabet, 1);

類別數改爲1類。

  • run_yolo函數:
//demo(cfg, weights, thresh, cam_index, filename, voc_names, 20, frame_skip, prefix, avg, .5, 0,0,0,0);
demo(cfg, weights, thresh, cam_index, filename, voc_names, 1, frame_skip, prefix, avg, .5, 0,0,0,0);

類別數改爲1類。

(4)修改examples/detector.c文件

  • validate_detector_recall函數:
list *plist = get_paths("/home/liguiyuan/deep_learning/project/darknet/scripts/VOC_person/person_test.txt");
  • run_detector函數:
//int classes = option_find_int(options, "classes", 20);
int classes = option_find_int(options, "classes", 1);

類別數改爲1類

(5)修改cfg/yolov3-voc_person.cfg文件:

複製cfg/yolov3-voc.cfg文件並命名爲yolov3-voc_person.cfg,如果想用其他模型修改類似。

【yolo】層中 classes 把20改成1,因爲我們檢測的類別只有penson,有3個地方要改;

【yolo】層的上一層【convolutional】,即網絡中的最後一層卷積,的filters改爲18,也是3處地方要修改。計算方法是這樣:1*1*[3*(4+1+1)]=18。[num*(coords+1+classes) ,num爲3,因爲yolov3預測3個boxes(yolov1爲1個,yolov2爲5個),coords爲4個座標(預測boundingbox的 x,y,w,h), 1表示置信度,識別1個類別。N爲卷積核的size。

[convolutional]
size=1
stride=1
pad=1
filters=18
activation=linear

[yolo]
mask = 3,4,5
anchors = 10,13,  16,30,  33,23,  30,61,  62,45,  59,119,  116,90,  156,198,  373,326
classes=1
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1

(6)其他的一些參數可以按自己需求修改,比如batch, subdivisions等,都是在這個cfg文件裏面修改。我這裏把batch改爲32,要不然使用GTX1060顯卡訓練時會出現CUDA Error: out of memory。subdivision:這個參數意思是,讓你的每一個batch不是一下子都丟到網絡裏,而是分成多次丟到GPU內存中,例如2,就是分成2次讀入。

[net]
# Testing
# batch=1
# subdivisions=1
# Training
 batch=32
 subdivisions=16
width=416
height=416
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1

learning_rate=0.001
burn_in=1000
max_batches = 50200
policy=steps
steps=40000,45000
scales=.1,.1

 

3.訓練模型

下載預訓練darknet53.conv.74的weights權重文件,下載地址爲:

wget https://pjreddie.com/media/files/darknet53.conv.74

開始訓練模型:

./darknet detector train cfg/my_person_train.data cfg/yolov3-voc_person.cfg darknet53.conv.74

看到開始訓練了:

訓練中斷後從斷點開始訓練模型:

./darknet detector train cfg/my_person_train.data cfg/yolov3-voc_person.cfg backup/yolov3-voc_person.backup

使用GTX1060的GPU,經過30多個小時的訓練,訓練了10000批次,在person_backup目錄下看到 yolov3-voc_person_10000.weights等訓練好的模型。這裏附上我訓練的模型鏈接: https://pan.baidu.com/s/1sfeEqdR-K-9q4NZFD5FdmQ   提取碼: 8n8g 

 

4.檢測模型的結果

修改cfg/yolov3-voc_person.cfg 文件,把Training的batch, subdivisions屏蔽掉,使用Testing的batch, subdivisions 都爲1

# Training
#batch=32
#subdivisions=2
# Testing
batch=1
subdivisions=1

檢測圖片:

./darknet detect cfg/yolov3-voc_person.cfg backup/yolov3-voc_person.backup data/person.jpg

攝像頭實時檢測:

./darknet detector demo cfg/my_person_train.data cfg/yolov3-voc_person.cfg backup/yolov3-voc_person.backup

效果圖如下:

文章到這裏完成了整個訓練的過程。

注:轉載請註明出處!


文章裏部分教程參考:https://blog.csdn.net/yudiemiaomiao/article/details/71635257

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章