【youcans动手学模型】目标检测之 SPPNet 模型

2023-07-25 09:24:28

欢迎关注『youcans动手学模型』系列
本专栏内容和资源同步到 GitHub/youcans



本文用 PyTorch 实现 SPPNet 网络模型。


1. SPPNet 卷积神经网络模型

何恺明、张祥雨、任少卿、孙剑在 ECCV 2014 发表的论文【用于视觉识别的深度卷积网络的空间金字塔池化】,提出了 SPPNet 模型。

Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun, Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition

【下载地址】: https://arxiv.org/abs/1406.4729
【GitHub地址】: [OverFeat_pytorch] https://github.com/BeopGyu/OverFeat_pytorch

http://research.microsoft.com/en-us/um/people/kahe/

在这里插入图片描述


1.1 论文摘要

现有的深度卷积神经网络(CNNs)需要固定大小的输入图像。这一人为的要求可能会降低任意大小/比例的图像或子图像的识别精度。

我们提出一种 SPP Net 网络结构,采用另一种池化策略 “空间金字塔池化”,对任意大小/比例的输入图像,都可以生成固定长度的表示。金字塔池化对对象变形也具有鲁棒性。基于这些优势,SPP Net 通常可以改进所有基于卷积神经网络的图像分类方法。

在 ImageNet 2012 数据集上,使用 SPP Net 可以提高了各种卷积神经网络架构的准确性。在 Pascal VOC 2007 和 Caltech101 数据集上,SPP Net 使用单个完整图像表示,无需微调即可实现最先进的分类结果。

对于目标检测任务,使用 SPP Net 只需要从整个图像中计算一次特征图,就可以以任意区域(子图像)为输入,生成固定大小的输出特征图表示,用于训练检测器。由于 SPP Net 可以避免重复计算卷积特征,它比 R-CNN 方法快 24~102倍,并在 Pascal VOC 2007 上实现了更好的或相当的精度。

在2014 年 ImageNet 视觉识别挑战赛(ILSVRC2014)中,SPP Net 在目标检测任务获得了第二名,在图像分类任务获得了第三名。


1.2 技术背景

最近,基于深度网络的方法在图像分类、对象检测、许多其他识别任务甚至非识别任务方面取得很大进展。然而,在卷积神经网络的训练和测试中存在一个问题:卷积神经网络需要固定的输入图像大小(如224*224),这限制了输入图像的纵横比和规模。

固定大小的输入图像带来两个问题:(1)很多场景所得到图像并不是固定大小的,(2)如果对图片进行切割,可能会丢失到重要信息。

当应用于任意大小的图像时,当前的方法大多通过裁剪或缩放将输入图像拟合为固定大小。但是裁剪的区域可能不包含整个对象,而缩放的结果可能会导致几何失真,从而影响识别的准确性。此外,当对象比例变化时,预定义的比例可能不合适。总之,要求固定大小的输入图像,忽略了实际图像的尺度问题。

为什么卷积神经网络需要固定的输入大小呢?卷积神经网络由卷积层和全连接层两部分组成。卷积层以滑动窗口的方式操作,并不需要输入图像具有固定大小,并且可以生成任何大小的输出特征图。但是,全连接层要求具有固定尺寸的输入向量。因此,卷积神经网络固定尺寸的约束是来自全连接层,这些层存在于网络模型的末段。

本文引入了一个空间金字塔池化层(Spatial Pyramid Pooling, SPP)来处理卷积神经网络的固定大小约束。具体来说,我们在最后一个卷积层的顶部增加了一个 SPP 层。SPP 层生成固定大小的输出,然后将其输出到全连接层(或其它分类器)。换句话说,我们在卷积层和全连接层之间通过 SPP 执行信息“聚合”,而不是在开始就对图像进行裁剪或缩放。

在这里插入图片描述

作为词袋模型(BoW)的扩展,空间金字塔池化(通常称为 空间金字塔匹配或 SPM),是计算机视觉中最成功的方法之一。它将图像划分为从精细到粗糙的层次,并在其中聚合局部特征。在卷积神经网络流行之前,SPP 一直是分类和检测系统中的关键组成部分,但是在卷积神经网络中尚未得到应用。

SPP Net 对于深度卷积神经网络具有几个显著特性:

(1)无论输入图像的大小如何,SPP Net 都能够生成固定长度的输出,这是以前的卷积和池化层所不能实现的。

(2) SPP 使用多级空间容器,而滑动窗口池化仅使用单个窗口大小。多级池化对于目标变形具有更好的鲁棒性。

(3) 由于输入尺度的灵活性,SPP Net 可以汇集在可变尺度上提取的特征。

SPP Net 不仅可以从任意大小的图像/窗口中生成用于测试的表示,还允许我们在训练过程中提供不同大小或比例的图像。用可变大小的图像进行训练增加了尺度不变性并减少了过度拟合。

我们开发了一种简单的多尺寸训练方法。在每个epoch中,我们用给定的输入大小训练网络,在下一个epoch切换到另一个输入大小。实验表明,这种多尺度训练可以收敛,而且测试精度更好。

SPP网络在目标检测方面也显示出强大的能力。R-CNN 通过深度卷积网络提取候选窗口的特征,在目标检测任务中的性能领先。但 RCNN 对每张图像需要重复计算数千个候选区域的特征图,计算非常耗时。

很多研究都尝试了在特征图(而不是图像区域)上训练/运行检测器在本文中,我们只对整个图像进行一次卷积处理(无论窗口的数量如何),然后通过 SPP 在特征图上提取特征。SPP Net 通过 SPP 在任意窗口大小的灵活性,获得了卓越的准确性和效率。基于 R-CNN 的处理流程,SPP Net 计算特征比 R-CNN 快 24~102 倍,同时具有更好的精度。

总结:

与无 SPP 的卷积神经网络相比,SPP Net 可以增强更深、更大的各种网络。

通过不同大小/定位窗口的特征图,进行多视图测试,可以提高分类精度。


1.3 空间金字塔池化

卷积层可以接受任意大小的输入。卷积层使用滑动滤波器,其输出与输入具有大致相同的纵横比。这些输出被称为特征图,它们不仅涉及响应的强度,还涉及空间位置。

卷积层可以接受任意的输入大小,产生的输出特征图的大小也是可变的。而分类器(SVM/softmax)或全连接层需要固定长度的输入向量。

空间金字塔池化方法,通过局部的空间窗口池化来生成固定长度的输出向量,并且可以保留空间信息。只要池化的空间窗口的大小与图像大小成比例,则无论图像大小如何,都可以得到固定大小或长度的输出向量。

在这里插入图片描述

因此,为了对任意大小的图像采用卷积神经网络,我们将原有网络中最后一个池化层替换为空间金字塔池化层。

(1)通过空间金字塔池化,输入图像可以是任何大小。这不仅允许任意的纵横比,而且允许任意的比例。

通过选择不同尺寸的卷积核(w,h)和不同的步长(stride),我们可以将任意尺寸大小和比例的输入图像,调整为任何比例的输出图像,或调整为固定长度的矢量 输出,以便应用于分类器。

(2)尺度在传统图像处理方法中起着重要作用,尺度对深度网络的准确性也很重要。通过不同尺度和参数的卷积核,网络可以从图像中提取不同尺度的特征。

选择不同尺寸的卷积核(kw,kh)和不同的步长(stride),构造 3 级(3*3, 2*2, 1*1)金字塔池化层。

如图所示,网络的最后一个卷积层的输出特征维度为 (h,w,ch),作为 SPP 层的输入。金字塔的最底层,设置卷积核大小等于图像的高宽,池化输出维度为 1*1,这实际上是“全局池化”操作;金字塔的上一层,卷积核大小是图像高宽的 1/2,池化输出维度为 2*2;金字塔的顶层,卷积核大小是图像高宽的 1/3 或1/4,池化输出维度为 3*3 或 4*4。将这 3级池化输出都展平为一维后,拼接后得到长度为 1 + 2 ∗ 2 + 4 ∗ 4 = 21 1+2*2+4*4=21 1+22+44=21 的一维向量。对于 ch=256,拼接后一维向量的总长度为 21 ∗ 256 = 5376 21*256=5376 21256=5376。(注:第3层输出维度,论文图中为 4*4,示例中为 3*3,都是由用户选择确定的)


1.4 目标检测

在目标检测任务中,R-CNN 首先使用选择性搜索从图像中选出 2000 个候选窗口,然后将每个窗口中的图像区域变形到固定大小 227×227 作为输入图像,使用卷积神经网络提取每个窗口的特征,最后用 SVM 分类器进行特征检测。R-CNN 在一张图像的 2000个窗口上反复运行卷积网络,耗时很大,而在测试阶段的特征提取是主要瓶颈。

我们对整张图像运行卷积网络获得特征图,然后对图像中的每个候选窗口,在特征图的对应窗口上应用空间金字塔池化,形成这个窗口的一个固定长度表示 。 因为只应用一次卷积网络,我们的方法快得多。

在这里插入图片描述


例如,在每个候选窗口中,我们使用 4 级空间金字塔(1*1,2*2,3*3,6*6)来汇集特征,将这 4级输出都展平为一维后,拼接后得到长度为 1 + 2 ∗ 2 + 3 ∗ 3 + 6 ∗ 6 = 50 1+2*2+3*3+6*6=50 1+22+33+66=50 的一维向量。对于 ch=256,得到一维向量的总长度为 256 ∗ 50 = 12800 256*50=12800 25650=12800


1.5 总结

(1)SPP-Net 允许不同大小的输入图片,可以获得相同尺寸的特征向量。

(2)SPP-Net 在特征检测任务中只对整张图像运行一次卷积网络,速度比 RCNN 快得多。

(3)仍然使用选择性搜索(SS)来产生候选区域,这个过程的速度比较慢。


2. 在 PyTorch 中定义 SPP 模型类

2.1 SPP 层的参数计算

k h = ⌈ h i n / n ⌉ = c e i l ( h i n / n ) s h = ⌈ h i n / n ⌉ = c e i l ( h i n / n ) p h = ⌊ ( k h ∗ n − h i n + 1 ) / 2 ⌋ = f l o o r ( ( k h ∗ n − h i n + 1 ) / 2 ) h n e w = h i n + 2 ∗ p h k w = ⌈ w i n / n ⌉ = c e i l ( w i n / n ) s w = ⌈ w i n / n ⌉ = c e i l ( w i n / n ) p w = ⌊ ( k w ∗ n − w i n + 1 ) / 2 ⌋ = f l o o r ( ( k w ∗ n − w i n + 1 ) / 2 ) w n e w = w i n + 2 ∗ p w \begin{matrix} k_h &= &\lceil {h_{in}}/{n} \rceil &= &ceil( {h_{in}}/{n})\\ s_h &= &\lceil {h_{in}}/{n} \rceil &= &ceil( {h_{in}}/{n})\\ p_h &= &\lfloor (k_h*n-h_{in}+1)/2 \rfloor &= &floor( (k_h*n-h_{in}+1)/2)\\ h_{new} &= &h_{in} + 2*p_h\\ \\ k_w &= &\lceil {w_{in}}/{n} \rceil &= &ceil( {w_{in}}/{n})\\ s_w &= &\lceil {w_{in}}/{n} \rceil &= &ceil( {w_{in}}/{n})\\ p_w &= &\lfloor (k_w*n-w_{in}+1)/2 \rfloor &= &floor( (k_w*n-w_{in}+1)/2)\\ w_{new} &= &w_{in} + 2*p_w \end{matrix} khshphhnewkwswpwwnew========hin/nhin/n⌊(khnhin+1)/2hin+2phwin/nwin/n⌊(kwnwin+1)/2win+2pw======ceil(hin/n)ceil(hin/n)floor((khnhin+1)/2)ceil(win/n)ceil(win/n)floor((kwnwin+1)/2)

其中, k h , k w k_h, k_w kh,kw 为卷积核的高度、宽度, s h , s w s_h, s_w sh,sw 为高度、宽度方向的步长, p h , p w p_h,p_w ph,pw 为边界填充值(两侧乘2)。

注意卷积核和步长的计算公式都使用 ceil() 向上取整,而 padding 使用 floor() 向下取整。


2.2 定义 SPP Layer

构建 SPP 层(空间金字塔池化层)的例程如下。

# 构建 SPP 层(空间金字塔池化层)
class SPPlayer(nn.Module):
    def __init__(self, num_levels, pool_type='max_pool'):
        super(SPPlayer, self).__init__()
        self.num_levels = num_levels
        self.pool_type = pool_type

    def forward(self, x):
        b, c, h, w = x.size()
        xSPP = torch.zeros([b, 0]).cuda()
        for i in range(self.num_levels):
            level = i + 1
            kh, kw = math.ceil(h/level), math.ceil(w/level)
            sh, sw = math.ceil(h/level), math.ceil(w/level)
            ph, pw = math.floor((kh*level-h+1)/2), math.floor((kw*level-w+1)/2)

            # 最大池化
            tensor = nn.functional.max_pool2d(x, kernel_size=(kh,kw), stride=(sh,sw), padding=(ph,pw))
            # 平均池化
            # tensor = nn.functional.avg_pool2d(x, kernel_size=(kh,kw), stride=(sh,sw), padding=(ph,pw))
            # 展平为一维后拼接
            xSPP = torch.cat((xSPP, tensor.view(b, -1)), 1)  # (1+2*2+3*3)*c = 14*c
        return xSPP

2.3 SPPnet 模型类

参考 AlexNet 模型,使用 SPP Layer 构造 SPPnet 模型如下。

class SPPnet(nn.Module):
    # Expected input size is 64x64
    def __init__(self, spp_level=3, num_classes=10):
        super(SPPnet, self).__init__()
        self.spp_level = spp_level
        self.num_grids = 0
        for i in range(spp_level):
            self.num_grids += (i+1)**2  # 1*1, 2*2, 3*3

        self.features = nn.Sequential(OrderedDict([
            ('conv1', nn.Conv2d(3, 128, 3)),
            ('relu1', nn.ReLU()),
            ('pool1', nn.MaxPool2d(2)),
            ('conv2', nn.Conv2d(128, 128, 3)),
            ('relu2', nn.ReLU()),
            ('pool2', nn.MaxPool2d(2)),
            ('conv3', nn.Conv2d(128, 128, 3)),
            ('relu3', nn.ReLU()),
            ('pool3', nn.MaxPool2d(2)),
            ('conv4', nn.Conv2d(128, 128, 3)),
            ('relu4', nn.ReLU())
        ]))

        self.spp_layer = SPPlayer(spp_level, 'max_pool')

        self.classifier = nn.Sequential(OrderedDict([
            # ('dropout', nn.Dropout(p=0.2, inplace=True)),
            ('fc1', nn.Linear(self.num_grids*128, 1024)),
            ('fc1_relu', nn.ReLU()),
            ('fc2', nn.Linear(1024, num_classes)),
        ]))

    def forward(self, x):
        x = self.features(x)
        x = self.spp_layer(x)
        x = self.classifier(x)
        return x

模型的结构如下。

----------------------------------------------------------------
        Layer (type)               Output Shape         Param #
================================================================
            Conv2d-1          [-1, 128, 62, 62]           3,584
              ReLU-2          [-1, 128, 62, 62]               0
         MaxPool2d-3          [-1, 128, 31, 31]               0
            Conv2d-4          [-1, 128, 29, 29]         147,584
              ReLU-5          [-1, 128, 29, 29]               0
         MaxPool2d-6          [-1, 128, 14, 14]               0
            Conv2d-7          [-1, 128, 12, 12]         147,584
              ReLU-8          [-1, 128, 12, 12]               0
         MaxPool2d-9            [-1, 128, 6, 6]               0
           Conv2d-10            [-1, 128, 4, 4]         147,584
             ReLU-11            [-1, 128, 4, 4]               0
         SPPlayer-12                 [-1, 1792]               0
           Linear-13                 [-1, 1024]       1,836,032
             ReLU-14                 [-1, 1024]               0
           Linear-15                   [-1, 10]          10,250
================================================================
Total params: 2,292,618
Trainable params: 2,292,618
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.05
Forward/backward pass size (MB): 10.66
Params size (MB): 8.75
Estimated Total Size (MB): 19.45
----------------------------------------------------------------

3. SPPnet 模型的训练

[SPP-net_pytorch/ GitHub]
https://github.com/zjZSTU/SPP-net/tree/master/py

import torch
import torch.nn as nn


class ZFNet(nn.Module):

    def __init__(self, num_classes=1000):
        super(ZFNet, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(64, 192, kernel_size=5, stride=2, padding=2),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
            nn.Conv2d(192, 384, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(384, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(256, 256, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.MaxPool2d(kernel_size=3, stride=2),
        )
        self.avgpool = nn.AdaptiveAvgPool2d((6, 6))
        self.classifier = nn.Sequential(
            nn.Dropout(),
            nn.Linear(256 * 6 * 6, 4096),
            nn.ReLU(inplace=True),
            nn.Dropout(),
            nn.Linear(4096, 4096),
            nn.ReLU(inplace=True),
            nn.Linear(4096, num_classes),
        )

    def forward(self, x):
        x = self.features(x)
        x = self.avgpool(x)
        x = torch.flatten(x, 1)
        x = self.classifier(x)
        return x


【本节完】


版权声明:
欢迎关注『youcans动手学模型』系列
转发请注明原文链接:
【youcans动手学模型】目标检测之 SPPNet 模型
Copyright 2023 youcans, XUPT
Crated:2023-07-25


更多推荐

初始化列表

目录必须在初始化列表初始化的条件:explicit多参数强制类型转换静态成员​编辑对于静态成员变量需要在构造函数里初始化吗?静态成员函数:题目1:求1+2+3+...+n_牛客题霸_牛客网(nowcoder.com)要求类对象只能在栈上:必须在初始化列表初始化的条件:1:const修饰的成员变量(只有一次初始化的机会,

VirtualBox安装RockyLinux并使用ssh访问

文章目录1前言2安装RockyLinux2.1新建虚拟机2.2设置虚拟机内存和CPU数量2.3设置虚拟机硬盘大小2.4完成设置2.5启动虚拟机2.6RockyLinux的安装2.6.1直接回车2.6.2等待check完成2.6.3设置语言2.6.4设置最小化安装2.6.5去除分区设置的感叹号2.6.7设置root账号的

什么是生成对抗网络 (GAN)?

什么是生成对抗网络(GAN)?钦吉兹·赛义德贝利·一、说明GAN(GenerativeAdversarialNetwork)网络是一种深度学习模型,由两个神经网络——生成器和判别器组成。生成器负责生成虚假的数据,而判别器负责判断数据的真实性。它们之间通过对抗学习的方式相互影响和学习,最终生成器能够生成更加真实的数据,而

Compositional Minimax Optimization学习之路

梯度最优化理论最优化基础---基本概念:凸优化、梯度、Jacobi矩阵、Hessian矩阵_哔哩哔哩_bilibili从图像来看:存在两点连线上的点不在集合内定义ax1+(1-a)x2其实就是两点连线上的点可用与函数围成的面积和与坐标轴围成的面积角度理解凸函数凸优化在定义域和F(X)都是凸集的问题(凸凸问题),就是凸优

如何使用Python和Numpy实现简单的2D FDTD仿真:详细指南与完整代码示例

第一部分:引言及FDTD简介引言:计算机模拟在许多科学和工程领域中都得到了广泛应用。在电磁学领域,有许多不同的数值方法用于模拟波的传播和散射。其中最为知名和广泛使用的一种方法是有限差分时域方法(FiniteDifferenceTimeDomain,FDTD)。在这篇文章中,我们将使用Python和Numpy库为你提供一

ES-索引管理

前言数据类型​搜索引擎是对数据的检索,所以我们先从生活中的数据说起。我们生活中的数据总体分为两种:结构化数据非结构化数据结构化数据:也称作行数据,是由二维表结构来逻辑表达和实现的数据,严格地遵循数据格式与长度规范,主要通过关系型数据库进行存储和管理。指具有固定格式或有限长度的数据,如数据库,元数据等。非结构化数据:又可

ArcGIS Maps SDK for JavaScript系列之四:添加自定义底图

目录Basemap类介绍Basemap类的常用属性Basemap类的常用方法使用Basemap添加自定义底图引用Basemap引用切片图层创建一个新的Basemap对象将自定义图层应用到地图视图中引入并创建Camera对象引入并创建SceneView对象Basemap类介绍Basemap类是ArcGISMapsSDKf

TypeScript算法题实战——剑指 Offer篇(5)

目录一、平衡二叉树1.1、题目描述1.2、题解二、数组中数字出现的次数2.1、题目描述2.2、题解三、数组中数字出现的次数II3.1、题目描述3.2、题解四、和为s的两个数字4.1、题目描述4.2、题解五、和为s的连续正数序列5.1、题目描述5.2、题解六、翻转单词顺序6.1、题目描述6.2、题解七、滑动窗口的最大值7

竞赛 基于机器视觉的银行卡识别系统 - opencv python

1前言🔥优质竞赛项目系列,今天要分享的是基于深度学习的银行卡识别算法设计该项目较为新颖,适合作为竞赛课题方向,学长非常推荐!🧿更多资料,项目分享:https://gitee.com/dancheng-senior/postgraduate2算法设计流程银行卡卡号识别技术原理是先对银行卡图像定位,保障获取图像绝对位置

OpenCV(四十六):特征点匹配

1.特征点匹配的定义特征点匹配是一种在两幅图像中寻找相互对应的特征点,并建立它们之间的对应关系的过程。具体而言,首先通过特征检测算法在两幅图像中寻找相互对应的特征点,然后,对于每个特征点,通过描述子提取算法计算其描述子,最后,使用匹配算法对两组特征点的描述子进行比较,以找到相互匹配的特征点对。2.DMatch()用于表

XREAL 联合创始人吴克艰谈AR:下一代计算平台及其关键技术

//编者按:一种行业观点是,AR或是未来十年、三十年的革命性技术,是下一代计算平台。近半个世纪,我们总能听到苹果在AR行业的创新动作,开辟了新的硬件范式。AR/VR行业为苹果不断欢呼的同时,激发了人们的好奇心——究竟,人类在戴上AR眼镜的那一瞬间,感知与交互从二维平面延伸到三维空间,科幻片场景触手可及之时,和世界的交互

热文推荐