- 论文题目:FeaturePyramidNetworksforObjectDetection
- 论文链接:https://arxiv.org/abs/1612.03144
所要解决的问题
针对小目标检测网络,CV领域一般用的是特征金字塔。即将原图以不同的比例采样,然后得到不同分辨率的图像进行训练和测试,在多数情况下是有效的。但是特征金字塔的计算、内存和时间开销都非常大,导致在工程中应用是及其困难。FPN(即特征金字塔)提出了一个独特的特征金字塔网络来避免图像金字塔产生的超高计算量,同时可以较好的处理目标检测中的尺度变化问题,对于小目标检测具有很强的鲁棒性。
简介
下图表示FPN的整体结构:
图a 多尺度金字塔 这是一个特征图像金字塔,整个过程是先对原始图像构造图像金字塔,然后在图像金字塔的每一层提出不同的特征,然后进行相应的预测。这种方法的缺点是计算量大,需要大量的内存;优点是可以获得较好的检测精度。
图b 浅层的网络更关注于细节和位置信息,高层的网络更关注于语义信息,而高层的语义信息能够帮助我们准确的检测出目标,因此我们可以利用最后一个卷积层上的feature map来进行预测。
图c 同时利用低层特征和高层特征,分别在不同的层同时进行预测,这是因为我的一幅图像中可能具有多个不同大小的目标,区分不同的目标可能需要不同的特征,对于简单的目标我们仅仅需要浅层的特征就可以检测到它,对于复杂的目标我们就需要利用复杂的特征来检测它。整个过程就是首先在原始图像上面进行深度卷积,然后分别在不同的特征层上面进行预测。
图d(FPN网络) 首先我们在输入的图像上进行深度卷积,然后对Layer2上面的特征进行降维操作(即添加一层1x1的卷积层),对Layer4上面的特征就行上采样操作,使得它们具有相应的尺寸,然后对处理后的Layer2和处理后的Layer4执行加法操作(对应元素相加),将获得的结果输入到Layer5中去。其背后的思路是为了获得一个强语义信息,这样可以提高检测性能。其实看下面的代码就可以明白,把卷积之后的{Ck}层和上采样之后的{Pk}层进行相加,目的是把低层次高分辨率的信息和高层次强语义的信息结合起来,提高检测性能和小目标识别。
FPN结构
自底向上
这是神经网络的前向计算,就是由卷积和池化层组成的特征提取网络。在这个自底向上的结构中,一个stage对应特征金字塔的一个level。对于以ResNet为backbone的主干网络,选取conv2、conv3、conv4、conv5层的最后一个残差block层特征作为FPN的特征,记为{C2、C3、C4、C5},也即是FPN网络的4个级别。这几个特征层相对于原图的步长分别为4、8、16、32。
自上向下和侧向连接
自上向下是前向计算后将输出的特征图放大的过程,我们一般采用upsample(上采样)来实现。FPN的巧妙之处就在于从高层特征上采样不仅可以利用顶层的高语义、低分辨率信息(有助于分类),而且利用浅层的、低语义、高分辨率信息(有助于定位)。为了将上述两者相结合,论文提出了类似于残差结构的侧向连接。向连接将上一层经过上采样后和当前层分辨率一致的特征,通过相加的方法(如:pytorch的torch.cat或torch.add)进行融合。同时为了保持所有级别的特征层通道数都保持一致,这里使用1*1卷积来实现。
ResNet+FPN网络结构及代码实现
# Build the shared convolutional layers.
# Bottom-up Layers
# Returns a list of the last layers of each stage, 5 in total.
# 扔掉了C1
_, C2, C3, C4, C5 = resnet_graph(input_image, "resnet101", stage5=True)
# Top-down Layers
# TODO: add assert to varify feature map sizes match what's in config
P5 = KL.Conv2D(256, (1, 1), name='fpn_c5p5')(C5)
P4 = KL.Add(name="fpn_p4add")([
KL.UpSampling2D(size=(2, 2), name="fpn_p5upsampled")(P5),
KL.Conv2D(256, (1, 1), name='fpn_c4p4')(C4)])
P3 = KL.Add(name="fpn_p3add")([
KL.UpSampling2D(size=(2, 2), name="fpn_p4upsampled")(P4),
KL.Conv2D(256, (1, 1), name='fpn_c3p3')(C3)])
P2 = KL.Add(name="fpn_p2add")([
KL.UpSampling2D(size=(2, 2), name="fpn_p3upsampled")(P3),
KL.Conv2D(256, (1, 1), name='fpn_c2p2')(C2)])
# 把每个{Pk}层都乘以一个3*3的卷积生成特征图.
P2 = KL.Conv2D(256, (3, 3), padding="SAME", name="fpn_p2")(P2)
P3 = KL.Conv2D(256, (3, 3), padding="SAME", name="fpn_p3")(P3)
P4 = KL.Conv2D(256, (3, 3), padding="SAME", name="fpn_p4")(P4)
P5 = KL.Conv2D(256, (3, 3), padding="SAME", name="fpn_p5")(P5)
# P6 is used for the 5th anchor scale in RPN. Generated by
# subsampling from P5 with stride of 2.
P6 = KL.MaxPooling2D(pool_size=(1, 1), strides=2, name="fpn_p6")(P5)
# Note that P6 is used in RPN, but not in the classifier heads.
rpn_feature_maps = [P2, P3, P4, P5, P6]
mrcnn_feature_maps = [P2, P3, P4, P5]
实验
FPN对Fast RCNN的影响
使用和实验1相同的规则对Fast RCNN做了实验,结果如下表所示。
FPN对Segment Proposal的影响
结论
本文提出了一种简单、有效的建立特征金字塔的方式。它的使用对RPN方法和Fast/Faster RCNN方法都有极大的性能提升。另外,它的训练和测试时间和普通的Faster RCNN方法相差很小。因此,它可以作为图像特征金字塔的一种较好的替代。
论文开源代码
https://github.com/unsky/FPN