目标定位与检测系列,10:YOLOv3

论文原文地址:YOLOv3

摘要

我们针对YOLO提供了一些改进。我们在设计上做出了一些改变让它效果更好。我们也训练了这个相当好的网络。它比之前的网络大一点但是准确率更高。不用担心,它的速度依然很快。在320*320的图片上,YOLOv3可以达到单张图片检测速度22ms、28.2的mAP,和SSD准确率一样但速度是它的3倍。在使用以前的IOU mAP标准上,YOLOv3表现非常好。在一个Titan X上检测速度为51ms,精度为57.9AP50AP_{50}AP50​,而RetinaNet检测速度为198ms,精度为57.5AP50AP_{50}AP50​,精度相当但速度却是它的3.8倍。和往常一样,所有的代码都在:https://pjreddie.com/yolo/.

YOLOv3的工作

边界框预测。 YOLOv3中对边界框的预测遵循了YOLO9000中的方法。依旧使用维度聚类来生成anchors。对于每个bbx,网络预测4个值,即txt_xtx​、tyt_yty​、twt_wtw​和tht_hth​,最终的预测映射关系为:

bx=σ(tx)+cxby=σ(ty)+cybw=pwetwbh=phethb_x = \sigma(t_x)+c_x \\ b_y = \sigma(t_y)+c_y \\ b_w = p_we^{t_w } \\ b_h = p_he^{t_h}bx​=σ(tx​)+cx​by​=σ(ty​)+cy​bw​=pw​etw​bh​=ph​eth​

其中,cxc_xcx​和cyc_ycy​为当前cell相对于图片左上角的偏移量,pwp_wpw​和php_hph​是anchor的宽高。下图给了更直观的解释(和YOLOv2中的图相同)

目标定位与检测系列,10:YOLOv3

在训练过程中,采用最简单的误差平方和损失。另外,采用Logistic回归来预测每个bbx的目标得分。当一个先验bbx和某个ground truth的重合度比其他的bbx都高,那结果应该为1;那些重合度大于指定阈值但不是最好的先验bbx将被忽略。

类别预测。 某些数据集可能存在多类别标签,如在Open Image Dataset中,存在许多重叠的标签(Woman和Person等)。对于这样的多类别标签,Softmax函数不再使用。因此,在YOLOv3中采用logistic分类器代替。损失函数采用交叉信息损失。

跨尺度预测。 YOLOv3在3个不同大小的特征图进行预测。每一个尺度上都预测bbx位置信息(4个值)、包含目标的概率(1个值)和类别概率(C个值,C为类别数)。设anchor数量为A(每个位置预测A个bbx),所以,对于一个大小为N*N的特征图,最终预测得到的张量shape为N*N*[A*(4+1+C)]。另外,为了提高预测的准确性,YOLOv3中还利用上采样来融合不同深度的特征图信息,最终的特征图相对于原始图片的下采样倍数分别为32倍、16倍和8倍。

特征提取网络。 作者提出了一种新的网络结构用于特征提取,该网络包含53个卷积层,因此叫Darknet-53。按照文中的话来说,这个网络是YOLOv2中的Darknet-19和残差网络的结合体。具体体现在:(1)采用多个3*3和1*1的卷积层相连的形式;(2)引入残差网络中的shortcut连接。具体的结构如下:

目标定位与检测系列,10:YOLOv3

在Keras实现的YOLOv3中,对应的Darknet-53代码如下:

def DarknetConv2D(*args, **kwargs):
    """Wrapper to set Darknet parameters for Convolution2D."""
    darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)} # 对卷积层使用L2正则化
    darknet_conv_kwargs['padding'] = 'valid' if kwargs.get('strides')==(2,2) else 'same'
    darknet_conv_kwargs.update(kwargs)
    return Conv2D(*args, **darknet_conv_kwargs)

def DarknetConv2D_BN_Leaky(*args, **kwargs):
    """Darknet Convolution2D followed by BatchNormalization and LeakyReLU."""
    no_bias_kwargs = {'use_bias': False} # 不使用偏置项
    no_bias_kwargs.update(kwargs)
    return compose(
        DarknetConv2D(*args, **no_bias_kwargs),
        BatchNormalization(),
        LeakyReLU(alpha=0.1))

def resblock_body(x, num_filters, num_blocks):
    '''A series of resblocks starting with a downsampling Convolution2D'''
    # Darknet uses left and top padding instead of 'same' mode
    x = ZeroPadding2D(((1,0),(1,0)))(x)
    x = DarknetConv2D_BN_Leaky(num_filters, (3,3), strides=(2,2))(x)
    for i in range(num_blocks):
        y = compose(
                DarknetConv2D_BN_Leaky(num_filters//2, (1,1)),
                DarknetConv2D_BN_Leaky(num_filters, (3,3)))(x)
        x = Add()([x,y])
    return x

def darknet_body(x):
    '''Darknent body having 52 Convolution2D layers'''
    # 对应论文中的Darknet-53
    x = DarknetConv2D_BN_Leaky(32, (3,3))(x)
    x = resblock_body(x, 64, 1)
    x = resblock_body(x, 128, 2)
    x = resblock_body(x, 256, 8)
    x = resblock_body(x, 512, 8)
    x = resblock_body(x, 1024, 4)
    return x

从代码中可以看到更多的细节:(1)大部分卷积层后都接有BN层和LeakyReLU层,可以把这种结构看成一个基本的卷积单元;(2)每个残差块中包含3个卷积单元,对应的卷积核分别是3*3、1*1、3*3,且卷积核1*1的卷积层的维度是3*3的一半,shortcut连接从第一个卷积层后引入到第三个卷积层后。(3)在每个残差块的1*1卷积层卷积步长为2,输出的特征图大小变为输入的一半,从而实现了下采样的过程(因此网络中不再需要最大池化进行下采样)

作者在文中也给出了Darknet-53和其他几种典型结构的对比:

目标定位与检测系列,10:YOLOv3

可以发现,Darknet-53在准确率上可以和152层的残差网络相媲美,而在速度上达到78FPS,满足实时性的需求。

实验结果

作者文章最后把YOLOv3和其他的一些目标检测网络在准确率和检测速度上进行了对比,先看准确率:

目标定位与检测系列,10:YOLOv3

对比结果可以发现:(1)相对于前一个版本YOLOv2,v3有着明显的提升;(2)相比于准确率最高的RetinaNet,v3仍有一些差距,但在老式的评价方法AP50AP_{50}AP50​上,差距比较小;(3)相比于SSD系列,v3在精度上实力相当;(4)对比于Faster R-CNN系列算法,v3在精度上略弱,但差距不大。

再来看一下速度上的对比:

目标定位与检测系列,10:YOLOv3

可以明显地看出,YOLOv3系列的检测速度是其他算法的好几倍,当然,前提是依然保持着非常不错的mAP。从数据上也可以发现,貌似只有YOLOv3系列能达到时时检测的要求。