目标检测从入门到精通—SPP-Net详细解析(三)

时间:2021-08-18 00:39:06

SPP-Net网络结构分析

Author:Mr. Sun

Date:2019.03.18

Loacation: DaLian university of technology

论文名称:《Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition》

摘要:

   我们之前学习了基于深度学习进行目标检测的R-CNN算法,它虽然是一个开创性的理论,但是本身存在很多缺点,是有很多可以改进的地方的。本篇研究的Paper是何恺明大神在2014年发表的。这篇Paper最大的创新点在于提出了空间金字塔池化(Spatial Pyramid Pooling),简称SPP-Net。这个算法(algorithm)比R-CNN算法的速度快了很多倍(20-104倍)。我们知道在现有的CNN中,对于结构已经确定的网络,需要输入一张固定大小的图片,比如224*224、32*32、96*96等。这样对于我们希望检测各种大小的图片的时候,需要经过裁剪(Crop),或者缩放等一系列操作,这样往往会降低识别检测的精度,于是paper提出了“空间金字塔池化”方法,这个算法的牛逼之处,在于使得我们构建的网络,可以输入任意大小的图片,不需要经过裁剪缩放等操作,只要你喜欢,任意大小的图片都可以。不仅如此,这个算法用了以后,精度也会有所提高,总之一句话:不得不学的一篇论文。 

1、为什么提出SPP-Net?

  R-CNN的候选框是通过Selective search方法得到的,一张图片大概有2000个Region Proposals,然后通过crop/warp进行处理(归一化),将每个Region Proposal送入CNN中进行卷积特征的提取,这样导致:

  (1)训练时间非常慢,因为一张图片产生2000个Region Proposals,都会送入CNN中进行训练;
  (2)识别准确率很低,因为产生的Region Proposals都会通过crop/warp操作,resize到同一大小送入CNN中进行训练,这样会造成图片信息的缺失或者变形失真,会降低图片识别的正确率。

  如何解决上述两个存在的问题呢?

    (a)共享卷积计算:将一张完整图片整体送入CNN中进行特征提取,然后将一张图片的多个Region Proposals映射到最后的特征层上,形成每个Region Proposal的Feature Maps,进而加速特征的提取;

    (b)设计可以处理任意尺度的图像的结构:不同尺寸的Feature Map,映射称同样大小的特征。这个结构就是空间金字塔池化。 

  在传统的CNN网络和物体检测相关的文章中,比如R-CNN,他们都要求输入固定大小的图片,这些图片或者经过裁切(Crop)或者经过变形缩放(Warp),都在一定程度上导致图片信息的丢失和变形,限制了识别精确度。两种方式如下图1所示。 目标检测从入门到精通—SPP-Net详细解析(三)

图1:图像尺寸处理方式

  究竟是什么原因导致卷积网络中必须要固定输入图片的尺寸呢?

  (a)首先我们来看卷积操作:卷积操作对图片输入的大小会有要求吗?比如一个5*5的卷积核,我输入的图片是30*30的大小,可以得到(26,26)大小的图片(这里并没有进行padding,且stride=1),并不会影响卷积操作。我输入600*500,它还是照样可以进行卷积,也就是卷积对图片输入大小没有要求,只要你喜欢,任意大小的图片进入,都可以进行卷积。

  (b)再来看看池化操作:池化对图片大小会有要求吗?比如我池化大小为(2,2)我输入一张30*40的,那么经过池化后可以得到15*20的图片。输入一张53*22大小的图片,经过池化后,我可以得到26*11大小的图片。因此池化这一步也没对图片大小有要求。只要你喜欢,输入任意大小的图片,都可以进行池化。

  (c)最后看全连接层:既然池化和卷积都对输入图片大小没有要求,那么就只有全连接层对图片结果又要求了。因为全连接层我们的连接劝值矩阵的大小W,经过训练后,就是固定的大小了,比如我们从卷积到全连层,输入和输出的大小,分别是50、30个神经元,那么我们的权值矩阵(50,30)大小的矩阵了。因此空间金字塔池化,要解决的就是从卷积层到全连接层之间的一个过度。

  总之,卷积层的参数和输入图像的尺寸无关,它仅仅是一个卷积核在图像上滑动,不管输入图像是多少都没关系,只是对不同大小的图片卷积出不同大小的特征图,池化层对输入图像的尺寸也没有任何限制,只是获得不同的特征图而已,但是全连接层的参数就和输入图像大小有关,因为它要把输入的所有像素点连接起来,需要指定输入层神经元个数和输出层神经元个数,所以需要规定输入的feature的大小。

因此,对于图片限制其长度的根源是全连接层。以下图为例说明:

目标检测从入门到精通—SPP-Net详细解析(三)                             目标检测从入门到精通—SPP-Net详细解析(三)

  作为全连接层,如果输入的x维数不等,那么参数w肯定也会不同,因此,全连接层是必须是固定的输入,输出个数的。

2、SPP-Net的网络结构

目标检测从入门到精通—SPP-Net详细解析(三)

目标检测从入门到精通—SPP-Net详细解析(三)

  SPP-Net在最后一个卷积层后,接入了金字塔池化层,使用这种方式,可以让网络输入任意的图片,而且还会生成固定大小的输出。

 3、什么是空间金字塔池化(Spatial Pyramid Pooling )?

  在讲解什么是空间金字塔池化之前,我们先从空间金字塔特征说起(这边先不考虑池化)。如下图4中,当我们输入一张图片的时候,我们利用不同大小的刻度,对一张图片进行了划分。图4中,利用了三种不同大小的刻度,对一张输入的图片进行了划分,最后总共可以得到16+4+1=21个块,我们即将从这21个块中,每个块提取出一个特征,这样刚好就是我们要提取的21维特征向量

目标检测从入门到精通—SPP-Net详细解析(三)

图4:空间金字塔的结构

  空间金字塔最大池化(SPP-Max)的过程,其实就是从这21个图片块中,分别计算每个块的最大值,从而得到一个输出特征向量(跟上一层卷积核深度一样可能是256)。最后把一张任意大小的图片转换成了一个固定大小的21维特征(当然你可以设计其它维数的输出,增加金字塔的层数,或者改变划分网格的大小)。上面的三种不同刻度的划分,每一种刻度我们称之为金字塔的一层,使用多个不同刻度的层,可以提高我们所提取特征的鲁棒性。每一个图片块大小我们称之为:Sliding Windows Size了。如果你希望,金字塔的某一层输出n*n个特征,那么你就要用Windows Size大小为:(w/n,h/n)进行池化了。具体的SPP过程如下图5所示:

目标检测从入门到精通—SPP-Net详细解析(三)

图5:SPP的处理过程

 空间金字塔池化的意义是什么呢?

  当网络输入的是一张任意大小的图片,这个时候我们可以一直进行卷积、池化,直到网络的倒数几层的时候,也就是我们即将与全连接层连接的时候,就要使用金字塔池化,使得任意大小的特征图都能够转换成固定大小的特征向量,这就是空间金字塔池化的意义(多尺度特征提取出固定大小的特征向量)。

  空间金字塔池化的的公式是什么呢?

  根据空间金字塔池化的意义可以知道,无论最后一个卷积层得到的feature maps的大小是怎样的,我们都将其转化为了(4*4+2*2+1*1)*256的全连接层,不过需要注意的是,可以转化的条件是虽然这些feature maps的大小不同,但是feature maps的channels相同(这里为256),那么如何将不同大小的feature maps进行spp 呢?论文中给出的空间金字塔的三个Sliding Window Size 和 strides如下图6所示:

目标检测从入门到精通—SPP-Net详细解析(三)

图6:论文中空间金字塔的三个池化窗口的尺寸

  接下来我们来具体分析一下在空间金字塔中不同尺度的池化窗口和步长的计算方法:

  假设输入的大小为a*a*c(最后一个卷积核的特征),然后,将这 c 个 a * a 的Feature Maps分别分成了[1*1,2*2,4*4]大小的块(这里的分,指的是移动窗口池化),期望的输出为1*1*c,2*2*c,4*4*c,然后将reshape成(1*1+2*2+4*4)*c的二维数组,具体这儿是采用Max pooling操作来实现的,不过pool层的Sliding Window size和stride是不同的,具体有如下公式: 输出为[n,n],输入为[a,a],假设取上整运算为"++()",取下整运算为"--()",那么pool_size=++(n/a),stride=--(n/a),这样我们就将其转化为了n*n*c的矩阵,例如(13*13,10*10)都要转化为(4*4),那么采用[p_s=4,,s=3],[p_s=3,s=2]的池化操作后便可以得到。(这里有一个问题哈,就是如果(7*7)也要得到4*4的话,计算得到的size=2,stride=1,利用公式算出来得到的池化为(6*6)与预期的4*4不符,这里暂时还有问题,不清楚具体原因是什么)。

  我在这里有一个很大的疑问?

  在做目标检测的时候,SPP-Net是将Selective Search算法提取的2000个Region Proposal映射到要输出的最后一个卷积层,大概是要除以所有步长的乘积,如果Region Proposal很小,映射之后的特征更小(比如3*3),这样的话我们该如何应用(4*4)的金字塔池化呢?到底作者是怎么处理这种情况的呢?现在还是不能很好的理解这一点。

  空间金字塔池化的代码分析:

  代码返回的是SPP Layer之后要输出的神经元个数,代码中bins=[1,2,3],经过处理之后就可以得到对应的(1*1+2*2+3*3)*256=14*256=3584个神经元,即无论前面的Feature Map是多大的,经过SPP Layer处理之后得到固定大小的神经元,然后就可以和全连接层进行矩阵运算了!

 import tensorflow as tf
import math class SPPLayer():
def __init__(self,bins,feature_map_size):
self.strides = []
self.filters = []
# print(type(feature_map_size))
self.a = float(feature_map_size)
self.bins = bins
self.n = len(bins) 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]]) return self.output

4、SPP-Net网络的训练方式(单尺度和多尺度)

   Paper中将网络的训练分为两种:一种是Single-size,一种是Multi-size。

  先讲解single-size的训练过程:

  从理论上讲,SPP-Net支持直接以多尺度的原始图片作为输入后直接反向传播(Backpropagation)。实际上(Actually),Caffe等实现中,为了计算的方便,GPU,CUDA等比较适合固定尺寸的输入,所以训练的时候输入的图片是固定了尺度了的。以224*224的输入为例:在Conv5之后的特征图为:13x13*256(a*a*c);金字塔层bins:   n*n;将pooling层作为Sliding window pooling。Windows_size=[a/n] 向上取整 , Stride_size=[a/n]向下取整。得到的特征是n*n个特征向量(向量的长度是256)。对于pool 3*3:      Windows_size=5 的计算公式是:[13/3]向上取整=5 ,Stride_size= 4的计算公式是:[13/3]向下取整。如果输入改成180x180,这时候Conv5出来的Reponse map为10x10,类似的方法,能够得到新的pooling参数(滑动池化窗口的大小和步长)。

  对于Multi-size training训练:

  使用两个尺度进行训练:224*224 和180*180,训练的时候,224x224的图片通过Crop得到,180x180的图片通过缩放224x224的图片得到。之后,迭代训练,即用224的图片训练一个epoch,之后180的图片训练一个epoch,交替地进行。

  两种尺度下,在SSP后,输出的特征维度都是(9+4+1)x256,参数是共享的,之后接全连接层即可。作者在论文中说,这样训练的好处是一样的收敛速度,模型的测试精度却提高了。

 

目标检测从入门到精通—SPP-Net详细解析(三)的更多相关文章

  1. 目标检测从入门到精通—R-CNN详细解析(二)

    R-CNN目标检测详细解析 <Rich feature hierarchies for Accurate Object Detection and Segmentation> Author ...

  2. 第三十四节,目标检测之谷歌Object Detection API源码解析

    我们在第三十二节,使用谷歌Object Detection API进行目标检测.训练新的模型(使用VOC 2012数据集)那一节我们介绍了如何使用谷歌Object Detection API进行目标检 ...

  3. 《html5 从入门到精通》读书笔记(三)

    二.标签详解 标签 描述 4 5 <form> 定义表单. 4 5 <h1> to <h6> 定义标题1到标题6. 4 5 <head> 定义关于文档的 ...

  4. &num;Deep Learning回顾&num;之基于深度学习的目标检测(阅读小结)

    原文链接:https://www.52ml.net/20287.html 这篇博文主要讲了深度学习在目标检测中的发展. 博文首先介绍了传统的目标检测算法过程: 传统的目标检测一般使用滑动窗口的框架,主 ...

  5. AI佳作解读系列&lpar;五&rpar; - 目标检测二十年技术综述

    计算机视觉中的目标检测,因其在真实世界的大量应用需求,比如自动驾驶.视频监控.机器人视觉等,而被研究学者广泛关注.   上周四,arXiv新出一篇目标检测文献<Object Detection ...

  6. 皮卡丘检测器-CNN目标检测入门教程

    目标检测通俗的来说是为了找到图像或者视频里的所有目标物体.在下面这张图中,两狗一猫的位置,包括它们所属的类(狗/猫),需要被正确的检测到. 所以和图像分类不同的地方在于,目标检测需要找到尽量多的目标物 ...

  7. 目标检测入门论文YOLOV1精读以及pytorch源码复现&lpar;yolov1&rpar;

    结果展示 其中绿线是我绘制的图像划分网格. 这里的loss是我训练的 0.77 ,由于损失函数是我自己写的,所以可能跟大家的不太一样,这个不重要,重要的是学习思路. 重点提示 yolov1是一个目标检 ...

  8. Android Studio教程从入门到精通

    最新2.0系列文章参考: Android Studio2.0 教程从入门到精通Windows版 - 安装篇Android Studio2.0 教程从入门到精通Windows版 - 入门篇Android ...

  9. 【转】Zabbix 3&period;0 从入门到精通&lpar;zabbix使用详解&rpar;

    [转]Zabbix 3.0 从入门到精通(zabbix使用详解) 第1章 zabbix监控 1.1 为什么要监控 在需要的时刻,提前提醒我们服务器出问题了 当出问题之后,可以找到问题的根源   网站/ ...

随机推荐

  1. Ceph分层存储分析

    最近弄Ceph集群考虑要不要加入分层存储 因此花了点时间研究了下 1,首先肯定要弄清Ceph分层存储的结构 ,结构图大概就是下图所示 缓存层(A cache tier)为Ceph客户端提供更好的I/O ...

  2. &lbrack;转&rsqb;PDF预览插件PDFObject&period;js

    本文转自:http://pdfobject.com/index.php When possible, use standardized HTML markup and avoid JavaScript ...

  3. Android的一些常用命令提示符(cmd)指令

    在<Android基础之用Eclipse搭建Android开发环境和创建第一个Android项目>中我曾介绍过如何给Android SDK配置环境变量,现在它就有用武之地了,我们可以直接在 ...

  4. stm32智能小车之路之小车启动

           首先.安装完小车后最激动的还是想让他跑动,那么就開始吧.写个简单的程序測试下电机是否正常.打开keil软件新建一个project,详细简历keilproject不会的请百度.或者call ...

  5. Head First 设计模式 第4章工厂模式

    第4章 工厂模式 在介绍工厂模式之前,先让我们来看一个例子. 这里有一个Pizza类,用来生产pizza,并返回对象,具体代码如下: package com.ek.factory.simple; im ...

  6. struts2 内容记录

    多xml文件配置 在开发过程中我们经常会将每一张表(如:user表)的struts.xml文件分开,便于管理,故需要建立struts_user.xml文件管理请求等.那么需要用到inculde标签. ...

  7. python基础 &lpar;函数名&comma;闭包&comma;和迭代器&rpar;

    1.函数名作用 函数名本质上就是函数的内存地址或对象. 1.可以被引用 2.可以被当作容器类型的元素 3.可以当作函数的参数和返回值 4.如果记不住的话,那就记住一句话,就当普通变量用 2.闭包 什么 ...

  8. C&sol;S和B&sol;S的应用的区别

    C/S: C是指Client,S是指Server.C/S模式就是指客户端/服务器模式.通过它可以充分利用两端硬件环境的优势,将任务合理分配到Client端和Server端来实现,降低了系统的通讯开销. ...

  9. typescript handbook 学习笔记3

    概述 这是我学习typescript的笔记.写这个笔记的原因主要有2个,一个是熟悉相关的写法:另一个是理清其中一些晦涩的东西.供以后开发时参考,相信对其他人也有用. 学习typescript建议直接看 ...

  10. 用 CSS 实现元素垂直居中,有哪些好的方案?

    1.不知道自己高度和父容器高度的情况下, 利用绝对定位只需要以下三行: parentElement{ position:relative; } childElement{ position: abso ...