YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现

Pytorch-YOLOv5

  • 利用VOC2007对模型进行训练
    • 下载VOC2007数据集
    • 转换为YOLO标注格式的文件
    • 修改配置文件
      • 1. 修改voc.yaml文件
      • 2.修改yolov5s.yaml文件
      • 3. 修改train.py
    • 出现了的问题
  • 解决之前的一些疑惑?
    • PASCAL_VOC中的正负样本
    • 数据采样平衡
      • 1.过采样(over-sampling)
      • 2.欠采样(under-sampling)
      • 3.获取更多样本
  • References

经过YOLOv5训练一个简单的识别鱼的模型(超详细的入门级教程)(一)的学习后,明白了训练模型的全套流程,现在我们的问题是如何提升模型的性能
非常重要的一个环节便是数据集,一个好的数据集对于模型性能是至关重要的。
我们首先利用VOC2007数据集来对模型进行复现

利用VOC2007对模型进行训练

下载VOC2007数据集

  • 首先,附上VOC数据集下载链接VOC数据集下载。
    我们选择的是VOC2007数据集,可以看到现在最新版本的数据集是VOC2012,数量也多了很多。
    需要下载训练集和测试集:
    YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第1张图片
  • TIPS:用迅雷下载速度起飞哦!!!(之前我只下载了训练集,在这里补下测试集)
    YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第2张图片
  • 将两个压缩文件在同一级目录下解压到当前文件夹,两个压缩包的内容会合并在VOCdevkit
    YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第3张图片
  • 恍然大悟,原来加上了测试集之后,VOC2007数据集就是正常人能理解的排序了。当看到训练集中第一个图片000005.jpg时云里雾里。YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第4张图片

转换为YOLO标注格式的文件

  • 这时我们对于VOC2007数据集有了进一步的理解,有以下文件:
    YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第5张图片
    我们做的时目标检测任务,而且不是人的动作识别(具有人体部位的数据)(layout是做这个任务的)所以上述的SegmentationClass,SegmentationObject,Layout,Segmentation文件夹我们都不需要。

  • data目录下,放入VOCdevkit(包括了一整个VOC2007数据集),在相同的文件夹内创建voc2yolo.py,内容如下,并运行

import xml.etree.ElementTree as ET
import pickle
import os
from os import listdir, getcwd
from os.path import join
 
# 这里就体现出来了咱们在1.2步骤的时候我说的尽量按照那个目录名进行操作的优势,
# 在这可以剩下很多去修改名称的精力
# sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]
sets=[ ('2007', 'train'), ('2007', 'val'), ('2007', 'test')]  # 我只用了VOC2007
 
classes = ["aeroplane", "bicycle", "bird", "boat", "bottle",
           "bus", "car", "cat", "chair", "cow",
           "diningtable", "dog", "horse", "motorbike", "person",
           "pottedplant", "sheep", "sofa", "train", "tvmonitor"]
#classes = ["face"]  # 修改为自己的label
 
def convert(size, box):
    dw = 1./(size[0])  # 有的人运行这个脚本可能报错,说不能除以0什么的,你可以变成dw = 1./((size[0])+0.1)
    dh = 1./(size[1])  # 有的人运行这个脚本可能报错,说不能除以0什么的,你可以变成dh = 1./((size[0])+0.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(year, image_id):
    in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id))
    out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, 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 year, image_set in sets:
    if not os.path.exists('VOCdevkit/VOC%s/labels/'%(year)):
        os.makedirs('VOCdevkit/VOC%s/labels/'%(year))
    image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split()
    list_file = open('%s_%s.txt'%(year, image_set), 'w')
    for image_id in image_ids:
        list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id))
        convert_annotation(year, image_id)
    list_file.close()
 
# 这块是路径拼接,暂时用不上,先都注释了
# os.system("cat 2007_train.txt 2007_val.txt 2012_train.txt 2012_val.txt > train.txt")
# os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt > train.all.txt")

可以看到生成了三个数据集的路径txt文件,和label文件夹。

修改配置文件

还是原来的步骤,我们继续巩固以下~

1. 修改voc.yaml文件

这里我们直接修改voc.yaml的文件,可以看到还有coco.yaml文件,其实就是你的数据集的配置文件罢了!!

# PASCAL VOC dataset http://host.robots.ox.ac.uk/pascal/VOC/
# Download command: bash ./data/get_voc.sh
# Train command: python train.py --data voc.yaml
# Default dataset location is next to /yolov5:
#   /parent_folder
#     /VOC
#     /yolov5
 
 
# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
train: F:\AI\datasets\VOC2007\VOCdevkit\VOC2007\2007_train.txt
val: F:\AI\datasets\VOC2007\VOCdevkit\VOC2007\2007_val.txt
 
# number of classes
nc: 20
 
# class names
names: ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog',
        'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor']

可以看到博主把常用数据集都放在了一个路径里,整理在一起!!值得学习

2.修改yolov5s.yaml文件

YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第6张图片
该文件需要修改的地方是第2行nc的个数,需要改成自己数据集类别的个数,对于VOC数据集改成20。

3. 修改train.py

在这里插入图片描述
其他的一些超参数自行修改即可。

出现了的问题

  1. 发现原博主的数据路径用的是/,但是在win下用的是\,所以找不到label(可能是这个原因)
    voc2yolo.py中最后的代码修改为
 list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id))
 换成
  list_file.write('%s\VOCdevkit\VOC%s\JPEGImages\%s.jpg\n'%(wd, year, image_id))
  1. 但是发现还是找不到label,我们把将里面所有txt文件复制到 [ VOCdevkit\VOC2007\JPEGImages ]中即可。

  2. 这时,常规错误出现了
    在这里插入图片描述
    这类报错原因是显卡内存太小了,一次装不下太多的图片,因此可以通过–batch-size调整每个批次样本的个数,默认值是16。报错后可以调整为8试试,如果还是报错,再调整为4试试,如果还是报错,那请找一把锤子,瞄准电脑主机箱用力地砸下去…
    我的batch-size已经改成1了伙计们。。
    那下一个教程咱们就利用远程服务器来进行模型训练吧!~

解决之前的一些疑惑?

PASCAL_VOC中的正负样本

  • Main文件夹下可以看到很多的txt文件,作用是什么呢?就是分出test集、train集和val集。还有一个trainval集(train+val和合集嘛)
    刚打开的时候发现有84个txt文件,其中有四个不带前缀,剩下的80个呢分别对应了VOC中的20个类别的四个数据集txt文件。
    YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第7张图片
    traintesttrainvalval里面的内容如下,就是很常规的划分数据集:
    YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第8张图片

  • 但是在那20个类别的数据对应的traintest…文件中,除了划分数据之外,还做了正负样本的标注
    YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第9张图片
    YOLOv5超详细的入门级教程(训练篇)(二)——VOC2007模型复现_第10张图片

  • 这个1-1所代表的正负样本究竟是什么呢?

    1. 在机器学习中,数据预处理一般包括数据清洗数据集成数据采样。而正负样本涉及到了数据采样的问题,因此后面也提一下。
    2. 简单来说,和概率论中类似,一般我们看一个问题时,只关注一个事件(希望它发生或者成功,并对其进行分析计算),而正样本就是属于我们关注的这一类别的样本,负样本就是指不属于该类别的样本。
    3. 一般来说,比如我们训练分类器时,希望样本中正负样本的比例是接近于1:1的。因为如果正样本占比很大(比如90%)或者负样本占比远超正样本,那么训练结果可想而知,获得的分类器在测试中的效果会很差。(这也得出了为什么我们在教程一中的检测器效果很差的原因,60张图片全是奇奇怪怪的正样本)

数据采样平衡

所以在进行数据采样时,如何解决数据不平衡的问题呢?

1.过采样(over-sampling)

这是一种较为直接的办法,即通过随机复制少数类来增加其中的实例数量,从而可增加样本中少数类的代表性。

2.欠采样(under-sampling)

这种方法也比较直接,即通过随机消除占多数类的样本来平衡类分布,直到多数类和少数类实现平衡。

3.获取更多样本

上面的两种方法比较直接方便,但也存在弊端,比如过采样可能会导致过拟合,欠采样可能无法很好地利用有限的数据(这也可能会造成过拟合)。因此最好还是获取更多的样本来补充,我认为主要有下面两种方法:

  1. 采集
    例如在海贼王漫画的样本中,我们要进行20x20大小的海贼检测,那么为了获取尽可能多的负样本,我们可以截取一张1000x1000大小的海王类图像,将其拆分为20x20大小的片段加入到负样本中(即50x50地进行分割)。
  2. 生成
    为了获得更多负样本,我们也可将前面1000x1000的海王类图像先拆分为10x10大小,这就比之前多出了4倍的负样本图像。不过要注意的是,为了保持大小的一致,还需进一步将其拉伸至20x20的大小。
    当然,其实不需要从体积上达到这么大的比例,关键是像素尺寸的匹配

References

  1. 手把手教会使用YOLOv5训练VOC2007数据集https://blog.csdn.net/Jwenxue/article/details/107912834 皇天不负有心人,在我想要探索的时候已经有好心人把路摸索好了,可以愉快的跟随前人的经验!(这个博主的帖子非常的清晰)
  2. PASCAL VOC数据集分析https://blog.csdn.net/zhangjunbob/article/details/52769381
  3. machine learning笔记:数据采样之正样本和负样本https://gsy00517.github.io/machine-learning20200118112156/ 通俗易懂,但是网站总是出bug啊,网页五光十色的。。

你可能感兴趣的:(YOLOv5超详细的入门级教程,深度学习,pytorch,计算机视觉)