arxiv 论文地址
yolov2代码pytorch版本
☀️ YOLOv2和YOLO9000算法在2017年CVPR上被提出,重点解决YOLOv1召回率和定位精度方面的误差。YOLOv2 是在YOLOv1的基础上改进得到,改进之处主要有:Batch Normalization (批量归一化)、High Resolution Classfier(高分辨率的分类器)、Convolutional With Anchor Boxes (带锚框的卷积)、Dimension Clusters (维度聚类)、Direct location prediction (直接位置预测)、Fine-Grained Feature (细粒度特性)、Multi-Scale Training (多尺度训练),它的特点是“更好,更快,更强”。
YOLO9000 的主要检测网络也是YOLO v2,同时使用WordTree来混合来自不同的资源的训练数据,并使用联合优化技术同时在ImageNet和COCO数据集上进行训练,目的是利用数量较大的分类数据集来帮助训练检测模型,因此,YOLO 9000的网络结构允许实时地检测超过9000种物体分类,进一步缩小了检测数据集与分类数据集之间的大小代沟。
❄️BN是什么?
批量归一化,即在卷积或池化之后,激活函数之前,对每个数据输出进行标准化,使得均值为0,方差为1。这样网络就不需要每层都去学数据的分布,收敛会更快。效果示意图如下:
BN 参考论文:《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》
❄️BN带来的好处
(1) 减轻了对参数初始化的依赖,利于调参。
(2) 让网络提高了收敛性。通过标准化上层输出,均衡输入数据分布,训练更快,可以使用更高的学习率(learning rate)和衰减(decay)
(3) BN一定程度上增加了泛化能力,消除了对其他形式的正则化(regularization)的依赖,代替dropout解决过拟合问题。
(4) 通过标准化输入,降低激活函数(Activation Function)在特定输入区间达到饱和状态的概率,避免梯度弥散(Gradient Vanishing)问题;
2️⃣改进方法二:高分辨率的分类器(High resolution classifier)。
所有最先进的检测方法都使用在ImageNet上预先训练的分类器(提取浅层特征),而非完全重新训练模型,从而提高效率。
从AlexNet开始,大多数分类器都对小于256 × 256的输入图像进行操作. YOLOv1在预训练时采用的是224*224的输入(在ImageNet数据集上进行),然后在检测的时候采用448*448的输入,这会导致从分类模型切换到检测模型的时候,模型还要适应图像分辨率的改变。
YOLOv2则将预训练分成两步:先用224*224的输入在ImageNet数据集训练分类网络,大概160个epoch(将所有训练数据循环跑160次)后将输入调整到448*448,再训练10个epoch(这两步都是在ImageNet数据集上操作)。然后利用预训练得到的模型在检测数据集上fine-tuning。这样训练得到的模型,在检测时用448*448的图像作为输入可以顺利检测。这个高分辨率的分类网络使我们的mAP增加了近4%。
☀️ fine-tuning: 用已经训练好的模型,加上自己的数据集,来训练新的模型。即使用别人的模型的前几层,来提取浅层特征,而非完全重新训练模型,从而提高效率。一般新训练模型准确率都会从很低的值开始慢慢上升,但是fine-tuning能够让我们在比较少的迭代次数之后得到一个比较好的效果。
RPN网络一个最重要的概念就是anchor,启发了后面的SSD和YOLOv2等算法,虽然SSD算法称之为default box,也有算法叫做prior box,其实都是同一个概念,他们都是anchor的别称, 简单理解为在图像上预设好的不同大小,不同长宽比的参照框。
我们不再需要计算Haar、Hog等特征,直接让神经网络输出,
每个anchor认为自己是否含有物体的概率,物体中心点与anchor自身的中心点位置的偏移量,以及相对于anchor宽高的比例。
faster- rcnn 系列选择的先验框的比例都是常规的(如三个scale大小,每个大小对应三种比例1:1、1:2、2:1,一共9种先验框),并不一定完全适合数据集。Yolov2用标注的检测框进行聚类提取先验框。
作者应该是把加号误写成了减号。anchor的预测公式来自于Faster-RCNN(原文链接), 原文给的公式如下图:
公式中,符号的含义解释一下: x x x是坐标预测值, x a x_a xa 是anchor坐标(预设固定值), x ∗ x^∗ x∗是坐标真实值(标注信息),其他变量 y,w,h以此类推,t 变量是偏移量。然后把前两个公式变形,就可以得到正确的公式:
x = ( t x ∗ w a ) + x a x=\left(t_{x} * w_{a}\right)+x_{a} x=(tx∗wa)+xa
y = ( t y ∗ h a ) + y a y=\left(t_{y} * h_{a}\right)+y_{a} y=(ty∗ha)+ya
σ \sigma σ表示的是sigmod函数,其值域在0到1之间。
感受野是啥?
卷积的过程就是对特征进行浓缩,浓缩后的一个点代表原始输入图片的多大区域 就相当于感受野。
❔ 如果堆叠3个3*3的卷积层,并且保持滑动窗口步长为1,其感受野就是7*7的了。这跟一个使用7*7卷积核的结果是一样的,那为什么非要堆叠3个小卷积呢?
假设输入大小都是 h ∗ w ∗ c h*w*c h∗w∗c,并且都使用c个卷积核(得到c个特征图),可以来计算
一下其各自所需参数
很明显,堆叠小的卷积核所需的参数更少一些,并且卷积过程越多,特征提取
也会越细致,加入的非线性变换也随着增多,还不会增大权重参数个数,这就
是VGG网络的基本出发点,用小的卷积核来完成体特征提取操作。
❄️ 最后一层时感受野太大了,小目标可能丢失了,需融合之前的特征。
其中,Passthrough Module 构造如下图所示,它将相邻的部分作为深度叠加上去,这样便能在缩小的特征途中仍然保有其细部特征。
重组层采用每个交替像素并将其放入不同的通道。让我们以 4x4 像素的单个通道为例,如下所示。重组层将大小减小到一半,并创建了 4 个通道,相邻像素位于不同的通道中。因此,来自 Conv13_512 的 Reorg 层的输出将为 2048x13x13。
然而现今的物件侦测系统大多依赖VGG-16 作为特征提取器。VGG-16 是一个非常强大的分类器,但它的网路结构过于庞大且复杂。
YOLO 采用的是较快的GoogLeNet 架构,虽说整体mAP 表现较VGG-16 差一些,但是却换来更快速、更少的预测运算。
YOLOv2 中,使用的是一个全新的架构: Darknet-19
YOLOV2的网络结构详细版
我们最后的模型叫做Darknet-19,有19个卷积层和5个最大池层。详细描述请参见下表。Darknet-19只需要55.8亿次操作就可以处理一幅图像,但在ImageNet上却能达到72.9%的前一精度和91.2%的前五精度。
☀️ Darknet 的实际输入 416 ∗ 416 416*416 416∗416(上图中的224*224只是举个例子),通过5次降采样后,输出为 13 ∗ 13 13*13 13∗13 ( 416 / ( 2 5 ) = 13 416/(2^5)=13 416/(25)=13)
Darknet 没有全连接层,但有5次降采样。为什么去掉全连接层了呢?
因为全连接层容易过拟合,训练慢。(参数太多)如下图,YOLOv1中通过全连接层将 7 ∗ 7 ∗ 1024 7*7*1024 7∗7∗1024的特征图变换为 7 ∗ 7 ∗ 30 7*7*30 7∗7∗30的特征图。但是这种变换完全可以通过一个 3 ∗ 3 3*3 3∗3的卷积核做到,从而节省参数。
所有卷积核的大小有两种 3 ∗ 3 3*3 3∗3和 1 ∗ 1 1*1 1∗1。为什么要选择这种大小的卷积核呢?
3 ∗ 3 3*3 3∗3的卷积核借鉴了VGG的思想: 当使用比较小的卷积核做卷积时,比较省参数,感受野大,因此训练的模型会更好。用 1 ∗ 1 1*1 1∗1卷积核来降维节省参数,训练的快。
这部分前面有提到,就是训练处理的小trick。
这里的Training for Classification都是在 ImageNet上进行预训练。
YOLOv2的训练主要包括三个阶段:
第一阶段:在ImageNet分类数据集上从头开始预训练Darknet-19,训练160个epoch。输入图像的大小是224*224,初始学习率为0.1。另外在训练的时候采用了标准的数据增加方式比如随机裁剪,旋转以及色度,亮度的调整等。
第二阶段:将网络的输入调整为448*448,继续在ImageNet数据集上fine-tuning分类模型,训练10个epoch。参数的除了epoch和learning rate改变外,其他都没变,这里learning rate改为0.001。
第三阶段:修改Darknet-19分类模型为检测模型,并在检测数据集上继续fine-tuning网络。
网络修改包括:移除最后一个卷积层、global avgpooling层以及softmax层,新增了三个3*3* 2014卷积层,同时增加了一个passthrough层,最后使用1*1卷积层输出预测结果。
在YOLOv1中,类别概率是由cell来预测的,一个cell对应的两个box的类别概率是一样的,但是在YOLOv2中,类别概率是属于box的,每个box对应一个类别概率,而不是由cell决定,因此这边每个box对应25个预测值(5个座标加20个类别值)
(1)YOLOv1: S ∗ S ∗ ( B ∗ 5 + C ) = > 7 ∗ 7 ( 2 ∗ 5 + 20 ) S*S* (B*5 + C) => 7*7(2*5+20) S∗S∗(B∗5+C)=>7∗7(2∗5+20)
其中B对应Box数量,5对应边界框的定位信息(w,y,w,h)和边界框置信度(Confidience)。分辨率是7*7,每个Cell预测2个Box,这2个Box共用1套条件类别概率(1*20)。
(2)YOLOv2: S ∗ S ∗ K ∗ ( 5 + C ) = > 13 ∗ 13 ∗ 5 ( 5 + 20 ) S*S*K* (5 + C) => 13*13*5(5+20) S∗S∗K∗(5+C)=>13∗13∗5(5+20)
分辨率提升至13*13,对小目标适应性更好,借鉴了FSRCNN的思想,每个Cell对应K个Anchor box(YOLOv2中K=5,记得上文提到过聚类得到5个先验框),每个Anchor box对应1组条件类别概率(1*20)
论文里没有说明先验框匹配和loss是怎么做的,所以有很多细节可以参考 YOLO在TensorFlow上的实现darkflow(见yolov2/train.py)
接下来的损失函数和匹配原则参考这篇文章
❄️ 损失函数
(1)YOLOv2和yolov1一样,每个网格单元只选择IOU最大的先验框负责预测,计算坐标误差、置信度误差(此时target为1)以及分类误差。而其它的边界框只计算置信度误差(此时target为0)。
(2) YOLOv2和YOLOv1的损失函数一样,为均方差函数。但实现起来更为复杂。
Loss计算公式:
λ noobj ∑ i = 0 l.h*l.w ∑ j = 0 l.n 1 i j noobj ( C i − C ^ i ) 2 + λ o b j ∑ i = 0 l.h*l.w ∑ j = 0 l . n 1 i j o b j ( C i − C ^ i ) 2 \lambda_{\text {noobj }} \sum_{i=0}^{\text {l.h*l.w }} \sum_{j=0}^{\text {l.n }} 1_{i j}^{\text {noobj }}\left(C_{i}-\hat{C}_{i}\right)^{2}+\lambda_{o b j} \sum_{i=0}^{\text {l.h*l.w }} \sum_{j=0}^{l . n} 1_{i j}^{o b j}\left(C_{i}-\hat{C}_{i}\right)^{2} λnoobj ∑i=0l.h*l.w ∑j=0l.n 1ijnoobj (Ci−C^i)2+λobj∑i=0l.h*l.w ∑j=0l.n1ijobj(Ci−C^i)2
+ λ class ∑ i = 0 l.w*l.h ∑ j = 0 l.n 1 i j obj ∑ c ∈ classes ( p i ( c ) − p ^ i ( c ) ) 2 +\lambda_{\text {class }} \sum_{i=0}^{\text {l.w*l.h }} \sum_{j=0}^{\text {l.n }} 1_{i j}^{\text {obj }} \sum_{c \in \text { classes }}\left(p_{i}(c)-\hat{p}_{i}(c)\right)^{2} +λclass ∑i=0l.w*l.h ∑j=0l.n 1ijobj ∑c∈ classes (pi(c)−p^i(c))2
+ λ coord ∑ i = 0 l.w*L.h ∑ j = 0 j . n 1 i j o b j ( 2 − w i ∗ h i ) [ ( x i − x ^ i ) 2 + ( y i − y ^ i ) 2 + ( w i − w ^ i ) 2 + ( h i − h ^ i ) 2 ] +\lambda_{\text {coord }} \sum_{i=0}^{\text {l.w*L.h }} \sum_{j=0}^{j . n} 1_{i j}^{o b j}\left(2-w_{i} * h_{i}\right)\left[\left(x_{i}-\hat{x}_{i}\right)^{2}+\left(y_{i}-\hat{y}_{i}\right)^{2}+\left(w_{i}-\hat{w}_{i}\right)^{2}+\left(h_{i}-\hat{h}_{i}\right)^{2}\right] +λcoord ∑i=0l.w*L.h ∑j=0j.n1ijobj(2−wi∗hi)[(xi−x^i)2+(yi−y^i)2+(wi−w^i)2+(hi−h^i)2]
+ 0.01 ∗ ∑ i = 0 l.włl.h ∑ j = 0 j . n 1 i j n o o b j [ ( p j x − x ^ i ) 2 + ( p j y − y ^ i ) 2 + ( p j w − w ^ i ) 2 + ( p j h − h ^ i ) 2 ] +0.01 * \sum_{i=0}^{\text {l.włl.h }} \sum_{j=0}^{j . n} 1_{i j}^{n o o b j}\left[\left(p_{j x}-\hat{x}_{i}\right)^{2}+\left(p_{j y}-\hat{y}_{i}\right)^{2}+\left(p_{j w}-\hat{w}_{i}\right)^{2}+\left(p_{j h}-\hat{h}_{i}\right)^{2}\right] +0.01∗i=0∑l.włl.h j=0∑j.n1ijnoobj[(pjx−x^i)2+(pjy−y^i)2+(pjw−w^i)2+(pjh−h^i)2]
W,H分别指的是特征图(13*13)的宽与高;
A指的是先验框数目(这里是5);
各个λ值是各个loss的权重系数,参考YOLOv1的loss;
第一项 :计算background的置信度误差
先计算各个预测框的IOU值,并且取IOU的最大值,如果该值小于一定的阈值(YOLOv2使用的是0.6),那么这个预测框就标记为background,需要计算noobj的置信度误差;
第二项是计算先验框与预测宽的坐标误差,但是只在前12800个iterations间计算,我觉得这项应该是在训练前期使预测框快速学习到先验框的形状;
第三项计算与某个ground truth匹配的预测框各部分loss值,包括坐标误差、置信度误差以及分类误差
⛄️匹配原则
对于某个ground truth,首先要确定其中心点要落在哪个cell上,然后计算这个cell的5个先验框与ground truth的IOU值(YOLOv2中bias_match=1),计算IOU值时不考虑坐标,只考虑形状,所以先将先验框与ground truth的中心点都偏移到同一位置(原点),然后计算出对应的IOU值,IOU值最大的那个先验框与ground truth匹配,对应的预测框用来预测这个ground truth。
在计算obj置信度时,在YOLOv1中target=1,而YOLOv2增加了一个控制参数rescore,当其为1时,target取预测框与ground truth的真实IOU值。对于那些没有与ground truth匹配的先验框(与预测框对应),除去那些Max_IOU低于阈值的,其它的就全部忽略,不计算任何误差。这点在YOLOv3论文中也有相关说明:YOLO中一个ground truth只会与一个先验框匹配(IOU值最好的),对于那些IOU值超过一定阈值的先验框,其预测结果就忽略了。这和SSD与RPN网络的处理方式有很大不同,因为它们可以将一个ground truth分配给多个先验框。
尽管YOLOv2和YOLOv1计算loss处理上有不同,但都是采用均方差来计算loss。
另外需要注意的一点是,在计算boxes的和误差时,YOLOv1中采用的是平方根以降低boxes的大小对误差的影响,而YOLOv2是直接计算,但是根据ground truth的大小对权重系数进行修正:l.coord_scale * (2 - truth.w*truth.h),这样对于尺度较小的boxes其权重系数会更大一些,起到和YOLOv1计算平方根相似的效果。
WordNet 是一个语言库,它将名词间的关系建构起来,而ImageNet 也是参照WordNet 进行标注。然而,语言十分的复杂,每一个名词也无法单纯归在某一类别之下,如: 「狗」(dog)同时是「犬」(canine)类别以及「家畜」类别之下。也因此,WordNet 并非是一个树状结构,而是一个有向图结构(directed graph)。
作者们并不采用整个WordNet 的图结构,而是从中抽取其视觉名词重新制作一个树状结构。
每一个视觉名词都可以循着一条( 或多条) 路径到达root ( physical object物理物件),如果每一个名词仅一条路径到达root 者便先加入树状结构中,若有多条路径则采用最短路径。依照这样的方式建构出一个WordTree,一个视觉概念的阶层式模型(Hierarchical Model)。
一旦WordTree 被建构出来,那么我们便可以计算出每一个节点之机率以及下位节点之条件机率 :
原本的Label Space 是1000 维的空间,现在引进了WordTree 后,会变成一个1369 维的空间。而前面有提到,softmax 必须要在类别互斥的前提下进行,因此在机率计算上面也要有所改变,一个节点的所有下位词都会进行一次softmax。
这样的作法,我们提高了label 维度,但准确度下降不多。再者,若我们给出一张不确定类别的图象( 一张看不出来种类的狗图片) 进行预测,可能在下位词( 各种类别的狗) 的预测机率会很低,但在上位词( 狗) 的部分可以有着较高的预测机率。
上述的分类预测上可以有着不错的预测,在物件侦测上仍然可以用这样的方式训练。
我们会给定一个阀值,首先侦测模型上会先预测
,来确认是否存在待预测的物件,之后随着WordTree 往下遍历每一个类别,并给给出每一个节点的机率,分裂时会往机率较高的节点往下走,最后给出一个高于阀值的节点类别作为预测类别。
我们可以使用WordTree将多个数据集以一种合理的方式组合在一起。我们只需将数据集中的类别映射到树中的synsets。图6显示了一个使用WordTree组合ImageNet和COCO标签的示例。WordNet是非常多样化的,所以我们可以对大多数数据集使用这种技术。
既然WordTree 可以扩展ImageNet 的标注,那么也可以用来结合不同的资料集,为了可以训练出一个大型的物件侦测系统,论文中利用WordTree 来结合COCO 资料及以及ImageNet 的前9000 个标注类别资料,这样庞大的资料集一共有9418 个类别。
由于ImageNet 相对来说比COCO 资料集大非常多,作者有针对COCO 进行Oversampling ( 超取样、过取样),来让两者的比例接近4:1。
利用这样的资料集来对YOLOv2 进行训练,但原本选择5 个Anchor Boxes 改为3 个。
若今天模型处理「侦测样本」时,对于分类误差我们只会分配给相同级别以上的类别,举例来说,今天如果label 是「狗」,那我们便只会将分类误差往上分配,而不会配给「狗」以下的节点。这其实很直觉,因为以下的节点该怎么分类我们完全没有资讯。
若今天要处理的是「分类样本」时,我们只需要处理Loss function 中的分类误差项即可,因此,找出拥有此分类机率最高的边界框,然后在WordTree 上计算误差即可。
除此之外,论文中还给了一个条件,预测边界框必须与真实边界框有大于的IOU。
经过这样的联合训练后,YOLO9000 可以预测超过9000种物件,并且在半监督学习的条件下学习到一些成果。(我不会说它非常厉害,因为事实上它的mAP 也还不到20%)
这篇论文中提出了两个即时的物件侦测系统–YOLOv2 以及YOLO9000。
YOLOv2 可以在多种尺寸图像中运行,并且在速度与精准度之间取得平衡; YOLO9000 则是提供一个即时框架,利用物件侦测及分类的联合训练演算法以及WordTree 资料集来侦测多达9000种物件。
WordTree 的概念可以让分类标注提供更大的运用空间,并且可以利用来进行弱监督学习,也可以利用这样的概念结合各种不同任务的资料集,对于分类有很大的助益。
参考
[论文] YOLO9000 : Better, Faster, Stronger 解读
YOLOv2——引入:Anchor+特征融合 (目标检测)(one-stage)(深度学习)(CVPR 2017)
深度学习中 Batch Normalization为什么效果好?