Yolo訓(xùn)練自己的數(shù)據(jù)集完整記

0. 前言

雖然網(wǎng)上教程一大把,但是作為小白,訓(xùn)練自己的數(shù)據(jù)集還是費了點事。記錄下一些關(guān)鍵點,少踩一點坑。
本文假設(shè)已經(jīng)擁有以下條件:

1. Linux操作系統(tǒng),已經(jīng)安裝好CUDA和cuDNN(參考教程:http://www.itdecent.cn/p/bc614f23d9eb)
2. 耐心

1. 安裝Darknet

Darknet是Yolo作者自己寫的一個網(wǎng)絡(luò)框架,官方網(wǎng)站是:https://pjreddie.com/
這部分跟著官方教程走就可以了,主要是以下步驟:

1)下載源碼

git clone https://github.com/pjreddie/darknet
cd darknet

2)修改Makefile文件,啟用GPU,cuDNN,OpenCV等相關(guān)支持

xed ./Makefile
# 修改
GPU=0
CUDNN=0
OPENCV=0
# 為
GPU=1
CUDNN=1
OPENCV=1

3)編譯

make

最終得到可執(zhí)行文件“darknet


2. 準(zhǔn)備數(shù)據(jù)集

整個過程難就難在數(shù)據(jù)集的準(zhǔn)備(先無腦訓(xùn)練,除開網(wǎng)絡(luò)調(diào)優(yōu))。
先看看訓(xùn)練的命令需要準(zhǔn)備哪些東西

./darknet detector train prcv.data prcv-tiny.cfg

其中“./darknet detector train”為訓(xùn)練的固定命令,使用detector參數(shù)調(diào)用的是main函數(shù)里的run_detector函數(shù)。這也是后面為啥txt標(biāo)注文件在與images同級的labels目錄下的原因。與detect參數(shù)查找標(biāo)注文件的路徑不一樣。
最重要的是prcv.data、prcv-tiny.cfg兩個參數(shù)。這兩個文件,prcv.data即與數(shù)據(jù)集相關(guān),prcv-tiny.cfg與網(wǎng)絡(luò)結(jié)構(gòu)相關(guān)。
看看prcv.data文件都有些啥內(nèi)容

classes = 1
train  = /home/user/Prcv2018/Dataset/mission/detection/train/train_set.txt
valid  = /home/user/Prcv2018/Dataset/mission/detection/train/test_set.txt
names = /home/user/Prcv2018/Dataset/mission/detection/train/prcv.names
backup = /home/user/Prcv2018/Train/backup
eval = prcv

可以看出,里面是與數(shù)據(jù)集相關(guān)的配置參數(shù),classes是目標(biāo)類別數(shù),train、valid分別是訓(xùn)練集和測試集txt文件所在路徑,names為目標(biāo)內(nèi)別名稱。backup為訓(xùn)練中間權(quán)重文件保存目錄。很明顯,這個文件里指出的三個文件與數(shù)據(jù)集相關(guān),是需要準(zhǔn)備的。train_set.txt里是訓(xùn)練集圖片路徑,test_set.txt里是測試集圖片路徑。

本文描述的數(shù)據(jù)集是PRCV2018行人計數(shù)挑戰(zhàn)賽的數(shù)據(jù)集,鑒于保密協(xié)議,不公開放出。該數(shù)據(jù)集的特點是方框標(biāo)注出頭和肩的位置。目標(biāo)分類數(shù)量只有一類:person。

因此,這三個文件的內(nèi)容分別是:

# prcv.names
person

# train_set.txt
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000158_R_2018_05_17_18_40_00_left_027091.jpg
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000158_R_2018_05_17_11_37_26_left_008230.jpg
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000158_R_2018_05_17_11_37_26_left_017198.jpg
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000158_R_2018_05_17_14_00_00_left_023063.jpg
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000051_R_2018_05_17_14_00_00_left_030518.jpg
... ...

# test_set.txt
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000051_R_2018_05_17_14_00_00_left_031430.jpg
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000158_R_2018_05_17_14_00_00_left_019522.jpg
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000051_R_2018_05_17_11_43_47_left_000923.jpg
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000052_R_2018_05_17_18_40_01_left_010092.jpg
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000051_R_2018_05_17_16_00_00_left_028425.jpg
... ...

以上描述了訓(xùn)練集和測試集,以及類別名稱。對于訓(xùn)練來講,還差什么?
對了,就是標(biāo)注文件。darknet識別的標(biāo)注文件是txt格式。它位于圖片的上級目錄labels目錄下。

# 比如train_set.txt里是
/home/user/Prcv2018/Dataset/mission/detection/train/images/202018000051_R_2018_05_17_14_00_00_left_031430.jpg
# 那么對應(yīng)的標(biāo)注文件 
202018000051_R_2018_05_17_14_00_00_left_031430.txt
# 應(yīng)該在
/home/user/Prcv2018/Dataset/mission/detection/train/lables
# 目錄下

標(biāo)注文件中標(biāo)簽的數(shù)據(jù)格式:

<object-class> <x> <y> <width> <height>
# Where x, y, width, and height are relative to the image's width and height. 

以上是官方說明,說的很簡單,實際意思是:

1. x,y是中心點的坐標(biāo)
2. 坐標(biāo)點是相對于整張圖片的相對比例
3. 寬高也是相對于整張圖片的相對比例
4. 類別編號是從0開始的
# eg.
# 圖片大小是320*240,目標(biāo)框中心位置是(160,120),框的寬高是[32,24],類別編號是0
# 那么,對應(yīng)的txt文件內(nèi)容應(yīng)該是
# 0 0.5 0.5 0.1 0.1

源數(shù)據(jù)集的格式是左上角坐標(biāo)x,y,框的寬高w,h(像素)。生成訓(xùn)練集和測試集以及標(biāo)簽格式轉(zhuǎn)換py文件如下(第一次寫python,實現(xiàn)得可能不太優(yōu)雅):

import os

img_with = 320      # 圖片寬
img_height = 240    # 圖片高

rootdir = '/home/user/Prcv2018/Dataset/mission/detection/train'


def f2s(num):
    return str(round(num, 6))


def convert_data(path, tgt_path):
    out_file = open(tgt_path, 'w')
    with open(path, 'r') as txt_file:
        while True:
            line = txt_file.readline()
            if not line:
                break
            org_x, org_y, org_w, org_h = [int(i) for i in line.split()]
            tgt_x = (org_x + org_w / 2.0) / img_with
            tgt_y = (org_y + org_h / 2.0) / img_height
            tgt_w = org_w / img_with
            tgt_h = org_h / img_height
            # 單分類,序列號只有0
            tgt_line = '0 ' + f2s(tgt_x) + ' ' + f2s(tgt_y) + ' ' + f2s(tgt_w) + ' ' + f2s(tgt_h) + '\n'
            out_file.writelines(tgt_line)
    out_file.close()


def main():
    img_dir = rootdir + '/images'
    ann_dir = rootdir + '/annotations'
    lab_dir = rootdir + '/labels'
    # 1. 分出訓(xùn)練集和測試集
    train_txt = open(rootdir + '/train_set.txt', 'w')
    test_txt = open(rootdir + '/test_set.txt', 'w')
    img_list = os.listdir(img_dir)
    for i in range(0, len(img_list)):
        path = os.path.join(img_dir, img_list[i])
        if i % 5 == 0:
            test_txt.writelines(path + '\n')
        else:
            train_txt.writelines(path + '\n')
    train_txt.close()
    test_txt.close()
    # 2. 處理標(biāo)簽數(shù)據(jù)
    target_dir = lab_dir
    # 得到目錄下列表名(相當(dāng)于ls命令)
    txt_list = os.listdir(ann_dir)
    for i in range(0, len(txt_list)):
        path = os.path.join(ann_dir, txt_list[i])
        tgt_path = os.path.join(target_dir, txt_list[i])
        if os.path.isfile(path):
            # 處理文件
            convert_data(path, tgt_path)


if __name__ == '__main__':
    main()

其中測試集數(shù)據(jù)量占20%。


3. 訓(xùn)練

前面提到的另一個重要文件prcv-tiny.cfg。先放出它的具體內(nèi)容:

[net]
batch=128
subdivisions=1
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
max_batches = 40200
policy=steps
steps=-1,100,20000,30000
scales=.1,10,.1,.1

[convolutional]
batch_normalize=1
filters=32
size=7
stride=3
pad=1
activation=leaky

... ...

[convolutional]
batch_normalize=1
size=1
stride=1
pad=1
filters=128
activation=leaky

[convolutional]
size=1
stride=1
pad=1
filters=30           <-------------------------------------------
activation=linear

[region]
anchors = 1.08,1.19,  3.42,4.41,  6.63,11.38,  9.42,5.11,  16.62,10.52
bias_match=1
classes=1
coords=4
num=5
softmax=1
jitter=.2
rescore=1

object_scale=5
noobject_scale=1
class_scale=1
coord_scale=1

absolute=1
thresh = .5
random=1

這個文件里面包含了一些參數(shù)和網(wǎng)絡(luò)結(jié)構(gòu),每項具體的釋義網(wǎng)上一大把,就不過多解釋了。唯一需要注意的是最后一層filters數(shù)量的計算,

filters = num×(classes + coords + 1)= 5*(1+4+1) = 30

當(dāng)然,width=416,height=416中的416為32的13倍。
訓(xùn)練目錄如下

user@Phynion:~/Prcv2018/Train$ ls
backup  darknet  data  prcv.data  prcv-tiny.cfg  test.jpg

訓(xùn)練目錄位置隨意,注意prcv.data、prcv-tiny.cfg里的路徑即可。data\labels目錄下的圖片用于顯示時的框和標(biāo)簽。
執(zhí)行

./darknet detector train prcv.data prcv-tiny.cfg

開始訓(xùn)練



可以看到,對整個CPU、GPU、內(nèi)存的要求都還是蠻高的。


4. 測試

訓(xùn)練結(jié)束后,權(quán)重文件保存在backup文件夾內(nèi),名稱為prcv-tiny_final.weights。
打開prcv-tiny.cfg文件,#備注掉

#batch=128
#subdivisions=1

運行

./darknet detector test prcv.data prcv-tiny.cfg backup/prcv-tiny_final.weights test.jpg

可以看到結(jié)果



嗯,還不錯的樣子...

--- over ---

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容