FPN(Feature Pyramid Network)基础概念
论文来源:https://arxiv.org/abs/1612.03144
1.简介
作者开发了一种具有横向连接的自顶向下架构,用于在所有尺度上构建高级语义特征映射。这种FPN架构在几个应用程序中作为通用特征提取器表现出了显著的改进。包括一个自下而上的通道,自上而下的连接和横向连接,如下所述。
优势:在几乎不增加额外计算量情况下解决多尺度检测问题,提高了准确性(尤其是图像中的小物体检测)。
下面简单介绍几种特征提取结构:
a.对某一输入图片我们通过压缩或放大从而形成不同维度的图片作为模型输入,使用同一模型对这些不同维度的图片分别处理后,最终再将这些分别得到的特征(feature maps)组合起来就得到了我们想要的可反映多维度信息的特征集。此种方法缺点在于需要对同一图片在更改维度后输入处理多次,因此对计算机的算力及内存大小都有较高要求
b.则只拿单一维度的图片做为输入,然后经CNN模型处理后,拿最终一层的feature maps作为最终的特征集。显然此种方法只能得到单一维度的信息。优点是计算简单,对计算机算力及内存大小都无过高需求。此方法为大多数R-CNN系列目标检测方法所用像R-CNN/Fast-RCNN/Faster-RCNN等。因此最终这些模型对小维度的目标检测性能不是很好。
c.同样是拿单一维度的图片做为输入,不过最终选取用于接下来分类或检测任务时的特征组合时,此方法不只选用了最后一层的high level feature maps,同样也会选用稍靠下的反映图片low level 信息的feature maps。然后将这些不同层次(反映不同level的图片信息)的特征简单合并起来(一般为concat处理),用于最终的特征组合输出。此方法可见于SSD当中。不过SSD在选取层特征时都选用了较高层次的网络。比如在它以VGG16作为主干网络的检测模型里面所选用的最低的Convolution的层为Conv4,这样一些具有更低级别信息的层特征像Conv2/Conv3就被它给漏掉了,于是它对更小维度的目标检测效果就不大好。
d.同图(c)中的方法有些类似,也是拿单一维度的图片作为输入,然后它会选取所有层的特征来处理然后再联合起来做为最终的特征输出组合。即在图c的基础上,加上了一个自顶向下的特征融合操作:对高层次进行上采样,得到较大的特征图(等于下层特征图的维度)。然后将结果与下层特征图相加(add)。
2. 详细结构
2.1自下而上模块
对输入的图像逐层进行CNN提取特征,结果是较底的层反映较浅层次的图片信息特征像边缘等;较高的层则反映较深层次的图片特征像物体轮廓、乃至类别等。
2.2自上而下模块
上层的特征输出一般其 size比较小,但却能表示更强的的图片语义信息,但是,size较大等得特征层也不是没有作用,需要按一定的比例结合这两方面的信息。具体做法是:
(1)最上一层的特征层经过1*1卷积层降维度,然后进行上采样(2倍)。
(2)将左边的次上层经过1*1卷积降维,与(1)的结果相加操作。
Tip:每一层的结果都可以提供预测
3. 代码参考
(ResNet后添加FPN部分实现)
# 返回值是5个计算后的特征图 # 丢弃C1(卷积参数太大) _, C2, C3, C4, C5 = resnet_graph(input_image, "resnet101", stage5=True) # 从上而下模块 # KL=keras.layers 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) #横向连接生成特征图 mrcnn_feature_maps = [P2, P3, P4, P5] # P6 is used for the 5th anchor scale in RPN. #P6 = KL.MaxPooling2D(pool_size=(1, 1), strides=2, name="fpn_p6")(P5) #rpn_feature_maps = [P2, P3, P4, P5, P6]