ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Spatial Pyramid Pooling

2022-02-06 11:31:18  阅读:204  来源: 互联网

标签:Pyramid frac SPP torch Pooling Spatial 大小 卷积 size


1. 摘要

现有的深度卷积神经网络(CNNs)需要一个固定大小的(例如,224×224)的输入图像。这个要求是“人工的”,可能会降低对任意大小/尺度的图像或子图像的识别精度。在这项工作中,我们为网络配备了另一种池化策略,即“空间金字塔池化”,以消除上述需求。新的网络结构,称为SPP-net,可以生成一个固定长度的表示,而不管图像的大小/规模。金字塔池对对象变形也具有鲁棒性。有了这些优点,SPP-net一般应该改进所有基于cnn的图像分类方法。

2. 介绍

我们正在目睹我们的视觉社区的一个快速、革命性的变化,主要是由深度卷积神经网络(CNNs)[1]和大规模训练数据[2]的可用性造成的。基于深度网络的方法最近在图像分类,目标检测,上大大改进了许多其他识别任务[,甚至非识别任务。然而,在cnn的训练和测试中存在一个技术问题:普遍的cnn需要一个固定的输入图像大小(例如,224×224),这限制了高宽比和输入图像的比例。当应用于任意大小的图像时,当前的方法大多将输入图像的固定大小,或通过裁剪,或通过扭曲,如图1(上)所示。但裁剪的区域可能不包含整个对象,而扭曲的内容可能导致不必要的几何失真。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPhmtU51-1644117964662)(C:\Users\Liujiawang\AppData\Roaming\Typora\typora-user-images\image-20220206104230076.png)]

由于内容丢失或失真,可能会影响识别精度。此外,一个预定义的当物体的尺度变化时,可能不合适。固定输入大小忽略了涉及规模的问题。

为什么CNN需要一个固定的输入大小呢

CNN主要由两部分组成:卷积层和后面的全连接层。卷积层以滑动窗口的方式运行,输出特征映射表示激活的空间排列(图2)。事实上,卷积层并不需要固定的图像大小,并且可以生成任何大小的特征映射。另一方面,完全连接的图层需要根据它们的定义有固定的大小/长度的输入。因此,固定大小的约束只来自于全连接的层,它们存在于网络的更深层次的阶段。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BnfAnyqH-1644117964664)(C:\Users\Liujiawang\AppData\Roaming\Typora\typora-user-images\image-20220206103949425.png)]

SPPNet

作者针对这个问题,提出了在CNN的最后一个卷积层之后,加入一个SPP层,也就是空间金字塔池化,对之前卷积得到的特征进行”整合”(aggregation),然后得到一个固定长度的特征向量,再传到全连层去.如下图.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j2hF3sUL-1644117964665)(C:\Users\Liujiawang\AppData\Roaming\Typora\typora-user-images\image-20220206104241375.png)]

SPP是词袋模型(Bag-of-Words)的扩展.为什么说是扩展?词袋模型没有特征的空间信息(就像它只能统计一个句子中每个单词的词频,而不能记录词的位置信息一样).在深层CNN里加入SPP会有3个优势: 1) 相比之前的滑动窗池化(sliding window pooling),SPP可以对不同维度输入得到固定长度输出. 2) SPP使用了多维的spatial bins(我的理解就是多个不同大小的窗),而滑动窗池化只用了一个窗. 3) 因为输入图片尺度可以是任意的,SPP就提取出了不同尺度的特征.作者说这3点可以提高深度网络的识别准确率。

SPP结构图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4CJG9cLW-1644117964666)(C:\Users\Liujiawang\AppData\Roaming\Typora\typora-user-images\image-20220206104440941.png)]

输入不同尺寸的图片,经过卷积层会输出的不同大小的feature map.把最以后一次卷积后的池化层去掉,换成一个SPP去做最大池化操作(max pooling).如果最后一次卷积得到了k个feature map,也就是有k个filter,SPP有M个bin(M个不同维度的pyramid),那经过SPP得到的是一个kM维的向量.我的理解是,比如上图中第一个feature map有16个bin,一共有256个feature map,每一个经过16个bin的max pooling得到16个数,那256个feature map就是16x256的向量了,第二个产生4x256维向量,SPP的bin大小可以选择多个,所以经过SPP还能产生4x256,1x256维的向量。

实际上,SPP也就是多个池化层的组合,再将每个池化层的输出拼接起来,池化层不像卷积层,没有什么参数需要学习;所以需要确定的就只有池化层的kernel size和strid。

取整符号:

  • ⌊⌋:向下取整符号 \(\lfloor 59/60\rfloor\)=0,有时也用 floor() 表示
  • ⌈⌉:向上取整符号 \(\lceil 59/60\rceil\)=1, 有时也用ceil() 表示

池化后矩阵大小计算公式:

  • 没有步长(Stride):\((h+2p−f+1)∗(w+2p−f+1)(h+2p−f+1)∗(w+2p−f+1)\)
  • 有步长(Stride):\(\lfloor \frac{h+2p−f}{s}+1\rfloor*\lfloor \frac{w+2p−f}{s}+1\rfloor\)

完整公式:

\[H_{out}=\lfloor \frac{H_{in}+2\times padding[0]-dilation[0]\times (kernel_size[0]-1)-1}{stride[0]}+1\rfloor \\ W_{out}=\lfloor \frac{W_{in}+2\times padding[1]-dilation[1]\times (kernel_size[1]-1)-1}{stride[1]}+1\rfloor \]

SPP所用公式:

假设

  • 输入数据大小是\((c,h_{in},w_{in})\),分别表示通道数,高度,宽度
  • 池化数量:\((n,n)\)

那么则有

  • 核(Kernel)大小: \(⌈\frac{h_{in}}{n},\frac{w_{in}}{n}⌉=ceil(\frac{h_{in}}{n},\frac{w_{in}}{n})\)
  • 步长(Stride)大小: \(⌊\frac{h_{in}}{n},\frac{w_{in}}{n}⌋=floor(\frac{h_{in}}{n},\frac{w_{in}}{n})\)

我们可以验证一下,假设输入数据大小是(10,7,11), 池化数量(2,2):

那么核大小为(4,6), 步长大小为(3,5), 得到池化后的矩阵大小的确是2∗2。

Warning:

这里还有一个问题,例如输入数据大小分别为\((1,1,7,7),(1,1,8,8)\)的时候,池化数量为\((3,3)\),根据上述公式我们计算出的核大小都是\((3,3)\),步长都是\((2,2)\)。

发现在feature map为8的上面会多出一个,我们直接舍去就好。

import torch
import torch.nn as nn
# 仅定义一个 3x3 的池化层窗口
m = nn.MaxPool2d(kernel_size=(3, 3),stride=2)
input1 = torch.randn(1, 1, 7, 7)
input2=torch.randn(1,1,8,8)  # 再增加一维
input2[:,:,:-1,:-1]=input1
out1=m(input1)
out2=m(input2)

input1:

tensor([[[[-1.1362, 0.1328, 1.4978, -0.0211, -1.3889, -0.0423, -1.0154],
[-0.1284, -0.3292, -0.0764, -1.4095, 0.3154, 0.6622, -0.4282],
[-1.2616, 2.4910, -0.8188, -1.3509, 1.3286, -0.5559, 0.2009],
[-0.2745, 0.4059, 1.1654, -0.7211, 0.7951, -0.6104, -0.0327],
[-0.4799, 0.4474, -0.8476, 1.5097, 0.8168, -0.0067, -0.0267],
[-0.0743, -0.3155, -0.2110, 2.1554, -1.4772, 0.0029, 1.0181],
[ 0.2785, -0.6448, 1.8553, 1.3306, 1.3171, 0.6611, -0.7505]]]])

input2:

tensor([[[[-1.1362, 0.1328, 1.4978, -0.0211, -1.3889, -0.0423, -1.0154,
-1.3181],
[-0.1284, -0.3292, -0.0764, -1.4095, 0.3154, 0.6622, -0.4282,
0.5563],
[-1.2616, 2.4910, -0.8188, -1.3509, 1.3286, -0.5559, 0.2009,
-0.2980],
[-0.2745, 0.4059, 1.1654, -0.7211, 0.7951, -0.6104, -0.0327,
-1.4227],
[-0.4799, 0.4474, -0.8476, 1.5097, 0.8168, -0.0067, -0.0267,
-0.2595],
[-0.0743, -0.3155, -0.2110, 2.1554, -1.4772, 0.0029, 1.0181,
0.3205],
[ 0.2785, -0.6448, 1.8553, 1.3306, 1.3171, 0.6611, -0.7505,
-0.3688],
[ 1.1252, -0.5459, -0.4047, 0.9076, 0.5693, -0.8110, 0.6319,
1.4798]]]])

out1.shape:

torch.Size([1, 1, 3, 3])

out2.shape:

torch.Size([1, 1, 3, 3])

out1:

tensor([[[[2.4910, 1.4978, 1.3286],
[2.4910, 1.5097, 1.3286],
[1.8553, 2.1554, 1.3171]]]])

out2:

tensor([[[[2.4910, 1.4978, 1.3286],
[2.4910, 1.5097, 1.3286],
[1.8553, 2.1554, 1.3171]]]])

当然还可以通过公式修改,更改padding,这样就不会出现多一个的情况:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YYTX896v-1644117964667)(C:\Users\Liujiawang\AppData\Roaming\Typora\typora-user-images\image-20220206112200671.png)]

具体参考:

SPPNet

3. 代码

#coding=utf-8

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

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

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

        self.levels = levels   # [[3,3],[4,4],[5,5]]
        self.pool_type = pool_type

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

            # 选择池化方式
            if self.pool_type == 'max_pool':
                tensor = F.max_pool2d(x, kernel_size=kernel_size, stride=stride).view(num, -1)
            else:
                tensor = F.avg_pool2d(x, kernel_size=kernel_size, stride=stride).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

Reference:

[1]Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition

标签:Pyramid,frac,SPP,torch,Pooling,Spatial,大小,卷积,size
来源: https://www.cnblogs.com/a-runner/p/15865423.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有