目標檢測neck經典算法之FPN的源碼實現

     ┌────────────────────────────────────────────────────┐│          初始化構造 (__init__)                      │└────────────────────────────────────────────────────┘↓【1】參數保存 + 基礎配置斷言↓【2】判斷使用哪些backbone層(start→end)↓【3】判斷是否添加額外輸出(extra conv)↓【4】構建 lateral convs(1×1 conv,統一通道)fpn convs(3×3 conv,用于輸出)↓【5】構建 extra convs(如 RetinaNet 的 P6/P7)

FPN構造階段

【1】參數保存 + 基礎配置斷言

def __init__(
self,
in_channels: List[int],
out_channels: int,
num_outs: int,
start_level: int = 0,
end_level: int = -1,
add_extra_convs: Union[bool, str] = False,
relu_before_extra_convs: bool = False,
no_norm_on_lateral: bool = False,
conv_cfg: OptConfigType = None,
norm_cfg: OptConfigType = None,
act_cfg: OptConfigType = None,
upsample_cfg: ConfigType = dict(mode='nearest'),
init_cfg: MultiConfig = dict(type='Xavier', layer='Conv2d', distribution='uniform')
參數名含義
in_channels主干輸出的每層特征圖的通道數列表,如 [256, 512, 1024, 2048]
out_channels所有 FPN 輸出層的統一通道數,典型值是 256
num_outs最終 FPN 輸出特征層數,≥ in_channels 個數
start_level從哪個輸入層開始構造 FPN,默認是 0(即從 C2 開始)
end_level構造到哪個輸入層結束(exclusive)。-1 表示一直到最后
add_extra_convs是否添加額外層(如 P6、P7),可為 bool 或 str
relu_before_extra_convs添加額外層前是否加 ReLU 激活
no_norm_on_lateral橫向連接的 1x1 卷積是否加 norm(BN、GN)
conv_cfg/norm_cfg/act_cfg可選的 conv、norm、activation 配置
upsample_cfg上采樣的參數配置,默認最近鄰插值
init_cfg初始化配置,使用 Xavier 初始化 Conv2d 層
init_cfg = dict(type='Xavier', layer='Conv2d', distribution='uniform'
)
assert isinstance(in_channels, list)

初始化 BaseModule 的父類構造器,并傳入權重初始化配置:
表示所有的 Conv2d 層都會用 Xavier(均勻分布)初始化權重
這符合多數檢測模型中推薦的初始化方式
斷言 in_channels 是列表,例如 [256, 512, 1024, 2048],即來自主干網絡的多層特征圖的通道數。
參數保存

self.in_channels = in_channels               # 輸入通道數列表
self.out_channels = out_channels             # 輸出通道數
self.num_ins = len(in_channels)              # 輸入特征數量
self.num_outs = num_outs                     # 期望的輸出數量

這些值將用于后續構建:

lateral_convs: 1x1 卷積,輸入通道數由 in_channels 決定
fpn_convs: 3x3 卷積,輸出通道數都為 out_channels

👇 額外功能配置

self.relu_before_extra_convs = relu_before_extra_convs     # ReLU 加在 extra convs 前
self.no_norm_on_lateral = no_norm_on_lateral               # 控制 lateral conv 是否加 norm
self.fp16_enabled = False                                  # 是否支持混合精度(保留)

relu_before_extra_convs:可提高非線性表達能力(如 RetinaNet 中默認開啟)
no_norm_on_lateral:關閉 norm 通常用于節省資源或部署推理
fp16_enabled:暫未使用,框架中可能由 AMP 插件開啟
👇 上采樣方式配置

self.upsample_cfg = upsample_cfg.copy()

? 總結一句話:
這部分是 FPN 構建流程的“設置區”,所有后續模塊的搭建都將以這些參數為基礎,決定網絡寬度、深度、融合方式與行為特性,是 FPN 構造邏輯的入口與地基。

輸入: C2 C3 C4 C5
通道: 256 512 1024 2048 → self.in_channels
目標: 構建 P2~P5 或 P3~P7(num_outs = 4~5)
每層通道統一為 256 → self.out_channels
配置:

  • start_level = 1 → 從 C3 開始
  • end_level = -1 → 一直用到最后
  • add_extra_convs=True → P6、P7
    上采樣方式: nearest → self.upsample_cfg

【2】確定使用哪些 backbone 層(start_level 和 end_level)

if end_level == -1 or end_level == self.num_ins - 1:self.backbone_end_level = self.num_insassert num_outs >= self.num_ins - start_level
else:self.backbone_end_level = end_level + 1assert end_level < self.num_insassert num_outs == end_level - start_level + 1
self.start_level = start_level
self.end_level = end_level

FPN 使用的層數 = self.backbone_end_level - self.start_level
如果 end_level = -1(默認) → 使用從 start 到最后的所有層
否則 → 精準地用 start~end_level(閉區間)
num_outs 決定最終輸出多少層
- 必須 >= 使用的層數(如果你還想加 extra conv)
- 如果 end_level 被限定 → 不能加 extra conv

【3】判斷是否添加額外輸出(extra conv)

self.add_extra_convs = add_extra_convs
assert isinstance(add_extra_convs, (str, bool))
if isinstance(add_extra_convs, str):assert add_extra_convs in ('on_input', 'on_lateral', 'on_output')
elif add_extra_convs:  # Trueself.add_extra_convs = 'on_input'

False → 不加額外層
True → 加,默認用 ‘on_input’
‘on_input’, ‘on_lateral’, ‘on_output’ → 指定來源

【4】構建 lateral convs(1×1 conv,統一通道)

self.lateral_convs = nn.ModuleList()
self.fpn_convs = nn.ModuleList()

初始化兩個用于保存卷積層的“有序列表容器”,用于搭建橫向連接(lateral)和輸出卷積(fpn)結構。

📦 nn.ModuleList() 是什么?
nn.ModuleList 是 PyTorch 提供的一種特殊列表容器,專門用于存放多個子模塊(如多個 nn.Conv2d)。
🎯 作用:
能像 Python 列表一樣逐個添加、訪問模塊
最重要的是:所有子模塊會自動注冊到整個模型里,參數能被 model.parameters() 正確獲取
支持 .to(), .cuda(), .eval() 等模型操作

        for i in range(self.start_level, self.backbone_end_level):l_conv = ConvModule(in_channels[i],       # 輸入通道:來自 backbone 的這一層out_channels,         # 輸出通道:FPN 要統一為同一個通道1,                    # 卷積核大小:1x1conv_cfg=conv_cfg,norm_cfg=norm_cfg if not self.no_norm_on_lateral else None,act_cfg=act_cfg,inplace=False)fpn_conv = ConvModule(out_channels,         # 輸入通道:是前面橫向卷積輸出out_channels,         # 輸出通道:保持不變3,                    # 卷積核大小:3x3padding=1,            # 保持尺寸不變conv_cfg=conv_cfg,norm_cfg=norm_cfg,act_cfg=act_cfg,inplace=False)self.lateral_convs.append(l_conv)self.fpn_convs.append(fpn_conv)

🧠 意思是:
對 backbone 中從 start_level 到 backbone_end_level - 1 的每一層,都要創建兩個卷積模塊:

lateral_conv: 橫向 1×1 卷積(通道變換)
使用 1×1 卷積,快速調整通道數
fpn_conv: 輸出 3×3 卷積(特征增強)
用途:提取輸出金字塔特征
每個融合后的 feature map(如 P5、P4、P3、P2)都需要進一步通過一個 3×3 卷積處理
這樣可以補充一些局部上下文信息
🧰 ConvModule 是什么?
它是 mmcv 提供的封裝類,包含:
Conv → Norm → Activation
所以上面兩個模塊實際是:
l_conv: Conv1x1 → (BN?) → (ReLU?)
fpn_conv: Conv3x3 → BN → ReLU
根據 norm_cfg 和 act_cfg 傳什么,可以構造不同風格的 FPN(GroupNorm+ReLU)

【5】構建 extra convs(如 RetinaNet 的 P6/P7)

# add extra conv layers (e.g., RetinaNet)extra_levels = num_outs - self.backbone_end_level + self.start_levelif self.add_extra_convs and extra_levels >= 1:for i in range(extra_levels):if i == 0 and self.add_extra_convs == 'on_input':in_channels = self.in_channels[self.backbone_end_level - 1]else:in_channels = out_channelsextra_fpn_conv = ConvModule(in_channels,out_channels,3,stride=2,padding=1,conv_cfg=conv_cfg,norm_cfg=norm_cfg,act_cfg=act_cfg,inplace=False)self.fpn_convs.append(extra_fpn_conv)

? 總結:
這段代碼實現的邏輯是:

計算還需要補充的額外層數(P6、P7),例如從 3 層(C3C5)擴展到 5 層(P3P7)。
根據 add_extra_convs 參數,選擇從 原始特征(如 C5)還是 前一層輸出(如 P5)開始構造新層。
使用 stride=2 的 3×3 卷積生成額外層(P6、P7),減少尺寸并保持通道一致。
將構造的卷積層存入 self.fpn_convs 列表中,便于后續 forward() 使用。
在這里插入圖片描述

構建部分描述
輸入通道數 (in_channels)記錄 backbone 輸出層的通道數(如 C3~C5)
輸出通道數 (out_channels)所有輸出層的統一通道數,通常為 256
lateral_convs1×1 卷積,用于統一每層的通道數
fpn_convs3×3 卷積,用于對融合后的特征圖進行增強與提取
extra_convsnum_outs 大于 backbone 層數時,添加的擴展卷積(如 P6、P7)
upsample_cfg上采樣配置,控制如何調整不同尺度特征圖的大小
初始化配置 (init_cfg)控制卷積層的權重初始化方式,通常為 Xavier 初始化

FPN的前向傳播階段

【1】輸入校驗:

檢查輸入特征數量是否匹配:
assert len(inputs) == len(self.in_channels)

  • 通常 inputs 為來自 backbone 的 C3, C4, C5
    def forward(self, inputs: Tuple[Tensor]) -> tuple:"""Forward function.Args:inputs (tuple[Tensor]): Features from the upstream network, eachis a 4D-tensor.Returns:tuple: Feature maps, each is a 4D-tensor."""assert len(inputs) == len(self.in_channels)

【2】構建 lateral 特征(橫向路徑):

對每一層 inputs[i] 執行 1×1 卷積 → lateral[i]
-得到 lateral 特征列表:
laterals = [L3, L4, L5]

 # build lateralslaterals = [lateral_conv(inputs[i + self.start_level])for i, lateral_conv in enumerate(self.lateral_convs)]

🎯 這一段的目的是:
把從 backbone 傳入的每一層特征圖(如 C3、C4、C5)先經過一個 1×1 卷積,統一它們的通道數,變成 FPN 的 lateral feature(橫向特征圖),供后續 top-down 融合用。

🔧 每一步在干什么?
? inputs[i + self.start_level]
取的是 backbone 輸出的第 start_level 層開始的特征

例如 start_level = 1,就從 C3 開始

如果你傳入了 [C2, C3, C4, C5],那 inputs[1] = C3,依此類推

? self.lateral_convs 是什么?
init 構造階段構建的 nn.ModuleList

里面放的是多個 ConvModule(1×1),用來統一通道數

例如:

self.lateral_convs = [
Conv1x1(512 → 256), # for C3
Conv1x1(1024 → 256), # for C4
Conv1x1(2048 → 256), # for C5
]
? 組合:生成 lateral[i]
每一層 lateral 是這樣來的:

lateral[i] = self.lateral_convs[i](inputs[i + start_level])
🧠 為什么要這樣處理?
Backbone 各層通道數不同(512、1024、2048)

但 FPN 要求統一為 out_channels = 256

所以要用 1×1 卷積做通道壓縮

這樣才能保證后續 “逐像素相加”(top-down 融合)是合法的。

🔚 輸出結果是:

laterals = [L3, L4, L5] # 每個 shape 是 [B, 256, H, W]
例如:

L3 = Conv1x1(C3)
L4 = Conv1x1(C4)
L5 = Conv1x1(C5)
這些 laterals 就是 FPN 的 主干金字塔通道,接下來就會進入 top-down 融合流程了。

【3】自頂向下特征融合(Top-Down 路徑)

從高層 L5 開始,依次向下融合:

  L4 ← L4 + upsample(L5)L3 ← L3 + upsample(L4)
  • 使用 F.interpolate 進行上采樣(默認 scale_factor=2)
# build top-down pathused_backbone_levels = len(laterals)for i in range(used_backbone_levels - 1, 0, -1):# In some cases, fixing `scale factor` (e.g. 2) is preferred, but#  it cannot co-exist with `size` in `F.interpolate`.if 'scale_factor' in self.upsample_cfg:# fix runtime error of "+=" inplace operation in PyTorch 1.10laterals[i - 1] = laterals[i - 1] + F.interpolate(laterals[i], **self.upsample_cfg)else:prev_shape = laterals[i - 1].shape[2:]laterals[i - 1] = laterals[i - 1] + F.interpolate(laterals[i], size=prev_shape, **self.upsample_cfg)

【4】構建輸出特征(FPN conv 輸出層)

outs = [self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels)
]

【5】添加額外層輸出(Extra Levels:P6 / P7 等)

# part 2: add extra levelsif self.num_outs > len(outs):# use max pool to get more levels on top of outputs# (e.g., Faster R-CNN, Mask R-CNN)if not self.add_extra_convs:for i in range(self.num_outs - used_backbone_levels):outs.append(F.max_pool2d(outs[-1], 1, stride=2))# add conv layers on top of original feature maps (RetinaNet)else:if self.add_extra_convs == 'on_input':extra_source = inputs[self.backbone_end_level - 1]elif self.add_extra_convs == 'on_lateral':extra_source = laterals[-1]elif self.add_extra_convs == 'on_output':extra_source = outs[-1]else:raise NotImplementedErrorouts.append(self.fpn_convs[used_backbone_levels](extra_source))for i in range(used_backbone_levels + 1, self.num_outs):if self.relu_before_extra_convs:outs.append(self.fpn_convs[i](F.relu(outs[-1])))else:outs.append(self.fpn_convs[i](outs[-1]))
return tuple(outs)

在這里插入圖片描述
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/84770.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/84770.shtml
英文地址,請注明出處:http://en.pswp.cn/web/84770.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

extern關鍵字:C/C++跨文件編程利器

在 C 和 C 中&#xff0c;extern 是一個關鍵字&#xff0c;用于聲明變量或函數是在其他文件中定義的。它主要用于實現多個源文件之間的符號共享。 目錄 &#x1f4cc; 一、C語言中的 extern 1. 基本作用 2. 示例說明 定義全局變量&#xff08;只在一個 .c 文件中&#xff…

編程語言的演化與選擇:技術浪潮中的理性決策

&#x1f4dd;個人主頁&#x1f339;&#xff1a;一ge科研小菜雞-CSDN博客 &#x1f339;&#x1f339;期待您的關注 &#x1f339;&#x1f339; 一、引言&#xff1a;為什么“選對語言”比“掌握語言”更重要&#xff1f; 在軟件開發的世界里&#xff0c;語言是一切的基礎。…

【StarRocks系列】StarRocks vs Mysql

目錄 StarRocks 簡介 核心特性 典型應用場景 StarRocks vs MySQL&#xff1a;核心區別詳解 關鍵差異總結 如何選擇&#xff1f; StarRocks 簡介 StarRocks 是一款高性能、全場景、分布式、實時分析型的數據庫&#xff08;MPP - 大規模并行處理&#xff09;。它誕生于解決…

Axios 知識點全面總結

文章目錄 Axios 知識點全面總結一、Axios 基礎概念1. 什么是 Axios&#xff1f;2. 核心特性 二、安裝與基本用法1. 安裝2. 基本請求示例 三、請求方法與參數四、請求配置選項&#xff08;config&#xff09;五、攔截器&#xff08;Interceptors&#xff09;六、錯誤處理七、取消…

【軟考高級系統架構論文】論 SOA 在企業集成架構設計中的應用

論文真題 企業應用集成(Enterprise Application Integration, EAI)是每個企業都必須要面對的實際問題。面向服務的企業應用集成是一種基于面向服務體系結構(Service - Oriented Architecture, SOA)的新型企業應用集成技術,強調將企業和組織內部的資源和業務功能暴露為服務,實…

springboot 提供的可擴展接口

一、spring 和 springboot Spring框架提供了全面的基礎架構支持。包含依賴注入和開箱即用等模塊&#xff0c;如&#xff1a;Spring JDBC 、Spring MVC 、Spring Security、 Spring AOP 、Spring ORM 、Spring Test Spring Boot 約定大于配置-----消除了設置Spring應用程序所需…

python學習打卡day55

DAY 55 序列預測任務介紹 知識點回顧 序列預測介紹 單步預測多步預測的2種方式 序列數據的處理&#xff1a;滑動窗口多輸入多輸出任務的思路經典機器學習在序列任務上的劣勢&#xff1b;以隨機森林為例 作業&#xff1a;手動構造類似的數據集&#xff08;如cosx數據&#xff09…

Leetcode hot100 Java刷題

文章目錄 快排146. LRU 緩存acm模式樹的前中后序遍歷acm模式鏈表的基本操作1. 兩數之和49. 字母異位詞分組128. 最長連續序列283. 移動零11. 盛最多水的容器15. 三數之和42. 接雨水53. 最大子數組和56. 合并區間73. 矩陣置零48. 旋轉圖像141. 環形鏈表142. 環形鏈表 II24. 兩兩…

Linux 命令詳解 —— 進程管理

文章目錄 精通Linux操作系統(以Centos7為例)進程管理ps常用組合進程狀態 STAT 詳解高級篩選與格式化輸出按條件過濾進程自定義輸出字段顯示進程樹關系排障場景定位高 CPU檢查僵尸進程查看進程的線程查看進程打開的文件/網絡連接常用組合速查top前5摘要區進程列表信息交互式命令…

【軟考高級系統架構論文】論湖倉一體架構及其應用

論文真題&#xff1a; 隨著5G、大數據、人工智能、物聯網等技術的不斷成熟&#xff0c;各行各業的業務場景日益復雜&#xff0c;企業數據呈現出大規模、多樣性的特點&#xff0c;特別是非結構化數據呈現出爆發式增長趨勢。在這一背景下&#xff0c;企業數據管理不再局限于傳統…

Docker 高級管理筆記

前言&#xff1a;Docker 高級管理概述 隨著 Docker 技術的廣泛應用&#xff0c;容器化已成為現代軟件開發與部署的核心方式。本筆記聚焦 Docker 高級管理中的兩大關鍵技術 —— 容器通信與數據持久化&#xff0c;深入解析 Docker 網絡模式、端口映射、容器互聯機制及數據卷管理…

Spring Boot 項目初始化

一、什么是 CommandLineRunner CommandLineRunner 是 Spring Boot 提供的一個 函數式接口&#xff0c;聲明如下&#xff1a; 該接口只有一個 run(String... args) 方法&#xff0c;會在 Spring Boot 容器啟動完成后被自動調用。 你可以將它理解為一種“鉤子函數”&#xff0c;…

C# winform教程(二)----ComboBox

一、作用 一個可以輸入也可以下拉的列表框。 二、屬性 一般我們都是使用下拉列表&#xff0c;不使用在線編輯&#xff08;本人沒用過&#xff09; 屬性 名稱內容含義items組合框中項可以定義下拉列表的值DropDownStyle外觀和功能是否可以填寫&#xff0c;一般選擇dropdownli…

FFmpeg裁剪視頻在Android上的實現

添加依賴&#xff1a; implementation com.arthenica:mobile-ffmpeg-full:4.4.LTS 代碼實現&#xff1a; fun cropMiddleThird(inputPath: String, outputPath: String) {val cmd arrayOf("-y", // 覆蓋輸出文件"-i", inputPath,"-filter:v&quo…

openEuler 22.03 sp1 更新openssh 10.0p2 —— 筑夢之路

華為歐拉openEuler制作openssh 9.1/9.2/9.3 p1 rpm——筑夢之路_歐拉構建openssh-CSDN博客 上面是需要修改的sshd_config配置&#xff0c;將這3行注釋掉。 附上22.03 sp1的yum源文件 # cat openEuler.repo #generic-repos is licensed under the Mulan PSL v2. #You can use t…

AGI(4)大模型的推理綜述

本文源自基于基礎模型的推理綜述《A Survey of Reasoning with Foundation Models》&#xff0c;因為原文有點難于理解&#xff0c;在這個論文的基礎上增加了自己的解釋和理解&#xff0c;重新整理成此文。大家可以通過查看原文閱讀原始論文。 1、推理的概念 推理是解決復雜問題…

Rust 中的宏與函數

在 Rust 編程中&#xff0c;宏&#xff08;Macro&#xff09;和函數&#xff08;Function&#xff09;是兩種非常重要的編程工具。雖然它們都可以用來組織代碼和實現復用&#xff0c;但它們在定義方式、作用原理、性能、靈活性以及適用場景等方面存在諸多不同。本文將詳細介紹 …

c++中左值與右值

在 C++ 中,左值(lvalue) 和 右值(rvalue) 是表達式的基本屬性,它們決定了表達式能否被賦值、取地址等操作。 1. 核心定義 左值(lvalue) 特點:表示一個具名的、持久的對象,可位于賦值語句左側。示例: int x = 42; // x是左值 x = 100; // 合法:左值可…

DeepSeek14-open-webui 常用概念區分

I、“Tools & Functions” 與 Pipelines&#xff08;工作流系統&#xff09;區別 以下是“Tool & Functions”與“Pipelines”的區別、適用場景及作用的詳細分析&#xff0c;內容基于參考文檔提取與總結&#xff1a; 一、本質區別 維度Tool & FunctionsPipeline…

PaddleOCR + Flask 構建 Web OCR 服務實戰

1、前言 隨著圖像識別技術的發展,OCR(光學字符識別)已經成為很多應用場景中的基礎能力。PaddleOCR 是百度開源的一個高性能 OCR 工具庫,支持中英文、多語言、輕量級部署等特性。 而 Flask 是一個輕量級的 Python Web 框架,非常適合快速構建 RESTful API 或小型 Web 應用…