Feature Pyramid Networks for Object Detection
- 1.摘要
- 2.引言
- 2.1 低级特征对于检测小物体很重要
- 2.2 算法目标
- 3. 文献综述
- 3.1 Hand-engineered features and early neural networks
- 3.2 Deep ConvNet object detectors
- 3.3 Methods using multiple layers
- 4.Feature Pyramid Networks【特征金字塔】
- 4.1 Bottom-up pathway【自下而上路径】
- 4.2 Top-down pathway and lateral connections【自上而下路径和横向连接】
- 5. 应用
- 5.1 Feature Pyramid Networks for RPN【用于RPN的特征金字塔网络】
- 5.2 Feature Pyramid Networks for Fast R-CNN【Fast R-CNN的特征金字塔网络】
- 6.特征金字塔的优点与结论
- 7.论文阅读总结
- 8. `mask rcnn`使用`FPN`相关代码实现
- 8.1 Resnets模块构建
- 8.2 FPN构建
- 8.3 源码问题
本文参加新星计划人工智能(Pytorch)赛道: https://bbs.csdn.net/topics/613989052
- 论文链接:
- https://arxiv.org/pdf/1612.03144.pdf
1.摘要
特征金字塔
:- 用于检测不同尺度对象的识别系统的基本组成部分。
特征金字塔
需要大量计算和内存特征金字塔
计算和内存都是密集型的。
特征金字塔网络(FPN)的体系结构
:- 利用深度卷积网络固有的多尺度金字塔层次来构造具有边际额外成本的
特征金字塔
。 - 具有横向连接的自顶向下架构,用于在所有尺度上构建高级语义特征图。
- 是一种通用的特征提取器,在一些应用中显示出显著改进。
- 利用深度卷积网络固有的多尺度金字塔层次来构造具有边际额外成本的
2.引言
- 基于图像金字塔构建的
特征金字塔
【特征化图像金字塔】 为识别不同尺度的物体构成了标准解决方案的基础。- 这些金字塔比例不变,因为对象的比例变化通过在金字塔中移动其级别来抵消。
- 该特性使模型能够通过在位置和金字塔级别上扫描模型来检测大范围内的对象。
- 手工提取特征很大程度上被
深度卷积网络(ConvNets)
提取的特征所取代的原因。- 能够表示更高层次的语义
ConvNets
虽然对尺度变化也具鲁棒性,从而有助于从单个输入尺度上计算的特征中进行识别。即使有了这种稳健性,但是仍然需要金字塔来获得最准确的结果。
图像金字塔
的每个级别进行特征化的主要优点- 产生了多尺度特征表示,所有级别都是语义强的,包括高分辨率级别。
图像金字塔
问题图像金字塔
的每个层次特征化具有明显的局限性。推断时间显著增加,使得这种方法对于实际应用来说不切实际。图像金字塔
上的端到端网络在测试集内存方面是不可行- 因此,如果利用
图像金字塔
,则仅在测试时间使用图像金字塔
,这会在训练/测试时间推断之间产生不一致。
- 注意:
图像金字塔
并不是计算多尺度特征表示的唯一方法。- 深层
ConvNet
逐层计算特征层次,通过下采样层,特征层次具有固有的多尺度金字塔形状。 - 网络中的这种特征层次结构产生了不同空间分辨率的特征图,引入了由不同深度引起的较大语义差距。 导致高分辨率特征图具有低水平的特征,这些特征损害了它们识别物体的表现能力
- 深层
2.1 低级特征对于检测小物体很重要
单镜头检测器(SSD)
是第一次尝试使用ConvNet
的金字塔特征层次结构。- 理想情况下,
SSD
样式的金字塔将重用在正向过程中计算的来自不同层的多尺度特征图,因此不会产生成本。 - 但为了避免使用低级特征,SSD放弃了重复使用已经计算的层,而是从网络的高层开始构建金字塔,然后添加几个新层。
- 因此,它错过了重用特征层次结构的高分辨率特征图的机会。我们证明这些对于检测小物体很重要
2.2 算法目标
- 自然地利用
ConvNet
特征层次结构的金字塔形状,同时创建在所有尺度上都具有强语义的特征金字塔。 - 为了实现目标,我们依赖于通过
自上而下的路径
和横向连接
将低分辨率、语义强的特征与高分辨率、语义弱的特征相结合的架构。 - 结果是一个在所有级别都具有丰富语义的特征金字塔,并是从单个输入图像尺度快速构建【单张图片构建的】。
- 这些特征金字塔可用于替换特征化的图像金字塔,而不牺牲表现力、速度或内存。
使用架构的区别
- 其他论文采用自上而下和跳过连接的类似架构目标是生成一个精细分辨率的高级特征图,在该图上进行预测。
- 我们的方法利用该体系结构作为特征金字塔,其中在每个级别上独立地进行预测(例如,对象检测)。
特征金字塔
很容易扩展到掩模方案,并与严重依赖图像金字塔的最先进方法相比,提高了实例分割平均召回率(AR)和速度。特征金字塔结构
可以用所有尺度进行端到端训练,并在训练/测试时一致使用,这将是使用图像金字塔不可行的内存 【解决内存不足问题】。FPN
能够实现比所有现有的最先进的方法更高的精度。此外,这种改进是在不增加单尺度基线测试时间的情况下实现的。
3. 文献综述
3.1 Hand-engineered features and early neural networks
手工提取特征和早期神经网络
。SIFT特征
最初是在尺度空间极值处提取的,并用于特征点匹配。HOG特征
,以及后来的SIFT特征
,都是在整个图像金字塔上密集计算的。- 这些
HOG和SIFT
金字塔已被用于图像分类、物体检测、人体姿态估计等许多工作。 Dollár等人
通过首先计算稀疏采样(按比例)的金字塔,然后对缺失水平进行插值,证明了快速金字塔计算。
3.2 Deep ConvNet object detectors
深度卷积物体检测
。- 随着现代深度
ConvNets
的发展,OverFeat和R-CNN
等物体探测器在精度上有了显著提高。 OverFeat
采用了一种类似于早期神经网络人脸检测器的策略,将ConvNet
作为图像金字塔上的滑动窗口检测器。R-CNN
采用了基于区域提案的策略,其中每个提案在使用ConvNet
进行分类之前都进行了规模归一化。SPPnet
证明了这种基于区域的检测器可以更有效地应用于在单个图像尺度上提取的特征图。- 最近更准确的检测方法,如
Fast R-CNN和Faster R-CNN
,提倡使用从单个尺度计算的特征,因为它在准确性和速度之间提供了良好的权衡。然而,多尺度检测仍然表现更好,尤其是对于小物体。
- 随着现代深度
3.3 Methods using multiple layers
使用多层特征的方法
。- 通过在
ConvNet
中使用不同的层来改进检测和分割。 FCN
在多个尺度上对每个类别的部分分数求和,以计算语义分割。- 其他几种方法
(HyperNet、ParseNet和ION)
在计算预测之前连接多层的特征,这相当于对转换后的特征求和。 SSD和MS-CNN
在不组合特征或分数的情况下预测特征层次的多个层的对象- 最近有一些方法利用横向/跳跃连接,在分辨率和语义级别上关联低级别特征图,包括用于分割的
U-Net和SharpMask
,用于人脸检测的重组器网络,以及用于关键点估计的堆叠沙漏网络。 Ghiasi
等人提出了FCN
的拉普拉斯金字塔表示,以逐步细化分割。- 尽管这些方法采用了金字塔形状的体系结构,但它们不同于在所有级别上独立进行预测的特征化图像金字塔。
- 对于金字塔结构,仍然需要图像金字塔来识别多个尺度的对象
- 通过在
4.Feature Pyramid Networks【特征金字塔】
目标
- 利用
ConvNet
的金字塔特征层次结构,该层次结构具有从低到高的语义,并构建一个贯穿始终的具有高级语义的特征金字塔。产生的特征金字塔网络是通用的
- 利用
重点关注模块
- 滑动窗口提议器(
区域提议网络
,简称RPN
) - 基于区域的检测器
(Fast R-CNN)
。
- 滑动窗口提议器(
实现方法
- 以任意大小的单尺度图像作为输入,并以全卷积的方式在多个级别上输出成比例大小的特征图。
- 该过程独立于骨干卷积架构,使用
ResNets
骨干卷积架构。
金字塔的构建包括
自下而上路径
自上而下路径
横向连接
。
4.1 Bottom-up pathway【自下而上路径】
自下而上路径
- 是主干ConvNet的前馈计算,它计算由几个尺度的特征图组成的特征层次,尺度步长为2。
- 通常有许多层产生相同大小的输出映射,这些层处于同一网络阶段。
- 对于特征金字塔,为每个阶段定义一个金字塔级别。
- 选择每个阶段最后一层的输出作为我们的特征图参考集,将对其进行丰富以创建我们的金字塔。
- 因为每个阶段的最深层都应该具有最强的特征
- 具体使用方法
- 对于
ResNets
,使用每个阶段的最后一个残差块
输出的特征激活。 - 对于
conv2、conv3、conv4和conv5
输出,将这些最后残差块
的输出表示为{C2、C3、C4、C5}
,并注意到它们相对于输入图像具有{4、8、16、32}
个像素的步长。 - 由于
conv1
占用大量内存,因此没有将其包含在金字塔中。
- 对于
4.2 Top-down pathway and lateral connections【自上而下路径和横向连接】
-
自上而下路径和横向连接
- 通过从更高的金字塔级别上采样空间上更粗糙但语义上更强的特征图来产生更高分辨率的特征。
- 然后,通过横向连接,利用自下而上路径的特征来增强特征。
- 每个横向连接合并来自自下而上路径和自上而下路径的相同空间大小的特征图。
- 自下而上的特征图具有较低级别的语义,但它的激活更准确地定位,因为它被
下采样
的次数更少
-
自上而下特征图的构建块
- 对于较粗分辨率的特征图,将空间分辨率上采样
2
倍(使用最近邻上采样)。 - 然后通过逐元素相加将上采样映射与相应的自下而上映射(其经历
1×1
卷积层以减小通道维度)合并。 - 迭代此过程,直到生成最精细的分辨率映射。
- 开始迭代时,需在
C5
上附加一个1×1
的卷积层,即可生成最粗略的分辨率图。 - 最后,在每个合并的图上附加一个
3×3
的卷积来生成最终的特征图,这是为了减少上采样的混叠效应。 - 最后一组特征图被称为
{P2,P3,P4,P5}
,对应于分别具有相同空间大小的{C2,C3,C4,C5}
- 对于较粗分辨率的特征图,将空间分辨率上采样
-
因为金字塔的所有级别都使用共享分类器/回归器,所以固定了所有特征图中的特征维度(通道数,表示为
d
,设置d=256
)。所有额外的卷积层都有256
个通道输出。 -
在这些额外的层中不存在非线性,我们根据经验发现这些层的影响很小。
5. 应用
- 此方法是在深层
ConvNets
中构建特征金字塔的通用解决方案。 - 在下文中,在
RPN
中采用了特征金字塔
来生成边界框建议,在Fast R-CNN
中采用了方法来进行对象检测。 - 为了证明方法的简单性和有效性,对的原始系统进行了最小的修改,使其适应
特征金字塔
。
5.1 Feature Pyramid Networks for RPN【用于RPN的特征金字塔网络】
RPN
是一种滑动窗口类不可知对象检测器【RPN称区域建议网络
,用来提取前景与背景】。- 最初
RPN
设计中,在单尺度卷积特征图的顶部
,在密集的3×3
滑动窗口上评估小型子网络,执行对象/非对象二元分类和边界框回归。 - 是通过
3×3
卷积层和两个用于分类和回归的2个1×1
卷积来实现的,我们称之为网络头【network head】。 - 对象/非对象标准和边界框回归目标是关于一组称为锚点的参考框定义的。
锚
具有多个预定义的比例和纵横比,以覆盖不同形状的对象
- 最初
- 通过用
FPN
替换单比例特征图来调整RPN
。-
在
特征金字塔
的每个级别上附加一个相同设计的头部(3×3
个conv
和两个节点1×1个conv
)。因为头部在所有金字塔级别的所有位置上密集滑动,因此没有必要在特定级别上设置多尺度锚
-
为每个级别分配一个单一比例的
锚点
。- 将锚点定义为在
{P2、P3、P4、P5、P6}
上,分别具有{32*32、64*64、128*128、256*256、512*512}
像素的区域。 - 在每个级别使用多个纵横比
{1:2、1:1、2:1}
的锚点。所以金字塔上总共有15
个锚。
- 将锚点定义为在
-
根据锚点的交集
(IoU)
比率和ground-truth bounding boxes
为锚点分配训练标签。 -
如果
锚
对于给定的ground-truth bounding boxe
s具有最高的IoU
,或者对于任何ground-truth bounding boxes
的IoU
超过0.7
,则锚
被分配正标签,如果锚
的IoU
对于所有ground-truth bounding boxes
都低于0.3
,则锚
将被分配负标签。
-
ground-truth
框的比例并没有明确用于将其分配给金字塔的级别; -
ground-truth
框与锚
相关联,锚
已被分配到金字塔级别。
-
- 头部【heads】参数在所有特征金字塔级别上是共享的;
- 评估了不共享参数的替代方案,并观察到了类似的准确性。
- 共享参数的良好性能表明:特征金字塔的所有级别共享相似的语义级别。
- 这一优点类似于使用
特征化图像金字塔
的优点,其中公共头部分类器可以应用于以任何图像尺度计算的特征
- 总结:通过调整,
RPN
可以用FPN
进行训练和测试。
5.2 Feature Pyramid Networks for Fast R-CNN【Fast R-CNN的特征金字塔网络】
-
Fast R-CNN
- 是一种基于区域的对象检测器,使用
感兴趣区域(RoI)
来提取特征。 Fast R-CNN
最常见的是在单尺度特征图上执行。- 要将其与
FPN
一起使用,需要将不同规模的ROI
分配到金字塔级别
- 是一种基于区域的对象检测器,使用
-
特征金字塔训练
Fast R-CNN
实现- 当基于区域的检测器在图像金字塔上运行时,需要调整它们的分配策略。
- 通过将宽度
w
和高度h
的RoI
(在网络的输入图像上)分配给我们的特征金字塔
的层 P k P_k Pk:224
是规范的ImageNet
预训练大小-
k
0
k_0
k0是
w
×
h
=
22
4
2
w×h=224^2
w×h=2242的
RoI
应该映射到的目标层。
- 类似于使用
C
4
C_4
C4作为单尺度特征图的基于
ResNet的Faster R-CNN
系统,将 k 0 k_0 k0设置为4
。 - 意味着如果RoI的尺度变小(例如,
224的1/2
),则应该将其映射到更精细的分辨率级别(例如,k=3
)。
- 将预测头部(在
Fast R-CNN
中,头是类特定的分类器和边界框回归器)附加到所有级别的所有ROI
。- 头部都共享参数,无论在那一级别。
ResNet
模块与特征金字塔
的不同ResNet
的conv5
层(一个9
层深子网络)被用作conv4
特征的顶部,特征金字塔使用conv5
最为顶部。- 与
ResNet
不同的是,我们只简单地采用RoI
池来提取7×7
个特征,并在最终分类和边界框回归层
之前附加两个隐藏的1024-d
完全连接(fc)
层(每个层后面跟着ReLU
)。 - 这些层是随机初始化的,因为
ResNets
中没有可用的预训练fc
层。 - 与标准
conv5
头相比,我们的2-fc MLP head
头重量更轻、速度更快
-
总结:基于这些调整,可以在特征金字塔的顶部训练和测试
Fast R-CNN
。
6.特征金字塔的优点与结论
- 优点
特征金字塔
大大提高了RPN
对对象尺度变化的鲁棒性【在小物体(AR1ks)上的性能提高了12.9分】- 在自下而上的金字塔上,不同级别之间存在很大的语义差距,尤其是对于非常深的
ResNets
,即自上而下路径非常重要。 - 通过
横向连接从自下而上的映射
的更精细级别直接传递到自上而下的映射找到更精确的特征位置。 RPN
是一种具有固定窗口大小的滑动窗口检测器,因此在金字塔级别上扫描可以提高其对尺度方差的鲁棒性FPN
在检测器要识别的小物体上具有良好的性能。- 对于基于
区域的对象检测器
,我们的特征金字塔
优于单尺度特征。 Fast R-CNN
在高分辨率特征图上使用低级别特征,删除自上而下的连接会显著降低准确性共享特征
可以小幅度提高准确性,还减少了测试时间特征金字塔
不依赖于图像金字塔,只使用单个输入图像比例,但在小规模对象上仍然具有出色的AP。- 是一个通用的特征提取器,可以取代图像金字塔来解决其他多尺度检测问题
- 结论
- 提出了一个干净简单的框架,用于在
ConvNets
中构建特征金字塔
。 - 该方法比几个强大的基线和竞赛获胜者有了显著的改进。
- 它为特征金字塔的研究和应用提供了一个实用的解决方案,而不需要计算
图像金字塔
。 - 研究表明:尽管深度
ConvNets
具有强大的代表能力及其对尺度变化的隐含鲁棒性,但使用金字塔表示来明确解决多尺度问题仍然至关重要。
- 提出了一个干净简单的框架,用于在
7.论文阅读总结
- 低级特征对于检测小物体很重要,所以
不可以舍弃前期特征图
- 特征金字塔每个阶段的最深层都应该具有最强的特征,提出了
自下而上路径
- 低级特征对于检测小物体很重要,需将特征图大小与通道统一,即较粗分辨率的特征图**【小的特征图】**生成最精细的分辨率映射 【大的特征图】,提出了
自上而下路径和横向连接
8. mask rcnn
使用FPN
相关代码实现
tensorflow
相关文章链接:Mask R-cnn 代码运行报错总结pytorch
相关文章链接:pytorch环境之mask-rcnn源码实现
8.1 Resnets模块构建
-
dentity_block
:输入维度和输出维度相同,可以串联,用于加深网络的- 代码位置
def identity_block(input_tensor, kernel_size, filters, stage, block,use_bias=True, train_bn=True): """ The identity_block is the block that has no conv layer at shortcut【identity_block是在快捷方式中没有对流层的块】 IdentityBlock输入维度和输出维度相同,可以串联,用于加深网络的 :param input_tensor:input tensor 【输入张量】 :param kernel_size: default 3, the kernel size of middle conv layer at main path 【默认值3,主路径上中间conv层的内核大小】 :param filters: list of integers, the nb_filters of 3 conv layer at main path 【整数列表,主路径上3个conv层的nbfilter】 :param stage: integer, current stage label, used for generating layer names 【整数,当前阶段标签,用于生成图层名称】 :param block: 'a','b'..., current block label, used for generating layer names 【“a”,“b”。。。,当前块标签,用于生成图层名称】 :param use_bias: Boolean. To use or not use a bias in conv layers. 【布尔值。在对流层中使用或不使用偏置。】 :param train_bn: Boolean. Train or freeze Batch Norm layers 【布尔值。训练或冻结批次标准层】 :return: """ nb_filter1, nb_filter2, nb_filter3 = filters conv_name_base = 'res' + str(stage) + block + '_branch' bn_name_base = 'bn' + str(stage) + block + '_branch' x = KL.Conv2D(nb_filter1, (1, 1), name=conv_name_base + '2a',use_bias=use_bias)(input_tensor) x = BatchNorm(name=bn_name_base + '2a')(x, training=train_bn) x = KL.Activation('relu')(x) x = KL.Conv2D(nb_filter2, (kernel_size, kernel_size), padding='same',name=conv_name_base + '2b', use_bias=use_bias)(x) x = BatchNorm(name=bn_name_base + '2b')(x, training=train_bn) x = KL.Activation('relu')(x) x = KL.Conv2D(nb_filter3, (1, 1), name=conv_name_base + '2c',use_bias=use_bias)(x) x = BatchNorm(name=bn_name_base + '2c')(x, training=train_bn) x = KL.Add()([x, input_tensor]) x = KL.Activation('relu', name='res' + str(stage) + block + '_out')(x) return x
- 代码位置
-
conv_block
:输入和输出的维度是不一样的,所以不能连续串联,它的作用是改变网络的维度;- 代码位置
def conv_block(input_tensor, kernel_size, filters, stage, block,strides=(2, 2), use_bias=True, train_bn=True): """ conv_block is the block that has a conv layer at shortcut 【conv块是在快捷方式中具有conv层的块】 ConvBlock输入和输出的维度是不一样的,所以不能连续串联,它的作用是改变网络的维度; :param input_tensor:input tensor 【输入张量】 :param kernel_size:default 3, the kernel size of middle conv layer at main path 【默认值3,主路径上中间conv层的内核大小】 :param filters:list of integers, the nb_filters of 3 conv layer at main path 【整数列表,主路径上3个conv层的nbfilter】卷积核个数,channel个数 :param stage:integer, current stage label, used for generating layer names 【整数,当前阶段标签,用于生成图层名称】 :param block:'a','b'..., current block label, used for generating layer names 【“a”,“b”。。。,当前块标签,用于生成图层名称】 :param strides:步长 strides=(2, 2) :param use_bias:Boolean. To use or not use a bias in conv layers. 【布尔值。在对流层中使用或不使用偏置。】 :param train_bn:Boolean. Train or freeze Batch Norm layers Note that from stage 3, the first conv layer at main path is with subsample=(2,2) And the shortcut should have subsample=(2,2) as well 【布尔值。训练或冻结批次标准层 注意,从第3阶段开始,主路径上的第一个对流层具有子样本=(2,2) 快捷方式也应具有子采样=(2,2)】 :return: """ nb_filter1, nb_filter2, nb_filter3 = filters conv_name_base = 'res' + str(stage) + block + '_branch' bn_name_base = 'bn' + str(stage) + block + '_branch' x = KL.Conv2D(nb_filter1, (1, 1), strides=strides,name=conv_name_base + '2a', use_bias=use_bias)(input_tensor) x = BatchNorm(name=bn_name_base + '2a')(x, training=train_bn) x = KL.Activation('relu')(x) # 使用kernel_size大小的卷积核卷积,padding="same",特征图大小不变 x = KL.Conv2D(nb_filter2, (kernel_size, kernel_size), padding='same',name=conv_name_base + '2b', use_bias=use_bias)(x) x = BatchNorm(name=bn_name_base + '2b')(x, training=train_bn) x = KL.Activation('relu')(x) # 升维,增加channel通道数 x = KL.Conv2D(nb_filter3, (1, 1), name=conv_name_base +'2c', use_bias=use_bias)(x) x = BatchNorm(name=bn_name_base + '2c')(x, training=train_bn) # 恒等映射 shortcut = KL.Conv2D(nb_filter3, (1, 1), strides=strides,name=conv_name_base + '1', use_bias=use_bias)(input_tensor) shortcut = BatchNorm(name=bn_name_base + '1')(shortcut, training=train_bn) x = KL.Add()([x, shortcut]) x = KL.Activation('relu', name='res' + str(stage) + block + '_out')(x) return x
- 代码位置
-
resnet_graph
:Resnets结构
- 代码位置
def resnet_graph(input_image, architecture, stage5=False, train_bn=True): """ Build a ResNet graph.【构建ResNet图。】 :param input_image: :param architecture: Can be resnet50 or resnet101 【可以是resnet50或resnet101】 :param stage5: Boolean. If False, stage5 of the network is not created【布尔值。如果为False,则不创建网络的阶段5】 :param train_bn:Boolean. Train or freeze Batch Norm layers 【布尔值。训练或冻结批次标准层】 :return: """ assert architecture in ["resnet50", "resnet101"] # Stage 1 x = KL.ZeroPadding2D((3, 3))(input_image) # 将特征图外部添加0,使边缘特征提取更加充分 x = KL.Conv2D(64, (7, 7), strides=(2, 2), name='conv1', use_bias=True)(x) # 卷积,卷积核为7*7,channel=64,扫描步长为2,卷积核有偏差项,总参数为7*7+1 x = BatchNorm(name='bn_conv1')(x, training=train_bn) # 批量归一化 x = KL.Activation('relu')(x) # 非线性激活函数 C1 = x = KL.MaxPooling2D((3, 3), strides=(2, 2), padding="same")(x) # 最大池化 # Stage 2 x = conv_block(x, 3, [64, 64, 256], stage=2, block='a', strides=(1, 1), train_bn=train_bn) # 残差网络映射操作 x = identity_block(x, 3, [64, 64, 256], stage=2, block='b', train_bn=train_bn) C2 = x = identity_block(x, 3, [64, 64, 256], stage=2, block='c', train_bn=train_bn) # Stage 3 x = conv_block(x, 3, [128, 128, 512], stage=3, block='a', train_bn=train_bn) x = identity_block(x, 3, [128, 128, 512], stage=3, block='b', train_bn=train_bn) x = identity_block(x, 3, [128, 128, 512], stage=3, block='c', train_bn=train_bn) C3 = x = identity_block(x, 3, [128, 128, 512], stage=3, block='d', train_bn=train_bn) # Stage 4 x = conv_block(x, 3, [256, 256, 1024], stage=4, block='a', train_bn=train_bn) block_count = {"resnet50": 5, "resnet101": 22}[architecture] for i in range(block_count): x = identity_block(x, 3, [256, 256, 1024], stage=4, block=chr(98 + i), train_bn=train_bn) C4 = x # Stage 5 if stage5: x = conv_block(x, 3, [512, 512, 2048], stage=5, block='a', train_bn=train_bn) x = identity_block(x, 3, [512, 512, 2048], stage=5, block='b', train_bn=train_bn) C5 = x = identity_block(x, 3, [512, 512, 2048], stage=5, block='c', train_bn=train_bn) else: C5 = None return [C1, C2, C3, C4, C5]
- 代码位置
8.2 FPN构建
- 代码位置
if callable(config.BACKBONE):
# 特征图大小在减小,channel通道数再增加
_, C2, C3, C4, C5 = config.BACKBONE(input_image, stage5=True,train_bn=config.TRAIN_BN)
else:
_, C2, C3, C4, C5 = resnet_graph(input_image, config.BACKBONE,stage5=True, train_bn=config.TRAIN_BN)
# Top-down Layers 【自上而下层:特征融合,对应位置相加】
# ,通道数全部要统一为config.TOP_DOWN_PYRAMID_SIZE=256
P5 = KL.Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), name='fpn_c5p5')(C5)
# p5上采样(使用线性插值),与c4(统一通道数的c4)特征融合
P4 = KL.Add(name="fpn_p4add")([KL.UpSampling2D(size=(2, 2), name="fpn_p5upsampled")(P5),KL.Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), name='fpn_c4p4')(C4)])
# p4上采样,与c3(统一通道数的c3)特征融合
P3 = KL.Add(name="fpn_p3add")([KL.UpSampling2D(size=(2, 2), name="fpn_p4upsampled")(P4),KL.Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), name='fpn_c3p3')(C3)])
# p3上采样,与c2(统一通道数的c2)特征融合
P2 = KL.Add(name="fpn_p2add")([KL.UpSampling2D(size=(2, 2), name="fpn_p3upsampled")(P3),KL.Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (1, 1), name='fpn_c2p2')(C2)])
# Attach 3x3 conv to all P layers to get the final feature maps.【将3x3 conv附加到所有P层以获得最终的要素地图。】
# 特征再次融合:通道数不变
P2 = KL.Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), padding="SAME", name="fpn_p2")(P2)
P3 = KL.Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), padding="SAME", name="fpn_p3")(P3)
P4 = KL.Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (3, 3), padding="SAME", name="fpn_p4")(P4)
P5 = KL.Conv2D(config.TOP_DOWN_PYRAMID_SIZE, (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用于RPN中的第五锚定标度。通过步距为2的P5的二次采样生成。】
P6 = KL.MaxPooling2D(pool_size=(1, 1), strides=2, name="fpn_p6")(P5) # 以p6特征图大小作为区域框的生成
# Note that P6 is used in RPN, but not in the classifier heads.【注意,P6用于RPN,但不用于分类器头部。】
rpn_feature_maps = [P2, P3, P4, P5, P6]
mrcnn_feature_maps = [P2, P3, P4, P5]
8.3 源码问题
tensorflow
版本这个源码并没有实际运行起来,tensorflow2
版本改动太大,官网是1
版本的,虽然报错修改,但是运行的输出有问题,并且出现了损失为nan
1后期使用的pytorch
框架实现的,只是使用这个代码看如何实现的,因为pytorch
版本封装好了,看不到实际如何构建步骤。