当前位置:   article > 正文

YOLOv1目标检测算法——通俗易懂的解析

yolov1目标检测算法——通俗易懂的解析

YOLOv1目标检测算法

前言

目标检测=识别+定位
  RCNNFast RCNNFaster RCNN都是先提取候选框再送入检测网络,忽略了全局的信息。对R-CNN系列感兴趣的可以跳转到我的另一篇博文:R-CNN、Fast RCNN和Faster RCNN网络介绍YOLO是个单阶段的模型,直接在特征图上进行信息提取,是一个全局的信息。接下来我们分两步讲解YOLOv1模型:预测阶段和训练阶段。此外关于yolov1的代码解读,逐行逐句的分析你可以在我的另一篇博文在查看:YOLOv1代码分析——pytorch版保姆级教程
  如下图是两种框架,一种是普通的YOLOv1框架,一种是微型的。微型的速度更快,模型更小,但是准确率有所下降。
在这里插入图片描述

一.预测阶段

  我们先看预测阶段,输入网络的是一个 448 × 448 448\times448 448×448的图像,输出的是一个 7 × 7 × 30 7\times7\times30 7×7×30的张量,这个张量里面就包含了我们要检测的类别,坐标信息,解析这些参数就能得到目标检测结果。那么这个张量为什么是 7 × 7 × 30 7\times7\times30 7×7×30呢?看下面的图,首先网络把输入的图像经过一些列卷积等操作划分成了 s × s s\times s s×s个grid, s = 7 s=7 s=7。其中,每个grid需要预测2个bounding box(预测框),这两个框的大小宽高都不确定,可能很大,很宽,也可能很小,很窄。只要这个bounding box的中心点落在这个grid里面就说明这个bounding box是由这个grid所预测的。每个bounding box包含5个参数( x , y , h , w , c x,y,h,w,c xyhwc),其中 x , y , h , w , c x,y,h,w,c xyhwc分别表示预测框的中心点横坐标,纵坐标,高度和宽度,以及bounding box里面包含物体的概率。在下图的展示中的最上面的一个图,框的线越粗表示置信度越高,越细表示置信度越低。总共 49 49 49个grid生成 98 98 98bounding box。同时每个grid还能生成所有类别的条件概率,什么意思呢?上面不是讲了每个bounding box里面有五个参数吗,其中一个 c c c置信度参数,表示包含物体的概率,那么每个grid还能生成所有类别的条件概率就是在这个grid包含物体的条件下,是哪一个类别的概率(是猫还是狗),这样就获得了20各类别的各自概率,就对应这30维向量的后面20个参数。如下图的最下面的图所示,每个类别的概率用不同的颜色标出来了,每个grid之只能预测一个概率最高的类别(经过后处理和NMS得到)。通过上面的分析我们可以得出30是怎么来的,他就是 2 × 5 ( x , y , h , w , c ) + 20 2\times5(x,y,h,w,c)+20 2×5(xyhwc)+20再次强调后面20维的向量是在包含物体的条件下计算出来的某个类别的概率。简单概括YOLOv1就是输入是一个 448 × 448 448\times448 448×448的图像,输出是一个 7 × 7 × ( 2 × 5 + 20 ) = 1470 7\times7\times(2\times5+20)=1470 7×7×(2×5+20)=1470个数字。
  这个地方你应该会有疑问,一个grid预测两个bounding box,而一个grid只预测一个类别,那么用哪个bounding box来预测呢,还有如果我预测的两个bounding box一个包含物体,一个不包含物体,或者两个bounding box都包含物体,那么我应该用哪个bounding box里面的 c c c来计算是哪个类别的物体呢?这个问题我们下面讲后处理过程的时候再讲。

在这里插入图片描述
在这里插入图片描述
  如上图所示,用颜色表示不同的类别,粗细表示置信度就得到了上面的中间图的结果,经过过滤低置信度的框,NMS处理就得到得了最终的目标检测预测结果。
  注意:以上分析的仅仅是预测阶段,预测阶段,预测阶段
  接下来我们再来讲下YOLOv1预测阶段的后处理过程置信度过滤+非极大值抑制(NMS)
  后处理时做什么呢?就是把刚才预测的杂乱无章的98个bounding box进行筛选过滤,重复的预测框只保留一个,获得最终目标检测的结果。主要包含把低置信度的框去掉,重复的框去掉,只保留一个。如下图所示,后处理就是把 7 × 7 × 30 7\times7\times30 7×7×30的张量变成最后的检测结果,中间过程先看成黑盒子。
NMS处理过程:

  1. 找到置信度最大的框,该框是目标的概率是最大的,得到第1个框,注意这里是按照框的置信度排序来寻找的;
  2. 依次计算其他相同类别框与第1个框的重合度(IOU值),如果大于一定阈值,抑制掉;
  3. 剩下的框中,同样找置信度最大的框,为第2个框,抑制掉重合的框;
  4. 反复执行上述步骤,直到剩下最后一个框,此亦为目标框。

NMS处理详细介绍可以参考这个博文:NMS——非极大值抑制
在这里插入图片描述

  现在我们来看下这 7 × 7 × 30 7\times7\times30 7×7×30维张量是如何变成最后的预测结果的,如下图所示,我们先取出其中的一个grid来看,每个grid包含30个数字 5 + 5 + 20 5+5+20 5+5+20构成,每个grid预测两个bounding box,后面的20表示该grid对20个类别的条件类别概率,就是假设该grid预测物体的条件下,20个类别的概率分别是多少。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

        我们上面讲的都是假设该grid预测物体的条件下,20个类别的概率分别是多少。下面我们把这20个条件概率把他单独拿出来跟bounding box的置信度相乘,即条件概率乘上条件本身发生的概率就得到了全概率,就是说该bounding box包含物体的概率乘上在他包含物体的条件下各个类别的概率,那么就得到了他真正是属于哪个类别的概率。那么每个grid都能获得2个20维的全概率,如下图的黄色竖条,分别表示两个预测框。总共98个bounding box一共获得98个20维的全概率向量。图中的黄色竖条表示他20个类别的概率分别是多少。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
        通过上面的处理,我们获得了下面的乱图:就是上面的98个黄色竖条可视化出来的结果,粗细表示置信度(包含物体的概率),颜色表示类别。
在这里插入图片描述
  我们如何通过上面的乱图获得最后的目标检测结果呢?
  答:按照类别进行非极大值抑制。
  我们先假设狗是20个类别中的第一个类别。设置一个阈值,先把小于设定阈值的给抹零,然后按照狗概率高低进行排序,然后在对排序后的结果进行非极大值抑制。经过非极大值抑制之后就获得了一幅图上所有狗这一目标的检测结果,对其他的类别做同样的操作获得检测结果。
在这里插入图片描述
在这里插入图片描述
  经过上面的操作就获得了一个包含很多零的稀疏矩阵,如果某个向量里面含有非零值,我们就把他的类别拎出来,得分也拎出来,就得到得了最终的目标检测结果。
在这里插入图片描述在这里插入图片描述
  注意:以上后处理仅仅是预测阶段,预测阶段,预测阶段,训练阶段不需要NMS处理,因为在训练阶段,不管他是不是负责预测物体,他在损失函数里面都占有一席之地。只是在训练出来模型之后进行待测图片预测之后才需要进行后处理,把低置信度的框过滤掉,再把重复识别物体的重复框值挑出一个。训练和预测一定要分开理解。
  在实际代码中,我们在进行非极大值抑制之前实际上是先滤除每个grid cell中置信度比较小的那个框,只对最终剩余的49个框按照类别进行非极大值抑制。有疑问的可以跳转到我对yolov1的代码讲解博文看下源码分析的predict.py部分代码的Decode函数。下面附上部分代码,完整源码请跳转到:YOLOv1代码分析——pytorch版保姆级教程

   # 接受的result的形状为1*7*7*30
    def Decode(self, result):
        # 去掉batch维度
        result = result.squeeze()
        # 提取置信度信息,并在最后一个维度增加一维,跟后面匹配result[:, :, 4]的形状为7*7,最后变为7*7*1
        grid_ceil1 = result[:, :, 4].unsqueeze(2)
        # 同上
        grid_ceil2 = result[:, :, 9].unsqueeze(2)
        # 两个置信度信息按照维度2拼接
        grid_ceil_con = torch.cat((grid_ceil1, grid_ceil2), 2)
        # 按照第二个维度进行最大值求取,一个grid ceil两个bbox,两个confidence,也就是找置信度比较大的那个,形状都是7*7
        grid_ceil_con, grid_ceil_index = grid_ceil_con.max(2)
        # 找出一个gird cell中预测类别最大的物体的索引和预测条件类别概率
        class_p, class_index = result[:, :, 10:].max(2)
        # 计算出物体的真实概率,类别最大的物体乘上置信度比较大的那个框得到最终的真实物体类别概率
        class_confidence = class_p * grid_ceil_con
        # 定义一个张量,记录位置信息
        bbox_info = torch.zeros(7, 7, 6)
        for i in range(0, 7):
            for j in range(0, 7):
                # 获取置信度比较大的索引位置
                bbox_index = grid_ceil_index[i, j]
                # 把置信度比较大的那个框的位置信息保存到bbox_info中,另一个直接抛弃
                bbox_info[i, j, :5] = result[i, j, (bbox_index * 5):(bbox_index+1) * 5]
        # 真实目标概率
        bbox_info[:, :, 4] = class_confidence
        # 类别信息
        bbox_info[:, :, 5] = class_index
        # 返回预测的结果,7*7*6    6 = bbox4个信息+类别概率+类别代号
        return bbox_info
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

二.训练阶段

  下面我们开始介绍训练阶段!训练阶段!训练阶段!
  刚才我们讲了YOLOv1的预测阶段,即模型训练好之后送入待测图片获得目标检测结果的过程。下面我们介绍下YOLOv1模型的训练阶段。这也是理解YOLOv1模型的关键,下面我尽量用通俗的语言来讲解怎么训练YOLOv1模型。
  我们知道深度学习(监督学习)都是通过梯度下降和反向传播的方法迭代的去微调网络神经元的权重来使得损失函数最小化的过程,而目标检测就是一个典型的监督学习问题,在训练集上给定的有输入图像,又有输入图像的标签,即在输入的图像上面已经标记好的ground truth,我们要设计的算法就是尽可能的去拟合这个人工标记的框,让损失函数尽可能最小,损失函数我们后面再介绍。
  以下图为例,如下图是一个人工标注的框(绿色的框),人工标注的框的中心落点在哪个grid, 就由哪个grid预测出的两个bounding box中的一个去负责拟合这个绿框,并且这个grid输出的类别也应该是这个ground truth的类别。也就是说,前面不是讲过每个grid要获得20个类别的条件类别概率吗,那么这20个类别的概率乘以每个框的置信度获得这个全概率。以图中的狗为例,狗的概率应该是最大的,就是这个grid的代表类别,每个grid只能预测出一个类别,就是概率最大的类别。也就是说每个grid只能预测出一个物体,即YOLOv1最多预测出 7 × 7 = 49 7\times7=49 7×7=49个物体(个人理解:这里如果按照上面的98个框进行非极大值抑制则最多可以预测98个目标,如有不对,请各位小伙伴指正)。
在这里插入图片描述

  还记得前面的紫色字体提出的问题吗?训练阶段,每个grid预测两个bounding box,那么究竟由哪个bounding box来负责拟合这个ground truth呢?(注意:预测阶段是通过置信度过滤和NMS处理,和训练阶段的处理方法不一样,不要搞混了,这一点也是很多小伙伴容易搞混的地方)。训练阶段应该是由那个和ground truth交并比(IoU)最大的去负责拟合ground truth。如下图就是外围大的框去负责拟合逼近ground truth。另外一个框直接被抛弃掉了,什么都不用做,只需要让他的object尽量小就行了,在损失函数中会体现出来。所以我们损失函数的设计就是让负责预测物体的框尽可能的拟合ground truth
在这里插入图片描述
  上面讲的是有ground truth中心点落在的grid,那么如果没有ground truth落在的grid怎么处理呢?这时候grid所预测的两个框都被直接抛弃了,这两框只需要让置信度越小越好,置信度越接近与0越好,其他什么都不要做。
  注意:以上讲的bounding box无论多大,多宽,他们的中心点都落在所属的grid里面。
在这里插入图片描述

三.损失函数

  通过上面的介绍,我们就能构建YOLOv1的损失函数了。
在这里插入图片描述
  观察上面的损失函数,总共有五项(都是一个回归问题)。

  • 第一行表示负责检测物体的bounding box中心点定位误差,要和ground truth尽可能拟合, x x x带上标的是标注值,不带上标的是预测值。
  • 第二行表示负责检测物体的bounding box的宽高定位误差,加根号是为了使得对小框的误差更敏感,如下图所示。
  • 第三行是负责检测物体的bounding box的置信度误差,标签值实际上就是bounding boxground truth I o U IoU IoU。预测的置信度应该是越跟 I o U IoU IoU越接近越好。
  • 第四行是不负责检测物体的bounding box的置信度误差,就是所有被抛弃的bounding box。让他们预测出来的置信度最好都是0,标签值就是0。这个被抛弃的bounding box包含两类bounding box,一类是负责检测物体的grid预测被抛弃的bounding box,一类是不负责检测物体的grid预测的bounding box
  • 第五行是负责检测物体的grid的分类误差。上面的 λ c o o r d \lambda_{coord} λcoord λ n o o b j \lambda_{noobj} λnoobj在原文中分别赋值50.5,表示对真正负责检测物体的bounding box和置信度误差给与更高的权重,不负责检测物体的bounding box和置信度误差给与比较低的权重。
    如果一个bounding box负责检测物体,那么他所在的那个grid也负责检测物体,如果一个bounding box负责检测物体,那么另外一个bounding box就不负责检测物体。也就是在上面的黄框,红框和蓝框所对应的变量,黄框如果为1,蓝框也为1,红框和绿框一个为1,,另一个则为0。下标 i i i表示 s × s s\times s s×s个grid, j j j表示 j j jbounding box,咋YOLOv1里面 i = 49 i=49 i=49 j = 2 j=2 j=2

总结一下损失函数,需要计算的是由四部分:

  • 没有ground truth落入的grid cell的置信度损失
  • 有ground truth落入的grid cell的负责预测物体的位置损失和置信度损失
  • 有ground truth落入的grid cell的不负责预测物体的置信度损失
  • 有ground truth落入的grid cell的类别损失

yolov1的标签形式:

先创建一个全0的矩阵 7 × 7 × 30 7 \times7 \times30 7×7×30的lable,label里面记录的是每个标注框(包含物体)的宽高信息以及中心点坐标相对于所在网格左上角的偏移量,置信度标签值1,类别信息,两份标注框的信息一样的,其他的位置全为0。那么在训练网络的时候需要生成的数据格式也是这样的,不然不好计算损失。

需要计算以下几个损失函数(对应着代码分析看):

  1. 不包含物体的每个grid cell的两个置信度损失
  2. 包含目标每个的grid cell的置信度损失(两个框,一个负责预测物体,一个不负责预测物体的损失都要计算),在计算不包含物体的这个标注框置信度损失的时候需要把标签对应的这个框置信度设置为0,也就是让这个不负责预测物体的框置信度越为0越好。
  3. 包含目标的grid cell中负责预测物体的框的位置损失
  4. 负责预测物体所在grid cell的类别损失
    在这里插入图片描述
      以上我们从测试和训练两个阶段分别介绍了YOLOv1的理论部分,接下来的YOLOv2模型等我的下一篇文章吧(附一个总结):
    YOLOv2目标检测算法——通俗易懂的解析
    YOLOv3目标检测算法——通俗易懂的解析
    YOLOv4目标检测算法——通俗易懂的解析
    YOLOv5目标检测算法——通俗易懂的解析
    欢迎各位大佬批评指正!
声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号