目录
YOLOv2目标检测算法
前沿
前面我们讲过了 YOLOv1
目标检测算法,不了解的小伙伴可以参考一下博文:
这篇文章我们仍然通俗的介绍下 YOLOv2
, YOLOv2
又叫做 YOLO9000
,顾名思义,就是能够检测出9000种类别(有点夸张了,实际上根本做不到,不过作者提到的思路倒是很值得借鉴)。
YOLOv2
对 YOLOv1
做了很多的改进,如加了 BN
, anchor
,多尺度训练等 tricks
。 YOLOv2
是 YOLO
系列承上启下的算法,可以说 YOLO
算法真正的开始算是从 YOLOv2
开始的,当然这也不是说 YOLOv1
你不用了解了,了解 YOLOv1
对学习后面的 YOLO
系列具有很大的帮助。
YOLOv1
属于 YOLO
系类的最初版本,存在着很多的问题,如 recall比较低、 准确度差、 定位能力差、 检测小目标和 密集型目标性能很差等问题。我们看一下 YOLOv1
跟他那个时代已经存在的目标检测模型进行比较。
上面讲了
YOLOv1
存在四个问题, recall比较低、 准确度差、 定位能力差、 检测小目标和 密集型目标性能很差,于是 YOLOv2
就尝试着对 YOLOv1
进行改进,尝试解决这些问题。下面我们正式进入 YOLOv2
的讲解。
; 一.YOLOv2的改进
YOLOv2
采取了各种各样的手段对 YOLOv1
做改进:
- BN
- High Resolution Classifier(高分辨率的分类器)
- Anchor(通过聚类得到)
- Dimension Cluster
- Direct location prediction
- Fine-Grained Feature
- Multi-Scale Training
下面我们一个一个的来看上面的 tricks
分别都做了什么事情
1.1.BN
BN就是 Batch Normalization
,批标准化, BN
做了什么事情呢?实际上他就是对神经元的输出进行一个归一化的操作,把每个神经元的输出减去均值再除以标准差变成一个以 0
为均值, 1
为标准差的一个分布的过程。为什么要进行这个 BN
操作呢,不做行不行呢? 我们知道很多激活函数如: sigmoid
, tanh
激活函数,他们在 0
附近是非饱和区(关于什么是饱和区什么是非饱和区请参考:激活函数中的硬饱和,软饱和,左饱和和右饱和)。如果神经元的输出太大或者太小的话就会陷入激活函数的饱和区,饱和区就意味着梯度消失(导数为 0
),导致模型难以训练,所以我们通过 BN
强行的把神经元的输出集中到 0
附近。 BN
是包含测试阶段和训练阶段的,尤其要注意测试阶段是怎么处理的,用不好反而适得其反,具体可以参考:Batch Normalization详解以及pytorch实验以及我的一篇博文关于 BN
和 LN
的简单介绍:Batchnorm和Layernorm的区别。通过上面的对 BN
层的介绍,我们不难得出 BN
层一般放在输出特征层的后面,激活函数的前面。总结一下 BN
层有哪些好处:
- 加快模型训练的收敛速度
- 改善梯度,远离饱和区
- 可以使用大的学习率,使得模型对初始化不敏感(如果没有使用
BN
层就有可能导致神经元的输出都在非饱和区,所以经过BN之后对初始化的敏感度就降低了。) - 起到正则化的作用,防止过拟合甚至可以起到
dropout
层。 *这里留一个问题:BN和dropout都可以防止过拟合,为什么他们两个一块用不行?
具体可参考:BN和Dropout同时使用的问题
; 1.2.High Resolution Classifier
一般模型的训练都是以224 × 224 224\times224 224 ×224尺寸大小在ImageNet上进行训练的,而我们的 YOLOv1
模型最后的输入图像时448 × 448 448\times448 448 ×448,如果一个模型原来是在小尺寸上进行训练的现在又在大尺寸上进行训练,那么网络就要学会切换这两种分辨率会带来模型性能的下降。 YOLOv2
怎么解决这个问题呢?作者用了最简单粗暴方法,直接在分类图像数据集上以448 × 448 448\times448 448 ×448的尺寸进行骨干网络的训练,让网络适应大分辨率。实际上作者训练的时候是只在分类图像训练骨干网络的最后10个 eopchs
切换到448 × 448 448\times448 448 ×448,节省训练时间。通过使用 High Resolution Classifier
,模型提高了3.5%mAP。
1.3.anchor
anchor
是 YOLOv2
的核心。 YOLOv3
, YOLOv4
, YOLOv5
也是用 anchor
机制,近期的一些目标检测算法开始使用 anchor free
机制。下面我们来讲下什么是 anchor
机制。我们在 YOLOv1
里面没有使用 anchor
机制,直接使用两个 bounding box
,谁跟 ground truth
的 I o U IoU I o U大,谁负责去拟合这个 ground truth
。这个时候可能会遇到一个问题,这两个 bounding box
是不受任何约束的,想怎么变怎么变,想多大就多大,想多宽就多宽,不受约束,你觉得让这样的完全不受限制的框去拟合 ground truth
好吗,他能够很准确的预测出待检测物体吗?我们不能说他不好,显然很难训练,没有给他一个限定范围,完全让他俩任意生长,这样做显然是不好的。于是在 YOLOv2
里面就引入了 anchor
机制,我们不让这两个 bounding box
随机生长了,给他们一个限制,加个紧箍咒,给他们一个参考,这个参考就是 anchor
,又叫做 先验框。如上图所示以两个 anchor
为例,一个瘦高,一个矮胖分别负责预测不同形状的物体。这样的话 bounding box
就不会随机生长了,就会有了自己的使命了,知道自己应该预测什么样的物体了。所以这个时候每个预测框只需要预测出它相较于这个 anchor
的偏移量就行了。
这个时候你又该问了,偏移量又是个什么鬼?不急,带我娓娓道来,上面我们知道了什么是 anchor
,所谓的 anchor
就是预先设置好的一个限制框,预测框只能根据这个限制框进行预测。下面我们讲下偏移量。上图就是 YOLOv2
给出的改进,通过预测偏移量代替直接预测 bounding box
,你看到这可能一脸懵逼,这么多符号都是啥意思?我来解释下其中的含义:
- b x , b y , b w , b h b_{x},b_{y},b_{w},b_{h}b x ,b y ,b w ,b h :模型最终得到的检测结果。
- t x , t y , t w , t h t_{x},t_{y},t_{w},t_{h}t x ,t y ,t w ,t h :模型要预测的结果。
- c x , c y c_{x},c_{y}c x ,c y :
grid
的左上角坐标。 - p w , p h p_{w},p_{h}p w ,p h :
anchor
的宽和高,因为anchor
是人为设定的,所以这两个值是固定的。
有没有发现上面少了一项,置信度。置信度的标签是P r ( o b j e c t ) ∗ I o U ( b , o b j e c t ) = σ ( t o ) Pr(object)*IoU(b,object)=\sigma(t_{o})P r (o bj ec t )∗I o U (b ,o bj ec t )=σ(t o ),这地方和 YOLOv1
是一样的,只不过这地方的置信度经过了一个归一化操作。
通过上面 anchor
机制,我们从直接预测 bounding box
改为预测一个偏移量,这个偏移量是什么呢,他是不是就是一个基于 anchor
框的宽和高和 grid
的先验位置的偏移量,得到最终目标的位置,这种方法也叫作 location prediction
。注意,因为物体的宽高可能很大,所以并没有对t w , t h t_{w},t_{h}t w ,t h 进行限制。只是把t x , t y t_{x},t_{y}t x ,t y 中心坐标限制在了 grid
里面了(这里有个小小的 bug
,我们先不讲,可以自己先想一下, YOLOv4
会改进)。刚才我们说了t x , t y , t w , t h t_{x},t_{y},t_{w},t_{h}t x ,t y ,t w ,t h 是模型要预测的值,那么这几个值应该怎么计算呢?
对 grid cell
进行归一化:
观察上面的图,我们可以计算出:
t x = l o g [ 0.07 1 − 0.07 ] = − 1.12 t y = l o g [ 0.116 1 − 0.116 ] = − 0.882 t w = l o g ( 240 280 ) = − 0.067 t h = l o g ( 380 230 ) = 0.218 t_{x}=log[\frac{0.07}{1-0.07}]=-1.12\ t_{y}=log[\frac{0.116}{1-0.116}]=-0.882\ t_{w}=log(\frac{240}{280})=-0.067\ t_{h}=log(\frac{380}{230})=0.218 t x =l o g [1 −0.07 0.07 ]=−1.12 t y =l o g [1 −0.116 0.116 ]=−0.882 t w =l o g (280 240 )=−0.067 t h =l o g (230 380 )=0.218
关于上面的 0.07
, 0.116
这些值是怎么来的,我知道你肯定有疑问。下面我们来看下。
首先要明确一下,我们要对每个 grid cell
都进行归一化操作,也就是每个计算都是针对每个 grid cell
来的。我们知道b x = s i g m o i d ( t x ) + c x b_{x}=sigmoid(t_{x})+c_{x}b x =s i g m o i d (t x )+c x ,又因为s i g m o i d ( t x ) = 1 1 + e − t x sigmoid(t_{x})=\frac{1}{1+e^{-t_{x}}}s i g m o i d (t x )=1 +e −t x 1 ,从而可以推出t x = l o g ( b x − c x 1 − ( b x − c x ) ) t_{x}=log(\frac{b_{x}-c_{x}}{1-(b_{x}-c_{x})})t x =l o g (1 −(b x −c x )b x −c x ),其他坐标同理,代入 anchor
框的宽高、 grid
的坐标和预测框的宽高即可计算出t x , t y , t w , t h t_{x},t_{y},t_{w},t_{h}t x ,t y ,t w ,t h 四个值。
通过上面计算之后,要预测的值就变成了t x , t y , t w , t h = − 1.12 , − 0.882 , − 0.063 , 0.132 t_{x},t_{y},t_{w},t_{h}=-1.12,-0.882,-0.063,0.132 t x ,t y ,t w ,t h =−1.12 ,−0.882 ,−0.063 ,0.132这是一个偏移量,这个时候再预测是不是比之前直接预测 bounding box
容易多了。
理解这个anchor的核心就是,先验框是事先设定好的,但是呢,我只需要设定的anchor的宽高信息,中心点坐标我自己来预测,因为这个中心点坐标已经被限制在了这个anchor所在的grid cell中了,再怎么预测都不会逃出这个anchor所在的grid cell。
通过上面的讲解,我相信你对 anchor
和偏移量已经有了了解了,下面我们回归正题,继续回到 YOLOv2
。在 实际训练和预测的时候, YOLOv2
是把整个图像给划分成13 × 13 13\times13 13 ×13个 grid
,每个 grid
有 5
个 anchor
,就是事先指定了5种大小不同的先验框,每个 anchor
都对应一个预测框,而这个预测框只需要去负责预测输出他相对于他所在的 anchor
偏移量就行了。如下图所示,如果要预测小女孩,人工标注框的中心点落在哪个grid里面就应该由哪个 grid
产生的五个 anchor
中与 ground truth
的I o U IoU I o U最大的那个 anchor
去负责拟合 ground truth
。而这个 anchor
对应的预测框只需要预测他相较于这个 anchor
的偏移量就行了,意思就是在这个 anchor
的基础上进行预测。
我们知道每个
grid
都有 5
个 anchor
, ground truth
落在哪个 grid
就由哪个 grid
产生的五个 anchor
中的一个来负责预测预测这个 ground truth
,总共5个 anchor
,那么由哪一个 anchor
去负责预测呢?由与 anchor
的I o U IoU I o U最大的那个去负责预测。 这个 anchor
对应的预测框只需要输出它相比于它所在的 anchor
的偏移量就行了。我们上面有讲到
YOLOv2
最后输出的网格大小是13 × 13 13\times13 13 ×13,这个网格的长宽都是奇数,是为了便于预测物体的只有一个中心的 grid
,如果是偶数的话就会有好几个。通过上面的介绍可以知道
YOLOv2
模型输出的结构也改了,如下图所示,在 YOLOv1
里面没有用 anchor
,而是直接划分成7 × 7 7\times7 7 ×7个 grid
,每个 grid
输出两个 bounding box
,每个 bounding box
有两个未知参数和一个置信度,以及每个 grid
还预测出了 20
个类别的条件类别概率。在 YOLOv2
里面模型就变得稍微复杂一些, YOLOv1
里面的类别是由 grid
负责,在 YOLOv2
里面类别变成由 anchor
负责了,每个 grid
产生 5
个 anchor
,每个 anchor
除了产生 4
个定位参数和一个置信度参数之外还有 20
个类别的条件类别概率,所以在 YOLOv2
里面每个 anchor
都会产生 25
个数,总共输出5 × 25 = 125 5\times25=125 5 ×25 =125个数。即 YOLOv2
总共输出13 × 13 × 125 = 21125 13\times13\times125=21125 13 ×13 ×125 =21125个数。最终的目标检测结果就是从这21125个数里面提取出来的。下图可视化了
YOLOv2
的 anchor
的产生过程。上面每个
grid
将产生 5
个 anchor
,你有没有疑问,为什么一定是5个,我产生10个不行吗?当然可以,只不过没必要,太浪费,导致网络参数太多。作者通过聚类的方法对 PASCAL VOC
和 COCO
数据集的长宽比进行了聚类操作,黑色框表示VOC数据集,紫色框表示COCO数据集。通过观察左图可以发现,聚类的长宽比种类越多,所覆盖的I o U IoU I o U也越大,但是模型也会变的更复杂,作者在此取了个折中,直接取值为5,兼顾了准确度效率。长宽度如下图的有图所示。通过聚类的方法确定 anchor
数目。比双阶段的目标检测,如 Faster RCNN
手动去选择 anchor
要科学很多,聚类产生 anchor
一直到 YOLOV5
还在用,后面就开始使用 anchor free
了。; 1.4.Fine-Grained Features(细粒度特征)
总结一句话就是长宽变为原来的一半,通道数变为原来的4倍。如下图的展示了一个可视化,就是网络中的Pass Throuth层。
1.5.Multi-Scale Training
在模型训练期把不同的图像大小输入到模型,这个地方你还不会有疑问,输入图像大小变了,我的网络模型不用调整吗?作者肯定考虑了这个问题,他用了一个全局平均池化层,对每个通道求平均来替换全连接层。如下图所示,速度跟图像尺寸成反比,精度跟输入图像尺寸成正比。
最后我们再来看张图,作者加了这些tricks到底有没有用。
上面我们讲的都是针对
YOLOv2
为什么会更好,下面我们再来介绍下 YOLOv2
为什么会更快。作者换了骨干网络,我们知道 YOLOv1
作者使用的是 VGG
骨干网络,很臃肿,在 YOLOv2
里面换成了作者自己设计 Darknet19
。; 二.损失函数
下面我们讲一下 YOLOv2
的损失函数。
在
YOLOv2
论文里面并没有提到上面的损失函数,网上大神根据 YOLOv2
的代码整理出来的。首先,我们看下损失函数的最外层,总共有三次求和,分别是遍历所有的宽,高和
anchor
的个数。然后是这个求和符号后面总共有三项,每项前面还有一个条件系数,这个系数非0即1。系数后面又有个λ \lambda λ,表示权重:
- 第一项(第一行)是预测框或者说
anchor
与ground truth
的I o U IoU I o U是否小于0.6,代码中给的阈值Thresh
为0.6,他们是被抛弃的框,并不负责预测物体,他们的置信度越为0越好,括号里面为什么会有个负号?他表示是( 0 − b i j k o ) 2 (0-b_{ijk}^o)^2 (0 −b ijk o )2,0就是那些被抛弃的框的标签,b i j k o b_{ijk}^o b ijk o 表示预测框置信度。怎么计算I o U IoU I o U? 可以参考我的另一篇博文,I o U IoU I o U的进化之路。IoU、GIoU、DIoU、CIoU四种损失函数总结。 - 第二项(第二行)是否是前12800次迭代,即是否是模型训练的早期。在模型训练的早期遍历四个定位参数,要让anchor的四个参数与预测框的四个参数尽可能的重合。就是让模型在训练的早期让五个
anchor
能够各司其职,尽快找到自己负责的物体,让模型能够更加的稳定。 - 第三项(第三行及后面的)是这个
anchor
负责检测物体,一个grid
产生5个anchor
,这个负责检测物体的anchor
就是5个anchor
里面与ground truth
最大的那个anchor
,也就是一个ground truth
只分配给一个anchor
。这个地方你会不会有疑问,负责预测物体的grid
的里面的不是与I o U IoU I o U最大但是又与ground truth
的I o U IoU I o U大于0.6的那些anchor
怎么处理。或者你也可以这样理解,总共三类anchor
,第一类是与gouund truth
的I o U IoU I o U最大的anchor
,用来负责预测物体,第二类是与gouund truth
的I o U IoU I o U小于0.6的,也就是损失函数第一项,第三类是与gouund truth
的I o U IoU I o U大于0.6但又不是与ground truth
的I o U IoU I o U最大的那些anchor
怎么处理?作者怎么处理的呢?他是直接把第三类的给抛弃了。对于负责预测物体的这个anchor
总共要计算三项,分别是定位误差、置信度误差、分类误差。定位误差就是计算ground truth
的位置和预测框的位置的误差。置信度误差就是计算anchor
与标注框ground truth
的I o U IoU I o U与预测框置信度得误差,这里anchor
与I o U IoU I o U的标签是置信度标签,YOLOv1
也是这样设计的。而分类误差就是计算标注框类别和预测框类别的误差,并且遍历所有类别。
三.检测更多类别
损失函数我们讲过了,下面我们来继续接着讲作者是怎么加测更多类别的。
不是重点,简单了解即可,我们知道 COCO
也不过就80个类别,作者是怎么做到能够检测出9000各类别的,是在夸大其词还是真的能够做到检测9000个类别?
YOLOv2
作者是这样做的,他把 COCO
数据集和 ImageNet
数据集进行联合训练,我们知道 ImageNet
数据集有两万多个类别,但是呢 ImageNet
又没有定位标签,只有分类标签,并且 ImageNet
的分类细粒度还很高,比如狗这个类别,他是什么狗,是阿拉斯加还是二哈,还是金毛,拉布拉多等。于是作者把他两个联合起来训练让模型能够学习到更多细粒度的特征。主要就是让目标检测数据集学定位,分类数据集学识别。这个地方简单了解即可,这里不做过多介绍。
至此,我们的YOLOv2模型理论部分也基本上介绍完了, YOLOv3
的理论部分我们下篇文章再讲,在此,附一个 YOLO
系列的文章链接:
YOLOv1目标检测算法——通俗易懂的解析
YOLOv3目标检测算法——通俗易懂的解析
YOLOv4目标检测算法——通俗易懂的解析
YOLOv5目标检测算法——通俗易懂的解析
欢迎各位大佬批评指正。
Original: https://blog.csdn.net/qq_38683460/article/details/126333243
Author: I松风水月
Title: YOLOv2目标检测算法——通俗易懂的解析
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/682253/
转载文章受原作者版权保护。转载请注明原作者出处!