@huanghaian
2020-10-31T18:22:33.000000Z
字数 8870
阅读 1930
mmdetection
在熟悉了前置算法faster rcnn、ssd和yolov1后理解yolov2算法就会非常轻松,因为他本质上没有提出全新的思想,而是基于前人所提思想对yolov1进行改进而已。其主要特点可以归纳如下:
(1) 引入了BN层、新的骨架网络和特征增强模块
(2) 引入了流行的anchor框设计,并且anchor设置直接采用kmean算法得到,不需要人为设置,更加友好
(3) 采用了高分辨率分类器微调训练以及多尺度检测器训练等等训练trick
通过这些改进,大幅提升了yolov1的定位准确度和召回率,同时速度也是极快,如下图所示:
由于此时BN层已成为主流模块,可以大幅提高网络收敛速度和泛化能力,基于此作者设计了新的骨架网络称为darknet19,其结构如下:
因为除了分类部分整个网络包括19层卷积,故命名为darknet19,每个convolutional都包括conv+bn+leakyrelu模块,可以明显看出其是标准的和vgg相同的直筒结构。该分类网络首先在imagenet上面进行重新训练,然后把最后的conv+avg+softmax去掉,得到骨架权重作为目标检测的预训练权重。
假设网络输入大小是416x416,那么骨架网络最后层输出shape为(b,1024,13,13),也就是说采用固定stride=32。如果直接在骨架网络最后一层再接几个卷积层,然后进行预测完全没有问题。作者认为13x13的特征图对于大物体检测来说是够了,但是对于小物体,特征图提供的信息就不一定够了,很可能特征已经消失了,故作者结合特征融合思想,提出一种新的passthrough层来产生更精细的特征图,本质上是一种特征聚合操作,目的是增强小物体特征图信息。如下所示:
上面的第一个输入箭头是darknet-19的最后一个max pool运行前的特征图,假设图片输入是416x416,那么最后一个max pool层输入尺度为26x26x512,在该层卷积输出后引入一个新的分支:passthrough层,将原来尺度为26x26x512特征图拆分13x13x2048的特征图,然后和darkent-19骨架最后一层卷积的13x13x1024特征图进行concat,得到13x13x3072的特征图,然后经过conv+bn+leakyrelu,得到最终的预测图。下面有打印网络结构图,更加容易理解。
上面的图通道画的不对,实际上由于通道太大了,消耗内存太多,故作者实际上做法是先将26x26x512降维为26x26x64,然后拆分为13x13x256,concat后,得到13x13x1280的特征图。
采用darknet库代码打印网络结构如下所示:
layer filters size input output
0 conv 32 3 x 3 / 1 416 x 416 x 3 -> 416 x 416 x 32
1 max 2 x 2 / 2 416 x 416 x 32 -> 208 x 208 x 32
2 conv 64 3 x 3 / 1 208 x 208 x 32 -> 208 x 208 x 64
3 max 2 x 2 / 2 208 x 208 x 64 -> 104 x 104 x 64
4 conv 128 3 x 3 / 1 104 x 104 x 64 -> 104 x 104 x 128
5 conv 64 1 x 1 / 1 104 x 104 x 128 -> 104 x 104 x 64
6 conv 128 3 x 3 / 1 104 x 104 x 64 -> 104 x 104 x 128
7 max 2 x 2 / 2 104 x 104 x 128 -> 52 x 52 x 128
8 conv 256 3 x 3 / 1 52 x 52 x 128 -> 52 x 52 x 256
9 conv 128 1 x 1 / 1 52 x 52 x 256 -> 52 x 52 x 128
10 conv 256 3 x 3 / 1 52 x 52 x 128 -> 52 x 52 x 256
11 max 2 x 2 / 2 52 x 52 x 256 -> 26 x 26 x 256
12 conv 512 3 x 3 / 1 26 x 26 x 256 -> 26 x 26 x 512
13 conv 256 1 x 1 / 1 26 x 26 x 512 -> 26 x 26 x 256
14 conv 512 3 x 3 / 1 26 x 26 x 256 -> 26 x 26 x 512
15 conv 256 1 x 1 / 1 26 x 26 x 512 -> 26 x 26 x 256
16 conv 512 3 x 3 / 1 26 x 26 x 256 -> 26 x 26 x 512
17 max 2 x 2 / 2 26 x 26 x 512 -> 13 x 13 x 512
18 conv 1024 3 x 3 / 1 13 x 13 x 512 -> 13 x 13 x1024
19 conv 512 1 x 1 / 1 13 x 13 x1024 -> 13 x 13 x 512
20 conv 1024 3 x 3 / 1 13 x 13 x 512 -> 13 x 13 x1024
21 conv 512 1 x 1 / 1 13 x 13 x1024 -> 13 x 13 x 512
22 conv 1024 3 x 3 / 1 13 x 13 x 512 -> 13 x 13 x1024
23 conv 1024 3 x 3 / 1 13 x 13 x1024 -> 13 x 13 x1024
24 conv 1024 3 x 3 / 1 13 x 13 x1024 -> 13 x 13 x1024
25 route 16
26 conv 64 1 x 1 / 1 26 x 26 x 512 -> 26 x 26 x 64
27 reorg / 2 26 x 26 x 64 -> 13 x 13 x 256
28 route 27 24
29 conv 1024 3 x 3 / 1 13 x 13 x1280 -> 13 x 13 x1024
30 conv NUM 1 x 1 / 1 13 x 13 x1024 -> 13 x 13 x NUM
31 detection
------26--27------
| |
25 28
0--1--2...--16--|--17--....--24--|--29--30--31
其中26是1x1x64卷积核,27是reorg特征重排层(passthrough),25和28是快捷连接,31是loss层,故总共网络一共是31层,28和24连接处是拼接操作
对于reorg的具体操作如下:
很容易理解,就是对每个2x2的格子进行切分,然后变成4个通道。那么这样做的好处是啥?为了讲清楚这个问题,需要引入一个新知识点:sub-pixel upsample或者亚像素卷积,其可视化操作如下所示:
仔细观察可以看出,reorg其实就是亚像素卷积的反向操作。亚像素卷积是一种替代简单上采样的操作,在超分辨率任务中是基本操作,目的是由小图得到大图,好处是可以对高层特征编码进更多的空间信息,如果在前面连接卷积操作,就可以变成可学习上采样了,所以亚像素卷积比双线性插值(不可学习)和反卷积(会强制补0)要好。那么reorg的好处也就很容易理解了:可以最大程度保证不丢失空间信息,且总信息量不变,保证能够和低分辨率特征融合。
为了说明正负样本定义,需要先分析anchor设定。为了克服yolov1里面每个网格最多只能检测单个物体的缺陷,和faster rcnn和ssd一样也引入了anchor设置。其设定每个网格一共5个anchor,这5个anchor也不需要手动设计,而是通过Kmean算法自动计算得到,其计算过程为:
(1) 设定Kmean聚类个数n即为anchor个数
(2) 统计整个数据集上面的所有bbox的宽高,然后随机选择n个聚类中心,运行kmean算法,将1-iou作为距离准则,不断迭代,直到kmean的n个聚类中心不再变化为止,此时就得到了n个anchor的wh值
具体距离准则公式为:
box是所有统计的bbox的wh,centroid是聚类中心。
需要注意的是:为了更好将聚类后的anchor和gt bbox匹配,最好将所有图片采用和训练时一样的数据增强策略处理,并且统一到网络输入大小后,再统计数据集的bbox宽高,可以模拟尽可能多的数据分布,统计会更加精确。yolov2在coco数据集上的典型anchor比例如下:
0.57273, 0.677385, 1.87446, 2.06253, 3.33843, 5.47434, 7.88282, 3.52778, 9.77052, 9.16828
其数值含义是原图尺度聚类anchor除以stride得到的在特征图尺度上的值。
假设输入是416x416,那么输出层是13x13,head预测层输出shape为(b,5*(xywh+conf+cls_num),13,13),一共13x13x5=845个anchor。
(1) 正负属性定义
在设定anchor后就需要对每个anchor位置样本定义正负属性了。其规则和yolov1一样简单:保证每个gt bbox一定有一个唯一的anchor进行对应,匹配规则就是IOU最大。具体就是:对于某个gt bbox,首先要确定其中心点要落在哪个网格内,然后计算这个网格的5个anchor与该gt bbox的IOU值,计算IOU值时不考虑坐标,只考虑形状(因为此处anchor没有坐标xy信息),所以先将anchor与gt bbox的中心点对齐(简单做法就是把anchor的xy设置为gt bbox的中心点即可),然后计算出对应的IOU值,IOU值最大的那个anchor与gt bbox匹配,对应的预测框用来预测这个gt bbox。其余anchor暂时算作负样本。
有1个情况需要思考清楚:假设有2个gt bbox的中心都落在同一个网格,且形状差异较大,此时这2个gt bbox应该会匹配到不同的anchor,这是我们希望的。但是如果差异比较小,导致都匹配上同一个anchor了,那么后一个gt bbox会把前一个gt bbox匹配的anchor覆盖掉,导致前面的gt bbox变成负样本了,这就是常说的标签重写问题。
上述匹配规则在不同复现版本里面有不同实现(官方代码也有,但是从来没有开启该设置),基于上述匹配规则并且借鉴ssd的做法,可以额外加上一个匹配规则:当每个gt bbox和最大iou的anchor匹配完成后,对剩下的anchor再次和对应网格内的gt bbox进行匹配(必须限制在对应网格内,否则xy预测范围变了),当该anchor和gt bbox的iou大于一定阈值例如0.7后,也算作正样本,即该anchor也负责预测gt bbox。这样的结果就是一个gt bbox可能和好几个anchor匹配,增加了正样本数,理论上会更好。
(2) 忽略属性定义
此时已经确定了所有anchor所对应的正负属性了,但是我们可以试图分析一种情况:假设某个anchor的预测bbox和某个gt bbox的iou为0.8,但是在前面的匹配策略中认为其是负样本,这是非常可能出现的,因为每个gt bbox仅仅和一个anchor匹配上,对于其附近的其余anchor强制认为是负样本。此时由于该负样本anchor预测的情况非常好,如果强行当做负样本进行训练,给人感觉就是不太对劲,但是也不能当做正样本(因为匹配规则就是如此设计的),此时我们可以把他当做灰色地带的样本也就是忽略样本,对这类anchor的预测值不计算loss即可,让他自生自灭吧!
故作者新增了忽略样本这个属性(算是yolo系列正负样本定义的一个特色吧),具体计算过程是:遍历每个anchor的预测值,如果anchor预测值和其余所有gt bbox的所有iou值中,只要有一个iou值大于阈值(通常是0.6),则该anchor预测值忽略,不计算loss,对中间灰色区域就不管了,保证学习的连续性。
yolov2的bbox编解码结合了yolov1和ssd的做法,具体是:对于xy中心点值的预测不变,依然是预测相对当前网格左上角偏移。但是对于wh的预测就不同了,主要原因是有anchor了,其wh预测值是gt bbox的宽高除以anchor的wh,然后取log操作即可(和ssd的bbox编解码是一样的),并且gt box和anchor尺度都会先映射到特征图上面再计算。可以发现xy的预测范围是0~1,但是wh的预测范围不定。
确定了编码过程,那么解码过程也非常简单,就是上面图中的公式,cx,cy的范围是0~13,因为xy预测范围是0~1,故对预测的tx,ty会先进行sigmoid操作(yolov1没有进行sigmoid),然后加上当前网格左上角坐标,最后乘上stride即可得到bbox的中心坐标,而wh就是预测值tw,th进行指数映射然后乘上特征图尺度的anchor宽高值,最后也是乘上stride即可。
相比于yolov1的编解码方式,采用anchor有诸多好处,最主要是可以克服yolov1早期训练极其不稳定问题,原因是其在早期训练时候每个网格都会输出B个任意形状的预测框,这种任意输出可能对某些特定大小的物体有好处,但是会阻碍其他物体学习,导致梯度出现瞬变,不利于收敛。而通过引入基于统计物体wh分布的先验anchor,相当于限制了回归范围,训练自由度减少了,自然收敛更加容易,训练更加稳定了。
不要被上面的loss公式吓到,其实非常简单。首先和yolov1一样,所有分支loss都是l2 loss,同时为了平衡正负样本,其也有设置不同分支的loss权重值。
(1) bbox回归分支loss
首先1k^truth的含义是所有正样本为1,否则是0,也就是说bbox回归分支仅仅计算正样本loss。truth^r是gt bbox的编码target值,b^r是回归分支预测值tx,ty,tw,th,对这4个值计算l2 loss即可。
1t < 128000是指前128000次迭代时候额外考虑一个回归loss,其中prior^r表示anchor,表示预测框与先验框anchor的误差,注意不是与gt bbox的误差,可能是为了在训练早期使模型更快学会先预测先验框的位置。
关于1t< 128000这个loss,可以详细说明下,其官方代码写法为:
truth是已经编码后xywh分支的target了,可以发现此时xy label是假设gt bbox中心一定在网格中心,而wh label就是anchor(这个l.biases就是对原图尺度的anchor除以stride后变成特征图尺度的值)除以特征图大小(典型是13),可以简单认为在每个gt bbox所属网格内会附加一个gt bbox中心点一定在网格,但是wh会依据匹配情况改变的gt bbox,对于任何一个正样本anchor,其xy label是始终不变的,而wh label会随着所属gt bbox的匹配情况而改变,也就是说这个loss的目的是希望忽略xy的学习,而侧重于将预测wh学习出anchor的形状,可能有助于后面的收敛。但是不可忽略的一个点是:由于这个loss和图示下一行的坐标loss是一起学习的,就会出现回归分支预测wh的含义出现冲突,一个loss希望其学习anchor的shape,一个loss希望其学习gt bbox相对anchor的缩放,为了避免这个现象,作者实际上把1t< 128000这个loss仅仅在前期用,并且故意设置权重值比较低(典型值是0.01)。目前第三方复现都没有考虑这个loss,可能作用确实很小吧。
需要特别注意的是:由于bbox宽高不一致,表现在数值上就会时大时小,对于l1或者l2来说,梯度就不一样大,这是不好的,因为极端情况就是网络只学习大物体,小物体由于梯度太小被忽略了。虽然前面引入了log(yolov1里面是通过平方根克服),但是还可以进一步克服,具体就是基于gt bbox的宽高,给大小物体引入一个不一样大的系数,具体是2 - 特征图尺度的gtbbox的w×h,这样对于尺度较小的boxes其权重系数会更大一些,可以放大误差,该项权重和上再乘上一个可变因子,该技巧对小物体的mAP有较大提升。
(2) 分类回归loss
分类分支预测值是b^c,truth^c表示对应的one-hot类别编码,该分支也仅仅是计算正样本anchor。
(3) 置信度分支loss
对于置信度分支loss计算和yolov1里面完全相同,对于正样本其label是该anchor预测框结果解码后还原得到预测bbox,然后和gt bbox计算iou,该iou值作为label,用于表示有没有物体且当有物体时候预测bbox的准确性,对于负样本其label=0。上图中的其实就是表示负样本,可以看出忽略样本全程都是不参与loss计算的。
(1) 高分辨率图片进行分类器微调训练
YOLOv2的训练主要包括三个阶段。第一阶段就是先在ImageNet分类数据集上预训练Darknet-19,此时模型输入为 224x224,共训练160个epochs。然后第二阶段将网络输入调整为448x448,继续在ImageNet数据集上finetune分类模型,训练10个epochs,此时分类模型的top-1准确度为76.5%,而top-5准确度为93.3%。第三个阶段就是修改Darknet-19分类模型为检测模型,并在检测数据集上继续finetune网络。在imagenet训练两次的原因是直接切换分辨率,检测模型可能难以快速适应高分辨率。
(2) 多尺度训练
由于YOLOv2模型中只有卷积层和池化层,所以YOLOv2的输入可以不限于416x416大小的图片。为了增强模型的鲁棒性,YOLOv2采用了多尺度输入训练策略,具体来说就是在训练过程中每间隔一定的iterations之后改变模型的输入图片大小,例如{320,352,...608},在训练过程,每隔10个iterations随机选择一种输入图片大小,然后只需要修改对最后检测层的处理就可以重新训练。
整体训练流程为:
对于每个预测框,首先根据类别置信度确定其类别与分类预测分值,将类别概率和confidence值相乘。然后根据阈值(如0.5)过滤掉阈值较低的预测框。对于留下的预测框进行解码(具体见1.4bbox编解码小节),根据先验框得到其真实的位置参数。解码之后,一般需要根据置信度进行降序排列,然后仅保留top-k个预测框。最后就是进行NMS算法,过滤掉那些重叠度较大的预测框,最后剩余的预测框就是检测结果了。
以上是本文所有组件所带来的性能提升。
(1) batch norm
BN的作用主要是加快收敛,避免过拟合,将其应用于yolov2可以提升2.4%mAP
(2) new network和hi-res classifier
new network指的是所提出的darknet19,hi-res classifier高分辨率分类微调训练有助于检测模型快速适应高分辨率,将其应用于yolov2可以提升3.7%mAP
(3) convolutional+anchor boxes
在yolov1中其采用全连接层输出,并且设置每个网格预测B个物体,边界框的wh预测是直接相对整图大小归一化的,而由于各个图片中存在不同尺度和长宽比的gt bbox,YOLOv1要学习适应不同物体的形状是比较困难的(因为本身cnn就不具有尺度不变性),这会导致YOLOv1在精确定位方面表现较差。yolov2参考了faster rcnn或者ssd的anchor bbox做法来提高召回率,具体是head模块输出改为全卷积形式,并且每个网格设置n个anchor bbox,此时就可以真正实现每个网格预测多个物体。
将anchor boxes替换掉yolov1中的B个预测框做法后,YOLOv1的mAP有稍微下降,但是召回率大大提升,由原来的81%升至88%,主要原因就是相比yolov1的最多只能预测98个边界框(7x7x2),变成了最多可以预测13x13xnum_anchor个边界框,理论召回率虽然提升了,但是训练策略依然采用的是yolov1中的,正负样本不平衡问题应该是加剧了导致mAP稍有下降。
(4) dimension priors
其是指的kmean自动聚类算法,其最大优点是anchor不再需要人为设置,更加智能。
(5) location prediction
其是指本文所提的bbox编解码方式。
疑问:为何作者不直接采用ssd的编解码方式而要重新设计?在论文中作者也进行了解释,原因是采用ssd的xy预测方式,在刚开始训练时候其没有任何范围限制,可能预测bbox和gt bbox偏差很多,导致模型不稳定性,在训练时需要很长时间来预测出正确的offsets。而如果换成基于网格左上角预测,其会限制在0~1范围内,可以使得前期更加稳定,并且通过引入1t < 128000这个loss,可以进一步提高收敛速度。
那么ssd算法中训练前期会不会出现不稳定情况?我个人觉得也会但是没有yolo中的严重,因为匹配策略不一样。ssd是完全基于iou阈值进行匹配,只要anchor设置和iou阈值设置合适就可以保证正样本比较多,会更稳定。而yolo匹配策略仅仅是每个gt bbox一定匹配唯一的anchor,不管你anchor设置的如何,其正样本非常少,前期训练肯定更加不稳定。
(6) passthrough
其是指在head部分所提的从高分辨率特征图变成低分辨率特征图的特征融合操作
(7) multi-scale
其是指多尺度训练
(8) hi-res detector
其是指检测器训练采用更高分辨率的图片进行训练可以进一步提高性能
综合性能对比如下:
通过分析yolov1的召回率低、定位精度差的缺点,并且结合ssd的anchor策略,提出了新的yolov2算法,在BN、新网络darknet19、kmean自动anchor聚类、全新bbox编解码、高分辨率分类器微调、多尺度训练和passthrough的共同作用下,将yolov1算法性能进行了大幅提升,同时保持了yolov1的高速性能。