前言
最近因为工作需要,接触了目标检测领域的相关知识,主要学习了YOLO v1~v3的内容,主要参考的是YOLO官方提供的论文以及网上一些大佬的学习笔记。由于刚刚接触目标检测领域,有写的不对的地方还请指正。
YOLO v1
原文链接
研究背景
之前的目标检测经常被看作一个分类问题,常见的方法包括DPM(Deformable Parts Models)、R-CNN(Fast R-CNN、Faster R-CNN)等。
DPM:
采用对应类别的分类器去评估图像中各个位置和区域是否包含物体、包含那种物体。DPM采用滑动窗口(sliding window)机制,保证分类器可以遍历整张图片。
R-CNN
采用区域预测方式(region proposals methods):首先生成潜在的bbx(bounding box),再通过分类器判断是否包含物体,最后再去判断物体的类别。
缺点:每个部分分开训练,优化困难。
注:这部分翻译自YOLO v1的官方论文,没有更加深入地了解,以后有时间会进行更加细致的了解。
YOLO v1介绍
概述
YOLO将目标检测抽象成了一个简单的回归问题,输入图像,直接输出边界框坐标和类别概率,用一个系统完成了所有工作。
优点
- 检测速度快,YOLO的检测框架仅包含一个卷积神经网络,pipline非常简单;
- YOLO采用全局图像进行检测,不容易产生背景误判;
- YOLO学习到物体更加泛化的特征,在应要到新领域的时候,表现出较好的泛化性。
实现方法
这里先给一张论文中的图,展示YOLO的基本思想:
YOLO算法首先将输入图像分割成S*S的网格(grid cell),每个网格都会预测B个bbx,每个bbx又会包含5个预测值 :(x, y), (w, h)和置信度。
(x, y) :框的中心(相对于网格边界);
(w, h):bbx相对于整个图片的宽和高;
置信度: P r ( O b j e c t ) ∗ I O U t r u t h p r e d Pr(Object)*IOU_{truth}^{pred} Pr(Object)∗IOUtruthpred(若没有物体,置信度为0,否则置信度则为预测边框和真实边框的IOU(intersection over union,交并比))。
某个网格包含某一类别物体的置信度用 P r ( C l a s s ∣ O b j e c t ) Pr(Class|Object) Pr(Class∣Object)来表示,最终,一个bbx属于某个特定类别的置信分数可以表示为:
P r ( C l a s s ∣ O b j e c t ) ∗ P r ( O b j e c t ) ∗ I O U p r e d t r u t h = P r ( C l a s s i ) ∗ I O U p r e d t r u t h Pr(Class|Object)*Pr(Object)*IOU_{pred}^{truth}=Pr(Class_i)*IOU_{pred}^{truth} Pr(Class∣Object)∗Pr(Object)∗IOUpredtruth=Pr(Classi)∗IOUpredtruth
因此,YOLO最后的输出是 [ S , S , B ∗ 5 + C ] [S, S, B*5+C] [S,S,B∗5+C]的张量!!!
网络设计
YOLO的架构沿用了GoogleNet的形式,完整版的模型中包含24个卷积层和2个全连接层;快速版的YOLO(fast YOLO)减少了卷积层(9层),其他细节均是一样的。
注1:这里存一个疑,一个卷积核难道不应该是 7 ∗ 7 ∗ 192 7*7*192 7∗7∗192吗?
注2: 1 ∗ 1 1*1 1∗1的卷积核作用:特征降维,节省计算量;增加模型的非线性表达能力。
训练
1. 预训练
基于ImageNet 1000-class数据集,将YOLO中的前20个卷积层连接上一个平均池化层和全连接层进行了预训练。训练图片的像素为224*224。
2. 训练
在预训练网络模型的基础上加入了四个卷积层和两个全连接层,采用随机权重初始化策略,并将输入图像的像素从224224增加到448448。
3. 激活函数
- 最后一层采用线型激活函数;
- 其它层采用leaky rectified非线性激活函数:
ϕ ( x ) = { 0.1 x , o t h e r w i s e x , i f x > 0 \phi(x) = \lbrace_{0.1x,\space \space \space \space otherwise}^{x,\space \space \space \space \space \space \space \space \space \space if x> 0} ϕ(x)={0.1x, otherwisex, ifx>0
4. 损失函数
引入了 λ c o o r d \lambda_{coord} λcoord和 λ n o o b j \lambda_{noobj} λnoobj来增加边界框未知的损失并减少不包含无边的边界框置信预测的损失。这里设置 λ c o o r d = 5 \lambda_{coord} = 5 λcoord=5和 λ n o o b j = 0.5 \lambda_{noobj} = 0.5 λnoobj=0.5。这里盗用一张图,人家解释的非常好。
因此:
- 该损失函数只有当某一个对象出现在网格单元中才会对分类错误进行惩罚;
- 该损失函数仅在网格单元的bbx对标准答案负责时,才会对bbx的坐标误差进行惩罚。
5. 学习策略
训练集:PASCAL VOC 2007和2012数据集
超参数:Batch size=64;momentum=0.9;decay=0.0005
学习率设置:
1)、 第一个迭代,lr从10-3提升到10-2(如果学习率过大,模型发散);
2)、 以10-2的学习率训练75个迭代;
3)、 减少到10-3训练30个迭代;
4)、 减少到10-4再训练30个迭代。
防止过拟合:
1)、 Dropout:在第一个全连接层后添加dropout层,rate设为0.5;
2)、 数据增强:对原始图像进行随机放缩和平移;随机调整图像的曝光度。
YOLO v1的局限性
1、YOLO 具有很强的空间约束,这限制了YOLO对于相邻物体的检测能力。当相邻的物体数量过多且面积过小的物体时,YOLO将很难进行检测;
2、如果图片中出现新的、不常见的长宽比的图像时,YOLO的泛化能力较弱;
3、YOLO 对于大的bbx和小的bbx采用相同的损失函数,尽管采用了平方根技巧进行了优化,但是大的bbx的小错误可能没什么影响,当小的bbx如果有稍微的错误偏差将极大地影响到IoU。因此,YOLO中的主要误差属于定位误差。
补充
YOLO1中运用了非极大值抑制(Non-Maximum Suppression, NMS),这里简单提一下NMS在YOLO1中的应用。
YOLO v1每张图片包含 7 ∗ 7 ∗ 2 7*7*2 7∗7∗2个bbx,以Dog类别为例:
Step1:对于Dog类别,将98个bbx按照预测概率从高到低排序(对于极小概率框,可以提前将其概率设为0);
Step2:排序后,第一个bbx的置信度最高,然后依次比较后面的bbx与第一个bbx的IoU结果,剔除IoU超过阈值的bbx,保留低于阈值的bbx;
Step3:对于第一轮扫描后剩下的bbx,以次大概率为基准再次进行比较,重复Step2直到完成所有的扫描;
Step4:对于其他的类别重复进行Step1~Step3的操作,并进行纵向跨类比较,得出最后的检测结果。
注:跨类纵向比较是因为,在经历Step1~Step3后,仅能得出在某一类别中置信度最高的bbx,但是该bbx可能在其他类别中的概率也为最大且比这个类别的概率更大,因此在最终判定的时候需要进行纵向的跨类比较。
YOLO v2
原文链接
“YOLO9000: Better, Faster, Stronger”获得了2017CVPR最佳论文提名,文章中主要针对YOLO v1提出了两种结构上的优化——YOLO v2和YOLO9000,在保证处理速度的前提下,从预测更准确(Better),速度更快(Faster),识别对象更多(Stronger)这三个方面进行了改进。
YOLO v2介绍
YOLO v1的缺点:
1、对比Faster R-CNN,YOLO v1会产生大量的定位误差;
2、YOLO v1具有较低的召回率。
计算机视觉领域,通常更好的性能来源于训练更大、更深的网络,或者将多个模型组合在一起。但是YOLO v2为了保证检测速度,着重于网络的轻量化,具体的改进如下表所示:
注:表中罗列的方法大多数都能显著提升mAP,但是两种方法例外,一种是采用anchor box,另一种是使用新网络。其中anchor box虽然没有改变mAP,但是显著提升了召回率,而使用新网络则减少了33%的计算量。
具体改进措施
1、Batch Normalization
使用BN对网络进行优化,不仅提高了网络的收敛性,同时消除了对其他形式的正则化的依赖(可以去除v1中的Dropout层,且不会产生过拟合)。在YOLO v1每个卷积层增加BN层,不仅使mAP提高了2%,而且使得模型正则化。
BN简介:
Q1:为什么要归一化
A1:在神经网络的训练过程中,一般会将输入样本特征进行归一化处理,使数据变为均值为0,标准差为1的分布或者范围在0~1的分布。因为当我们没有将数据进行归一化的话,由于样本特征分布较散,可能会导致神经网络学习速度缓慢甚至难以学习。
Q2:BN的作用
A2:2015年Google提出了BN,因为传统的神经网络中,仅对输入层的数据进行归一化处理,没有在中间层进行归一化处理。考虑到数据在经历矩阵乘法和激活函数后,数据分布很可能再次变得发散(一般是整体分布逐渐往非线性激活函数的取指区间的上下限两端靠近),随着网络的加深,会导致反向传播过程中低层神经网络的梯度消失,这也是深层网络收敛越来越慢的本质原因。
BN的效果就是通过一定的规范化手段将每层神经网络任意神经元的输入值强行拉回均值为0、方差为1的标准正态分布。
BN在神经网络训练中的作用可以总结如下:
1、 加快模型的收敛速度;
2、 可以省略其他正则化处理方法,例如Dropout、L1、L2…
3、 提高模型的训练精度。
Q3:BN的流程
A3:BN作为神经网络的一层,放在激活函数之前
Step1:计算一个batch训练数据的均值和方差;
μ B = 1 m ∑ i = 1 m x i \mu_B=\frac{1}{m}\sum_{i=1}^m {x_i} μB=m1i=1∑mxi
σ 2 = 1 m ∑ i = 1 m ( x i − μ B ) 2 \sigma^2=\frac{1}{m}\sum_{i = 1}^m {(x_i-\mu_B)^2} σ2=m1i=1∑m(xi−μB)2
Step2:根据均值和方差对一个batch的数据做归一化,ϵ代表微小正数,防止分母为0;
x i ^ = x i − μ B σ B 2 + ϵ \widehat{x_i}=\frac{x_i-\mu_B}{\sqrt{\sigma_B^2+\epsilon}} xi =σB2+ϵ xi−μB
Step3:尺度变换和偏移,BN的精髓:将归一化的数据根据训练得到的尺度因子γ和平移因子β进行变换,这是因为归一化的数据会呈现正态分布,从而降低网络的表达能力。
y i = γ x i ^ + β y_i=\gamma\widehat{x_i}+\beta yi=γxi +β
2、高分辨率分类器
YOLO v2将网络的预训练分成了两步:
1、 先用 224 ∗ 224 224*224 224∗224的输入从头训练网络;
2、 再将输入调整到 448 ∗ 448 448*448 448∗448,再训练10个epoch进行fine tuning(微调)。
注:1、2两步均在ImageNet数据集上操作,与训练的时候采用 448 ∗ 448 448*448 448∗448
的高分类样本对模型进行微调,可以使网络逐渐适应 448 ∗ 448 448*448 448∗448的分辨率,从而缓解分辨率突然切换造成的影响。
3、引入Anchor Box
YOLO v1通过全连接层直接预测bbx的坐标位置,v2为了简化问题,借鉴了Faster R-CNN中利用区域候选网络(RPN)预测Anchor Box的思想,引入了Anchor Box。
1、移除了v1中的全连接层,并去掉了最后一个池化层,使最后一个卷积层的输出具有更高的分辨率;
2、缩小网络用于处理416416的输入图像,这么做的原因是希望得到的feature map具有奇数大小的宽和高。(奇数大小的宽和高会使每个feature map在划分cell的时候只有一个center cell),网络最终从416416的输入变成13*13大小的feature map输出;
3、对于每个网格单元,不再使用B个数量的bbx,而是通过维度聚类(Dimension Clusters)产生K个anchor box。
4、维度聚类(Dimension Clusters)
Faster R-CNN中anchor box的大小和比例是根据经验手动设定的,为了帮助网络实现更好地预测,YOLO v2采用k-means的方式对训练集的bbx做聚类。标准的k-means(欧氏距离),误差和box的尺寸息息相关(尺寸越大,误差越大),因此YOLO v2定义了新的距离函数:
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)
综合考虑了模型复杂度和召回率,YOLO v2中选择了k=5,也就是聚类出5个anchor box。
5、约束预测边框的位置
为了解决引入Anchor Box导致的网络训练初期不稳定的现象(作者认为,由于anchor box的边界没有任何的限定条件,因此box的中心可以出现在任何位置,从而导致模型不稳定)。因此,YOLO调整了预测共识,将预测边框的中心约束在特定的网格单元内:
b x = σ ( t x ) + c x b_x=\sigma(t_x)+c_x bx=σ(tx)+cx
b y = σ ( t y ) + c y b_y=\sigma(t_y)+c_y by=σ(ty)+cy
b w = p w e t w b_w=p_we^{t_w} bw=pwetw
b h = p h e t h b_h=p_he^{t_h} bh=pheth
P r ( O b j e c t ) ∗ I o U ( b , O b j e c t ) = σ ( t o ) Pr(Object)*IoU(b, Object) = \sigma(t_o) Pr(Object)∗IoU(b,Object)=σ(to)
其中, b x , b y , b w 和 b h b_x,b_y,b_w和b_h bx,by,bw和bh分别是预测边框的中心位置和宽高, c x 和 c y c_x和c_y cx和cy表示表示图片左上角到网格单元的距离, p w 和 p h p_w和p_h pw和ph是anchor box的宽和高, σ \sigma σ代表sigmoid函数, 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是需要学习的参数。
6、引入Passthrough layer
YOLO v2中最后输出1313的feature map,这会导致一些较小对象的特征已经不明显甚至被忽略掉了。为了更好地检测出一些较小的目标对象,最后输出的特征图需要保留更细节的信息。
举例:
在最后一个池化层之前,feature map的大小是2626512,这时候做两步处理,一个是经过池化+卷积,输出1313*1024的feature map;另一个是一拆成四,passthrough到池化层之后,两者叠加,作为最后的输出feature map。这里盗用一张图,帮助大家理解。。。
7、多尺度图像训练
YOLO v2可以输入任意尺寸的图像。保持下采样的倍数是32,控制输入图像的尺寸为{320,352,384,…,608},每10个batch更换一种尺寸进行训练,从而增强网络对于不同大小图像的鲁棒性。
网络结构与训练
1、框架改变
YOLO v1采用GoogleNet架构,比VGG-16快,但是精度稍低于VGG-16。
YOLO v2采用Darnet-19的结构,包含19个卷积层和5个最大池化层,每次池化后增加1倍的通道数量。
2、训练过程
1、 阶段一:Darknet-19预训练,模型输入为224*224,共训练160个epochs;
2、 阶段二:调整Darknet-19网络输入,继续在ImageNet数据集上fine tune分类模型,训练10个周期。
3、 阶段三:修改Darknet-19分类模型为检测模型,移除最后一个卷积层、池化层和Softmax层,新增三个卷积层 ( 3 ∗ 3 ∗ 104 ) (3*3*104) (3∗3∗104),同时增加了一个passthrough层,最后使用 1 ∗ 1 1*1 1∗1卷积层输出预测结果。输出结果的通道数为:num_anchors * (5 + num_classes)。监测模型的具体结构如下所示:(这里盗用了一下别人的图,方便大家理解)
注:25层route 16,表示16层的输出 ( 26 ∗ 26 ∗ 512 ) (26*26*512) (26∗26∗512)经过passthrough层,26层 1 ∗ 1 1*1 1∗1卷积降低通道数为64,输出 26 ∗ 26 ∗ 64 26*26*64 26∗26∗64,27层拆分输出 13 ∗ 13 ∗ 256 13*13*256 13∗13∗256,28层将27层的输出与24层的输出叠加,输出 13 ∗ 13 ∗ 1280 13*13*1280 13∗13∗1280。
3、误差函数
YOLO官方提供的论文中除了v1版本给出了误差函数的表达式,其余都没有给出,这里还是找了网上大神给出的误差函数表达式,方便大家理解:
YOLO9000
存在的问题:
目标检测数据集是非常有限的,相比于分类和标定的数据集,目标检测数据集的检测类别过少。
YOLO v2 → YOLO9000
YOLO9000基于YOLO v2的模型结构,将分类数据集和检测数据集进行了结合,扩展了v2可以检测的种类。
方法
构造WordTree
Q1:为什么要构造WordTree?
A1:COCO检测数据集的分类是粗糙的,例如猫/狗;ImageNet是细分类,例如哈士奇/泰迪。这意味着两个数据集中的对象类别不是完全的互斥关系,因此需要采用一种多标签分类模型。
Q2:WordTree的结构是什么:
A2:WordTree的构建参考了WordNet的思路,作者将这种有向图结构改成了树结构,其构建方式如下:
Step1:遍历训练集中所有的对象,并在WordNet中找到相应的节点;
Step2:对于每个名词,在WordNet找到从节点位置到根节点的路径,并添加到WordTree中;
Step3:对于有多条路径的名词(少数),选择最短路径添加。
此时,要预测一个结点的概率,可以根据WordTree,将该节点到根节点的条件概率依次相乘即可,例如:
Q3:实验的设置
A3:数据集:ImageNet 1K 和 WordTree 1K(根据ImageNet 1K生成的WordTree,1396个类别);
Softmax层:传统的分类结构在最后一层统一使用Softmax;WordTree结构采用多个Softmax分类器,每一个分类器计算一个根节点下互斥词的Softmax。
提出了联合训练法(Joint classification and detection)
数据集:
ImageNet和COCO,4 : 1,考虑到ImageNet样本比COCO多很多,COCO样本会适当做一些过采样。
Anchor box:
YOLO v2采用5个,为了减少计算量,YOLO9000采用3个;
输入输出:
YOLO v2和YOLO9000的输入都是4164163;
YOLO v2的输出是1313(5 * (4+1+20));
YOLO9000的输出是1313(5 * (4+1+9418))
误差传播:
对于分类样本,只计算分类误差;对于正常的检测图片,正常反向传播(分类损失仅在节点所对应的路径上进行反向传播)
YOLO v3
原文链接
YOLO v3的论文相比v2和v1的内容显得更加随意了一点(感觉像作者喝多了写的。。。),前面的废话就不过说了,直接来看YOLO v3所做出的改进。
YOLO v3的结构
这里要感谢一位大神,给出了YOLO v3的结构图(找了好久才找到。。。。):
DBL: YOLO3的基本组件,卷积 + BN + Leaky ReLU;
Resn: 残差组件,表示该res_block中含有多少个res_unit;
Concat: 张量拼接,类似YOLO v2中的passthrough层,可以扩充张量的维度(区别Resnet中的add,add不扩充维度)。
细节分析
1、YOLO v3使用了了Darknet-53的前52层(没有全连接层),使用了大量的残差网络进行跳层连接;去除了池化层,完全采用卷积网络中的stride来实现降采样(stride=2);
2、为了加强算法对小目标检测的精度,YOLO v3输出三个尺寸的feature map,三条预测支路也采用全卷积的结构,且最后输出的特征维度是255(3*(80+4+1),COCO数据集80类,3个bbx)
这里着重分析一下三条支路!!!
卷积网络在79层后,经过下方几个黄色的卷积层得到第一种尺度的检测结果。相比输入图像,这里用于检测的feature map有32倍的下采样。比如输入是416*416的话,这里的feature map就是 ( 13 ∗ 13 ) (13*13) (13∗13)了。由于下采样倍数高,这里feature map的感受野比较大,因此适合检测图像中尺寸比较大的对象。
为了实现细粒度的检测,第79层的feature map又开始作上采样(从79层往右开始上采样卷积),然后与第61层feature map融合(Concatenation),这样得到第91层较细粒度的feature map,同样经过几个卷积层后得到相对输入图像16倍下采样的feature map ( 26 ∗ 26 ) (26*26) (26∗26)。它具有中等尺度的感受野,适合检测中等尺度的对象。
最后,第91层feature map再次上采样,并与第36层feature map融合(Concatenation),最后得到相对输入图像8倍下采样的feature map ( 52 ∗ 52 ) (52*52) (52∗52)。它的感受野最小,适合检测小尺寸的对象。
3、Bounding Box的改变
YOLO v2中采用维度聚类得到5种anchor box;
YOLO v3在此基础上进行了改进,为每种下采样尺度设定了3种先验框(prior),总共聚类出9种尺寸的先验框,在COCO数据集中这9种先验框分别是:(10x13),(16x30),(33x23),(30x61),(62x45),(59x119),(116x90),(156x198),(373x326),分别分配给三类feature map:
YOLO v3对prior进行预测时,弃用了Softmax分类层,采用逻辑回归的方式,依然输出 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,再根据v2中的的公式计算出prior的位置。
这里需要注意的是:YOLO v3仅对最佳prior进行操作,逻辑回归的作用就是从九种prior中找到可能性最高的那一个。
4、损失函数的改变
YOLO v3中,除了w, h的损失函数依然采用总方误差之外,其他部分的损失函数用的是二值交叉熵,最后加到一起。
5、输入到输出的关系
参考文章
https://blog.csdn.net/c20081052/article/details/80236015
https://blog.csdn.net/zijin0802034/article/details/77097894
https://blog.csdn.net/zhazhiqiang/article/details/82669863
https://www.jianshu.com/p/d13ae1055302
https://blog.csdn.net/leviopku/article/details/82660381
https://blog.csdn.net/litt1e/article/details/88907542
结束语
第一次写博客,借鉴了很多大佬的东西,希望可以将自己学习的过程记录下来,时间仓促,这里就不多说了,在之后的博客中,会更加多一些自己的原创性内容,希望可以跟各位大神分享!!!