
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 ---