目录
参考资料
论文:
Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
博客:
[人工智能-深度学习-72]:卷积神经网络 - 空间金字塔池化SPP-Net网络与Pytorch代码实现
第1章 SPP-Net网络概述
1.1 什么是SPP-Net
SPP-Net是一种可以不用考虑图像大小,输出图像固定长度的网络结构,并且可以做到在图像变形情况下表现稳定。
在SPP-Net之前,所有的神经网络都是需要输入固定尺寸的图片,比如224x224(ImageNet)、32x32(LenNet)、96x96等。这样对于我们希望检测各种大小的图片的时候,需要经过缩放、裁剪等一系列操作,这都在一定程度上导致图片信息的丢失和变形,限制了识别精确度。
但是为什么CNN需要固定的输入呢?CNN网络可以分解为卷积网络部分以及全连接网络部分。我们知道卷积网络的参数主要是卷积核,完全能够适用任意大小的输入,并且能够产生任意大小的输出。但是全连接层部分不同,全连接层部分的参数是神经元对于所有输入的连接权重,也就是说输入尺寸不固定的话,全连接层参数的个数都不能固定。
1.2 SPP-Net的基本思想
SPP-Net对这些网络中存在的缺点进行了改进,基本思想是:输入整张图像,提取出整张图像的特征图,然后利用空间关系从整张图像的特征图中,在spatial pyramid pooling layer提取各个region proposal的特征。
一个正常的深度网络由两部分组成,卷积部分和全连接部分,要求输入图像需要固定size的原因并不是卷积部分而是全连接部分。所以SPP层就作用在最后一层卷积之后,SPP层的输出就是固定大小。
1.3 SPP-Net的好处与优点
- 输入不需要放缩到指定大小;
- 增加了一个空间金字塔池化层;
- 每幅图片只需要提取一次特征。
第2章 SPP网络结构
2.1 卷积层特征图
SPPNet通过可视化Conv5层特征,发现卷积特征其实保存了空间位置信息(数学推理中更容易发现这点),并且每一个卷积核负责提取不同的特征,比如(C)图175、55卷积核的特征,其中175负责提取窗口特征,55负责提取圆形的类似于车轮的特征。我们可以通过传统的方法聚集这些特征,例如词袋模型或是空间金字塔的方法。
2.2 空间金字塔池化层
我们先从空间金字塔特征提取说起(这边先不考虑池化
),空间金字塔是很久以前的一种特征提取方法,跟Sift、Hog等特征息息相关。为了简单起见,我们假设一个很简单两层网络:
- 输入层:一张任意大小的图片,假设其大小为(w,h)。
- 输出层:21个神经元。
也就是我们输入一张任意大小的特征图的时候,我们希望提取出21个特征。
空间金字塔特征提取的过程如下:
(1)图片输入
黑色图片代表卷积之后的特征图,其尺寸是任意的,该特征作为SPP网络的输入。
(2)特征提取网格的定义(SPP池化层网格的定义)
生成三个SPP池化核,每个SPP池化核代表的是输出特征的网格,网格的大小,不同的应用,大小不同,这里假设为:4x4, 2x2, 1x1。
SPP池化核网格的个数,决定了输出的大小,而不是输入的大小。
SPP池化核网格的作用是均分任意长度尺寸图片的像素,如:
- 4 x 4的网络,就是把输入图片均分成 4 x 4 = 16个块,而不管输入图片的尺寸到底是多少。
- 1 x 1的网络,就是把输入图片均分成 1 x 1 = 01个块,而不管输入图片的尺寸到底是多少。
这样可以得到16+4+1=21种不同的固定长度的输出块(Spatial bins)。
注意:SPP的池化层与CNN网络中的池化层的定义是不同的。
- CNN的池化层:定义的是池化核的大小和stride的大小。
- SPP的池化层: 首先, SPP定义三个池化核。其二,SPP池化核网格的大小,如4 x 4,不是池化核的大小,而是输出特征的大小。
(3)对任何长度的输入图片的重新切分/划分
如上图所示,当我们输入一张任意尺寸的图片时,我们利用不同网格大小的池化核,对一张图片进行了划分:
- 第1个SPP池化核=(4 x 4), 把一张完整的任意长度的输入图片,分成了16个块,也就是每个块的大小就是(w/4,h/4);
- 第2个SPP池化核=(2 x 2), , 把一张完整的任意长度的输入图片,分成了4个块,也就是每个块的大小就是(w/2,h/2);
- 第3个SPP池化核=(1 x 1), , 把一张完整的任意长度的输入图片,分成了1个块,也就是每个块的大小就是(w,h);
经过上述的切分,可以得到最后总共可以得到16+4+1=21个块,即一个固定长度的特殊输出。
(4)最大池化特征提取
对每个网格,按照各自占用输入特征的范围,各自独立的进行池化,支持的算法算法有:
- 最大池化
- 最小池化
- 平均池化
(5)固定长度的特征输出
最大池化后,每个网格输出一个最大池化值,一共输出21个最大特征值,这21个输出特征值组成了最终的输出。这样就可以把任意长度的特征值,转化为一个固定大小的特征值了,由于SPP采用了三层不同的池化核,因此转化后的特征值,既包含了高层抽象特征,也包含了低层的具体特征。
当然你可以设计其它维数的输出,增加金字塔的层数,或者改变划分网格的大小。
上面的三种不同刻度的划分,每一种刻度我们称之为:金字塔的一层
。
空间金字塔池化,使得任意大小的特征图都能够转换成固定大小的特征向量,这就是空间金字塔池化的奥义(多尺度特征提取出固定大小的特征向量)。
【备注】:
空间金字塔池化,与其他池化一样,对channel维度没有影响,最后输出的是21x21x256维的特征。
第3章 包含SPN结构的网络的训练
3.1 Multi-size training
使用两个尺度进行训练:比如,224x224 和180x180。
- 训练的时候,224x224的图片通过crop原始图片得到,180x180的图片通过进一步缩放224x224的图片得到。
- 之后,迭代训练,即用224x224的图片训练一个epoch,然后在用180x180的图片训练一个epoch,交替地进行。
两种尺度下,在SPP结构后,输出的特征维度都是(9+4+1)x256,参数是共享的,之后接全连接层即可。
3.2 Single-size
理论上说,SPP-net支持直接以多尺度的原始图片作为输入。
实际上,深度学习框架,为了计算的方便,GPU,CUDA等比较适合固定尺寸的输入,所以训练的时候输入是固定了尺度了的。
第4章 基于Pytorch实现SPPNet
参考:记录一下关于SPP(空间金字塔池化模块)使用pytorch的实现代码
# SPP模块主要是为了应对当网络中存在全连接层时,对中间层级的输出特征的形状具有不变的要求,其具体思想可以理解为将具有一定形状的输出特征分成指定数量的子特征,然后使用池化的手段将其转化成具有一定维度的特征矩阵,最后进行拼接,从而使网络具有输入图片任意尺寸的“适应能力”。
import math
import torch
import torch.nn.functional as F
class SPPnet(torch.nn.Module):
def __init__(self, num_layers, pool_type='max'):
super(SPPnet, self).__init__()
self.num_level = num_layers
self.pool_type = pool_type
def forward(self,input_feature):
batch, c, h, w = input_feature.size()
for i in range(self.num_level):
level = 1+1
kernel_size = (math.ceil(h/level), math.ceil(w/level))
stride = (math.ceil(h/level), math.ceil(w/level))
padding = (math.floor((kernel_size[0]* level-h+1)/2), math.floor((kernel_size[1]* level-w+1)/2))
if self.pool_type =='max':
tensor = F.max_pool2d(input_feature, kernel_size = kernel_size, padding = padding).view(batch,-1)
else:
tensor = F.avg_pool2d(input_feature, kernel_size = kernel_size, padding = padding).view(batch,-1)
#然后就可以按照SPPnet后面所接的全连接层Fc对tensor进行相应的操作了。