SPP空间金字塔池化(spatial pyramid pooling, SPP)原理与pytorc实现

1、为什么需要SPP?

过去的卷积神经网络CNN由卷积层+全连接层组成,其中卷积层对于输入数据的大小并没有要求,唯一对数据大小有要求的则是第一个全连接层,因此基本上所有的CNN都要求数据数据固定大小,例如著名的VGG模型则要求输入数据大小是(224×224)。

固定输入数据大小有两个问题:

1、很多场景所得到的数据并不是固定大小的,例如不同相机,不同手机,拍出来的图片其宽高比是不固定的;

2、有人说可以对图片进行切割,但是切割的话很可能会丢失到重要信息。

综上,SPP的提出就是为了解决CNN输入图像大小必须固定的问题,从而可以使得输入图像可以具有任意尺寸。

2、SPP原理

SPP空间金字塔池化(spatial pyramid pooling, SPP)原理与pytorc实现_第1张图片

如图所示,对于选择的不同大小的区域对应到卷积之后的特征图上,得到的也是大小不一致的特征图区域,特征图通道数为256,对于每个区域,通过三种划分方式进行池化:

1)直接对整个整个区域池化,每层得到一个点,共256个点,构成一个1×256的向量;

2)将区域划分成2×2的格子,每个格子池化,得到一个1×256的向量,共2*2=4个格子,最终得到4个1×256的向量 ;

3)将区域划分成4×4的格子,每个格子池化,得到一个1×256的向量,共4*4=16个格子,最终得到16个1×256的向量 ;

将三种划分方式池化得到的结果进行拼接,得到(1+4+16)×246=21×256的特征。

SPP做到的效果为:不管输入的图片是什么尺度,都能够正确的传入网络。

代码实现

#coding=utf-8

import math
import torch
import torch.nn.functional as F

# 构建SPP层(空间金字塔池化层)
class SPPLayer(torch.nn.Module):

    def __init__(self, num_levels, pool_type='max_pool'):
        super(SPPLayer, self).__init__()

        self.num_levels = num_levels
        self.pool_type = pool_type

    def forward(self, x):
        num, c, h, w = x.size() # num:样本数量 batch_size c:通道数 h:高 w:宽
        for i in range(self.num_levels):
            level = i+1
            kernel_size = (math.ceil(h / level), math.ceil(w / level))
            stride = (math.ceil(h / level), math.ceil(w / level))
            pooling = (math.floor((kernel_size[0]*level-h+1)/2), math.floor((kernel_size[1]*level-w+1)/2))

            # 选择池化方式
            if self.pool_type == 'max_pool':
                tensor = F.max_pool2d(x, kernel_size=kernel_size, stride=stride, padding=pooling).view(num, -1)
            else:
                tensor = F.avg_pool2d(x, kernel_size=kernel_size, stride=stride, padding=pooling).view(num, -1)

            # 展开、拼接
            if (i == 0):
                x_flatten = tensor.view(num, -1)
            else:
                x_flatten = torch.cat((x_flatten, tensor.view(num, -1)), 1)
        return x_flatten

 

你可能感兴趣的:(SPP空间金字塔池化(spatial pyramid pooling, SPP)原理与pytorc实现)