【項目實戰】yolov3-tiny人臉數據模型訓練


數據集准備:

  使用的是網上公開的widerface數據集(從事圖像標注的人都是專業的呀(http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/index.html)

  需要下載前四個文件,包括訓練集、驗證集、測試集和人臉標注的txt文件(並沒有原始的xml文件)。

  訓練集、驗證集、測試集的數據如下圖所示:

  其中每一個數據集中都包含60種不同場景下人的圖像。(該數據集包含不同類型的場景,場景比較豐富)如下圖所示:

  標注文件(txt/mat)

  標注文件中包含上述6個(3類)文件,分別適用於不同的環境。其中包含的標注信息如下圖所示:

  其格式和linux環境下需要的txt文件有所不同。

  因此,參考博客https://blog.csdn.net/u010397980/article/details/86764637將給定的標注文件重新轉化成xml文件。之后按照linux環境下數據集和文件格式的要求(如下圖所示),將不同場景下的圖像、xml文件放在一個文件夾下。

  下述是上面介紹的將給定的標注文件txt轉化為xml文件的代碼,在使用過程中,只需要更改標注文件的路徑txt_label和標注文件對應圖像的路徑img_path

  需要注意的是,通過下述代碼生成的xml文件與圖片一起分布在不同的場景當中,后續使用的時候需要提取。

 1 # -*- coding: utf-8 -*-
 2 # @Time    : 2019/9/25 20:11
 3 # @Author  : smw
 4 # @Email   : monologuesmw@163.com
 5 # @File    : txt2xml.py
 6 # @Software: PyCharm
 7 # 將txt生成xml文件
 8 import os
 9 import cv2
10 
11 from xml_writer import PascalVocWriter
12 
13 # txt_label = "wider_face_val_bbx_gt.txt"
14 # img_path = "WIDER_val/images"
15 
16 txt_label = "wider_face_train_bbx_gt.txt"   # 修改train/val自帶的標注txt文件的名稱
17 img_path = "WIDER_train/WIDER_train/images"  # 修改對應的train/val圖片的地址
18 
19 
20 def write_xml(img, res):
21 # 
22     img_dir = os.path.join(img_path, img)
23     print(img_dir)
24     img = cv2.imread(img_dir)
25     shape = img.shape
26     img_h, img_w = shape[0], shape[1]
27     writer = PascalVocWriter("./", img_dir, (img_h, img_w, 3), localImgPath="./", usrname="wider_face")
28     for r in res:
29         r = r.strip().split(" ")[:4]
30         print(r)
31         x_min, y_min = int(r[0]), int(r[1])
32         x_max, y_max = x_min + int(r[2]), y_min + int(r[3])
33         writer.verified = True
34         writer.addBndBox(x_min, y_min, x_max, y_max, 'face', 0)
35         writer.save(targetFile=img_dir[:-4] + '.xml')
36 
37 
38 with open(txt_label, "r") as f:   
39     line_list = f.readlines()  # 打開並讀取標注文件
40     for index, line in enumerate(line_list):
41         line = line.strip()
42         if line[-4:] == ".jpg":
43             print("----------------------")
44             # print index, line
45             label_number = int(line_list[index + 1].strip())
46             print("label number:", label_number)
47             # print line_list[index: index + 2 + label_number]
48             write_xml(line_list[index].strip(), line_list[index + 2: index + 2 + label_number])  # 寫xml

Main文件夾下txt的生成

  由於linux環境下的test.py會對所有的數據集重新進行訓練集和驗證集的划分,生成Main文件夾中的train.txt,val.txt(其中記錄的是參與訓練和驗證數據的名稱(不帶后綴))。而本數據集已經划分好了訓練集和驗證集,如果將所有的數據重新放置在一起重新划分,未免有些傻用test.py的感覺。而且可能會丟失掉之前數據集划分的依據。因此,我們將訓練數據集的xml文件放在一起驗證數據集的xml文件放置在一起,在windows10下對test.py文件進行改寫,生成指定xml文件對應的txt(生成txt的過程只需要xml文件,不需要圖像數據文件。因此,訓練、驗證數據是直接放在一起的)。然后,便可以將生成的txt文件移植到linux環境下的Main文件夾下

  整個過程的流程大致可以分為以下兩步:

  1. 將不同場景文件夾下的數據放置在一個文件夾中。(訓練、驗證的圖像數據集直接放在一起)

  2. 將不同場景文件夾下,生成的xml文件分別放置在train_xml和val_xml兩個文件夾下(用於后續生成Linux下對應的txt文件) ---- 上述兩部均同時人工進行

 數據整理 (兩個人,大概耗時1.5小時)

A. 圖像數據集包含所有的訓練、驗證數據集

  將給定數據集的 WIDER_train、WIDER_val中不同場景下的圖像數據均放置在同一個文件夾xJPEG下。由下圖所示,訓練、驗證數據共包含16106份。

B. xml數據集

  將給定數據集的WIDER_train、WIDER_val中,生成的xml文件放置到train_xml和val_xml文件夾中。分為訓練集的xml 測試集的xml 用於生成指定的txt(train.txt 和 val.txt)。

  

生成指定xml文件的txt [Main中的txt] (test.py的改寫)

  執行下述代碼將會生成上述的train.txt val.txt,然后移植到linux環境下的Main文件夾中

 1 # 根據指定訓練集和測試集的xml文件生成txt
 2 import os
 3 import random
 4 train_xmlfilepath = 'xml/train_xml'
 5 val_xmlfilepath = 'xml/val_xml'     # 獲取訓練、驗證xml文件夾的路徑
 6 txtsavepath = 'xml'
 7 total_train_xml = os.listdir(train_xmlfilepath)
 8 total_val_xml = os.listdir(val_xmlfilepath)  # 獲取文件夾下所有文件的列表
 9 
10 num_train = len(total_train_xml)
11 list_train = range(num_train)
12 num_val = len(total_val_xml)
13 list_val = range(num_val)
14 
15 ftrain = open('xml/train.txt', 'w')
16 fval = open('xml/val.txt', 'w')   # 打開兩個待生成的txt文件
17 
18 # train 的txt的書寫
19 for i in list_train:
20     name = total_train_xml[i][:-4] + '\n'
21     ftrain.write(name)
22 ftrain.close()
23 
24 # val 的txt的書寫
25 for j in list_val:
26     name = total_val_xml[j][:-4] + '\n'
27     fval.write(name)
28 fval.close()

  生成txt文件后,就可以將train文件夾和val文件夾下的xml文件合並在一起移植linux下的Annotations文件夾下。 【合並xml,移植linux Annotations文件夾下】  

Linux下lables文件夾下txt生成

voc_labels.py修改

  在linux下,對voc_labels.py文件中的下述部分進行修改。

  sets中更改文件夾設定的年份,以及Main文件夾中包含的txt數據集類型。修改類別名稱。

 part 2.2 在linux終端上執行

  在darknet文件夾下打開終端,執行下述指令。便會在labels文件夾下生成每一張圖像的

python --version 
python voc_labels.py

  至此,數據集的准備便已完成

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

  前期的准備工作和yolo v3中是一樣的。

  需要修改的配置文件有voc.data、voc.names、 yolov3-tiny.cfg文件

  • voc.names中修改訓練數據集中包含有哪些類標簽,一行一類標簽;這里改為了face
  • voc.data中修改類別的數量;voc_labels.py生成的txt的路徑,包括訓練和驗證兩部分;其余兩項一般不需要修改,默認的就可以。詳情如下:
1 classes = 1
2 train = /home/vtstar/yolo/darknet/2019_train.txt
3 valid = /home/vtstar/yolo/darknet/2019_val.txt
4 names = data/voc.names
5 backup = backup
  • yolov3-tiny.cfg中需要修改內容如下:
    • [net]下 batch 和subdivisions參數(根據PC的性能)
    • [convolutional]([yolo]層的前一層),fitters的數量,根據類別的數量進行修改(5+class_num)*3
    • [yolo] anchors修改為K-Means生成的錨框(采用默認的也可以);classes數量  

  Makefile文件除了修改GPU,CUDNN,OPENCV的配置參數外,還需要根據電腦的配置,修改一些關於nvcc的參數。

  下載yolo v3-tiny的權重測試:

wget https://pjreddie.com/media/files/yolov3_tiny.weights

  下載卷積層的權重

./darknet partial cfg/yolov3-tiny.cfg yolov3-tiny.weights yolov3-tiny.conv.13 13

  模型訓練

1 ./darknet detector train cfg/voc.data cfg/yolov3-tiny.cfg yolov3-tiny.conv.15 -gpus 0,1

  gpus后的0,1,2,...根據服務器中GPU的數量選擇。  

初始錨框的生成:(win10) 用於修改yolov3-tiny中的錨框信息

  使用keras版本的K-Means生成人臉檢測的錨框寬高:

6,8, 10,13, 15,19, 24,30, 42,54, 106,140

  重復了十幾次,選擇了精度最高的一組(72.98%)

 

  初始錨框生成的步驟

  使用Keras版本的darknet源碼中的voc_annotation.py文件放置到下載的數據集中,修改一些文件的路徑:

1 in_file = open('xml/train_xml/%s.xml' % image_id)
2 ...
3 image_ids = open('xml/%s.txt' % image_set).read().strip().split()
4 list_file = open('%s.txt' % image_set, 'w')
5 ...
6 list_file.write('%s/xJPEG/%s.jpg'%(wd, image_id))

  voc_annotation.py 代碼如下所示,可對應上述需要改動的內容。該程序的執行實際上不需要將xml文件、圖像文件放置在標准的文件夾下。

  下述代碼主要的作用是將圖像的路徑+名稱.jpg 和 xml文件中標注框的信息寫到txt中。需要的文件:

  1. 沒有后綴的圖像名稱,也就是上述給Main文件夾中放置的txt

  2. 不需要圖像文件,但需要圖像的路徑,用於寫到txt中,第三行的2007沒有用,trian將作為生成的txt的名稱。【標注文件中圖片的路徑不能作為寫到txt的路徑,標注文中總如果包含中文路徑,則in_file=open()中需要添加encoding= "utf-8" , 否則會報錯,默認gbk】

 1 import xml.etree.ElementTree as ET
 2 from os import getcwd
 3 sets = [('2007', 'train')]
 4 
 5 classes = ["face"]   # 不需要使用
 6 def convert_annotation(year, image_id, list_file):   # 將xml轉化為txt
 7     in_file = open('xml/train_xml/%s.xml' % image_id)  # xml的地址,需要圖像名稱作為xml名稱(二者名稱是一致的)
 8     tree=ET.parse(in_file)  # 此處,如果在標注時,存儲地址包含中文路徑,會報錯,需要將上一行代碼添加encoding="utf-8"
 9     root = tree.getroot()
10 
11     for obj in root.iter('object'):
12         difficult = obj.find('difficult').text
13         cls = obj.find('name').text
14         if cls not in classes or int(difficult)==1:
15             continue
16         cls_id = classes.index(cls)
17         xmlbox = obj.find('bndbox')
18         b = (int(xmlbox.find('xmin').text), int(xmlbox.find('ymin').text), int(xmlbox.find('xmax').text), int(xmlbox.find('ymax').text))
19         list_file.write(" " + ",".join([str(a) for a in b]) + ',' + str(cls_id))
20 
21 wd = getcwd()
22 
23 for year, image_set in sets:
24     image_ids = open('xml/%s.txt' % image_set).read().strip().split()   # 只包含圖像名稱的txt, 也是給Main文件夾中的txt
25     list_file = open('%s.txt' % image_set, 'w')  # 要生成txt文件的路徑,名稱這里使用了第3行代碼中的train
26     for image_id in image_ids:
27         list_file.write('%s/xJPEG/%s.jpg'%(wd, image_id))  # 修改圖像所在的路徑,當然實際上不需要圖像,只是把路徑當做字符串寫到txt中
28         convert_annotation(year, image_id, list_file)
29         list_file.write('\n')
30     list_file.close()

  通過文件,便可以生成python中的train.txt的模式(即路徑+框信息),如下圖所示:

  然后將生成train.txt放置到K-Means.py文件夾中,修改K-Means中的一些路徑,如下圖所示:

  在這里,我們將train.txt的名稱修改為train_2019.txt,避免和之前文件的重合。由於在yolo v3-tiny中進行訓練,網絡中只包含兩個尺度的輸出,相應錨框的數量為6,所以在此處聚類個數修改為6.  

  至此訓練前修改完成,可以在終端下通過命令進行訓練

 

  對於缺陷檢測、海底物體識別、無人機識別、字符識別等都可以通過這樣的方式進行操作。

  -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

參數調優

  更深度了解配置文件的參數:

  subdivision可以減輕顯卡的壓力

    訓練過程中出現nan的現象:

  • 如果運行中出現了nan, 是正常現象
  • 但如果全部都是nan的話,這就是訓練過程出了問題

  一般而言,出現nan 是梯度爆炸、分母為0、log取到0或負值的情況。

  

  改小batch/增大subdivisions,或者關閉多尺度訓練random = 0. random設置為1,會增加檢測的精度

  Makefile中arch架構與自己顯卡架構不匹配

    

  多GPU訓練有可能會出現訓練開始一段時間后 loss = nan、 IOU=nan,其他參數全為0的情況

  確保cfg文件是train模式時,batch、subdivisions不全為1  

  增加input圖像的尺寸,例如在.cfg文件中設置(height=608,width=608)以上(看之前uav的配置文件 確實有608的設置),可以增加檢測的精度

  random=1,可以增加模型精度。

    訓練中參數的意義

  1. 迭代次數
  2. 總體的Loss損失
  3. 平均的loss損失,期望該值越小越好,一般來說當這個數值低於0.060730 avg就可以終止訓練了。
  4. 當前的學習率,是在.cfg文件中設定的
  5. 當前批次訓練花費的總時長
  6. 這個數值是9798*64的大小,表示到目前為止,參與訓練的圖片的總量

  (1) Region Avg IOU:平均的IOU,代表着預測的Bounding Box和Ground truth的交集與並集之比,(batch/subdivision)期望該值趨近於1。

  (2) Class:是標注物體的概率,期望該值趨近於1。

  (3) Obj:期望該值趨近於1。

  (4) No Obj:期望該值越來越小,但不為0。

  (5) AvgRecall:期望該值趨近於1,召回率比較高說明效果較好

  包含.5R和.75R,分別表示IOU取0.5和0.75時模型的召回率;

  (6) count表示輸出有多少個目標總和

  這里的region 16和23 表示yolo v3-tiny 中兩個尺度的輸出,16卷積層為最大尺度的輸出,使用較大的mask,適合預測較小的物體;23卷積層為最小尺度的輸出,使用較小的mask,適合預測較大的物體。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM