YoloV1:https://arxiv.org/abs/1506.02640
YoloV2:https://arxiv.org/abs/1612.08242
YoloV3:https://arxiv.org/abs/1804.02767
官网:
https://pjreddie.com/darknet/yolov2/
https://pjreddie.com/darknet/yolo/
第三方实现:
https://github.com/eriklindernoren/PyTorch-YOLOv3#train
https://github.com/wlguan/Stronger-yolo
https://github.com/TencentYoutuResearch/ObjectDetection-OneStageDet/tree/master/yolo
作者提出了一种新的目标检测方法YOLOV1,先前的目标检测算法都是利用分类器进行检测。相反,YoloV1将目标检测当作回归问题进行处理,获取图像上各目标的位置及类别信息。YoloV1使用单个神经网络在一次前向推理中从整幅图像上预测各目标的边界框和类别信息。由于整个检测过程只使用了单个网络,因此可以直接以检测性能为评价标准进行端到端的优化。
YoloV1的处理速度非常快,基础YoloV1网络处理速度可以达到45 fps,更小版本的网络Fast YoloV1可以达到155 fps,同时mAP仍然是其他实时检测器的两倍。与其他检测算法相比,Yolo的缺点在于定位精度不够、不擅长检测小目标。
当前的目标检测系统一般都是利用分类器来执行目标检测。为了完成检测任务,这些系统缩放测试图像,并对不同位置的图像子块进行分类评估。可变形部件模型(DPM)使用滑动窗口,在整个图像上均匀的采集图像子块并对其进行分类以判别是否包含特定目标。R-CNN使用区域proposal方法首先在图像中生成潜在的边界框,然后在这些建议的框上运行分类器。分类后,使用后处理修正边界框,消除重复检测。RCNN的处理流程复杂、速度慢且难以优化。
作者将目标检测问题当作单个回归问题,使用单个卷积网络一次性从图像像素中回归出边界框坐标和类别概率。整个处理过程简洁明了,如图1所示。YoloV1以整幅图像为输入进行训练并直接优化。其优点有:
YoloV1的缺点:
YoloV1在准确性方面仍然落后于最先进的检测算法。虽然检测速度快,但它不擅长进行小目标的检测,另外对目标的定位精度也不够。
YoloV1将输入图像送入CNN,经过卷积、池化等操作后,得到了大小为 S × S S \times S S×S的feature map,该feature map的每一个点就对应于输入图像上的一个子块,相当于把输入图像划分成了 S × S S \times S S×S个网格。如果某个目标的中心点位于某网格中,则该网格负责该目标的检测,如图2中,狗的中心点位于(5,2)处,则(5,2)这一网格就负责狗的位置和类别预测。
每个网格预测B个边界框,这些框的位置大小各不相同,基本可以覆盖整个图像上可能出现的目标。每个框的预测内容包括置信度、类别信息和位置信息。
在POLCAL VOC数据集上,作者设置S = 7,B = 2。因为PASCAL VOC有20个类,因此C = 20。最终预测值大小为7×7×(5*2 + 20) = 7×7×30的张量。
通俗来说,YoloV1的处理过程是将一副图像分成S*S个网格,包含目标中心的网格负责对目标进行检测。每个网格只输出一组目标类别置信度,也就是VOC中的20个类别的概率。每个网格预测B个box,每个box包含五个值,分别是x,y,w,h和置信度。置信度为预测的box和真实box之间的IOU,坐标(x,y)为box的中心点相对于该网格的相对位置,w和h为box相对于图像整体宽高的相对比例。
三个值得注意的细节:
普通Yolo网络有24个卷积层,后面是2个全连接层。每一个网络块都首先使用1×1卷积层缩减输出通道数以减少计算量,后跟3×3卷积层。完整的网络如图3所示。
Fast YOLO使用9个卷积层,并且在这些层中滤波器数量也更少。
除了网络的大小不同,YOLO和Fast YOLO之间的所有训练和测试参数都是相同的。
网络的最终预测输出是7×7×30的张量。
网络结构:
作者首先使用ImageNet数据集预训练图3所示网络的前20层,然后在检测数据集上微调网络。前面已经有论文证明了在预训练网络上添加卷积和全连接层可以提高性能。这里作者在预训练模型上添加了四个卷积层和两个全连接层,新添加层随机初始化权重。因为检测通常需要更加细粒度的视觉信息,因此作者将网络的输入分辨率从224×224增加到448×448。
数据标注:
网络的最后一层预测了目标的类概率和边界框坐标。作者将边界框宽度和高度归一化为和图像宽度和高度的比例,使它们的取值介于0到1之间。我们将边界框x和y坐标参数化为相对于特定网格起始位置的归一化偏移量,因此它们也在0到1之间,也就是预测边界框的中心减去网格的左上角坐标再除以网格宽高之后的值。
激活函数:
网络的最后一层使用线性激活函数,其他层使用以下leaky relu激活函数:
损失函数:
训练过程中使用下述损失函数:
1 i o b j 1_i^{obj} 1iobj表示网格i中包含目标的示性函数, 1 i j o b j 1_{ij}^{obj} 1ijobj表示网格i中第j个预测框包含目标的示性函数。损失函数中,前两行是包含目标时各box的定位损失;第三行是包含目标时各box的置信度损失;第四行表示某box不包含目标时的损失,此时就只有置信度损失;最后一行表示各网格的类别置信度损失。
训练的目标是最小化模型输出的误差的平方和。之所以使用求和平方误差,是因为它很容易优化,但它与真正的目标检测指标(最大化mAP)并不完全一致。由于在训练图像中,大部分网格不包含任何目标,如果对定位误差与分类误差相等地加权,会将所有网格的“类别置信度”分数推向零,这可能造成训练不稳定,导致在早期训练过程出现离散。为了解决这个问题,作者增加了边界框坐标预测损失的权重,并减小了不包含对象的box的置信度预测损失的权重。作者使用了两个参数,λcoord和λnoobj来实现这一目的。设置λcoord= 5和λnoobj=0.5。
损失函数中对大尺度目标和小尺度目标的定位误差进行了相等加权,但同样的偏差对于大目标的影响要比对小目标的影响小,为了解决这个问题,损失函数中预测边界框宽度和高度的平方根,而不是宽度和高度(假设,两个正方形目标,一个尺度是49,另一个尺度是9,定位偏差3对于大目标的影响是3/49,对于小目标的影响是3/9。改成预测目标的尺度的平方根后,对于大目标的影响是 3 \sqrt{3} 3/7,对于小目标的影响是 3 \sqrt{3} 3/3,改变前后的影响程度比由 49/9 减小为 7/3)。
YOLO中每个网格预测多个边界框。在训练时,我们根据哪个预测框和真实框之间的IOU最高,将该预测器指定为负责目标的预测。这导致每个预测器具有了对某些大小、宽高比或目标类别的预测偏好,从而提高了整体召回率。
超参数:
作者在PASCAL VOC 2007和2012的训练和验证数据集上训练了大约135个epoch。训练过程中,batch大小为64,动量为0.9,衰减为0.0005。
训练过程中的学习率为,第一个epoch,慢慢将学习率从10-3提高到10-2,这是因为如果从高学习速度开始训练,模型通常会因梯度不稳定而发散。后面以10-2进行了75个epoch的训练,然后以10-3进行30个epoch的训练,最后以10-4进行30个epoch的训练。
为避免过拟合,使用了dropout和大量数据扩充。在第一个连接层之后,drop比例为0.5。
数据增强,我们引入了高达原始图像大小20%的随机缩放和变换。我们还在HSV颜色空间中随机调整图像的曝光和饱和度将图像扩充了1.5倍。
就像在训练中一样,预测是也只需要一次网络推理。在PASCAL VOC上,网络对每个图像预测98个边界框和每个框的类概率,然后保留置信度最高的49个框用于阈值过滤和NMS处理,得到最终的预测结果。
每个网格预测B个不同尺度的目标的设计保证了预测的空间多样性。通常很清楚一个目标落入哪个网格单元,并且网络仅为每个对象预测一个框。但是,一些大型目标或靠近多个网格边界的目标可以被多个网格定位,非最大抑制可解决重复检测。NMS将Yolo的mAP增加了2 - 3%。
YOLOV1对边界框预测施加了强大的空间约束,因为每个网格单元只预测两个框,并且只能有一个类。此空间约束限制了我们的模型可以预测的相邻目标的数量。因此,YoloV1不擅长成群的小目标的检测。
由于我们的模型学习从数据中预测边界框,因此很难对新的或不常见的宽高比和尺度泛化的很好。
因为网络结构包含了多个下采样层,用于预测的特征的空间尺寸较小,因此目标的定位精度较差。
YoloV1的损失函数中,大尺度目标的定位损失权重和小尺度目标的定位损失权重是一样的,这会导致同等比例的定位误差,大尺度目标的损失会比小目标大,小尺度目标的损失在总损失中占比较小,会带来定位的不准确。
Yolo的主要误差是定位误差。
检测结果类型:
Yolo主要误差是定位误差。Fast R-CNN相比于YoloV1,定位误差更少,但将背景误分类为目标的比例更高。
由于Yolo和Fast R-CNN的误差类型不同,将两者结合可以取得更好的结果。作者将Fast R-CNN预测的每一个目标也送入yolo中进行预测,如果两者的预测结果很接近,则增大其为目标的概率。
与现有最先进的检测系统相比,YOLOV1存在一些缺点。与Fast R-CNN相比,YOLOV1的错误主要是有大量的定位误差。此外,与基于区域提议的方法相比,YOLOV1具有相对较低的召回率。因此,本文在保持分类准确性的情况下主要关注改善召回率和定位精度。
计算机视觉领域趋向于使用更大、更深的网络,通常通过训练更大的网络或将多个模型集合在一起取得更好的效果。但是YOLOv2是速度很快的更加准确的检测网络。作者不是在YoloV1的基础上扩展网络,而是简化网络,但是却学习到了更加鲁棒的特征。
YoloV2从网络结构改善、先验框设计和训练技巧3个方面改善了检测效果。
YoloV2提出了一个全新的网络结构,称为DarkNet。原始的DarkNet拥有19个卷积层和5个池化层,在增加了一个Passthrough层后一共拥有22个卷积层,精度与VGGNet相当,但浮点计算量只有VGGNet的1/5左右,推理速度很快。
使用了BN层:BN层有助于解决反向传播中的梯度消失和爆炸,可以加速模型的收敛,同时还具有一定的防止过拟合的作用。通过在所有卷积层上添加BN层,mAP提高了2%。因为使用了BN层,所以删除了dropout而没有造成过拟合。
使用更小的卷积核:YoloV2中起始网络层使用连续的3 * 3 卷积代替YoloV1中的7 * 7卷积,这样既减少了计算量,又增加了网络深度。
Passthrough层:DarkNet还进行了深浅特征的融合,具体方法是将浅层的26 * 26 * 512的特征映射变换为13 * 13 * 2048的特征映射,然后和深层13 * 13 * 1024的特征在通道方向进行拼接。这种融合后的特征有助于小目标的检测,带来了1%的mAP的提升。
多阶段训练:最先进的检测算法都使用在ImageNet上预先训练的分类器进行微调。从AlexNet开始,大多数分类器的输入图像都是256×256的图像。YOLOV1输入224×224图像训练分类网络,然后将分辨率提高到448x448训练检测网络。这意味着模型需要在学习如何进行目标检测的同时还要适应输入分辨率的变化。
因此在YOLOv2中,作者改变了思路,采用了多阶段训练的做法。第一步,使用以224 x 224输入的ImageNet预训练模型;第二步,将ImageNet图像放大到448×448,对分类网络进行10个epoch的微调,让网络首先适应放大后的图像尺度;第三步,对448 x 448输入的分类网络去掉卷积层,在DarkNet上增加PassThrough层及3个卷积层,进行微调实现检测任务。这种多阶段训练方式使检测的mAP增加了近4%。
单个网格检测多类别目标:YoloV1中是单个网格只有一个分类信息,因此一个网格只能预测一个类型的目标,这造成了其对密集的小目标检测效果不佳。YoloV2改变思路,一个网格预测5个框,但每个框有自己的类别信息,因此每个框输出25个值,分别是类别信息(VOC数据集,20个类别)+4个坐标信息+1个置信度。对于一个网格,共输出了5 * 25 = 125个值,因此DarkNet最后一层为1 * 1 * 125的卷积层,保证了最终feature map的每一点处对应125个输出值。
YOLOV2借鉴了Faster R-CNN中Anchor box的设计,不再使用模型直接预测目标的位置,而是预测目标和anchor box之间的偏差,降低了预测难度。使用anchor box,检测的mAP略有下降,但召回率提升了7%。
先验框的选择:Faster R-CNN中是手工进行先验框的选择,YOLOV2不是手动选择先验框,而是在训练集的边界框上运行k-means聚类得到先验框的尺度和长宽比信息。如果使用欧式距离作为K-Means的度量标准,那么较大的框比较小的框会产生更大的误差。然而,真正较好的先验框和框的大小无关,而是与目标和先验框之间的IOU正相关。因此,作者提出了下述距离度量标准:
d(box,centroid) = 1 - IOU(box,centroid)
centroid是聚类时被选作中心的先验框,box表示其它候选先验框,d就是两者间的“距离”。候选框和聚类中心框的IOU越大,“距离”越近。
预选框的数量越多,平均IOU越大,但计算量也会越大,下图2左图给出了预选框数量和平均IOU之间的关系。作者综合考虑计算复杂度和召回率之间的权衡,选择k = 5。聚类结果如图2右图所示,矮宽的box较少、高窄的box较多,与手工设计的Anchor box明显不同。
下图给出了聚类时使用不同聚类中心数和不同度量标准的平均IOU的变化。可以看出,使用IOU作为度量标准时,5个聚类中心的平均IOU和使用欧式距离作为度量标准时的9个聚类中心的平均IOU相当。
直接进行位置预测:将YOLOV2和Anchor Box结合时,作者遇到了训练不稳定的问题,特别是在训练早期。作者分析训练不稳定主要是由于直接预测框的中心点位置(x,y)。在Faster R-CNN中,网络预测值是 t x t_x tx和 t y t_y ty,中心点坐标(x,y)计算公式如下:
x = ( t x ∗ w a ) + x a y = ( t y ∗ h a ) + y a x = (t_x * w_a) + x_a \\ y = (t_y * h_a) + y_a x=(tx∗wa)+xay=(ty∗ha)+ya
例如,tx = 1时,预测框会向右移动anchor box的宽度,tx = -1时预测框会向左移动anchor box的宽度。
作者认为上述预测方式没有对预测偏移进行限制,导致预测框可以出现在图像的任何位置,尤其是在训练初始阶段,。一开始的随机初始化使得模型需要很长时间才能稳定以预测合理的偏移。
我们不是预测偏移,而是遵循YOLO的方法,预测相对于网格位置的位置坐标。这将真值限制在0到1之间。我们使用logistic激活函数来约束网络预测落在此范围内。
网络对输出特征图中的每个网格预测5个边界框。网络对每一个边界框预测5个坐标, 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。预测值的计算公式为:
多尺度训练:YoloV2训练过程中不是固定输入图像大小,而是每隔几次epoch就改变输入的尺寸。由于网络的下采样倍数为32,因此作者从32倍数中随机选取输入分辨率:{320,352,…,608}。最小的输入分辨率是320×320,最大的输入分辨率是608×608。
多尺度训练可以迫使网络学习如何在多种输入尺度上进行目标检测,因此单个模型可以对不同分辨率的输入图像进行检测。想要速度快,就减少输入分辨率;想要精度高,就增大输入分辨率。
YoloV2与其他算法的准确率和效率的对比如图4所示。
表3,4,5为YOLOv2与其他最先进的检测算法的性能对比。
我们向YOLO提供一些更新!我们做了一些小的设计更改,以使其更好。我们还训练了这个非常庞大的新网络。它比上次有点大,但更准确。它仍然很快,不用担心。在输入为320×320时,YOLOv3以运行22毫秒运行,取得了28.2的mAP,与SSD一样准确,但速度提高了三倍。当我们查看旧的.5 IOU mAP检测指标时,YOLOv3非常好。它在Titan X上以51毫秒内达到57.9 的AP50。相比之下,RetinaNet在198毫秒内达到57.5的AP50,性能相似,但速度提高了3.8倍。
YoloV3吸收了其他检测网络的思想,引入了残差网络和特征融合,提出了DarkNet-53这一最新的网络结构。
上图引用自:https://zhuanlan.zhihu.com/p/40332004
YoloV3的速度没有之前的版本快,而是在保证实时检测的情况下努力提升检测精度。YoloV3还提供了一个tiny-DarkNet网络,用于更快速的目标检测。
如上节所示,YOLOv3预测3种不同尺度的box,做法和FPN类似。但YoloV3中每一个网格预测框的数量是3个,并且默认基于coco数据集进行测试,因此对每个尺度的每个feature map点处预测3个框,每个框的信息有4个位置偏移、1个包含目标的置信度和80个类别得分,因此最终的输出为N×N×[3 *(4 + 1 + 80)] = N x N x 255,所以这也是上图中所有尺度的最后的一个卷积层都是255个channel。
作者仍然使用k-means聚类来确定先验框。因为现在是3个尺度,所以作者一共预测了9个先验框,每个尺度3个。在COCO数据集上,9个先验框的大小分别为:(10×13),(16×30),(33×23),(30×61),(62×45),(59×119),(116×90),(156×198),(373×326)。前三个对应scale 1,用于预测小目标;中间3个对应scale 2,用于预测中等目标;最后三个对应scale 3,用于预测大目标。
yolov3的预测结果和YoloV2一致,为:
b x = σ ( t x ) + c x b y = σ ( t y ) + c y b w = p w e t w b h = p h e t h b_x = \sigma(t_x) + c_x \\ b_y = \sigma(t_y) + c_y \\ b_w = p_w e^{t_w} \\ b_h = p_h e^{t_h} bx=σ(tx)+cxby=σ(ty)+cybw=pwetwbh=pheth
在训练过程中,作者使用误差的平方和作为损失函数。如果真实坐标是 t ^ ∗ \hat t_* t^∗,预测值是 t ∗ t_* t∗,则梯度为 t ^ ∗ − t ∗ \hat t_* - t_* t^∗−t∗。
YOLOv3使用逻辑回归预测每个边界框是否包含目标的得分。如果一个先验框比其他先验框与某个真实目标的IOU更大,该先验框的类别标注就是1。如果某一个先验框与ground-truth之间的IOU不是最大的,但也超过了设定的IOU阈值,训练过程中应该忽略这个先验框。作者设置IOU阈值为0.5。这个标注标准和Faster R-CNN不同,为每一个真实目标只选择了一个先验框。
softmax需要类别是互斥的,而多个logistic回归则没有该要求,可以实现多类别的预测,如一幅图像既属于women类,又属于person类。实验证明,实验多个二分类器代替softmax分类器,并没有降低检测精度。
如表3所示,YoloV3效果很好。 就COCO而言,mAP与SSD相当,但速度快3倍。效果比RetinaNet略差。
原来的Yolo不擅长检测小目标。但是,现在我们看到了这种趋势的逆转。通过新的多尺度预测,我们看到YOLOv3具有相对较高的APs性能。但是,它在中型和大型目标上的性能相对较差。YoloV3的主要缺点依然是定位精度较差。
AP50-速度的曲线见图5,可以看到YOLOv3与其他检测系统相比具有显著的优势。也就是说,它更快更好。
作者在Yolo的官网提供了320,416,608大小的YoloV3的cfg文件和预训练模型。另外,作者还提供了一个小型的模型,YoloV3-Tiny的cfg文件和预训练模型。YOLOv3-tiny虽然速度进一步提升,但其mAP下降较多。
工程上可以使用TensorRT进一步优化YoloV3的时间效率。
使用TensorRT有两条实现路径:
这里研究第一条路径的实现。参考代码为https://github.com/NVIDIA-AI-IOT/deepstream_reference_apps/tree/master/yolo。
这里的代码主要包含了三部分内容,一是基于TensorRT实现YoloV2/YoloV3,二是将YoloV2/YoloV3做成gstreamer插件,三是将一系列的gstreamer插件组合起来组成deepstream应用。这里重点分析基于TensorRT实现YoloV2/YoloV3的代码。
主函数为apps/trt-yolo/trt-yolo-app.cpp,核心代码位于lib/文件夹下。
主函数中首先读取配置文件,使用gflags解析文件获取设置的参数。然后根据设置的网络类型去生成的对象,这里支持YoloV2,YoloV2-tiny,YoloV3,YoloV3-tiny四种网络。
代码的处理流程为在Yolo构造函数中解析要检测的类别、模型cfg文件,然后依据使用的数据类型创建对应的TensorRT Engine。
在创建TensorRT Engine的过程中,首先读取了预训练.weights模型的权重,然后按照流程创建了TensorRT的logger和network,设置了网络的输入,这里创建了一个和输入数据同样大小值为255.0的常量层,然后使用逐元素相除操作将输入数据的值归一化到了0 - 1之间。因为使用了两种不同的池化(输出和输入相等的池化、普通池化),因此设置了池化层的输出大小。接下来对每一个网络层(包括卷积层、shortcut层、yolo输出层、region层、reorg层、route层、upsample层和maxpool层),都使用了TensorRT的函数进行了实现。
在将各层通过TensorRT进行实现之后,就调用了m_Builder->buildCudaEngine完成了TensorRT Engine的创建,并将其保存成本地文件。后续再使用该Engine时,如果判断该Engine文件存在则直接读取该文件,并进行反序列化操作得到运行时上下文环境。
之后就是对输入图像进行推理的过程,首先读取图像,保持长宽比将输入图像的长边变换到模型的预设输入值(例如为608),对于图像的短边,则是添加border使用常量进行扩充使得其大小变成608 * 608。然后进行颜色变换,BGR -> RGB。这里作者提到了一个opencv的变换函数cv::dnn::blobFromImages,可以很方便的完成图像的缩放、中心裁剪、减均值、乘系数的操作,并将最后的结果转换为4维blob。
对输入图像完成变换之后,可以调用引擎对输入数据进行推理操作。
推理完成之后,获得了三个尺度的检测框。根据YoloV3的理论,每一个尺度的输出,都是gridSize * gridSize * numBBoxes * (5 + numClasses)的大小。对于每一个网格中的每一个box而言,首先获取其对应的anchor box的大小,然后依次去读取检测框的中心点x,y坐标和宽、高及当前box包含目标的概率等信息。这里由于将各网格的尺寸当作1来处理,所以模型前向运算计算的结果就是当前box的中心点距离其所属的网格的左上角的距离,距离也是归一化到了 0 到 1之间的,也就是对于坐标中心点,模型前向运算的输出就是 σ ( t x ) \sigma(t_x) σ(tx)和 σ ( t y ) \sigma(t_y) σ(ty)。对于box的宽高,模型前向运算的输出就是 e t w e^{t_w} etw和 e t h e^{t_h} eth。接着获取当前box中包含的目标的类别概率向量,将当前box包含目标的概率与该目标的类别概率相乘,取其最大值得到当前box包含的目标的类别概率。对于大于指定类别阈值的目标box,将获取的目标坐标乘以stride,进行边界控制,平移、缩放之后得到目标box在原始图像分辨率下的位置信息。
最后,对三个尺度检测到box按照类别进行NMS处理,消除重复的检测框,从而完成整个的目标检测过程。在YoloV3的实现代码里,充分体现了作者论文中提出的多尺度预测、直接预测bounding box的坐标而不是偏移量、使用了聚类得到的anchor box作为基准,在YoloV3中也使用了darknet53作为特征提取网络,这些点与作者论文中提出的思想完全对应。
在基于TensorRT实现的YoloV3代码中,还可以通过将float32转换成float16或int8进一步提升推理效率,这部分内容为TensorRT的通用内容,与Yolo关系不大,这里不再赘述。