深入浅出之FPN (Feature Pyramid Networks for Object Detection)网络 ...

鼠扑  金牌会员 | 2024-10-30 18:00:52 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 700|帖子 700|积分 2100

FPN(Feature Pyramid Network),即特征金字塔网络,是一种用于解决目标检测和语义分割中多尺度问题的深度学习网络结构。以下是对FPN网络的详细先容:
一、概述

FPN网络是在2017年的CVPR会议上提出的,主要目的是通过特征融合的方式,在不显著增长计算量的情况下,提升多尺度目标的检测性能,尤其是对小目标的检测能力。它通过构建多尺度特征金字塔,将高层特征图的语义信息与低层特征图的空间信息举行融合,生成具有丰富多尺度信息的特征表示。
二、FPN的提出配景、时间及作者

提出配景
 为了增强语义性,传统的物体检测模型通常只在深度卷积网络的末了一个特征图上举行后续操作,而这一层对应的下采样率(图像缩小的倍数)通常又比较大,如16、32,造成小物体在特征图上的有效信息较少,小物体的检测性能会急剧降落,这个问题也被称为多尺度问题。 ​ 解决多尺度问题的关键在于怎样提取多尺度的特征。传统的方法有图像金字塔(Image Pyramid),主要思路是将输入图片做成多个尺度,不同尺度的图像生成不同尺度的特征,这种方法简朴而有效,大量使用在了COCO等竞赛上,但缺点黑白常耗时,计算量也很大。 ​ 从前面几大主干网络的内容可以知道,卷积神经网络不同层的大小与语义信息不同,本身就雷同一个金字塔结构。
        

         如上图,金字塔底部可以较为浅层特征图,金字塔顶部可以较为深层特征图!
        浅层的特征图感受野小,比较适合检测小目标(要检测大目标,则其只“看”到了大目标的一部门,有效信息不敷);深层的特征图感受野大,适合检测大目标(要检测小目标,则其”看“到了太多的配景噪音,冗余噪音太多),因此FPN应运而生!!!
        2017年的FPN(Feature Pyramid Network)方法融合了不同层的特征,较好地改善了多尺度检测问题。
提出时间及作者
FPN由何凯明等人在2017年提出,这一网络结构的提出为解决物体检测中的多尺度问题提供了新的思路。
三、主要贡献

FPN的主要贡献在于:

  • 提出了一种新的特征金字塔结构:FPN通过构建自底向上和自顶向下的特征融合路径,将不同尺度的特征图举行融合,从而生成具有丰富多尺度信息的特征金字塔。
  • 显著提升了小物体检测的性能:在不显著增长计算量的情况下,FPN通过特征融合的方式,大幅度进步了模型对小尺度物体的检测能力。
  • 广泛应用于多种计算机视觉任务:FPN不仅适用于目标检测任务,还被广泛应用于语义分割、实例分割等任务中,并取得了显著的效果。
四、优缺点

优点

  • 多尺度特征融合:FPN通过融合不同尺度的特征图,使得模型能够同时处置惩罚不同大小的目标。
  • 计算效率高:在基本不增长计算量的情况下,FPN显著提升了模型的检测性能。
  • 机动性强:FPN可以轻松地与其他网络结构相结合使用,进步整体性能。
缺点(相对而言):

  • 对硬件资源有一定要求:虽然FPN的计算量没有显著增长,但处置惩罚多尺度特征图仍旧需要一定的硬件资源支持。
  • 网络结构相对复杂:FPN引入了自顶向下的特征融合路径和横向连接,使得网络结构相对复杂,大概增长模型练习的难度。

五、网络结构

FPN的网络结构主要由自底向上的特征提取路径和自顶向下的特征融合路径构成。自底向上的路径是基础网络(如ResNet)的正向传播过程,用于提取不同层次的特征图。自顶向下的路径则通过上采样和横向连接的方式,将高层特征图的语义信息与低层特征图的空间信息举行融合。
 FPN的总体架构如上图所示,主要包含自下而上网络、自上而下网络、横向连接与卷积融合4个部门。
FPN网络通过以下步调实现多尺度特征的提取和融合:

  • 自底向上的特征提取

    • 使用一个基础网络(如ResNet、VGG等)作为骨干网络(backbone),从输入图像中逐层提取特征。这些特征图(feature maps)在卷积神经网络中逐层通报,每一层都包含了不同尺度和不同层次的特征信息。
    • 在这个过程中,特征图的分辨率渐渐低落,而语义信息渐渐丰富。每一层特征图都代表了输入图像在不同尺度上的抽象表示。

  • 自顶向下的特征融合

    • 为了解决高层特征图分辨率低、细节信息少的问题,FPN引入了自顶向下的特征融合路径。
    • 高层特征图首先通过上采样(如双线性插值、反卷积等)操作,使其分辨率与低一层特征图相匹配。
    • 然后,将上采样后的高层特征图与对应的低层特征图举行融合。这种融合通常是通过逐元素相加(element-wise addition)或特征拼接(concatenation)的方式实现的。
    • 在融合过程中,为了保持特征图的通道数一致,大概需要对低层特征图举行1x1卷积操作来淘汰其通道数。

  • 横向连接

    • 自顶向下的特征融合路径与自底向上的特征提取路径之间通过横向连接(lateral connections)相连。这些横向连接将低层特征图的高分辨率信息与高层特征图的丰富语义信息相结合,形成新的特征图。
    • 横向连接不仅有助于通报低层特征图的细节信息,还可以增强高层特征图的定位能力。

  • 特征金字塔的构建

    • 通过上述步调,FPN构建了一个特征金字塔(feature pyramid)。这个金字塔包含了从底层到顶层的多个尺度的特征图,每个特征图都融合了不同层次的特征信息。
    • 特征金字塔的每一层都对应一个特定的尺度范围,使得模型能够同时处置惩罚不同大小的目标。

  • 猜测与输出

    • 在特征金字塔的每一层上,都可以独立地举行目标检测或语义分割等任务的猜测。
    • 对于目标检测任务,通常会在每个特征图上设置不同尺度和长宽比的锚点(anchors),并猜测锚点对应的边界框(bounding boxes)和类别标签。
    • 对于语义分割任务,则可以直接在特征图上应用分割器来猜测每个像素的类别标签。


六、pytorch实现

  1. '''FPN in PyTorch.
  2. See the paper "Feature Pyramid Networks for Object Detection" for more details.
  3. '''
  4. import torch
  5. import torch.nn as nn
  6. import torch.nn.functional as F
  7. from torch.autograd import Variable
  8. class Bottleneck(nn.Module):
  9.     expansion = 4
  10.     def __init__(self, in_planes, planes, stride=1):
  11.         super(Bottleneck, self).__init__()
  12.         self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=1, bias=False)
  13.         self.bn1 = nn.BatchNorm2d(planes)
  14.         self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
  15.         self.bn2 = nn.BatchNorm2d(planes)
  16.         self.conv3 = nn.Conv2d(planes, self.expansion*planes, kernel_size=1, bias=False)
  17.         self.bn3 = nn.BatchNorm2d(self.expansion*planes)
  18.         self.shortcut = nn.Sequential()
  19.         if stride != 1 or in_planes != self.expansion*planes:
  20.             self.shortcut = nn.Sequential(
  21.                 nn.Conv2d(in_planes, self.expansion*planes, kernel_size=1, stride=stride, bias=False),
  22.                 nn.BatchNorm2d(self.expansion*planes)
  23.             )
  24.     def forward(self, x):
  25.         out = F.relu(self.bn1(self.conv1(x)))
  26.         out = F.relu(self.bn2(self.conv2(out)))
  27.         out = self.bn3(self.conv3(out))
  28.         out += self.shortcut(x)
  29.         out = F.relu(out)
  30.         return out
  31. class FPN(nn.Module):
  32.     def __init__(self, block, num_blocks):
  33.         super(FPN, self).__init__()
  34.         self.in_planes = 64
  35.         self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False)
  36.         self.bn1 = nn.BatchNorm2d(64)
  37.         # Bottom-up layers
  38.         self.layer1 = self._make_layer(block,  64, num_blocks[0], stride=1)
  39.         self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2)
  40.         self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2)
  41.         self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
  42.         # Top layer
  43.         self.toplayer = nn.Conv2d(2048, 256, kernel_size=1, stride=1, padding=0)  # Reduce channels
  44.         # Smooth layers
  45.         self.smooth1 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
  46.         self.smooth2 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
  47.         self.smooth3 = nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1)
  48.         # Lateral layers
  49.         self.latlayer1 = nn.Conv2d(1024, 256, kernel_size=1, stride=1, padding=0)
  50.         self.latlayer2 = nn.Conv2d( 512, 256, kernel_size=1, stride=1, padding=0)
  51.         self.latlayer3 = nn.Conv2d( 256, 256, kernel_size=1, stride=1, padding=0)
  52.     def _make_layer(self, block, planes, num_blocks, stride):
  53.         strides = [stride] + [1]*(num_blocks-1)
  54.         layers = []
  55.         for stride in strides:
  56.             layers.append(block(self.in_planes, planes, stride))
  57.             self.in_planes = planes * block.expansion
  58.         return nn.Sequential(*layers)
  59.     def _upsample_add(self, x, y):
  60.         '''Upsample and add two feature maps.
  61.         Args:
  62.           x: (Variable) top feature map to be upsampled.
  63.           y: (Variable) lateral feature map.
  64.         Returns:
  65.           (Variable) added feature map.
  66.         Note in PyTorch, when input size is odd, the upsampled feature map
  67.         with `F.upsample(..., scale_factor=2, mode='nearest')`
  68.         maybe not equal to the lateral feature map size.
  69.         e.g.
  70.         original input size: [N,_,15,15] ->
  71.         conv2d feature map size: [N,_,8,8] ->
  72.         upsampled feature map size: [N,_,16,16]
  73.         So we choose bilinear upsample which supports arbitrary output sizes.
  74.         '''
  75.         _,_,H,W = y.size()
  76.         return F.upsample(x, size=(H,W), mode='bilinear') + y
  77.     def forward(self, x):
  78.         # Bottom-up
  79.         c1 = F.relu(self.bn1(self.conv1(x)))
  80.         c1 = F.max_pool2d(c1, kernel_size=3, stride=2, padding=1)
  81.         print(f'c1:{c1.shape}')
  82.         c2 = self.layer1(c1)
  83.         print(f'c2:{c2.shape}')  
  84.         c3 = self.layer2(c2)
  85.         print(f'c3:{c3.shape}')
  86.         c4 = self.layer3(c3)
  87.         print(f'c4:{c4.shape}')
  88.         c5 = self.layer4(c4)
  89.         print(f'c5:{c5.shape}')
  90.         # Top-down
  91.         p5 = self.toplayer(c5)
  92.         print(f'p5:{p5.shape}')
  93.         p4 = self._upsample_add(p5, self.latlayer1(c4))
  94.         print(f'latlayer1(c4):{self.latlayer1(c4).shape}, p4:{p4.shape}')
  95.         p3 = self._upsample_add(p4, self.latlayer2(c3))
  96.         print(f'latlayer1(c3):{self.latlayer2(c3).shape}, p3:{p3.shape}')
  97.         p2 = self._upsample_add(p3, self.latlayer3(c2))
  98.         print(f'latlayer1(c2):{self.latlayer3(c2).shape}, p2:{p2.shape}')
  99.         # Smooth
  100.         p4 = self.smooth1(p4)
  101.         p3 = self.smooth2(p3)
  102.         p2 = self.smooth3(p2)
  103.         return p2, p3, p4, p5
  104. def FPN18():
  105.     # return FPN(Bottleneck, [2,4,23,3])
  106.     return FPN(Bottleneck, [2,2,2,2])
  107. def test():
  108.     net = FPN18()
  109.     fms = net(Variable(torch.randn(1,3,600,900)))
  110.     for fm in fms:
  111.         print(fm.size())
  112.     # p=[fms[0], fms[1], fms[2] , fms[3]]
  113.     # print(p)
  114. test()
复制代码
七、FPN的演化版本

FPN(Feature Pyramid Networks,特征金字塔网络)自提出以来,在目标检测、语义分割等范畴展现出了强盛的性能。随着计算机视觉范畴的发展,FPN也经历了一系列的后续演化版本,这些版本在保持FPN基本思想的基础上,举行了不同的改进和优化。以下是一些FPN的后续演化版本:

  • PANet(Path Aggregation Network)

    • PANet通过引入一个额外的自下而上的路径增强,将低层次的特征信息通报到高层次的特征图中,从而增强特征金字塔的表示能力。这种结构有助于捕获更多层次的上下文信息,进步目标检测的准确性。

  • NAS-FPN(Neural Architecture Search Feature Pyramid Network)

    • NAS-FPN使用神经架构搜刮(NAS)技能来自动计划特征金字塔网络的结构。通过在大规模搜刮空间中搜刮最优的跨尺度连接模式,NAS-FPN能够找到比传统手工计划更高效的特征金字塔结构。

  • BiFPN(Bidirectional Feature Pyramid Network)

    • BiFPN是EfficientDet中的焦点组件之一,它进一步改进了FPN的跨尺度连接计谋。BiFPN通过引入双向连接和加权特征融合,使得特征金字塔中的每一层都能接收到来自其他全部层的信息,从而进步了特征的表示能力。

  • HRNet(High-Resolution Net)

    • 虽然HRNet本身不是FPN的直接演化版本,但它通过在整个网络中保持高分辨率的表示,实现了雷同于FPN的多尺度特征融合。HRNet通过并行连接不同分辨率的子网络,并在各个子网络之间举行特征互换,从而实现了高效的特征融合和多尺度表示。

  • 其他变体

    • 在此基础上,另有许多研究者提出了针对特定任务或数据集优化的FPN变体,如轻量级的FPN结构、针对小目标检测的FPN改进等。这些变体通常通过调解FPN的结构参数、引入留意力机制、结合其他网络组件等方式来进步性能。

需要留意的是,随着计算机视觉技能的不断发展,新的FPN演化版本也在不断涌现。因此,在实际应用中,需要根据具体任务和数据集的特点选择符合的FPN版本或变体。同时,也可以借鉴最新的研究成果,对FPN举行进一步的改进和优化。
八、应用场景

FPN的应用场景非常广泛,主要包括:

  • 目标检测:FPN通过构建特征金字塔,进步了模型对不同尺度目标的检测能力,在COCO、PASCAL VOC等目标检测数据集上取得了优异的体现。
  • 语义分割:在语义分割任务中,FPN通过融合多尺度特征图,使得模型能够同时使用深层特征的语义信息和浅层特征的空间信息,提升分割效果。
  • 实例分割:实例分割是目标检测和语义分割的结合体,FPN同样可以应用于实例分割任务中,进步模型的分割精度和鲁棒性。
总之,FPN作为一种创新的特征融合网络结构,在计算机视觉范畴具有广泛的应用前景和紧张的研究价值。
参考:

  • FPN网络结构及Pytorch实现

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

鼠扑

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表