由于在做目标检测任务,所以打算好好学完YOLO系列算法,前段时间做YOLOV1的笔记以及代码介绍,对于YOLO算法基本的思想有了一个较为清楚的了解:主要就是把图片分为 S ∗ S S*S S∗S的网格,每个网格预测B个bbox,每个bbox包含五个信息,分别为四个坐标信息和一个包含物体的置信度,所以每个gridcell输出 B ∗ 5 + 20 B*5+20 B∗5+20个信息,其中20为在该gridcell包含物体的条件下,物体属于每一类的条件概率。
YOLOV1的训练过程:将groundtruth处理为 S × S × ( B ∗ 5 + 20 ) S×S×(B*5+20) S×S×(B∗5+20)的向量作为target,将图片输入网络生成 S × S × ( B ∗ 5 + 20 ) S×S×(B*5+20) S×S×(B∗5+20)的prediction,然后计算LOSS函数,并对网络进行优化。
LOSS函数的计算:有两大类,分别为负责预测物体的gridcell和不负责预测物体的gridcell,对于负责预测物体的gridcell,又分为负责预测物体的bbox和不负责预测物体的bbox,根据bbox和groundtruth的IOU大小来选择由哪个bbox负责预测。(groundtruth中心点落在哪一个gridcell,就由该gridcell里与GT的IOU较大的那一个bbox负责预测该物体。)
YOLOV2在YOLOV1的基础上做了一些改进,以B站up主:同济子豪兄 YOLOV2教学视频为基础,读论文并做笔记。希望可以对YOLOV2有一个比较透彻的了解。
论文链接:YOLO9000:Better,Faster,Stronger
YOLOV2对于YOLOV1的改进重点在于提高定位精度、提高召回率上。
本论文将要介绍一种实时的、可以检测超过9000个类别的目标检测算法:YOLO9000。YOLO9000相比于YOLOV1做出了一些改进,首先是通过一种新的多尺度训练方法,YOLOV2可以在不同大小的图片上训练,这为算法在精度与速度之间提供了一种权衡。
FPS:指画面每秒传输帧数,是测量、显示动态视频的信息数量,每秒钟帧数越多,所显示的动作就会越流畅。通常,要避免动作不流畅的最低FPS是30
当将YOLOV2算法的速度提高到67FPS,即每秒67帧时,算法在VOC2007数据集上的MAP为76.8;当速度降低到40FPS时,map提高到78.6。由此可见,当牺牲算法速度而提高算法map后,算法map与FasterR-cnn、resnet和SSD基本相同,但是仍然速度很快,可以达到每秒40帧的实时效果。
之后作者提出了一种可以同时在目标检测和分类任务上进行训练的方法,在COCO数据集和Imagenet数据集上进行训练。这种联合训练的方法使YOLO9000可以对未标注过的物体类别数据进行预测。
对于目标检测任务来说,统一的目标应该是快速且准确,同时能够识别出各种各样的目标,对万物进行检测。自从神经网络出现后,目标检测框架无论在速度还是精度上都有了很大的提升,但是当数据量比较少时,大多数的框架依然具有局限性。
目标检测任务相比于其他任务如分类任务具有局限性,主要表现在数据量的多少上。对于目标检测任务来说,一张训练图片需要人工标注出物体框并且需要给出类别,这种标注任务费时费力,不像其他的任务如分类任务那样简单,因此这注定了目标检测任务的数据集在短时间内不会有分类任务数据集那样大的规模。
对于目标检测框架的这种局限性,作者在论文中所提出的办法为:利用大量的分类数据集,让目标检测任务不再仅仅局限于已经标注过的数据集,对于未标注过的类别数据集也可以进行有效的检测。
在笔记中,对于作者提出的扩大目标检测框架检测范围的办法不重点叙述,重点在于anchorbox以及其他的改进上。
相比于其他的目标检测算法如FasterR-CNN,YOLOV1在物体框的定位上误差较大,召回率比较低。因此对于YOLO的改进,重点在提高算法的召回率、降低定位误差,同时保持算法原有的分类精度。
计算机视觉近年来倾向于训练更大更深的网络,使得模型性能更好,但是在YOLOV2中,我们希望其能够在提高精度的情况下,依然速度很快,所以不能让网络规模很大,这样会导致参数量变多,速度减慢,所以YOLOV2通过简化网络,从而使得特征更容易学习。基于过去的研究工作,作者提出了以下几点改进:
关于BatchNormalization、regularization以及dropout会写笔记专门介绍。
High Resolution Classifier
目标检测方法会先在ImageNet数据集上对分类器进行预训练,大多数分类器的输入都是分辨率小于256×256的图像。YOLOV1使用224×224的小分辨率图像训练分类器,然后目标检测网络的输入尺度是 448 ∗ 448 448*448 448∗448,这意味着在进行检测时,网络要进行两种分辨率的切换,并且还需要时间去适应新的输入分辨率,这会降低网络的性能。
YOLOV2直接在 448 ∗ 448 448*448 448∗448的数据集上进行训练,使网络适应了大分辨率数据集。具体操作是首先在ImageNet数据集上微调分类网络使其在大分辨率图片上训练10个epoch,这样的操作会让网络适应高分辨率的输入;然后在检测数据集上微调之前训练好的网络。
Convolutional With Achor Boxes(重点)
YOLOV1在卷积产生的featuremap上直接使用全连接层去预测boundingboxes的位置坐标,这会导致在训练初始的时候,bbox的位置是完全随机的,需要大量的时间训练才会产生相对不错的效果。
而FasterRCNN并不是直接预测坐标,而是手动选取先验框,然后预测boundingboxes相对于anchor boxes的偏移量以及bboxes的置信度。相比起直接预测bboxes的位置坐标,预测偏移量会使问题更加简单,对于网络来说也更容易学习。因此在YOLOV2中借鉴了FasterR-CNN的思想,去掉YOLOV1的全连接层,使用anchorbox去预测boundingbox。
具体操作是:首先去掉池化层,从而让卷积层的输出有更高的分辨率;然后将网络的输入改成 416 ∗ 416 416*416 416∗416,这样做的原因是希望最后输出的featuremap有奇数数量的位置点,从而对于整张图片有一个单独的中心位置点,不至于落在四个位置点的中间 。
当整幅图只有一个大目标时,此时目标的中心点便有可能是整幅图的中心,奇数数量的位置点保证了图像的中心可以单独落在一个位置点,而不会出现偶数位置点落在四个位置点中间这种比较复杂的情况。
至于为什么要选择416,这与网络的结构有关系。图像经过网络后分辨率会降低到原来的1/32,所以既要保证输入图像的分辨率是32的倍数,还要保证最后输出的图像分辨率是奇数。
YOLOV1中的bboxes的置信度为Pr(object)*IOU(bbox与gt)[当gridcell中有目标时,使bbox的置信度等于其与gt的IOU]。
写到这里突然有点疑惑:为什么YOLOV1loss函数中bbox的置信度误差中的标签值是该bbox和gt的IOU,网络最终的目的不是要让负责预测物体的bbox与gt尽可能重合吗,那么标签值难道不应该是最终期望的bbox和gt的IOU,也就是1吗?
**
原因:其实是陷入了一种误区,loss函数中置信度误差存在的目的就是为了让网络最后输出的置信度=Pr(object)*IOU(bbox与gt),假如让标签值为1,那么最后每一个负责预测物体的bbox的置信度都会接近1,但是此时置信度并不会反映出该bbox与gt之间的重合程度,从而不会反映出模型在定位上的性能好坏。
**
所以loss函数置信度误差中标签值是该bbox和gt的IOU:就是为了使得最后预测的置信度满足论文中提到的公式:confidence score=Pr(object)*IOU(bbox与gt),从而通过置信度可以反映出模型的定位精度。
在YOLOV2中bbox的置信度与YIOLOV1相同。
使用anchorboxes后,map基本上变化不大,recall有了比较大的提升,这意味着precision下降了。recall上升的原因是:在YOLOV1中只有98个bboxes,而在YOLOV2中,有13×13×5个bbox,这意味着可以检测出的目标更多了,但bbox增多的同时也意味着目标检测错误的概率增加了,所以会导致precision降低。
在YOLO中引入anchor box机制后,有两个问题,分别是anchorbox该怎么选择、以及模型不稳定问题。
Fine-Grained Features
YOLOV2最后会在13×13的特征图上进行目标检测,这对于大目标的检测而言是足够的,但是如果要检测小目标,那就需要更细粒度的特征。
Faster R-CNN和SSD在不同大小的特征图上运行候选网络从而得到一系列不同的分辨率,而YOLOV2采用了一种不同的办法——通过添加一个passthrough layer,把模型更浅层的26×26分辨率的细粒度特征和最后的特征进行融合。
具体的操作参考下图:
将最后池化之前的26×26×512的特征图一拆四,得到4个13×13×512的特征图,将这四个特征图在通道上进行融合得到13×13×2048的特征图,和最后的特征图进行融合。
具体是怎样一拆四的,详见同济子豪兄的YOLOV2视频讲解,下图来自于讲解视频:
一拆四的结果就是将原来的特征图长宽减半,通道数变为原来的四倍。(上图还是很直观的。)
passthrough layer在网络中的具体结构如下图:(图片来自于B站UP主:霹雳吧啦Wz:YOLO系列理论合集)
passthrough layer将浅层特征盒深层特征进行融合,可以得到更细粒度的信息,有利于小目标的检测。
Multi-Scale Training
YOLOV1是在448×448的输入图片上进行训练的,YOLOV2中加入了anchorbox,输入图片大小为416×416。然后因为模型只使用了卷积层和池化层,所以它能够对输入图片下采样32倍,对图片进行resize。
为了让YOLOV2更具鲁棒性,能够在不同的大小的图片上运行,作者训练了如下的网络:每十个batch网络随机选择一个新的尺度的图片,因为模型要对输入图片下采样32倍,所以要输入图片的大小必须是32的倍,作者设置最小的尺度为320×320,最大尺度为68×608.
这种机制会让网络学会在各种各样的输入尺度上进行很好的预测,意味着相同的网络可以在不同的分辨率上进行目标检测。
如果你输入的图像分辨率比较小,那么网络会运行的很快,并且map不会太低;如果输入图像的分辨率比较大,那么map会相对较高,速度虽然比不上小分辨率运行速度,但是仍然可以达到一个实时效果。
因此YOLOV2通过图片的不同尺度提供了速度和精度之间的一种权衡。
上图说明了YOLOV2算法相比于其他算法的优越性。对于YOLOV2,当输入小分辨率图片,速度提高,精度会下降;当输入高分辨率图片,精度上升时,速度下降,因此取得速度和精度之间的权衡是比较重要的。
对于大多数的目标检测应用,如自动驾驶,都依赖于低延迟的目标预测,所以检测速度是很重要的。
大多数的目标检测框架都将VGG-16作为基本的特征提取网络,VGG-16确实是一个拥有较高精度的分类网络,但是它太复杂了。对于一个224×224的低分辨率图片,VGG-16的卷积层要进行30.69billion个浮点运算,运算量很大,且参数多,这必然会对网络的速度造成一定的影响。
YOLOV2基于Googlenet结构,使用了一种自定义的网络结构——Darknet19。Darknet19在一次前向传播中仅仅需要8.52billion的运算,虽然运算量上相比于VGG-16有了大幅的减少,但这并不意味着Darknet19的精度也大幅下降了,在精度上Darknet19的精度仅仅略差于VGG-16,这显然是很不错的。
Darknet19的网络结构:和VGG模型相似,在Darknet19网络中作者使用的大多数是3×3的卷积核,并且在每一次采样操作后,通道个数翻倍;受Networ in Network的启发,作者使用global average pooling(全局平均池化)进行预测,并且在3×3卷积之间使用1×1卷积进行特征降维。
全局平均池化:关于全局平均池化参考:知乎惊鸿:对于输出的每一个通道的特征图的所有像素计算一个平均值,效果相当于全连接层,但是参数量要远远小于全连接层。
全连接层是将三通道的feature map拉平成一维向量,比如26×26×32的特征图,全连接层操作之后变成了 26 ∗ 26 ∗ 3 26*26*3 26∗26∗3的一维向量,而经过GAP处理后,一维向量中只有 32 ∗ 1 32*1 32∗1个元素,参数量大大减少。
GAP可以适应各种尺度大小的图片,无论输入图片的尺寸是多少,只要最后的通道数是一样的,经过GAP处理后得到的都是一样大小的向量。而全连接层则需要输入固定尺寸大小的图片,这是因为全连接层的参数数量随着featuremap的大小而变,如果输入图片大小不一样,那么全连接层的参数数量就无法确定,而对于预训练好的模型来说,全连接层的权重矩阵是固定的。
Darknet19的网络结构包括用于分类和用于目标检测的网络结构。
(网络结构图来自:Lorin99:YOLO v2网络结构)用于分类的网络结构如下:
目标检测网络如下:
从上述图汇总可以发现,输入的图像经过下采样,最后输出的宽高均变为原来的1/32,因此输出入图像的分辨率要是32的倍数。
(关于网络的输出解释详见下面的YOLOV2网络输出)
YOLOV2的目标检测网络,最后输出的维度是 13 ∗ 13 ∗ [ B ∗ ( C + 5 ) ] 13*13*[B*(C+5)] 13∗13∗[B∗(C+5)],其中13是最后输出图像的分辨率,结合前面的gridcell和位置点,可以将 13 ∗ 13 13*13 13∗13理解为原始图像被分为 13 ∗ 13 13*13 13∗13的gridcell(同济子豪兄),每个gridcell宽高 32 ∗ 32 32*32 32∗32;或者 13 ∗ 13 13*13 13∗13的位置点,每个位置点是32个像素点的浓缩(Bubbliiiing)。
B表示的是每个gridcell的anchor boxes个数,在YOLOV2中的anchorboxes为5个,所以 B = 5 B=5 B=5;
C是目标检测数据集的类别数;
5表示的是用来预测bboxes位置的四个坐标信息+预测bbox置信度的置信度信息,也就是anchor box部分提到的 t x , t y , t w , t h , t o t_x,t_y,t_w,t_h,t_o tx,ty,tw,th,to。
图出自:X-猪:YOLO9000深入理解;截自:B站up主同济子豪兄关于YOLOV2的讲解视频
YOLOV2的LOSS函数如下图:(图来自于:wutheringcoo:YOLOv2损失函数细节研究)
①第一项:不负责预测物体的预测框的置信度误差。
M a x I O U < T h r e s h Max IOU
对于每一个gridcell有五个anchor,根据网络最后的输出,计算得到每个girdcell的5个预测框,这5个预测框是在对应anchor的偏移上得到的。
对于每个gridcell里的所有预测框,计算这些预测框和所有gt的IOU,假如最大 IOU小于阈值,那么这个gridcell就是不负责预测物体的gridcell,该gridcell就是背景。
这一项的存在就是防止将背景预测为目标。
②第二项
t < 12800 t<12800 t<12800:12800次之前的迭代,代表早期训练。在早期迭代中,计算预测框和对应先验框的定位误差,目的是让预测框在早期的时候就尽量向先验框靠拢。在后期训练中预测框与anchor基本相同,就可以在anchor的基础上进行微调,与gt的拟合速度更快。
③第三项
第三项主要计算的是预测框与真实框的定位误差、置信度误差和分类误差。
代码来自于:1273545169:YOLOv2–k_means方法
import numpy as np
# 计算质心和其他boxes的距离
def iou(box, clusters):
#关于boxIOU的计算方式:将两个box的中心重叠在一起,找到最小的高和宽,相乘得到交叉区域面积;
#并区域面积=两个box的面积相加-交叉区域面积。
x = np.minimum(clusters[:, 0], box[0])
y = np.minimum(clusters[:, 1], box[1])
intersection = x * y
box_area = box[0] * box[1]
cluster_area = clusters[:, 0] * clusters[:, 1]
iou_ = intersection / (box_area + cluster_area - intersection)
return iou_
#boxes是所有的gt框的宽高
def kmeans(boxes, k, dist=np.median):
rows = boxes.shape[0]
# rows行k列,每行对应一个boxes,每列是boxes与各个聚类中心的IOU
distances = np.empty((rows, k))
#
last_clusters = np.zeros((rows,))
np.random.seed()
# 选择初始聚类中心
# 在boxes中随机选取k个作为clusters
clusters = boxes[np.random.choice(rows, k, replace=False)]
while True:
for row in range(rows):
#计算每一个box和各个聚类中心的IOU
distances[row] = 1 - iou(boxes[row], clusters)
# 找到与各个box IOU最大的聚类中心索引
nearest_clusters = np.argmin(distances, axis=1)
# 当两次聚类结果相同时结束,即聚类中心不发生变化
if (last_clusters == nearest_clusters).all():
break
# 分别取w和h的平均值更新clusters
for cluster in range(k):
clusters[cluster] = dist(boxes[nearest_clusters == cluster], axis=0)
last_clusters = nearest_clusters
return clusters
Kmeans聚类的步骤:
Kmeans选择先验框:
① 从所有的gt框中随机选择k个框作为聚类中心;
② 对于每一个gt框,计算它和所有聚类中心的IOU。IOU计算方式:将两个box中心重叠在一起,然后计算IOU;
③ 将每一个gt框归到与其IOU最大的box所属的聚类中;
④ 对于每一个聚类求所有box高宽平均值,作为新的聚类中心box;
⑤ 如果聚类中心相比上次没有发生变化,则停止聚类; 如果发生变化,则重复第②步。
两个box的IOU计算图示如下:
从上述代码对距离公式进行解释:
d ( b o x , c e n t r o i d ) = 1 − I O U ( b o x , c e n t r o i d ) d(box,centroid)=1-IOU(box,centroid) d(box,centroid)=1−IOU(box,centroid)
如果某个boundingbox与聚类中心的IOU较大,那么经过上述公式计算,就变成了IOU越大,距离越小,最后将bbox归到与其IOU最小的聚类中心中。
相比于YOLOV1,YOLOV2主要做了如下的改进:
举个例子:当你初入一个新的领域时,如果没有别人指导的话,就需要自己盲目探索很长一段时间才能步入正轨,YOLOV1就相当于这种情况。网络的训练刚开始很随机,需要经过很长时间的训练才能输出比较有意义的预测值;
而YOLOV1的anchor box和坐标限制就相当于指导老师,让网络少做了很多无用功,很快就可以步入正轨,输出有意义的值。将预测框的中心坐标限制在对应的gridcell中,同时网络早期训练会让预测框向anchor box靠近,后期训练中预测框在anchor box的基础上调整拟合ground truth,大大减少了训练所需时间,且定位效果较好。