YOLO / V1

    算法首先将图像缩放为448*448尺寸,随后把输入图像划分成S*S的格子(grider cell,论文中S=7),每个格子grider cell 都需对每个类别的概率进行预测,然后对每个格子都预测B(论文中为2)个bounding boxes,每个bounding box都包含5个预测值:x,y(bounding,box的中心点,girder cell的偏移值),w,h和confidence。必将预测值归一化为0-1范围内。形成S*S*(5*B+C)个数据。和faster-rcnn的区别在于这里直接给出了bounding box的预测值,而faster-rcnn是给出的偏移量。需要结合anchor-box确定最后的预测值。

    confidence = P(object)*IOU。P(object)的值为0或者1,意味着是否grid cell中是否包含有object。那么如何确定object就在grid cell中呢?如果object的groundtruth的中心点在grid cell中,那么这个girder cell就是包换这个object的。从这里也可以想象的出grid cell与bounding box的区别。由于已经把图像分为S*S个格子,而每个格子只能确定一个类别,这也是yolo/V1的一个缺点。所以类别的概率是针对grid cell。 如果有C个类别的概率的话,那么每个grid cell都需要预测C个数值来表示grid cell包含的目标(object)属于某个类别概率。这样就可以计算每个类别此时的概率值对应的confidence_class = P(class/objec)*confidence。也就是说最后会得到20*(7*7*2)=20*98的score矩阵,括号里面是bounding box的数量,20代表类别。接下来的操作都是20个类别轮流进行:在某个类别中(即矩阵的某一行),将得分少于阈值(0.2)的设置为0,然后再按得分从高到低排序。最后再用NMS算法去掉重复率较大的bounding box(NMS:针对某一类别,选择得分最大的bounding box,然后计算它和其它bounding box的IOU值,如果IOU大于0.5,说明重复率较大,该得分设为0,如果不大于0.5,则不改;这样一轮后,再选择剩下的score里面最大的那个bounding box,然后计算该bounding box和其它bounding box的IOU,重复以上过程直到最后)。最后每个bounding box的20个score取最大的score,如果这个score大于0,那么这个bounding box就是这个socre对应的类别(矩阵的行),如果小于0,说明这个bounding box里面没有物体,跳过即可。NMS只在测试阶段选出备选框或者输出的预测框。在训练阶段,直接和groundtruth做IOU,选出IOU的最大值,从而确定当前grid cell的类别。

     论文中网络结构采用的是GoogleLeNet,可以对网络进行修改,比如说最后的全连接层。但是重点在于最后的输出一定要是7*7*30,30的组成部分为(5(四个坐标,+ 一个是否为前景object还是背景)*2(一个格子对应两个box) + 20(类别))。

    对于损失函数,有五个方面组成,具体可以看论文中。每个损失函数的权重不一样,具体原论文中介绍的很详细。总之就是x,y与groundtruth的loss;w,h宽高;前景、背景之间(即是否是object,是否是object的权重不一样);以及最终分类是否正确的loss。

YOLO/V2

     相对于v1版本,v2版本主要分为三个方面来提高网络方面的性能:better,faster,stronger;即更好、更快、更强。从这三个方面来对比V1版本,可谓是质的变化。

首先‘更好’的实现通过一下技术手段:

      batch normalization:在V2版本的网络中,在每一层的卷积后添加了batch normalization,该方法使mAP提升了2%,并且可以舍弃dropout优化而不会过拟合。

     high resolution classifier:在检测方法中基本上都是使用ImageNet预训练过得模型来提取特征并且很多使用了AlexNet网络,对于AlexNet网络,输入的网络会被resize到256*256的分辨率。在V1版本中,直接把分辨率提高到了448*448.这可能导致网络的识别率出现问题。

      在V2版本中,首先对分类网络(使用的不再是AlexNet,二十自定义的darkNet)进行fine tune,分辨率改成了448*448,在ImageNet数据集上训练了10轮(10epoch)。然后,还对检测网络部分也进行了fine tune。这样的改动也是mAP提升了4%。

     convolutional with anchor boxes:与V1版本进行框预测根本的不同在于这里使用了和faster-rcnn的一样的技术,即anchor的概念。这个可以参考一下faster-rcnn。另一方面在输入图像尺寸进行resize的时候尺寸变为416*416(注意此前说的448的尺寸是训练分类结构时输入图像的尺寸)并且去除了网络中的一个polling层,这样卷积层的输出能有更高的分辨率。为何尺寸变为416?这是因为经过下采样,可以使最后的特征图的尺寸变为奇数。这样的中心点就变成了一个。如果是偶数的话,就没有绝对的中心点,因为中心的四个点,都可以称为中心点。之所以选择奇数是因为大部分图像的目标(特别是大目标)的位置都在中心,因为这样做只需一个中心点center cell就可以确定大部分的图像目标(即在中心的目标),从而可以提升效率。

     dimension clusters:在faster-rcnn中的anchor会产生九个box(根据不同的长宽比例以及尺寸大小)。但是这里通过anchor产生box的方法是根据k-means方法进行聚类产生5类常见框的比例。这五类不是通过矩形的x,y,w,h,而是通过IOU确定。通过IOU聚类形成的五种形状和faster-rcnn的人工指定的9种达到的效果几乎接近。

    direct location prediction:该方案的提出主要是因为在前期进行训练时(利用faster-rcnn上利用偏移量,offset)的不稳定造成的,该不稳定的现象出现在预测box的(x,y)坐标上了。用通俗的说法就是,在图像特征图上进行anchor选择是,假如此时的anchor在特征图的右上角,而真实的目标在整个图像的左下角。那么此时的anchor也会对真实的目标进行偏移量的调整。按照理想目标,左下角的目标应该用左下角的anchor点进行偏移量的预测值,而如果不加限制,在训练前期或造成所有的anchor点都进行偏移量的大量调整。因此在初始化变量开始进行训练时,需要花费很长的时间才能够稳定的预测敏感物体的具体位置。因此作者使用了在V1版本的预测方法:直接预测框的位置,而不是通过anchor产生的框再加上偏移量形成的框位置。输出的五个坐标变量为(x,y,w,h,o)其中o为置信度,其实就是当前是否为object的分数。并且使groundtruth的坐标点转化(bounds)成0-1范围。

    fine-grained features:翻译为细粒度特征,其实就是类似于resnet的identity mappings结构,即把当前特征图的上一层添加一个转一层,转移到当前层,从而使更小尺度的物体检测更加有利。该方法(passthrough layer)使26*26*512的特征图连接到了13*13*2048的特征图上,并且不涉及到参数学习(只是进行特征重排,前面26 * 26 * 512的特征图使用按行和按列隔行采样的方法,就可以得到4个新的特征图,维度都是13 * 13 * 512,然后做concat操作,得到13 * 13 * 2048的特征图,将其拼接到后面的层,相当于做了一次特征融合,有利于检测小目标)

    multi-scale training:采用多尺度方法进行训练,因为网络中只有卷积和池化层,因此可以同动态调整进对输入不同尺寸的图像进行尺寸的统一(roi-pooling,但是本文没有用到)或者说没有了全连接层,参数的尺寸不用那么固定(我的理解是,不同的输入的尺寸只会影响特征图的输出尺寸,特征图的尺寸是不影响目标的检测,因为一个anchor点对应五个框,每一个框的预测采用五个坐标点。特征图尺寸只是影响anchor的个数。anchor的个数影响着同groundtruth(训练)或者NMS(推理)过程中的次数)。注意这一步是在检测数据集上fine tune时候采用的,不要跟前面在Imagenet数据集上的两步预训练分类模型混淆不同于固定输入网络的图片尺寸的方法,作者在几次迭代后就会微调网络。没经过10次训练(10 epoch),就会随机选择新的图片尺寸。YOLO网络使用的降采样参数为32,那么就使用32的倍数进行尺度池化{320,352,…,608}。最终最小的尺寸为320 * 320,最大的尺寸为608 * 608。接着按照输入尺寸调整网络进行训练。这种机制使得网络可以更好地预测不同尺寸的图片,意味着同一个网络可以进行不同分辨率的检测任务,在小尺寸图片上YOLOv2运行更快,在速度和精度上达到了平衡。

更快的速度在于:

    V2版本使用了基于GoogleNet的定制网络,在牺牲较小的预测概率精度上比VGG-16更加快速(224*224图片的浮点运算量从VGG-16的306.9亿次将为85.2亿次)。详细的GoogleNet网络在论文中有流程图,很详细。先采用该网络进行分类训练,然后去掉最后一层卷积层添加了三个3*3*1024的卷积层并且每一层后面跟一个1*1的卷积层后组成检测网络,输出维度是检测所需要的数量。对于VOC数据集,预测5种boxes大小,每个box包含5个坐标值和20个类别,所以总共是5 * (5+20)= 125个输出维度。同时也添加了转移层(passthrough layer ),从最后那个3 * 3 * 512的卷积层连到倒数第二层,使模型有了细粒度特征。

更加强壮主要是:作者使用联合训方法,结合wordtree等方法,使V2版本的检测种类扩充到了上千种。

YOLO/V3:

      作者也说出对yolo的改进也只是在于参考了一下其他学者的想法。分为一下几种的改进或者YOLO的技术延续。

     首先是对于bounding box的预测方法是和V2版本是一样的。同样是直接预测box的长宽而不是偏移量。YOLOv3使用逻辑回归(logistic regression)的方法给每个bounding box预测一个对象分数。如果bounding box prior比任何一个其他的bounding box prior重叠ground truth对象都多,这个值应该是1。如果bounding box不是最佳的但是重叠部分比某个阈值高就忽略此次预测(NMS方法)。作者系统只为每个ground truth对象分配一个bounding box prior。如果一个bounding box prior没有分配给ground truth对象,则不会对坐标或者类别预测造成损失,仅会对对象造成损失(这块相当于Faster-rcnn的rpn部分,可以想象成faster-rcnn训练时对rpn模块的单独训练)。

    2)类预测没有用softmax,因为有些bounding box包含多标签(不能很好地使用其他数据集);使用独立的logistics分类器与二值交叉熵损失。这个构想在更复杂的数据集上很有帮助。使用softmax强加一种假设,即:每一个框内仅由一个类,但是事实常不如此。多标签方法能更好的模拟数据。比如说在ImageNet的数据集上,有许多重叠的标签(女人和人)。

    3)从三个不同的特征尺度预测,和SSD中很像。对于anchor的预bounding box,通过聚类的方法选出了九种。对于九种聚类产生的方法,根据大小分别分配到三个不同特征尺度。三种不同特征尺度分别是darknet-53的第26,43以及最后一层输出来的特征图。

    4)网络特征提取命名为darknet-53,是由darkNet-19和residual网络混合而成。

YOLO/V3的推理和训练过程:

对于yolo的损失层,其组成部分不但包括model.out与y-true(经过处理的groundtruth),两者的格式相同,假设只有一个类别,[(?,13,13,18),(?26,26,18),(?52,52,18)],还包括了锚点框anchors与类别数num_class以及过滤阈值(和faster-rcnn中的rpn的loss计算过程很相似)。    

通过推理后输出yolo_output = [Y1,Y2,Y3],该输出是有三个Tensor组成,或者说是一个‘特征图’,channel的长度为num_anchor*(num_class + 5),num_anchor取3,这里取3的原因是,锚点框在Y1,Y2,Y3层分别为3.(在faster-rcnn中,由于直接就一层Y,所以一个锚点框对应为9),其中的表示意思为:类别数量 + (x,y,h,w)+ 置信分数,而他们的宽度不同,分别是13*13,26*26,以及52*52.。下面这张图是整个darknet网络的结构(图中output参数不对,这个是原生的darknet):

YOLO/V1、V2、V3目标检测系列介绍

      我们先看推理的具体过程。假设所有的变量已经训练好。从上图中的scale1,scale2,scale3(特征图的长宽分别为13,26,52)可知是darknet53中,从第26,45和52层中输出。此后经过一些列的变化到达了YOLO Detection的最终结果,也就是刚说的yolo_out。这个过程中我们暂定为yolo过程,该过程中间经过几次yolo_block。什么是yolo_block?,画图不太方便,在这里用用文字表述。首先Scale1输出的特征图进入yolo_block。yolo_block内是经过了CONV_1-->BN-->CONV_2-->BN-->CONV_3-->BN-->CONV_4-->BN-->CONV_5-->BN-->CONV_6-->BN-->CONV_7。其CONV的参数为stride = 1,而kerne_size分别为(1,3,1,3,1,3,1);filter_num为num*(1,2,1,2,1,2,num_anchors*(num_classes + 5)。5的概念前面已经解释了很多,filter_num是根据scale1,scale2,scale3的不同分别为512,256,128。这中间没有经过池化,而且卷积过程为SAME。所以特征图的长宽尺寸没有发生变化。

      我们看到scale1和图中的convs(我们这里为yolo_block,按照原生,这里只是一个conv,所以scale1输出的是52层,加上这一层为53,称为darkne53)有一条线和scale2的相连。因为这中间经过七次卷积,到底是哪一层的卷积输出后然后和scale2连接呢。程序中给出的结果为CONV5-->BN后的特征图。但是该输出的长宽为13*13,和scale2的长宽不同,因此这里利用tf.image.resize_nearest_neighbor()函数对输出的特征图进行了一次resize。经过resize后,长宽相同。然后concat成一个tensor进入scale2的yolo_block(图中为Convs)操作。scale3的操作和上述相同。经过这一系列的操作就形成了本小结开头的yolo_output = [Y1,Y2,Y3]。这之后就是对原图的映射以及通过每一个类的分数进行候选框的赛选,最终形成目标检测框以及类别。

损失函数

      在进行损失函数计算时,是通过一个训练 for循环,分别对Y1,Y2,Y3的的损失值进行计算然后在累加到一起。首先对Y1进行损失值计算:Y1的长宽分别为13*13.这里对anchors的选取为后三个,即尺寸最大的三对。特征图的尺寸较小,而anchor较大,以为这里对尺寸较大的物体比较敏感。接着进行yolo_head计算,(待续)