熟悉CNN网络的都清楚,CNN网络要求输入数据是固定大小的,因此在对数据预处理阶段都会对数据进行裁剪和缩放,但这是影响识别精度的一个主要原因。后来2014年,大神何凯明提出了SPP-net,成功的并且有效的解决了上述问题。首先介绍一下SPP-net的优点。
SPP的优点:1)任意尺寸输入,固定大小输出,2)层多,3)可对任意尺度提取的特征进行池化。
解决该问题的关键是不再对数据进行缩放和裁剪,而是直接用原始数据直接输入进网络。在CNN中,为什么不可以直接输入呢?
CNN分为三部分,卷积,池化,全连接。卷积层是用一个卷积核对输入进行操作,不论多大的输入,都可以进行卷积操作;池化层对输入数据的大小也没有要求;但是全连接层对输入数据的大小就有要求了,原因是全连接层的连接权值矩阵是个固定大小的矩阵,这就要求输入是固定的。重点来了,那既然卷积和池化对输入大小没有,那保留CNN网络的卷积和池化部分,将全连接层做一些改变。
SPP-net与CNN相比,保留了卷积和池化部分,在全连接层做了一些改变。首先上图,改变在哪里?
图中的卷积层包括卷积和池化两部分。SPP是放在卷积和全连接层之后的!!!
下面介绍一下为什么SPP对输入没有要求?
输入:一张任意大小的图片,假设其大小为(w,h)。
输出:N个神经元。
这张图片中N=21。用了三种不同大小的刻度,对一张输入的图片进行了划分,最后总共可以得到16+4+1=21个块,我们即将从这21个块中,每个块提取出一个特征,这样刚好就是我们要提取的21维特征向量。
第一张图片,我们把一张完整的图片,分成了16个块,也就是每个块的大小就是(w/4,h/4);
第二张图片,划分了4个块,每个块的大小就是(w/2,h/2);
第三张图片,把一整张图片作为了一个块,也就是块的大小为(w,h)。
python代码:
def spatial_pyramid_pooling(self,data):
self.input = data
self.batch_size = self.input.get_shape().as_list()[0]
for i in range(self.n):
x = int(math.floor(self.a/float(self.bins[i])))
self.strides.append(x)
x = int (math.ceil(self.a/float(self.bins[i])))
self.filters.append(x)
self.pooled_out = []
for i in range(self.n):
self.pooled_out.append(tf.nn.max_pool(self.input,
ksize=[1, self.filters[i], self.filters[i], 1],
strides=[1, self.strides[i], self.strides[i], 1],
padding='VALID'))
for i in range(self.n):
self.pooled_out[i] = tf.reshape(self.pooled_out[i], [self.batch_size, -1])
self.output = tf.concat(1, [self.pooled_out[0], self.pooled_out[1], self.pooled_out[2]])