特征金字塔网络及变体【详解及代码实现】

开发 人工智能
特征金字塔网络彻底改变了我们在神经网络中处理多尺度特征表示的方式,本文将深入探讨FPN。

在深度学习和计算机视觉领域,架构创新在推动技术进步中发挥了重要作用。在这些创新中,特征金字塔网络(Feature Pyramid Networks, FPN)脱颖而出,成为革命性的基础构建模块,彻底改变了我们在神经网络中处理多尺度特征表示的方式。本文将深入探讨FPN。

一、现代神经网络的三个主要组成部分(骨干网络、颈部网络和头部网络)  

在深入FPN之前,了解现代计算机视觉神经网络的三个主要组成部分非常重要:

1. 骨干网络(Backbone)

骨干网络通常是一个卷积神经网络(如ResNet或VGG),作为主要特征提取器。它处理原始输入图像,并生成不同尺度的层次化特征表示。可以将其视为捕捉从边缘、纹理到高级语义信息的基础。

2. 颈部网络(Neck)

颈部网络是骨干网络和头部网络之间的特征融合和增强模块。其主要目的是处理和组合来自骨干网络不同尺度或阶段的特征,以生成更具判别性的特征表示。可以将其视为一个加工厂,将来自不同来源的原材料(特征)提炼成更有用的产品。  

颈部网络可以执行多种操作,例如:  

  • 跨不同尺度的特征融合  
  • 通过额外卷积增强特征  
  • 管理不同网络层级之间的信息流  

特征金字塔网络是颈部网络架构的一种流行实现,但还有其他实现,如路径聚合网络(PANet)和高分辨率网络(HRNet)。

3. 头部网络(Head)

头部网络是任务特定的组件,使用经过优化的特征进行最终预测。不同任务(检测、分割、分类)需要不同的头部架构,但它们都受益于颈部网络提供的经过良好处理的特征。

二、为什么需要特征金字塔网络?  

计算机视觉中的多尺度挑战源于传统CNN架构的多个基本限制:  

1. 特征层次问题

随着CNN的深入,空间分辨率降低,而语义层次提高。例如,在典型的ResNet中:  

  • 早期层(如Conv1)具有1/2分辨率,捕捉基本特征(边缘、纹理)  
  • 中间层(如Conv3)具有1/8分辨率,捕捉中级特征(部件、模式)  
  • 深层(如Conv5)具有1/32分辨率,捕捉高级特征(物体、场景)  

2. 尺度变化

自然图像中的物体以不同的尺度出现。例如,在自动驾驶中:  

  • 附近的行人可能占据300x600像素  
  • 远处的车辆可能仅占据30x60像素  
  • 交通标志可能以任何大小出现  

3. 信息丢失 

传统的特征金字塔(如图像金字塔)保持了空间分辨率,但在较低层次缺乏语义强度,使其在现代深度学习中效率低下。这些问题的结合构成了计算机视觉中的重大挑战。  

现实世界的例子:想象一辆自动驾驶汽车试图检测街道上的物体。  摄像头看到的物体距离不同,有些近,有些远。  要检测远处的行人,系统需要处理高分辨率(详细)图像以捕捉小细节。  但问题在于:处理这些详细图像的早期网络层并不擅长理解内容。它们可能看到人的基本形状,但无法区分是行人还是路灯杆,因为它们缺乏深层次的理解。  

为什么不使用传统方法(如图像金字塔)?  这种方法提取的特征信息不够丰富,无法真正用于现代深度学习。  

我们陷入了两难选择: 要么获得良好的细节但理解力差,要么获得良好的理解力但细节差。这就像在放大镜(能看清细节但无法识别物体)和模糊眼镜(能识别物体但看不清细节)之间做出选择。  这种“看清”与“理解”之间的权衡正是研究人员开发特征金字塔网络的原因——最终解决这一困境。

三、特征金字塔网络(FPN)

图片、、图片、、

FPN通过三个关键组件结合了低层次和高层次特征:  

(1) 自下而上路径(骨干网络)

  • 这是常规的卷积神经网络前向传播。  
  • 特征逐渐变得更语义化,但空间分辨率降低。  
  • 每个阶段输出不同尺度的特征图(C₂, C₃, C₄, C₅)。  

(2) 自上而下路径

  • 从最深层开始,逐步上采样空间较粗糙但语义较强的特征。  
  • 创建更高分辨率的特征(P₅, P₄, P₃, P₂)。  
  • 使用最近邻上采样来增加分辨率。  

(3) 横向连接 

  • 1x1卷积减少骨干网络特征的通道维度。  
  • 逐元素加法合并自下而上和自上而下的特征。  
  • 3x3卷积平滑合并后的特征。  

1. 技术过程

  • 提取自下而上的特征 {C₂, C₃, C₄, C₅}  
  • 通过1x1卷积处理顶层特征C₅以创建P₅  
  • 上采样P₅并与处理后的C₄合并以创建P₄  
  • 此过程持续到P₂  
  • 最终金字塔的每个层级 {P₂, P₃, P₄, P₅} 包含丰富的语义信息,同时保持适当的空间分辨率  

2. 示例代码  

以下是一个使用ResNet-18骨干网络实现FPN进行图像分类的示例代码。  

import torch
import torch.nn as nn
import torchvision.models as models

class FPNNeck(nn.Module):
    def __init__(self, in_channels_list, out_channels):
        super(FPNNeck, self).__init__()
        
        # Lateral connections (1x1 convolutions)
        self.lateral_convs = nn.ModuleList([
            nn.Conv2d(in_channels, out_channels, 1)
            for in_channels in in_channels_list
        ])
        
        # Top-down pathway (upsampling + smoothing)
        self.fpn_convs = nn.ModuleList([
            nn.Conv2d(out_channels, out_channels, 3, padding=1)
            for _ in range(len(in_channels_list))
        ])
        
    def forward(self, features):
        # features should be ordered from highest resolution to lowest
        laterals = [conv(feature) for feature, conv in zip(features, self.lateral_convs)]
        
        # Top-down pathway
        for i in range(len(laterals)-1, 0, -1):
            laterals[i-1] += nn.functional.interpolate(
                laterals[i], size=laterals[i-1].shape[-2:], mode='nearest'
            )
        
        # Smoothing
        outputs = [conv(lateral) for lateral, conv in zip(laterals, self.fpn_convs)]
        return outputs

class ResNetFPN(nn.Module):
    def __init__(self, num_classes):
        super(ResNetFPN, self).__init__()
        
        # Load pretrained ResNet-18 as backbone
        resnet = models.resnet18(pretrained=True)
        self.backbone_layers = nn.ModuleList([
            nn.Sequential(resnet.conv1, resnet.bn1, resnet.relu, resnet.maxpool, resnet.layer1),
            resnet.layer2,
            resnet.layer3,
            resnet.layer4
        ])
        
        # FPN neck
        in_channels_list = [64, 128, 256, 512]  # ResNet-18 output channels
        self.fpn = FPNNeck(in_channels_list, out_channels=256)
        
        # Classification head
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(256 * 4, num_classes)  # 4 feature maps from FPN
        
    def forward(self, x):
        # Extract features from backbone
        features = []
        for layer in self.backbone_layers:
            x = layer(x)
            features.append(x)
            
        # FPN forward pass
        fpn_features = self.fpn(features)
        
        # Global average pooling on each FPN level
        pooled_features = []
        for feature in fpn_features:
            pooled = self.avgpool(feature)
            pooled_features.append(pooled.flatten(1))
            
        # Concatenate all pooled features
        x = torch.cat(pooled_features, dim=1)
        x = self.fc(x)
        
        return x
import torch
import torch.nn as nn
import torchvision.models as models

class FPNNeck(nn.Module):
    def __init__(self, in_channels_list, out_channels):
        super(FPNNeck, self).__init__()
        
        # Lateral connections (1x1 convolutions)
        self.lateral_convs = nn.ModuleList([
            nn.Conv2d(in_channels, out_channels, 1)
            for in_channels in in_channels_list
        ])
        
        # Top-down pathway (upsampling + smoothing)
        self.fpn_convs = nn.ModuleList([
            nn.Conv2d(out_channels, out_channels, 3, padding=1)
            for _ in range(len(in_channels_list))
        ])

FPNNeck类实现了FPN的核心架构:  

  • `lateral_convs` 创建1x1卷积,减少来自不同层级骨干网络特征的通道维度。  
  • `fpn_convs` 是3x3卷积,用于平滑合并后的特征。  
def forward(self, features):
        # features should be ordered from highest resolution to lowest
        laterals = [conv(feature) for feature, conv in zip(features, self.lateral_convs)]
        
        # Top-down pathway
        for i in range(len(laterals)-1, 0, -1):
            laterals[i-1] += nn.functional.interpolate(
                laterals[i], size=laterals[i-1].shape[-2:], mode='nearest'
            )
        
        # Smoothing
        outputs = [conv(lateral) for lateral, conv in zip(laterals, self.fpn_convs)]
        return outputs

前向传播展示了FPN如何处理特征:

  • 它对所有特征层级应用横向卷积。  
  • 它实现自上而下的路径:从最深层开始,上采样特征并将其添加到上一层级。  
  • 它对所有层级应用平滑卷积。
class ResNetFPN(nn.Module):
    def __init__(self, num_classes):
        super(ResNetFPN, self).__init__()
        
        # Load pretrained ResNet-18 as backbone
        resnet = models.resnet18(pretrained=True)
        self.backbone_layers = nn.ModuleList([
            nn.Sequential(resnet.conv1, resnet.bn1, resnet.relu, resnet.maxpool, resnet.layer1),
            resnet.layer2,
            resnet.layer3,
            resnet.layer4
        ])

ResNetFPN类组合所有内容:  

  • 使用预训练的ResNet-18作为骨干网络。  
  • 添加FPN颈部网络以处理来自ResNet四个阶段的特征。  
  • 添加一个简单的分类头,用于池化每个FPN层级的特征并最终分类。  
# FPN neck
        in_channels_list = [64, 128, 256, 512]  # ResNet-18 output channels
        self.fpn = FPNNeck(in_channels_list, out_channels=256)
        
        # Classification head
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(256 * 4, num_classes)  # 4 feature maps from FPN

对于分类任务:我们创建FPN颈部网络,用于处理来自ResNet四个阶段的特征。我们添加一个简单的分类头,其功能包括:

  • 对每个FPN层级的特征进行池化
  • 将它们连接在一起
  • 进行最终的分类
def forward(self, x):
        # Extract features from backbone
        features = []
        for layer in self.backbone_layers:
            x = layer(x)
            features.append(x)
            
        # FPN forward pass
        fpn_features = self.fpn(features)
        
        # Global average pooling on each FPN level
        pooled_features = []
        for feature in fpn_features:
            pooled = self.avgpool(feature)
            pooled_features.append(pooled.flatten(1))
            
        # Concatenate all pooled features
        x = torch.cat(pooled_features, dim=1)
        x = self.fc(x)
        
        return x

前向传播将所有内容结合在一起:

  • 输入图像通过ResNet骨干网络,收集每个阶段的特征。
  • 这些特征通过FPN颈部网络,生成特征金字塔。
  • 我们对金字塔的每个层级的特征进行池化。
  • 最后,我们结合所有这些特征以进行最终的分类预测。

四、变体(特征金字塔网络的演进)  

自FPN诞生以来,研究人员对其进行了多种改进。以下是一些常见变体:  

1. PANet(路径聚合网络)

  • 通过添加额外的自下而上路径增强信息流。  
  • 用于实例分割的Mask Scoring R-CNN和实时目标检测的Thunder-Net。  

2. BiFPN(双向FPN)

  • 引入加权双向跨尺度连接。  
  • 用于EfficientDet系列目标检测器。  

3. 最新模型(2023-2024)

  • RT-DETR:使用基于变形Transformer的FPN变体。  
  • DINO-V2:实现混合FPN-Transformer颈部网络。  
  • YOLOv8:采用受FPN启发的改进CSP-PAN颈部网络。  

五、结论  

特征金字塔网络代表了计算机视觉架构设计的一项重大成就。其有效处理多尺度特征表示并保持计算效率的能力,使其成为现代计算机视觉系统中不可或缺的组成部分。随着领域的不断发展,FPN的影响可以在新架构中看到,其设计原则继续激发神经网络设计的创新。  

责任编辑:赵宁宁 来源: 小白玩转Python
相关推荐

2017-07-26 10:32:51

计算机视觉卷积神经网络FPN

2017-08-02 00:12:50

CVPR 2017论文FPN网络

2018-10-11 15:05:56

测试软件自动化

2021-01-25 06:37:06

Css前端CSS 特效

2020-04-27 13:45:08

数据流沙Filecoin

2018-01-26 08:54:29

存储SSDHDD

2022-12-13 15:41:41

测试软件开发

2013-03-14 09:46:05

移动创业诺基亚NEIC大师论道

2009-11-04 10:51:19

程序员职业规划

2024-11-21 10:58:05

模型AI

2022-12-29 16:09:25

2010-07-08 14:02:35

UML建模流程

2022-09-03 08:06:44

测试开发DevOps

2020-04-08 08:00:00

开发者金字塔模型

2009-10-29 11:21:11

IT运维管理体系

2019-07-04 17:42:57

开发技能模型

2020-04-12 13:05:45

ASFApache董事会

2024-06-26 10:16:41

2024-11-18 17:31:27

2012-10-24 11:06:37

无线路由器
点赞
收藏

51CTO技术栈公众号