原文作者:aircraft

原文链接:pytorch 实战教程之 SPP(SPPNet---Spatial Pyramid Pooling)空间金字塔池化网络代码实现 和 SPPF (Spatial Pyramid Pooling Fast)详解​​

      学习YOLOv5前的准备就是学习DarkNet53网络,FPN特征金字塔网络,PANet路径聚合网络结构,(从SPP到SPPF)SPPF空间金字塔池化等。本篇讲从SPP到SPPF网络结构。。。(其他几篇已经发布在历史博客里------基本YOLO网络的前置学习就在这篇讲的差不多了,后面应该写YOLO目标检测了。。。。)

SPPNet(空间金字塔池化网络)详解​

SPPNet(Spatial Pyramid Pooling Network)是何凯明团队在2014年提出的创新架构,核心是​​空间金字塔池化(SPP)层​​,解决了传统卷积神经网络必须固定输入尺寸的限制。以下从技术原理到实践应用进行详解:

​一、SPPNet的诞生背景​

  1. ​传统CNN的痛点​​:

    • 全连接层需固定输入维度(如AlexNet强制缩放到227×227的图像大小)
    • 图像裁剪/变形导致信息丢失(如长宽比失真)---- 想象一下你的小仙女女朋友拍了很多美美哒的照片,这些照片的构图都不同,最后都要缩放到一个固定尺寸,那么每张照片还好看吗???
    • 多尺度特征难以有效融合
  2. ​SPP的核心突破​​:

    • 允许卷积层输出​​任意尺寸的特征图​
    • 通过SPP层将​​动态尺寸特征​​转换为​​固定维度向量​
    • 全连接层无需修改即可处理不同输入尺寸

二、空间金字塔池化(SPP)层原理​

1. ​​结构设计​

  • ​多级空间分箱(Spatial Bins)​​:

    • 预设金字塔层级:如 [4×4, 2×2, 1×1] 三级网格
    • 每级网格将特征图划分为n×n个子区域
  • ​自适应最大池化​​:
    • 每个子区域执行最大池化,输出1个值
    • 池化窗口尺寸自动计算:win_size = ⌈输入尺寸/n⌉
    • 步长(stride)自动计算:stride = ⌊输入尺寸/n⌋

2. ​​数学形式​

  • 输入特征图尺寸:W×H×C(宽×高×通道)
  • 第k级金字塔输出维度:n_k × n_k × C
  • 总输出维度:(Σ n_k²) × C(固定长度)

​示例​​:

  • 三级金字塔 [4×4, 2×2, 1×1]
  • 输出维度:(16 + 4 + 1) × C = 21C

具体流程:

       1、特征图分割:SPP 层将输入的特征图分割成多个不同大小的网格,这些网格的大小通常是 1x1、2x2、4x4 、8x8 等等,形成一个金字塔结构,每个网格的大小决定了池化操作的感受野;

       2、池化操作:对每个网格进行池化操作,通常使用最大池化(Max Pooling);最大池化会选择每个网格中的最大值作为输出。这样,每个网格都会生成一个固定大小的特征表示,需要注意的是这个池化操作是一个并发的过程;

       3、特征拼接:将所有网格的特征表示拼接起来,形成一个固定长度的特征向量;这个特征向量随后可以作为后续全连接层的输入;

为什么很多带全连接层的网络结构都需要固定归一化图像的大小呢(如AlexNet强制缩放到227×227的图像大小)?举个例子就是我在全连接层输出的网络结构是固定大小的,比如是21*512这个大小:

在最后一层卷积层我们已经固定用了512个卷积核,那么输入到全连接层的数据就是 w * h * 512 (特征图像的长宽乘通道数),那么在通道数固定的情况下,图像大小变化的话就会和全连接层对应不上。接口对接不起来!!!

拿全连接层所需数据大小21*512举例在卷积层输出的通道数固定为512的情况下,我们构建1x1、2x2、4x4的三层空间金字塔池化:

假设我们有一张 448×448 的输入图像,通过卷积层后得到特征图 56×56×512;接下来,我们使用 SPP 层进行处理:

    1.特征图分割:
        1x1 网格:整个特征图作为一份数据
        2x2 网格:特征图被均匀分割成 4 份数据
        4x4 网格:特征图被均匀分割为16份数据

   2. 池化操作:
        1x1 网格:最大池化后的特征图尺寸为 1×1×512;
        2x2 网格:最大池化后的特征图尺寸为 2×2×512=4×512=4×512;
        4x4 网格:最大池化后的特征图尺寸为 4×4×512=16×512=16×512;

   3. 特征拼接:
    拼接后的特征向量长度为:21 x 512  =10752    不管你输入图像的大小是多少227x227 ,512x512,318x318也好,经过三层的空间金字塔网络后提取的特征数目都是21份,加上通道数就是 21x512份。这样就不用在意输入图像数据的大小了。

下图就非常鲜明了,厚度就是通道数:

大概代码实现(任意输入图像尺寸,输出的大小一致):

import torch
import torch.nn as nn class SpatialPyramidPooling(nn.Module):
def __init__(self, levels=[4, 2, 1]):
super().__init__()
self.levels = levels def forward(self, x):
N, C, H, W = x.size()
outputs = []
for level in self.levels:
kh = H // level
kw = W // level
pool = nn.MaxPool2d(kernel_size=(kh, kw), stride=(kh, kw))
outputs.append(pool(x).view(N, -1))
return torch.cat(outputs, dim=1) # 使用示例
spp = SpatialPyramidPooling(levels=[4, 2, 1])
input = torch.randn(1, 256, 13, 13) # 任意尺寸输入
output = spp(input) # 输出固定维度: (1, 21 * 256)

代码的隐藏条件​

代码​​隐式要求输入尺寸必须能被所有level整除​​,否则实际输出网格数会偏离预设level:

  • ​反例​​:输入10×10,level=4

    • 池化核:10//4=2,步长2×2
    • 输出尺寸:(10-2)/2 +1 =5 → ​​5×5​​(而非预设的4×4)

​若想不管这个隐藏条件,还可以这样改,使用自适应池化:

class SpatialPyramidPooling(nn.Module):
def __init__(self, levels=[4, 2, 1]):
super().__init__()
self.pools = nn.ModuleList([
nn.AdaptiveMaxPool2d((level, level)) for level in levels
]) def forward(self, x):
N, C, _, _ = x.size()
outputs = [pool(x).view(N, -1) for pool in self.pools]
return torch.cat(outputs, dim=1)

AdaptiveMaxPool2d直接强制输出目标尺寸(如4×4),无需计算核尺寸

局限性​

  1. ​池化信息损失​​:

    • 最大池化丢弃非最大值信息
    • 后续的ROI Align改用双线性插值缓解此问题
  2. ​计算资源消耗​​:

    • 多级池化增加内存占用
    • 实时性弱于纯全卷积网络(FCN)

总结​

SPPNet通过空间金字塔池化,首次实现了CNN对任意尺寸输入的处理,奠定了多尺度特征融合的基础。其核心思想被后续众多模型(如Fast R-CNN、Mask R-CNN)继承发展,是深度学习发展史上的重要里程碑。理解SPP机制对掌握现代目标检测和图像分类模型至关重要。

SPPF(Spatial Pyramid Pooling Fast)详解​

SPPF(空间金字塔快速池化)是SPPNet的改进版本,由YOLO系列(如YOLOv5、YOLOv7)引入,旨在保持多尺度特征融合能力的同时显著提升计算效率。其核心思想是通过​​串行重复池化操作​​替代传统金字塔并行池化,减少计算冗余。以下是详细解析:

​一、SPPF与SPP的结构对比​

模块 池化方式 计算路径 参数量 输出特征维度
SPP 并行多级池化(如4×4, 2×2, 1×1) 独立分支 多级特征拼接
SPPF 串行重复池化(如三次5×5池化) 单链叠加 等效金字塔融合
特性 SPP SPPF
多尺度特征来源 离散分级(4×4, 2×2, 1×1) 连续叠加(5→9→13感受野)
计算效率 低(多分支并行池化) 高(单链串行池化,GPU优化)
硬件友好性 内存碎片化 连续内存操作
适用场景 分类网络(需全连接层) 检测网络(全卷积架构)

二、SPPF核心原理​

1. ​​串行池化设计​
  • ​池化核尺寸固定​​:通常使用5×5池化窗口,重复三次
  • ​步长固定为1​​:通过填充(padding)保持特征图尺寸不变
  • ​动态感受野叠加​​:每次池化扩大感受野,等效多尺度特征提取
2. ​​数学过程​

输入特征图尺寸:W×H×C

  • ​第一次池化​​:5×5池化 → 输出 W×H×C
  • ​第二次池化​​:5×5池化 → 输出 W×H×C
  • ​第三次池化​​:5×5池化 → 输出 W×H×C
  • ​特征拼接​​:原始输入 + 三次池化输出 → 最终维度 4C    而SPPF中的W×H始终不变,SPP则是通道数和金字塔后输出的数据不变

​等效感受野​​:

  • 三次5×5池化 ≈ 单次13×13池化(5+(5-1)x2 ) = 13
3. ​​计算效率优化​
  • ​参数共享​​:三次池化使用相同核尺寸,无需额外参数
  • ​内存连续性​​:串行操作减少内存碎片,提升GPU利用率
  • ​FLOPs对比​​(以256×13×13输入为例):
    • SPP:约 1.2 GFLOPs
    • SPPF:约 0.8 GFLOPs (​​速度提升33%​​)

具体示例演示​

​Case 1:输入尺寸 13×13×256​

  • ​第一次池化​​:

    • 输入:13×13×256
    • 输出:13×13×256(保持尺寸)
    • ​感受野​​:5×5(覆盖输入中5×5区域)
  • ​第二次池化​​:

    • 输入:13×13×256
    • 输出:13×13×256
    • ​感受野​​:9×9(叠加两次5×5池化)
  • ​第三次池化​​:

    • 输入:13×13×256
    • 输出:13×13×256
    • ​感受野​​:13×13(叠加三次池化覆盖全图)
  • ​拼接结果​​:

    • 输出尺寸:13×13×(256×4) = ​​13×13×1024​

虽然SPPF输出的空间尺寸 (W×H) 仍与输入相关,但后续网络通过 ​​全局池化(Global Pooling)​​ 或 ​​自适应层(Adaptive Layers)​​ 将其转换为固定维度

SPPF中的感受野计算示例​

以YOLOv5的SPPF模块为例(三次5×5池化,步长=1):

层数 操作 核尺寸(k) 步长(stride) 感受野计算 累计感受野
1 输入 - - 初始感受野=1 1×1
2 第一次池化 5×5 1 1 + (5-1)*1 = ​​5​ 5×5
3 第二次池化 5×5 1 5 + (5-1)*1 * 1 = ​​9​ 9×9
4 第三次池化 5×5 1 9 + (5-1)*1 * 1 * 1 = ​​13​ 13×13
  • 不强制固定维度​​:保留空间信息以支持目标检测中的位置敏感任务。
  • ​与后续卷积层兼容​​:YOLO的检测头(Head)直接处理可变尺寸特征图。
  • 错误理解​​:将SPPF用于分类任务(需全连接层)时,才需要额外添加全局池化。
  • ​正确场景​​:在YOLO等检测网络中,SPPF天然适配全卷积结构,​​无需任何后续约束​

YOLO中SPPF的实际应用​​:

在YOLOv5/v7中,SPPF模块后​​直接连接卷积层​​,无需全局池化:

# YOLOv5的C3模块(包含SPPF)
class C3(nn.Module):
def __init__(self, c1, c2):
super().__init__()
self.sppf = SPPF(c1) # 输出H×W×4C1
self.conv = nn.Conv2d(4*c1, c2, 1) # 压缩通道到C2 def forward(self, x):
x = self.sppf(x) # H×W×4C1 → 空间维度保留
x = self.conv(x) # H×W×C2 → 输入检测头
return x

三、SPPF的具体实现​

1. ​​PyTorch代码示例​:
import torch
import torch.nn as nn class SPPF(nn.Module):
def __init__(self, in_channels):
super().__init__()
self.pool = nn.MaxPool2d(kernel_size=5, stride=1, padding=2) def forward(self, x):
x1 = self.pool(x)
x2 = self.pool(x1)
x3 = self.pool(x2)
return torch.cat([x, x1, x2, x3], dim=1) # 输入示例:batch=1, channels=256, size=13x13
input = torch.randn(1, 256, 13, 13)
sppf = SPPF(256)
output = sppf(input) # 输出维度:[1, 1024, 13, 13]
2. ​​关键参数配置​
  • ​核尺寸​​:通常为5×5(平衡感受野与计算量)
  • ​填充策略​​:padding=kernel_size//2(保持尺寸不变)
  • ​拼接方式​​:沿通道维度拼接原始输入与池化结果

四、SPPF的优势分析​

  1. ​计算效率提升​​:

    • 减少分支操作,利用串行流水线加速
    • YOLOv5中SPPF比SPP快2.5倍(相同硬件)
  2. ​多尺度特征融合增强​​:

    • 三次池化等效于13×13、9×9、5×5多级感受野
    • 保留更精细的局部特征(对比SPP的跳跃式分级)
  3. ​硬件友好性​​:

    • 连续内存访问优化缓存命中率
    • 适合部署到边缘设备(如Jetson系列)
  4. ​模型兼容性​​:

    • 输入输出尺寸一致,可直接替换SPP模块
    • 无缝集成到ResNet、CSPNet等主流骨干网络

五、SPPF在YOLO中的实际应用​

1. ​​YOLOv5网络结构集成​
Backbone
├── Focus
├── Conv
├── C3
└── SPPF # 替换原始SPP模块
2. ​​性能提升对比(COCO数据集)​
模型 mAP@0.5 FPS (Tesla T4) 参数量 (M)
YOLOv5s 37.2 125 7.2
YOLOv5s+SPPF ​37.8​ ​142​ 7.3

​六、SPPF的局限性​

  1. ​小物体检测精度衰减​​:

    • 多次池化可能模糊微小物体的细节特征
    • 需配合FPN(特征金字塔)使用以缓解此问题
  2. ​理论感受野与实际差异​​:

    • 串行池化的等效感受野为线性增长,弱于SPP的指数级覆盖
  3. ​通道膨胀问题​​:

    • 输出通道数变为输入4倍,可能增加后续卷积计算量

七、改进变体​(下面这些都是不断改进升级的版本)

  1. ​SPPF+SE​​:

    • 加入通道注意力机制(Squeeze-and-Excitation)
    • 提升重要通道的权重分配
  2. ​ASPPF(Atrous SPPF)​​:

    • 使用空洞池化(Dilated Pooling)扩大感受野
    • 减少下采样带来的信息损失
  3. ​轻量化SPPF​​:

    • 采用深度可分离卷积(Depthwise Separable Conv)
    • 适用于移动端部署

还有SimSPPF,SPPCSPC,SPPFCSPC+都可以去了解一下都是YOLO不断升级中改进出来的。

八、总结​

SPPF通过巧妙的串行池化结构,在保持多尺度特征融合能力的同时,显著提升了计算效率。其设计体现了“​​简单即有效​​”的优化哲学,成为实时目标检测模型的标配模块。理解SPPF的工作原理对于优化模型速度和精度平衡至关重要,特别是在边缘计算和实时视频分析场景中具有重要价值。

参考博客:https://blog.csdn.net/CITY_OF_MO_GY/article/details/143897303

        https://developer.aliyun.com/article/1509544

        https://blog.csdn.net/weixin_38346042/article/details/131796263

pytorch 实战教程之 SPP(SPPNet---Spatial Pyramid Pooling)空间金字塔池化网络代码实现 和 SPPF (Spatial Pyramid Pooling Fast)详解​​的更多相关文章

  1. 空间金字塔池化(Spatial Pyramid Pooling, SPP)原理和代码实现(Pytorch)

    想直接看公式的可跳至第三节 3.公式修正 一.为什么需要SPP 首先需要知道为什么会需要SPP. 我们都知道卷积神经网络(CNN)由卷积层和全连接层组成,其中卷积层对于输入数据的大小并没有要求,唯一对 ...

  2. Spatial pyramid pooling (SPP)-net (空间金字塔池化)笔记(转)

    在学习r-cnn系列时,一直看到SPP-net的身影,许多有疑问的地方在这篇论文里找到了答案. 论文:Spatial Pyramid Pooling in Deep Convolutional Net ...

  3. 空间金字塔池化(Spatial Pyramid Pooling,SPP)

    基于空间金字塔池化的卷积神经网络物体检测 原文地址:http://blog.csdn.net/hjimce/article/details/50187655 作者:hjimce 一.相关理论 本篇博文 ...

  4. SPPNet论文翻译-空间金字塔池化Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition

    http://www.dengfanxin.cn/?p=403 原文地址 我对物体检测的一篇重要著作SPPNet的论文的主要部分进行了翻译工作.SPPNet的初衷非常明晰,就是希望网络对输入的尺寸更加 ...

  5. SPP空间金字塔池化技术的直观理解

    空间金字塔池化技术, 厉害之处,在于使得我们构建的网络,可以输入任意大小的图片,不需要经过裁剪缩放等操作. 是后续许多金字塔技术(psp,aspp等)的起源,主要的目的都是为了获取场景语境信息,获取上 ...

  6. SPPNet(特征金字塔池化)学习笔记

    SPPNet paper:Spatial pyramid pooling in deep convolutional networks for visual recognition code 首先介绍 ...

  7. 《手把手教你》系列技巧篇(二十九)-java+ selenium自动化测试- Actions的相关操作上篇(详解教程)

    1.简介 有些测试场景或者事件,Selenium根本就没有直接提供方法去操作,而且也不可能把各种测试场景都全面覆盖提供方法去操作.比如:就像鼠标悬停,一般测试场景鼠标悬停分两种常见,一种是鼠标悬停在某 ...

  8. 《手把手教你》系列技巧篇(三十八)-java+ selenium自动化测试-日历时间控件-下篇(详解教程)

    1.简介 理想很丰满现实很骨感,在应用selenium实现web自动化时,经常会遇到处理日期控件点击问题,手工很简单,可以一个个点击日期控件选择需要的日期,但自动化执行过程中,完全复制手工这样的操作就 ...

  9. 《手把手教你》系列技巧篇(四十五)-java+ selenium自动化测试-web页面定位toast-上篇(详解教程)

    1.简介 在使用appium写app自动化的时候介绍toast的相关元素的定位,在Web UI测试过程中,也经常遇到一些toast,那么这个toast我们这边如何进行测试呢?今天宏哥就分两篇介绍一下. ...

  10. 《手把手教你》系列技巧篇(四十六)-java+ selenium自动化测试-web页面定位toast-下篇(详解教程)

    1.简介 终于经过宏哥的不懈努力,偶然发现了一个toast的web页面,所以直接就用这个页面来夯实一下,上一篇学过的知识-处理toast元素. 2.安居客 事先声明啊,宏哥没有收他们的广告费啊,纯粹是 ...

随机推荐

  1. npm run的时候报错: this[kHandle] = new _Hash(algorithm, xofLen);

    在前面加入以下配置信息 set NODE_OPTIONS=--openssl-legacy-provider && 后面跟原来的启动配置信息 另外一种方式,可以避免修改package. ...

  2. 通过串口通信 对TCP传输层以下的理解

    这可能是近期暂时最后一篇c嵌入式的文章了 基础的串口使用 参照网上的stm32教程套路引入标准库,初始化芯片手册上对应串口引脚 ,初始化stm32串口功能,然后有数据了就自然在寄存器上,就这样,你的波 ...

  3. DeepSeek本地安装部署(指南)

    前言 这两天deepseek出圈了. 今天分享一下,如果在本地电脑部署和运行deepseek,实现AI对话的功能. 访问ollama官网: https://ollama.com/ 下载一个合适自己操作 ...

  4. 让AI碰撞!“天翼云息壤杯”高校AI大赛江苏赛区交流会热力开场!

    由中国电信集团有限公司主办,天翼云科技有限公司承办的"天翼云息壤杯"高校AI大赛正在火热进行中.为了提高江苏赛区学生的参赛热情,增强学生的创作能力,江苏电信.天翼云华东中心于12月 ...

  5. CPU算力如何计算

    本文分享自天翼云开发者社区<CPU算力如何计算>,作者:l****n 什么是算力 随着国家大力发展数字基础设施,算力的提升和普惠变得越来越重要,它注定会在人们的视线中占据很重要的一席.那么 ...

  6. Nityacke's 分块(未补全)

    P2801 教主的魔法 区间加区间查询一个数排名. 对于每个块,维护其有序序列.修改时散块暴力重构,整块打tag. 查询是简单的.时间复杂度 \(O(n\log B+\dfrac{qn}{B}\log ...

  7. Flink11--FliterAndKeyBy算子

    一.导入依赖 参考本人下博客 二.代码 FLink11FilterApp.java package net.xdclass.class9; import org.apache.flink.api.co ...

  8. test1111

    了解客户端和服务端的请求原理 HTTP协议及其组成 HTTPS交互原理分析 访问支付宝,微信的开放接口 都是基于HTTP 对外提供的开放服务 API都是基于HTTP协议的, 微服务中的服务之间的调用大 ...

  9. 海康SDK报错Structure.getFieldOrder()

    就是你调用的这个结构体以及其引用的其他结构体,可能没有getFieldOrder()的方法,你只要按照顺序把他填上去就好了.比如 public static class NET_DVR_TIME ex ...

  10. vscode launch program "xxx" does not exist

    Error Solution Please Click The Build Button Other This maybe is the one of Makefile Tools or 'c++ e ...