特征金字塔: 特征金字塔需要大量计算和内存 特征金字塔计算和内存都是密集型的。特征金字塔网络(FPN)的体系结构: 特征金字塔。特征金字塔 【特征化图像金字塔】 为识别不同尺度的物体构成了标准解决方案的基础。 
深度卷积网络(ConvNets)提取的特征所取代的原因。 ConvNets虽然对尺度变化也具鲁棒性,从而有助于从单个输入尺度上计算的特征中进行识别。即使有了这种稳健性,但是仍然需要金字塔来获得最准确的结果。图像金字塔的每个级别进行特征化的主要优点 图像金字塔问题 图像金字塔的每个层次特征化具有明显的局限性。推断时间显著增加,使得这种方法对于实际应用来说不切实际。图像金字塔上的端到端网络在测试集内存方面是不可行图像金字塔,则仅在测试时间使用图像金字塔,这会在训练/测试时间推断之间产生不一致。图像金字塔并不是计算多尺度特征表示的唯一方法。 ConvNet逐层计算特征层次,通过下采样层,特征层次具有固有的多尺度金字塔形状。单镜头检测器(SSD)是第一次尝试使用ConvNet的金字塔特征层次结构。SSD样式的金字塔将重用在正向过程中计算的来自不同层的多尺度特征图,因此不会产生成本。ConvNet特征层次结构的金字塔形状,同时创建在所有尺度上都具有强语义的特征金字塔。自上而下的路径和横向连接将低分辨率、语义强的特征与高分辨率、语义弱的特征相结合的架构。使用架构的区别 特征金字塔很容易扩展到掩模方案,并与严重依赖图像金字塔的最先进方法相比,提高了实例分割平均召回率(AR)和速度。 特征金字塔结构可以用所有尺度进行端到端训练,并在训练/测试时一致使用,这将是使用图像金字塔不可行的内存 【解决内存不足问题】。FPN能够实现比所有现有的最先进的方法更高的精度。此外,这种改进是在不增加单尺度基线测试时间的情况下实现的。
手工提取特征和早期神经网络。 SIFT特征最初是在尺度空间极值处提取的,并用于特征点匹配。HOG特征,以及后来的SIFT特征,都是在整个图像金字塔上密集计算的。HOG和SIFT金字塔已被用于图像分类、物体检测、人体姿态估计等许多工作。Dollár等人通过首先计算稀疏采样(按比例)的金字塔,然后对缺失水平进行插值,证明了快速金字塔计算。深度卷积物体检测。 ConvNets的发展,OverFeat和R-CNN等物体探测器在精度上有了显著提高。OverFeat采用了一种类似于早期神经网络人脸检测器的策略,将ConvNet作为图像金字塔上的滑动窗口检测器。R-CNN采用了基于区域提案的策略,其中每个提案在使用ConvNet进行分类之前都进行了规模归一化。 SPPnet证明了这种基于区域的检测器可以更有效地应用于在单个图像尺度上提取的特征图。Fast R-CNN和Faster R-CNN,提倡使用从单个尺度计算的特征,因为它在准确性和速度之间提供了良好的权衡。然而,多尺度检测仍然表现更好,尤其是对于小物体。使用多层特征的方法。 ConvNet中使用不同的层来改进检测和分割。FCN在多个尺度上对每个类别的部分分数求和,以计算语义分割。(HyperNet、ParseNet和ION)在计算预测之前连接多层的特征,这相当于对转换后的特征求和。SSD和MS-CNN在不组合特征或分数的情况下预测特征层次的多个层的对象U-Net和SharpMask,用于人脸检测的重组器网络,以及用于关键点估计的堆叠沙漏网络。Ghiasi等人提出了FCN的拉普拉斯金字塔表示,以逐步细化分割。
目标 ConvNet的金字塔特征层次结构,该层次结构具有从低到高的语义,并构建一个贯穿始终的具有高级语义的特征金字塔。产生的特征金字塔网络是通用的重点关注模块 区域提议网络,简称RPN)(Fast R-CNN)。实现方法 ResNets骨干卷积架构。金字塔的构建包括 自下而上路径自上而下路径横向连接。自下而上路径 ResNets,使用每个阶段的最后一个残差块输出的特征激活。conv2、conv3、conv4和conv5输出,将这些最后残差块的输出表示为{C2、C3、C4、C5},并注意到它们相对于输入图像具有{4、8、16、32}个像素的步长。conv1占用大量内存,因此没有将其包含在金字塔中。
自上而下路径和横向连接
下采样的次数更少
自上而下特征图的构建块
2倍(使用最近邻上采样)。1×1卷积层以减小通道维度)合并。C5上附加一个1×1的卷积层,即可生成最粗略的分辨率图。3×3的卷积来生成最终的特征图,这是为了减少上采样的混叠效应。{P2,P3,P4,P5},对应于分别具有相同空间大小的{C2,C3,C4,C5}因为金字塔的所有级别都使用共享分类器/回归器,所以固定了所有特征图中的特征维度(通道数,表示为d,设置d=256)。所有额外的卷积层都有256个通道输出。
在这些额外的层中不存在非线性,我们根据经验发现这些层的影响很小。

ConvNets中构建特征金字塔的通用解决方案。RPN中采用了特征金字塔来生成边界框建议,在Fast R-CNN中采用了方法来进行对象检测。特征金字塔。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 boxes具有最高的IoU,或者对于任何ground-truth bounding boxes的IoU超过0.7,则锚被分配正标签,如果锚的IoU对于所有ground-truth bounding boxes都低于0.3,则锚将被分配负标签。

ground-truth框的比例并没有明确用于将其分配给金字塔的级别;
ground-truth框与锚相关联,锚已被分配到金字塔级别。
特征化图像金字塔的优点,其中公共头部分类器可以应用于以任何图像尺度计算的特征RPN可以用FPN进行训练和测试。Fast R-CNN
感兴趣区域(RoI)来提取特征。Fast R-CNN最常见的是在单尺度特征图上执行。FPN一起使用,需要将不同规模的ROI分配到金字塔级别
特征金字塔训练Fast R-CNN实现
w和高度h的RoI(在网络的输入图像上)分配给我们的特征金字塔的层PkP_kPk: 224是规范的ImageNet预训练大小RoI应该映射到的目标层。
ResNet的Faster R-CNN系统,将k0k_0k0设置为4。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。
特征金字塔大大提高了RPN对对象尺度变化的鲁棒性【在小物体(AR1ks)上的性能提高了12.9分】ResNets,即自上而下路径非常重要。横向连接从自下而上的映射的更精细级别直接传递到自上而下的映射找到更精确的特征位置。RPN是一种具有固定窗口大小的滑动窗口检测器,因此在金字塔级别上扫描可以提高其对尺度方差的鲁棒性FPN在检测器要识别的小物体上具有良好的性能。区域的对象检测器,我们的特征金字塔优于单尺度特征。Fast R-CNN在高分辨率特征图上使用低级别特征,删除自上而下的连接会显著降低准确性共享特征可以小幅度提高准确性,还减少了测试时间特征金字塔不依赖于图像金字塔,只使用单个输入图像比例,但在小规模对象上仍然具有出色的AP。ConvNets中构建特征金字塔。图像金字塔。ConvNets具有强大的代表能力及其对尺度变化的隐含鲁棒性,但使用金字塔表示来明确解决多尺度问题仍然至关重要。不可以舍弃前期特征图自下而上路径自上而下路径和横向连接mask rcnn使用FPN相关代码实现tensorflow相关文章链接:Mask R-cnn 代码运行报错总结pytorch相关文章链接:pytorch环境之mask-rcnn源码实现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 = filtersconv_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 = filtersconv_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 1x = 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+1x = 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 2x = 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 3x = 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 4x = 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 5if 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 = Nonereturn [C1, C2, C3, C4, C5]

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]
tensorflow版本这个源码并没有实际运行起来,tensorflow2版本改动太大,官网是1版本的,虽然报错修改,但是运行的输出有问题,并且出现了损失为nan1后期使用的pytorch框架实现的,只是使用这个代码看如何实现的,因为pytorch版本封装好了,看不到实际如何构建步骤。
上一篇:什么是一致性
下一篇:考研复试——离散数学