目标检测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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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 应用…