大家好,又见面了,我是你们的朋友全栈君。
针对之前R-CNN的缺点,我们来看

1、每个候选区域都进行了卷积操作提取特征,计算量大速度低效。
2、对于卷积网络来讲都需要输入的图像尺寸固定(比如224×224)。这种人为的需要导致面对任意尺寸和比例的图像或子图像时降低识别的精度。当遇到任意尺寸的图像是,都是先将图像适应成固定尺寸,方法包括裁剪和变形。裁剪会导致信息的丢失,变形会导致位置信息的扭曲,就会影响识别的精度。
5.3.1.1 SPPNet与RCNN对比

R-CNN模型 | SPPNet模型 |
|---|---|
1、R-CNN是让每个候选区域经过crop/wrap等操作变换成固定大小的图像 2、固定大小的图像塞给CNN 传给后面的层做训练回归分类操作 | 1、SPPNet把全图塞给CNN得到全图的feature map 2、让SS算法得到候选区域与feature map直接映射,得到候选区域的映射特征向量(这是映射来的,不需要过CNN) 3、映射过来的特征向量大小不固定,所以这些特征向量塞给SPP层(空间金字塔变换层),SPP层接收任何大小的输入,输出固定大小的特征向量,再塞给FC层4、经过映射+SPP转换,简化了计算,速度/精确度也上去了 |
5.3.1.2 spatial pyramid pooling layer
全连接层或在SVM/softmax这一类的分类器需要输入规定长度的向量。

注:因为选择的是Alex-Net, conv5得到的feature map大小是13 13 256, 所以是256维向量
pooling使用max pooling,具体操作如下:

5.3.1.3 映射
如何得到每个候选区域的特征向量?这个步骤也大大提高了特征计算的速度!
SPPnet的做法是首先通过选择性搜索,对待检测的图片进行搜索出2000个候选窗口。这一步和R-CNN一样。然后把整张待检测的图片,输入CNN中,进行一次性特征提取,得到feature maps,然后在feature maps中通过映射关系找到各个候选框的区域。最后再对各个候选框采用金字塔空间池化,提取出固定长度的特征向量。

整个映射过程有具体的公式,如下
假设(x′,y′)(x′,y′)表示特征图上的坐标点,坐标点(x,y)表示原输入图片上的点,那么它们之间有如下转换关系,这种映射关心与网络结构有关:(x,y)=(S∗x′,S∗y′),即
其中 S 就是CNN中所有的strides的乘积,包含了池化、卷积的stride。论文中使用S的计算出来为2x2x2x2=16,在ZF-5结构中。
5.3.1.4 SPPNet 结果
看图理解:

卷积网络训练注意点:
SPPnet在微调时不能更新空间金字塔池化层之前的卷积层参数,这一点限制了深度网络的精度。关于最后一点其实不准确,SPPnet也可以反向传播,但是会很复杂。Ross Girshick在论文《Fast R-CNN》中认为SPPnet在微调时指出,当训练来自不同图像的ROI(候选区域或感兴趣区域)时反向传播经过SPP 层的效率非常低下,这时更新卷积层参数耗时较长,不能更新卷积层参数,而不是不可以更新。
为什么?SPP-Net中fine-tuning的样本是来自所有图像的所有RoI打散后均匀采样的,即RoI-centric sampling,这就导致SGD的每个batch的样本来自不同的图像,需要同时计算和存储这些图像的Feature Map,这个过程变得代价非常大。
特点:multi-scale feature extraction 能够进一步改善算法:论文中将image尺度调整为min(w,h)=s,s属于{480,576,688,864,1200},并且计算每个scale的feature maps。可以通过逐个通道池化的方法结合从每个尺度提取到的特征,也就是训练的时候在epoch中首先训练一个尺寸产生一个Model,然后加载该Model训练第二个尺度,直到训练完所有的尺寸为止。
测试的mAP与测试时间对比:

5.3.1.4 代码实现
import tensorflow as tf
import math
def Spp_layer(feature_map, bins):
''' 使用 [3, 2, 1] denote 3*3, 2*2, 1*1 bins做金字塔赤化输出'''
# get feature map shape
batch_size, a, _, _ = feature_map.get_shape().as_list()
pooling_out_all = []
for layer in range(len(bins)):
# 计算每个不同池化尺寸的输出
k_size = math.ceil(a / bins[layer])
stride = math.floor(a / bins[layer])
pooling_out = tf.nn.max_pool(feature_map,
ksize=[1, k_size, k_size, 1],
strides=[1, stride, stride, 1],
padding='VALID')
pooling_out_resized = tf.reshape(pooling_out, [batch_size, -1])
pooling_out_all[layer] = pooling_out_resized
# 特征图合并输出结果
feature_map_out = tf.concat(axis=1, values=pooling_out_all)
return feature_map_out来看下SPPNet的完整结构

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/137637.html原文链接:https://javaforall.cn