You only look once系列

YOLOV1

1、综述

Yolov1是CVPR2016的论文,Yolov1的网络速度很快,可以实时处理图片,达到45fps,还有一种改进的fast Yolo(减少了一些卷积层),可以达到155fps,精度也比较高,虽然会在定位上错误比较高,但是会在将背景预测为正样本的情况较少。主要的思想就是将一幅图片分成一个SxS grid,每个cell负责检测落在其中的gt框内容,那么只有当gt框的中心落在cell中,才算这个gt属于这个cell。且不像二阶段中有RPN阶段,Yolov1是直接输出最终得到的结果,结果维度为SxSx(B*5+C),其中SxS是cell的数量,B为每个cell中bbox的数量,C为类别数。在本轮文中使用的是C=20,B=2,S=7。

    

2、Details

首先,YOLOv1的网络是使用自己设计的darknet。使用的darknet网络最开始是在ImageNet上使用224x224(在进行目标检测的时候是448x448)的图进行预训练,预训练的任务是进行图像分类,大概训练了一周时间,在ImageNet 2012 validation set达到了top-5 accuracy 80%的效果,堪比GoogleNet的模型达到的效果。

考虑到检测的任务需要细粒度的信息,所以将输入图片的分辨率由224x224变成448x448。最终的预测结果是7x7x(5x2+20),其中5是1 class probabilities+ 4 bbox coordinates。需要将bbox的宽和长归一化,即相对于image width and height的比例,取值范围在0-1,bbox的中心坐标x,y则是相对于cell左上坐标的偏移,也是一个0-1范围的值(见下面代码)。

#如下是gt框计算loss需要的对应的值的过程
cellx = 1. * w / S
celly = 1. * h / S
for obj in allobj:
    centerx = .5*(obj[1]+obj[3]) #xmin, xmax
    centery = .5*(obj[2]+obj[4]) #ymin, ymax
    cx = centerx / cellx
    cy = centery / celly
    # obj[1] is lttop_x, obj[2] is lttop_y, obj[3] is rigbot_x, obj[4] is rigbot_y 
    obj[3] = float(obj[3]-obj[1]) / w
    obj[4] = float(obj[4]-obj[2]) / h
    obj[3] = np.sqrt(obj[3])
    obj[4] = np.sqrt(obj[4])
    obj[1] = cx - np.floor(cx) # centerx
    obj[2] = cy - np.floor(cy) # centery
    obj += [int(np.floor(cy) * S + np.floor(cx))]

在这里,最终的每一个obj为(None, centerx, centery, w, h, index),其中index是表示obj所在的cell,且是flatten后的index。但是这后面有一个处理(如下代码):

   # Calculate placeholders' values
    probs = np.zeros([S*S,C])
    confs = np.zeros([S*S,B])
    coord = np.zeros([S*S,B,4])
    proid = np.zeros([S*S,C])
    prear = np.zeros([S*S,4])
    for obj in allobj:
        probs[obj[5], :] = [0.] * C
        probs[obj[5], labels.index(obj[0])] = 1.
        proid[obj[5], :] = [1] * C
        coord[obj[5], :, :] = [obj[1:5]] * B
        prear[obj[5],0] = obj[1] - obj[3]**2 * .5 * S # xleft
        prear[obj[5],1] = obj[2] - obj[4]**2 * .5 * S # yup
        prear[obj[5],2] = obj[1] + obj[3]**2 * .5 * S # xright
        prear[obj[5],3] = obj[2] + obj[4]**2 * .5 * S # ybot
        confs[obj[5], :] = [1.] * B

在for循环中,obj[5]作为每个量的索引,因此,如果有两个gt在同一个cell中,在循环中后面循环到的会把同样cell的前一个gt的信息覆盖掉,所以,可以姑且认为yolov1比较自信的将所有的gt划分在不同的cell中,否则就会有检测不到的。(这一份代码是在github上有3k+的stars,而且不知道我理解的是否有误)。

Loss function

根据Loss再来看其他的一些细节部分:

1)其中\lambda _{coord}=5\lambda_{noobj}=0.5,之所以需要这两个权值:优化是使用平方和误差loss函数(比较直接简单),但是,它会将位置检测贡献的loss和分类贡献的loss平等对待,即会将两者的权值设为一致,这样是不科学的。因为在每个图中,许多的cell中不包含有目标,这其中的bbox的产生的loss容易对那些拥有目标的cell产生的loss产生影响。最终的效果就是导致模型的不稳定,导致训练在早期出现分歧。(在这里应该是会导致模型无法很好的收敛,来回动荡)。故采用两个权值来引入关注。

2)(上图)其中的长宽的loss,使用平方根的原因是为了减少大目标和小目标对loss的贡献值的差距,比如一个大目标,长宽值都比较大,那么直接与gt框的平方和会比较大,可以设想,将大目标和其对应bbox成比例缩小,虽然我们可以知道其对loss的贡献应该是一样的,但是通过w,h直接的平方和来计算,其结果相对于原来没缩小的会减少。所以,用平方根,算是一个减少差距的小trick。

3)loss是直接使用x,y,w,h计算的,虽然这些都是相对值,不像二阶段是直接计算偏移量。Yolov2也是沿用这样的计算。

4)在yolov1中,每个cell中有两个产生的bbox,但是每个gt只会对应一个正样本,选择与gt的IOU最高的那个bbox作为正样本。

5)P为对bbox是否有目标的预测值,gt的这个标签为最优IOU的值,即取gt与2个bbox的IOU值的最大值。这个loss是每个cell都会产生一个,当然得是包含有目标的cell。

6)其中的C是类别的probabilities。bbox是预测出来的,gt的则为1或0,因为有20个类,共20个值。C产生的loss有两个式子,一个是有目标bbox产生的,一个无目标bbox产生,且权值不同。

YOLOv2

第二个版本相当于提出了两个点,一个是对YOLOv1的改进,一个就是针对类别数提升到9000个类别,CVPR2016的在pascal和coco上的state-of-the-art,在VOC2007上有76.8的mAP,速度是67fps,如果是40fps的,则有78.6的mAP,比同时期的faster rcnn和SSD效果都要好,且速度更快。上述的是对YOLO的改进。而YOLO9000可以看作是一种训练的方式,没有着重在网络结构的修改,而是提出了一种训练方法(YOLO9000的联合训练可以为分类的数据来预测位置)。

一下是一些零散的结构改进:

1)比较值得注意的一点是这个版本使用了anchor box。v2将v1中最后的FC层都去掉。具体做法是

  • 首先移除掉一个池化层,这样可以获得更高的分辨率。
  • 将输入由原来的448x448变为416x416,这样做是为了在后面feature map中可以达到奇数个像素点,416/32=13,最后是13x13,这样会产生一个单独的中心点。(这样是基于一个设定,即如果有大目标的话,大目标的中心更可能倾向于在整个image的中心,那么有cell可以更好的捕捉到这个点,如果是偶数的话,可能就落在周围四个cell中的某一个中。这样的想法是有道理的,因为在图像到feature map的过程中,其实最中间的cell有一个比较大的感受野,可以捕捉到原始图像中很大中心区域。)

2)Batch Normalization:论文使用Batch Normalization作为正则化手段,这样其他的一些正则化手段(dropout)就可以去掉了。做法是在所有的卷积层后都加上batch normalization,将会有2%的提升。

3)高分辨率分类:现在大部分的目标检测模型都会使用在ImageNet上预训练的分类器,因此大多的输入为256x256,YOLOv1则是先用224x224训练分类器,目标检测网络训练的时候使用的是448x448的输入。那么在v2版本,就要在ImageNet上得到了预训练的分类器后,先将输入改为448x448后,再进行ImageNet上10个epoches的分类器训练。最后进行目标检测器的训练调整。这样的三个步骤会给mAP带来4%的提升。

4)使用anchor boxes,可以将分类从空间位置中解耦出来,即可以将其是做相对单纯的分类。使用anchor box,精度会有一定的下降。在v1中每幅图仅仅产生7x7x2=98个bbox,若是使用anchor box,则会产生比较多的box。如果不使用anchor,那么有69.5%的mAP,81%的召回率,如果使用了anchor,则有69.2%的mAP和88%的召回率。很明显,这样做是比较值得的。

5)anchor box的形状,是使用聚类算法来得到的,并且每个cell使用5个anchor box是比较好的效果和计算量的平衡。有一个值得注意的点,在使用聚类算法时,其度量的方法是使用IOU来参与其中的聚类度量。

6)在v1中,会出现一个早期迭代模型不稳定的情况,不稳定的来源则是因为直接对box坐标的预测,在v2中,是预测出相对于网格单元位置的位置坐标。其实我理解也可以是某种意义上的偏移offset,因为实际上,网络对每个Anchor box预测五个coordinates,分别是t_x, t_y, t_w, t_y, t_o,如下图所示的计算,其中C_x, C_y是cell相对于image左上的偏移。下面的Pr(object)是gt的label值。那么相对于预测值来说,其代表的含义就是Pr(object)*IOU(b,object)。P_w, P_h是anchor box的长宽,t_x, t_y经sigmoid函数的约束,取值在0-1,这样再加上偏移,会得到在cell中的位置b_x, b_y,这里应该是指代的中心坐标,这个点是无所谓,看具体的实现。中心坐标是我们的转换后的预测值,且是一个相对值。

这里的,实际可以这么认为,anchor box的作用是提供一个可参考的比较符合各个目标情况的长宽参考,那么5个anchor box就有五个参考,我们预测也会有5个预测组,都是基于这五个参考分别进行预测长宽的offset,至于对于中心坐标的预测,就是以cell的左上坐标来做一个位置偏移预测,最终如下来计算我们要投入loss计算的(b_x, b_y, b_w, b_h)。这样的方式给网络带来5%的提升。

后面的套路和v1一样,选取IOU最大的那个bbox作为正样本,其余的作为负样本?

猜你喜欢

转载自blog.csdn.net/github_37973614/article/details/84110176