【yolov1】详解yolov1理论 & 代码

目标检测要解决的3大问题:

1、有没有?

图片中是否有要检测的物体?(检测物体,判定前景背景)

2、是什么?

这些物体分别是什么?(检测到的物体是什么)

3、在哪里?

这些物体在哪里?(画框,描边,变色都行)

yolov1模型图

在这里插入图片描述

一.预测阶段

1.2 预测阶段后处理

在这里插入图片描述
把7x7x2=98个预测框进行筛选过滤,重复的预测框只保留一个。最终得到预测结果。
包括:

1.过滤掉置信度低的框
2.非极大值抑制

在这里插入图片描述
选出一个grid cell,它有30个值。每个grid cell预测两个预测框,用10个值表示。后面20个值是条件概率,即在该grid中存在目标的情况下,是某一个类的概率。

在这里插入图片描述

  用置信度与后面20个类的条件概率相乘得到每一个类的概率。
将全概率p() 拆分成 p(该 grid cell 存在目标的概率) x p(| 该grid cell 存在目标的概率)。
这样本来需要40个维度才能表示的概率,变成两个置信度和一个20维的条件概率即可。

在这里插入图片描述

将置信度 x 条件概率后,每个框生成两个20维的向量,表示每一个类的概率。
一共生成 7x7x2 = 9820维度的向量。

在这里插入图片描述

7x7x30的信息,可视化获得了中间图的结果。

1.2 后处理

在这里插入图片描述

把7x7x30的张量,变成目标检测结果。

在这里插入图片描述

假如第一行是dog类
1.设置dog类的概率如果<0.2,则置为0.
2.将dog类按照值进行排序。概率高的放在前面。
3.对排序之后的结果进行NMS。

1.3 NMS

取置信度值最大的框,bb47,它的框是黄色的部分。
在这里插入图片描述

先把第一个框跟第二框比较。如果他们的IOU>一个阈值,那么认为他们两个重复识别了一个目标。(7x7个gred cell 每个都对猫预测了两个框,共有98个预测框,这98个预测框,可能有好些个预测的不是图像上的同一个猫)
就把低置信度的框过滤掉。 (即将它设置为0

在这里插入图片描述

用bb47与其它框做IOU计算,过滤掉一些框之后,从第二个值非0的框,开始让它与其它非0的框再做一个IOU计算。

对每个类重复上面的结果。

最后得到一个稀疏的矩阵。
把不为0的索引找出来,表示类别。把分数值也取出来,可视化。

在这里插入图片描述

二.训练阶段

训练阶段没有NMS,
如下:训练阶段有个Ground True ,是人工标注的真实框。

我们的算法是为了拟合这个框,让损失函数最小化。
真实的框在哪个grid cell,就让这个grid cell预测的一个bodding box去拟合这个框。
两个预测框具体让哪一个框预测要看这两个预测框与真实框的IOU。
另一个框和其它Grid cell 预测的框只需要让他们置信度越来越小即可。

在这里插入图片描述

在这里插入图片描述

https://www.bilibili.com/video/BV15w411Z7LG?p=5&vd_source=ebc47f36e62b223817b8e0edff181613

每个预测框对应 维度为2x5 + 20 = 30的向量。 
20代表:yolov1用的数据有20个类别。这20个类别用独热编码表示,是哪个类别就让该类别标记为1.
5代表 : 4个坐标和一个置信度。
置信度计算的是预测边框 与 真实边框的 IOU,IOU用来选择哪个边框作为预测边框。

损失函数的设计:
要让与预测框 与 真实框的Loss变的很小。

负责预测物体的框,要与真实框Ground truth从坐标和宽高上都尽量一致。

在这里插入图片描述

1.计算预测中心点与真实中心点的损失。
2.计算预测的宽高与真实宽高的损失。

用根号,是使得小框对误差更敏感。

第三项负责计算置信度的误差

标签值是预测框真实框的IOU,作为标签值。

第四项是不负责检测目标的框,让它们的Loss值越小越好。让他们的权重小一些,因为他们比较多。

第五项:负责检测物体那个框的分类误差。比如真实框类别标注是狗,那么预测的类别是狗的概率让它越来越接近1。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

预测结果

在这里插入图片描述

当物体中心落到某个网格中,该网格就负责预测这个物体。
每个网络会生成两个预测框,所以yolov1共有 7x7x2=98个预测框,相对于fast-rcnn成百上千个预测框,yolov1少了很多。

参考:https://zhuanlan.zhihu.com/p/595221376

损失函数

在这里插入图片描述

1.对坐标进行预测,坐标损失

预测第i个网格,的第j个bbox是否有该object。 方法是计算每个网格的两个bbox与真实框的IOU,IOU最大bbox对应的网格中包含这个object。

在这里插入图片描述
在这里插入图片描述
如果不用根号,则是个线性关系 w越大,该物体与小物体的loss就越大。模型的学习会收到它的影响,主要去满足大物体去了。

超参数 λ_coord 默认是5,是为了平衡非物体(背景)的bbox过多的影响。因为目标对于背景来说是很小的,损失函数占比的权重就很小。

置信度损失

在这里插入图片描述
此时加入了一个非物体的置信度和权重 λnoobj=0.5,如果 让一个网络去学习n个类别的分类,那么必须让网络学习n+1个类别的分类。1是背景。背景占比比较多,所以调小它的权重。

对类别进行预测

判断是否有object的中心落入网格中,网格中包含有object的中心,就负责预测该object的类别概率。
在这里插入图片描述
直接预测类别-真实类别,这个方法不好,后面Yolo对它改进。

在这里插入图片描述
拥挤物体的中心,会跑到一个网格中去,那么一个网络就会预测两个物体。很不好。
对小物体检测也不好,小物体权重小。
对不规则物体(长宽比不正常)

voc数据集

以PASCAL VOC2017为例,它包含如下5个文件夹:

Annotations
JPEGImages
ImageSets
SegmentationClass
SegmentationObject

JPEGImages

PASCAL VOC提供的所有的图片,其中包括训练图片,测试图片。

Annotations

xml格式的标签文件,每个xml对应JPEGImage中的一张图片。各个目标的位置和类别。以(x,y)的格式不保存坐标点。
<annotation>  
    <folder>VOC2012</folder>                             
    <filename>2007_000392.jpg</filename> //文件名  
    <source>                             //图像来源(不重要)  
        <database>The VOC2007 Database</database>  
        <annotation>PASCAL VOC2007</annotation>  
        <image>flickr</image>  
    </source>  
    <size>                              //图像尺寸(长宽以及通道数)                        
        <width>500</width>  
        <height>332</height>  
        <depth>3</depth>  
    </size>  
    <segmented>1</segmented>            //是否用于分割(在图像物体识别中01无所谓)  
    <object>                            //检测到的物体  
        <name>horse</name>              //物体类别  
        <pose>Right</pose>              //拍摄角度  
        <truncated>0</truncated>        //是否被截断(0表示完整)  
        <difficult>0</difficult>        //目标是否难以识别(0表示容易识别)  
        <bndbox>                        //bounding-box(包含左下角和右上角xy坐标)  
            <xmin>100</xmin>  
            <ymin>96</ymin>  
            <xmax>355</xmax>  
            <ymax>324</ymax>  
        </bndbox>  
    </object>  
    <object>              //检测到多个物体  
        <name>person</name>  
        <pose>Unspecified</pose>  
        <truncated>0</truncated>  
        <difficult>0</difficult>  
        <bndbox>  
            <xmin>198</xmin>  
            <ymin>58</ymin>  
            <xmax>286</xmax>  
            <ymax>197</ymax>  
        </bndbox>  
    </object>  
</annotation> 

ImageSets

训练要用到的:

1.train.txt:训练集 (注意,均为图片名,没有后缀。以train.txt为例,分为两列,第一列为图像名如00012;第二列为-11-1表示目标在对应的图像没有出现,1则表示出现。)
2.val.txt:验证集
3.trainval.txt:训练和验证集
# 训练用不到的其它内容
Action:人的动作
Layout:人体的具体部位
Main: 图像物体识别的数据,总共20, 需要保证train val没有交集。
Segmentation:用于分割的数据
验证集(val)与测试集(test)是有区别的。
验证集:val是validation的简称,验证是否过拟合、以及用来调节训练参数等。
测试集:当模型训练完成后,用于检测模型的准确性。

https://blog.csdn.net/weixin_43570470/article/details/123659793

write_txt.py

'''
    读取XML文件信息
'''

import xml.etree.ElementTree as ET
import os
import random

VOC_CLASSES = (  # 定义所有的类名
    'aeroplane', 'bicycle', 'bird', 'boat',
    'bottle', 'bus', 'car', 'cat', 'chair',
    'cow', 'diningtable', 'dog', 'horse',
    'motorbike', 'person', 'pottedplant',
    'sheep', 'sofa', 'train', 'tvmonitor')   # 使用其他训练集需要更改
# 创建两个文件用于存放
train_set = open('voctrain.txt', 'w')
test_set = open('voctest.txt', 'w')
Annotations = 'VOCdevkit/VOC2017/Annotations'
# 返回Annotations文件下的所有文件名 (里面是所有xml文件,一个图片对应一个xml文件,是对图片的标注)
xml_files = os.listdir(Annotations)
# 打乱数据集
random.shuffle(xml_files)

train_num = int(len(xml_files) * 0.7) # 设置训练集个数
train_lists= xml_files[:train_num] # 训练集列表
test_lists= xml_files[train_num:] # 测试集列表

def parse_rec(filename): # 输入xml文件名
    tree = ET.parse(filename)
    objects = []
    # 读取xml文件中的 <object></object>下的内容
    '''
    <object>                            //检测到的物体  
        <name>horse</name>              //物体类别  
        <pose>Right</pose>              //拍摄角度  
        <truncated>0</truncated>        //是否被截断(0表示完整)  
        <difficult>0</difficult>        //目标是否难以识别(0表示容易识别)  
        <bndbox>                        //bounding-box(包含左下角和右上角xy坐标)  
            <xmin>100</xmin>  
            <ymin>96</ymin>  
            <xmax>355</xmax>  
            <ymax>324</ymax>  
        </bndbox>  
    </object>  

    '''
    for obj in tree.findall('object'):
        # 创建一个结构体
        obj_struct = {}
        # 获取到 difficult元素,如果值为1,表示目标难以识别,则跳过
        difficult = int(obj.findall('difficult').text)
        if difficult ==1:
            continue
        obj_struct['name'] = obj.find('name').text
        bbox = obj.find('bndbox')
        obj_struct['bbox'] = [int(float(bbox.find('xmin').text)),
                             int(float(bbox.find('ymin').text)),
                             int(float(bbox.find('xmax').text)),
                             int(float(bbox.find('ymax').text))]
        objects.append(obj_struct)

    return objects

def write_txt():
    count = 0
    for train_list in train_lists: # 生成训练集txt
        count += 1
        image_name = train_list.split('.')[0] + '.jpg'  # 图片文件名
        results = parse_rec(Annotations + train_list)
        if len(results) == 0:
            print(train_list)
            continue
        train_set.write(image_name)
        for result in results:
            class_name = result['name']
            bbox = result['bbox']
            class_name = VOC_CLASSES.index(class_name)
            train_set.write(' ' + str(bbox[0]) +
                            ' ' + str(bbox[1]) +
                            ' ' + str(bbox[2]) +
                            ' ' + str(bbox[3]) +
                            ' ' + str(class_name))
        train_set.write('\n')
    train_set.close()

    for test_list in test_lists:   # 生成测试集txt
        count += 1
        image_name = test_list.split('.')[0] + '.jpg'  # 图片文件名
        results = parse_rec(Annotations + test_list)
        if len(results) == 0:
            print(test_list)
            continue
        test_set.write(image_name)
        for result in results:
            class_name = result['name']
            bbox = result['bbox']
            class_name = VOC_CLASSES.index(class_name)
            test_set.write(' ' + str(bbox[0]) +
                            ' ' + str(bbox[1]) +
                            ' ' + str(bbox[2]) +
                            ' ' + str(bbox[3]) +
                            ' ' + str(class_name))
        test_set.write('\n')
    test_set.close()


if __name__ == '__main__':
    write_txt()

最终生成的训练集和数据集标签如下:
红框中前四个数是左上和右下角坐标,第五个数是类别。
一个图片可能否有多个目标,所有可能会有多个红框。
在这里插入图片描述

yoloData.py

encoder结果是7x7x30 = 7x7x(20+5+5)
20是这个数据集有20类,两个5是因为让这7x7个格子,每个格式产生两个候选框。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

computer_vision_chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值