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

本文详细介绍了深度学习中SPP层(Spatial Pyramid Pooling)的工作原理,它解决了输入图像大小任意的问题。SPP层通过对特征映射进行多尺度划分并应用最大池化,将不同尺寸的输入转换为固定长度的向量,适合全连接层处理。文章还给出了PyTorch实现SPP层的代码示例,包括卷积、最大池化和空间金字塔池化的步骤。
摘要由CSDN通过智能技术生成

在这里插入图片描述
上图是原文中给出的示意图,需要从下往上看:

首先是输入层(input image),其大小可以是任意的

进行卷积运算,到最后一个卷积层(图中是conv5)输出得到该层的特征映射(feature maps),其大小也是任意的

下面进入SPP层

我们先看最左边有16个蓝色小格子的图,它的意思是将从conv5得到的特征映射分成16份,另外16X256中的256表示的是channel,即SPP对每一层都分成16份(不一定是等比分,原因看后面的内容就能理解了)。

中间的4个绿色小格子和右边1个紫色大格子也同理,即将特征映射分别分成4X256和1X256份

那么将特征映射分成若干等分是做什么用的呢? 我们看SPP的名字就是到了,是做池化操作,一般选择MAX Pooling,即对每一份进行最大池化。

我们看上图,通过SPP层,特征映射被转化成了16X256+4X256+1X256 = 21X256的矩阵,在送入全连接时可以扩展成一维矩阵,即1X10752,所以第一个全连接层的参数就可以设置成10752了,这样也就解决了输入数据大小任意的问题了。

注意上面划分成多少份是可以自己是情况设置的,例如我们也可以设置成3X3等,但一般建议还是按照论文中说的的进行划分。

代码:

from math import floor, ceil
import torch
import torch.nn as nn
import torch.nn.functional as F
class SpatialPyramidPooling2d(nn.Module):
    def __init__(self, num_level, pool_type='max_pool'):
        super(SpatialPyramidPooling2d, self).__init__()
        self.num_level = num_level
        self.pool_type = pool_type
    def forward(self, x):
        N, C, H, W = x.size()
        print('多尺度提取信息,并进行特征融合...')
        print()
        for i in range(self.num_level):
            level = i + 1
            print('第',level,'次计算池化核:')
            kernel_size = (ceil(H / level), ceil(W / level))
            print('kernel_size: ',kernel_size)
            stride = (ceil(H / level), ceil(W / level))
            print('stride: ',stride)
            padding = (floor((kernel_size[0] * level - H + 1) / 2), floor((kernel_size[1] * level - W + 1) / 2))
            print('padding: ',padding)
            print()
            print('进行最大池化并将提取特征展开:')
            if self.pool_type == 'max_pool':
                tensor = (F.max_pool2d(x, kernel_size=kernel_size, stride=stride, padding=padding)).view(N, -1)
            else:
                tensor = (F.avg_pool2d(x, kernel_size=kernel_size, stride=stride, padding=padding)).view(N, -1)
            if i == 0:
                res = tensor
                print('展开大小为: ',res.size())  #64x1
                print()
            else:
                res = torch.cat((res, tensor), 1)  #  i=1 64x(1+4) i=2  64x(1+4+9)
                print('合并为: ',res.size())
                print()
        return res
class SPPNet(nn.Module):
    def __init__(self, num_level=3, pool_type='max_pool'):
        super(SPPNet, self).__init__()
        self.num_level = num_level
        self.pool_type = pool_type
        self.feature = nn.Sequential(nn.Conv2d(3, 64, 3),
                                     nn.ReLU(),
                                     nn.MaxPool2d(2),
                                     nn.Conv2d(64, 64, 3),
                                     nn.ReLU())
        # num_grid = 1 + 4 + 9 = 14
        self.num_grid = self._cal_num_grids(num_level)
        self.spp_layer = SpatialPyramidPooling2d(num_level)
        self.linear = nn.Sequential(nn.Linear(self.num_grid * 64, 512),
                                    nn.Linear(512, 10))
    def _cal_num_grids(self, level):
        count = 0
        for i in range(level):
            count += (i + 1) * (i + 1)
        return count
    def forward(self, x):
        print('x初始大小为:')
        N, C, H, W = x.size()
        print('N:', N, ' C:', C, ' H', H, ' W:', W)
        print()
        x = self.feature(x)
        print('x经过卷积、激活、最大池化、卷积、激活变成:')
        N, C, H, W = x.size()
        print('64(conv)->62(maxpool)->31(conv)->29')
        print('N:', N, ' C:', C, ' H', H, ' W:', W)
        print()
        print('x进行空间金字塔池化:')
        x = self.spp_layer(x)
        print('空间金字塔池化后,x进入全连接层:')
        x = self.linear(x)
        return x
if __name__ == '__main__':
    a = torch.rand((1, 3, 64, 64))
    net = SPPNet()
    output = net(a)
    print(output)

输出结果:

x初始大小为:
N: 1  C: 3  H 64  W: 64
 
x经过卷积、激活、最大池化、卷积、激活变成:
64(conv)->62(maxpool)->31(conv)->29
N: 1  C: 64  H 29  W: 29
 
x进行空间金字塔池化:
多尺度提取信息,并进行特征融合...1 次计算池化核:
kernel_size:  (29, 29)
stride:  (29, 29)
padding:  (0, 0)
 
进行最大池化并将提取特征展开:
展开大小为:  torch.Size([1, 64])2 次计算池化核:
kernel_size:  (15, 15)
stride:  (15, 15)
padding:  (1, 1)
 
进行最大池化并将提取特征展开:
合并为:  torch.Size([1, 320])3 次计算池化核:
kernel_size:  (10, 10)
stride:  (10, 10)
padding:  (1, 1)
 
进行最大池化并将提取特征展开:
合并为:  torch.Size([1, 896])
 
空间金字塔池化后,x进入全连接层:
tensor([[-0.0894, -0.1091, -0.1104,  0.0846, -0.0732,  0.0539,  0.0072, -0.0244,
         -0.0082,  0.0929]], grad_fn=<AddmmBackward>)
 
Process finished with exit code 0
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值