特征金字塔網(wǎng)絡(luò)及變體【詳解及代碼實現(xiàn)】
在深度學(xué)習(xí)和計算機視覺領(lǐng)域,架構(gòu)創(chuàng)新在推動技術(shù)進步中發(fā)揮了重要作用。在這些創(chuàng)新中,特征金字塔網(wǎng)絡(luò)(Feature Pyramid Networks, FPN)脫穎而出,成為革命性的基礎(chǔ)構(gòu)建模塊,徹底改變了我們在神經(jīng)網(wǎng)絡(luò)中處理多尺度特征表示的方式。本文將深入探討FPN。
一、現(xiàn)代神經(jīng)網(wǎng)絡(luò)的三個主要組成部分(骨干網(wǎng)絡(luò)、頸部網(wǎng)絡(luò)和頭部網(wǎng)絡(luò))
在深入FPN之前,了解現(xiàn)代計算機視覺神經(jīng)網(wǎng)絡(luò)的三個主要組成部分非常重要:
1. 骨干網(wǎng)絡(luò)(Backbone)
骨干網(wǎng)絡(luò)通常是一個卷積神經(jīng)網(wǎng)絡(luò)(如ResNet或VGG),作為主要特征提取器。它處理原始輸入圖像,并生成不同尺度的層次化特征表示。可以將其視為捕捉從邊緣、紋理到高級語義信息的基礎(chǔ)。
2. 頸部網(wǎng)絡(luò)(Neck)
頸部網(wǎng)絡(luò)是骨干網(wǎng)絡(luò)和頭部網(wǎng)絡(luò)之間的特征融合和增強模塊。其主要目的是處理和組合來自骨干網(wǎng)絡(luò)不同尺度或階段的特征,以生成更具判別性的特征表示??梢詫⑵湟暈橐粋€加工廠,將來自不同來源的原材料(特征)提煉成更有用的產(chǎn)品。
頸部網(wǎng)絡(luò)可以執(zhí)行多種操作,例如:
- 跨不同尺度的特征融合
- 通過額外卷積增強特征
- 管理不同網(wǎng)絡(luò)層級之間的信息流
特征金字塔網(wǎng)絡(luò)是頸部網(wǎng)絡(luò)架構(gòu)的一種流行實現(xiàn),但還有其他實現(xiàn),如路徑聚合網(wǎng)絡(luò)(PANet)和高分辨率網(wǎng)絡(luò)(HRNet)。
3. 頭部網(wǎng)絡(luò)(Head)
頭部網(wǎng)絡(luò)是任務(wù)特定的組件,使用經(jīng)過優(yōu)化的特征進行最終預(yù)測。不同任務(wù)(檢測、分割、分類)需要不同的頭部架構(gòu),但它們都受益于頸部網(wǎng)絡(luò)提供的經(jīng)過良好處理的特征。
二、為什么需要特征金字塔網(wǎng)絡(luò)?
計算機視覺中的多尺度挑戰(zhàn)源于傳統(tǒng)CNN架構(gòu)的多個基本限制:
1. 特征層次問題
隨著CNN的深入,空間分辨率降低,而語義層次提高。例如,在典型的ResNet中:
- 早期層(如Conv1)具有1/2分辨率,捕捉基本特征(邊緣、紋理)
- 中間層(如Conv3)具有1/8分辨率,捕捉中級特征(部件、模式)
- 深層(如Conv5)具有1/32分辨率,捕捉高級特征(物體、場景)
2. 尺度變化
自然圖像中的物體以不同的尺度出現(xiàn)。例如,在自動駕駛中:
- 附近的行人可能占據(jù)300x600像素
- 遠處的車輛可能僅占據(jù)30x60像素
- 交通標(biāo)志可能以任何大小出現(xiàn)
3. 信息丟失
傳統(tǒng)的特征金字塔(如圖像金字塔)保持了空間分辨率,但在較低層次缺乏語義強度,使其在現(xiàn)代深度學(xué)習(xí)中效率低下。這些問題的結(jié)合構(gòu)成了計算機視覺中的重大挑戰(zhàn)。
現(xiàn)實世界的例子:想象一輛自動駕駛汽車試圖檢測街道上的物體。 攝像頭看到的物體距離不同,有些近,有些遠。 要檢測遠處的行人,系統(tǒng)需要處理高分辨率(詳細)圖像以捕捉小細節(jié)。 但問題在于:處理這些詳細圖像的早期網(wǎng)絡(luò)層并不擅長理解內(nèi)容。它們可能看到人的基本形狀,但無法區(qū)分是行人還是路燈桿,因為它們?nèi)狈ι顚哟蔚睦斫狻?nbsp;
為什么不使用傳統(tǒng)方法(如圖像金字塔)? 這種方法提取的特征信息不夠豐富,無法真正用于現(xiàn)代深度學(xué)習(xí)。
我們陷入了兩難選擇: 要么獲得良好的細節(jié)但理解力差,要么獲得良好的理解力但細節(jié)差。這就像在放大鏡(能看清細節(jié)但無法識別物體)和模糊眼鏡(能識別物體但看不清細節(jié))之間做出選擇。 這種“看清”與“理解”之間的權(quán)衡正是研究人員開發(fā)特征金字塔網(wǎng)絡(luò)的原因——最終解決這一困境。
三、特征金字塔網(wǎng)絡(luò)(FPN)
圖片、、
FPN通過三個關(guān)鍵組件結(jié)合了低層次和高層次特征:
(1) 自下而上路徑(骨干網(wǎng)絡(luò))
- 這是常規(guī)的卷積神經(jīng)網(wǎng)絡(luò)前向傳播。
- 特征逐漸變得更語義化,但空間分辨率降低。
- 每個階段輸出不同尺度的特征圖(C?, C?, C?, C?)。
(2) 自上而下路徑
- 從最深層開始,逐步上采樣空間較粗糙但語義較強的特征。
- 創(chuàng)建更高分辨率的特征(P?, P?, P?, P?)。
- 使用最近鄰上采樣來增加分辨率。
(3) 橫向連接
- 1x1卷積減少骨干網(wǎng)絡(luò)特征的通道維度。
- 逐元素加法合并自下而上和自上而下的特征。
- 3x3卷積平滑合并后的特征。
1. 技術(shù)過程
- 提取自下而上的特征 {C?, C?, C?, C?}
- 通過1x1卷積處理頂層特征C?以創(chuàng)建P?
- 上采樣P?并與處理后的C?合并以創(chuàng)建P?
- 此過程持續(xù)到P?
- 最終金字塔的每個層級 {P?, P?, P?, P?} 包含豐富的語義信息,同時保持適當(dāng)?shù)目臻g分辨率
2. 示例代碼
以下是一個使用ResNet-18骨干網(wǎng)絡(luò)實現(xiàn)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類實現(xiàn)了FPN的核心架構(gòu):
- `lateral_convs` 創(chuàng)建1x1卷積,減少來自不同層級骨干網(wǎng)絡(luò)特征的通道維度。
- `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如何處理特征:
- 它對所有特征層級應(yīng)用橫向卷積。
- 它實現(xiàn)自上而下的路徑:從最深層開始,上采樣特征并將其添加到上一層級。
- 它對所有層級應(yīng)用平滑卷積。
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類組合所有內(nèi)容:
- 使用預(yù)訓(xùn)練的ResNet-18作為骨干網(wǎng)絡(luò)。
- 添加FPN頸部網(wǎng)絡(luò)以處理來自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
對于分類任務(wù):我們創(chuàng)建FPN頸部網(wǎng)絡(luò),用于處理來自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
前向傳播將所有內(nèi)容結(jié)合在一起:
- 輸入圖像通過ResNet骨干網(wǎng)絡(luò),收集每個階段的特征。
- 這些特征通過FPN頸部網(wǎng)絡(luò),生成特征金字塔。
- 我們對金字塔的每個層級的特征進行池化。
- 最后,我們結(jié)合所有這些特征以進行最終的分類預(yù)測。
四、變體(特征金字塔網(wǎng)絡(luò)的演進)
自FPN誕生以來,研究人員對其進行了多種改進。以下是一些常見變體:
1. PANet(路徑聚合網(wǎng)絡(luò))
- 通過添加額外的自下而上路徑增強信息流。
- 用于實例分割的Mask Scoring R-CNN和實時目標(biāo)檢測的Thunder-Net。
2. BiFPN(雙向FPN)
- 引入加權(quán)雙向跨尺度連接。
- 用于EfficientDet系列目標(biāo)檢測器。
3. 最新模型(2023-2024)
- RT-DETR:使用基于變形Transformer的FPN變體。
- DINO-V2:實現(xiàn)混合FPN-Transformer頸部網(wǎng)絡(luò)。
- YOLOv8:采用受FPN啟發(fā)的改進CSP-PAN頸部網(wǎng)絡(luò)。
五、結(jié)論
特征金字塔網(wǎng)絡(luò)代表了計算機視覺架構(gòu)設(shè)計的一項重大成就。其有效處理多尺度特征表示并保持計算效率的能力,使其成為現(xiàn)代計算機視覺系統(tǒng)中不可或缺的組成部分。隨著領(lǐng)域的不斷發(fā)展,F(xiàn)PN的影響可以在新架構(gòu)中看到,其設(shè)計原則繼續(xù)激發(fā)神經(jīng)網(wǎng)絡(luò)設(shè)計的創(chuàng)新。