论文全名:Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
熟悉卷积神经网络(CNN)的人都知道,为了使输出维度相同,必须在输入图片时归一化成相同尺寸。特征维度相同才能用于分类器的训练。输入维度相同这个限制太大了,这篇文章主要针对这个缺陷进行了改进,具体就是加入了空间金字塔池化(Spatial Pyrimid Pooling),使得不同维度的输入最后都能得到相同维度的输出。
参考博客:http://sinb.github.io/Whatyouknowabout/spp-net-1/
R-CNN存在的问题:(了解R-CNN可以看博文:http://blog.youkuaiyun.com/zhuzemin45/article/details/79586281)
作者认为,R-CNN所需要的crop/warp(输入图片尺寸归一化)产生物体截断或拉伸,导致输入CNN的信息丢失或扭曲,是造成任意尺度的图片识别准确率低的原因。传统的CNN需要先对训练图片进行处理,使其维度相同。具体有两种做法,裁剪(cropping)和扭曲(warping)。如下图:
CNN主要分为两块,卷积层和全连层(fully-connected layer),卷积层的输出是特征图(feature map),特征图反映出了原始输入图片中对filter(卷积核)激活的空间信息。卷积这一步不需要固定大小的,最后的全连接层才要求输入统一大小。
作者针对这个问题,提出了在CNN的最后一个卷积层之后,加入一个SPP层,也就是空间金字塔池化,对之前卷积得到的特征进行‘维度归一化’,然后得到一个固定长度的特征向量,再传到全连层去。如下图:
在深层CNN里加入SPP会有3个优势:
1) 相比之前的滑动窗池化(sliding window pooling),SPP可以对不同维度输入得到固定长度输出。
2) SPP使用了多维的spatial bins(多个不同大小的窗),而滑动窗池化只用同一尺寸的窗扫描.。
3) 因为输入图片尺度可以是任意的,SPP就提取出了不同尺度的特征。作者说这3点可以提高深度网络的识别准确率。
SPP-net既然只在网络的最后几层(深层网络)中加入,而本质上就是池化,所以它可以加入到其他CNN模型中,比如AlexNet。作者的实验表明,加入了SPP的AlexNet效果确实提升了。作者认为SPP应该能提升更复杂的网络的能力。
CNN提取特征:
一个CNN里,除了最后的全连层,前面的卷积层和池化层其实都可以看作是卷积运算(都是滑动窗)。卷积操作得到的结果(feature map)基本上和原始的输入图片是一个比例。feature map的意义是,它不但表示了响应的强度,也记录了响应的位置信息。对之前卷积得到的特征进行‘整合’,然后得到一个固定长度的特征向量,再传到全连层去。如下图: 上图可视化了卷积和池化提取的特征的意义,即feature map的发挥的作用,feature map上的每一个ceil(区域)对应这实际物体的某种形状或者纹理(特征)。
SPP-Layer构架:
若输入维度不同,卷积层会输出的不同大小的feature map.其实把BoW用在这些feature map上已经可以解决固定输出的问题了,但SPP使用local spatial bins来做池化,保留了空间信息,是一个提升.SPP的原理其实就一句话,SPP的spatial bins(也就是池化时候的窗口大小)是和输入图片的大小成比例的,所以spatial bins的数目也就固定下了,和输入大小无关.原理是下图.无论feature map的大小是多少,总是可以把一个feature map分成4x4,或者2x2,或者1x1,只是每一个小方块大小不一样.
具体地,在一个CNN里,把最以后一次池化层去掉,换成一个SPP去做最大池化操作(max pooling)。如果最后一次卷积得到了k个feature map,也就是有k个filter,SPP有M个bin,那经过SPP得到的是一个kM维的向量。比如上图中第一个feature map有16个bin,一共有256个feature map,每一个经过16个bin的max pooling得到16个数,那256个feature map就是16x256的向量了。SPP的bin大小可以选择多个,所以经过SPP还能产生4x256,1x256维的向量。
我们可以对不同比例,不同大小的图片进行处理,而且使用同一个CNN,只是对不同大小的图片,在最后的SPP里,bin的大小不同,但最后得到的特征确实相同维度。这样,我们把一张图片resize成不同尺度,放到同一个CNN里训练,就能得到不同尺度下的特征,和传统的SIFT类似。
训练:
虽然SPP理论上可以直接用BP来训练参数,但当时的使用了GPU的CNN工具,比如caffe和cuda-caffe都只能以固定大小的图片作为输入,作者使用了caffe,但是用了一些技巧来实现SPP的训练。
单一大小训练:
多尺度训练:
我们再考虑一种输入大小(180x180),两种大小也算是”多个大小训练”啊。这个180x180的直接取224x224图片的按尺度缩放图片,这两张图除了解析度不同别的都相同。180x180的图片经过第五层卷积后的feature map是10x10,这时我们依然用刚才的公式,窗口大小就是ceil(a / n),步长是floor(a / n),这样的话后得到的特征长度与之前的224x224的特征长度相同。举个例子,如果金字塔是3*3,即n=3,那么对于第一种大小,窗口长为5,步长4,第二中大小,窗口4,步长3,如下图:
红色区域代表窗口,经过最大池化后,两张图得到的特征向量长度均为3x3=9。所以180的网络和224的网络参数完全一样,于是SPP训练阶段,对于这两种网络只要共享参数即可。
实际训练时,为了减少不停转换网络带来的开销,需要使用全部数据一次训练一个网络,然后再换成第二个,作为一次迭代。所以作者根本没有实现训练不同大小输入的CNN的BP算法,只是针对各种各样大小不同的输入,定义出不同的网络,但这些网络实际上参数都相同,于是就可以用现有工具来训练。这个是训练阶段,需要不停转化网络,当训练好模型用于测试时,只要输入图片就行了。
SPP-Net目标检测实验:
R-CNN进行物体检测的方法是,首先从每张图片选出2000个候选窗口,然后把窗口变形到227x227,把每个窗口送入网络计算特征,然后训练SVM进行二分类。R-CNN效果很好,但是对每一张图片的2000个窗口都要送进卷积网络计算,很费时间。
SPP-NET可以直接对一张整图计算feature map(以及不同尺度的这张图片),然后只需要在feature map的不同区域进行SPPooling即可,没有了像R-CNN的从头卷积那一步,如下图:
作者还说像其它方法,比如DPM,SS等,虽然都从feature map来选择窗口,但这个窗口是预先固定的,而SPP不需要。
检测算法:
使用Selective Search产生2000个窗口,将图片短边缩放至s,使用网络对图片计算feature map。对每一个候选窗口,在feature map上使用4个空间金字塔{1x1,2x2,3x3,6x6}50个bin,于是可以产生256*50维特征,然后用这个特征训练SVM,每个图像类别分别训练一个SVM。
训练SVM需要构建正样本和负样本。最后这20个SVM可以用来计算物体属于每个类别的得分。
把输入图片的尺度增加,短边s分别是{480, 576, 688, 864, 1200},分别计算feature map,现在有很多组feature map了,怎么提取候选窗口的特征?一种方法当然是都提取,作者用的是,对每一个候选窗口,只选一个尺度对应的feature map,这个尺度应该使得经过相同的尺度变换的候选窗口的像素数接近224x224。然后就pool这一个feature map。
这个预训练好的网络也可以进行fine-tuned,但只调整全连层的参数。方法就是在最后一个全连层之后再加一个21路的全连层,20个类别,还有一个是负例的类别。精调细节不说了,这一阶段还做了对预测窗口的post-precess。
总结:
SPP-net对R-CNN最大的改进就是特征提取步骤做了修改,其他模块仍然和R-CNN一样。特征提取不再需要每个候选区域都经过CNN,只需要将整张图片输入到CNN就可以了,ROI特征直接从特征图获取。和R-CNN相比,速度提高了百倍。
SPP-net缺点也很明显,CNN中的conv层在微调时是不能继续训练的。它仍然是R-CNN的框架,离我们需要的端到端的检测还差很多。既然端到端如此困难,那就先统一后面的几个模块吧,把SVM和边框回归去掉,由CNN直接得到类别和边框可不可以?于是就有了Fast R-CNN。