• 【项目实战】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] + '
    '
    21     ftrain.write(name)
    22 ftrain.close()
    23 
    24 # val 的txt的书写
    25 for j in list_val:
    26     name = total_val_xml[j][:-4] + '
    '
    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('
    ')
    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,适合预测较大的物体。

      

  • 相关阅读:
    一本通1331后缀表达式的值
    一本通1198 逆波兰表达式
    一本通1311 求逆序对(归并排序应用)
    快速排序
    一本通1310 车厢重组(冒泡排序,类似逆序对)
    一本通1186 出现次数超过一半的数(类似桶排序)
    一本通1216 红与黑 (代码没有参考任何博客,完全是自己写的,我搜索出山了!!!)
    一本通1222 放苹果
    一本通 1212 LETTERS
    一本通1215 迷宫
  • 原文地址:https://www.cnblogs.com/monologuesmw/p/12802146.html
Copyright © 2020-2023  润新知