文章目录
- 1.RCNN
- 2.SPPNet
- 3.Fast RCNN
- 4.Faster RCNN
- 5.Feature Pyramid Networks(FPN)
- 6.其他的变种
参考综述文章:基于深度学习的目标检测算法综述(一)基于深度学习的目标检测算法综述(二)基于深度学习的目标检测算法综述(三)
优秀的GitHub仓库:
deep learning object detectionAwesome Object Detection
比较全面的整理:
我这两年的目标检测最全目标检测相关资料整理我这两年收藏的目标检测好文分享
1.RCNN
RCNN的思想:
- 首先通过选择性搜索提取一组对象建议(对象候选框)。
- 然后将每个提案重新调整成一个固定大小的图像,并输入到一个在 ImageNet 上训练得到的CNN模型来提取特征。
- 最后,利用线性SVM分类器对每个区域内的目标进行预测,识别目标类别。
2.SPPNet
SPPNet的结构如下图:
SPPNet的特点是在最后一个卷积层后,接入了金字塔池化层,使用这种方式,可以让网络输入任意的图片,而且还会生成固定大小的输出。
金字塔池化:以下图为例,黑色图片代表卷积之后的特征图,接着我们以不同大小的块来提取特征,分别是4x4,2x2,1x1,将这三张网格放到下面这张特征图上,就可以得到16+4+1=21种不同的块(Spatial bins),我们从这21个块中,每个块提取出一个特征,这样刚好就是我们要提取的21维特征向量。这种以不同的大小格子的组合方式来池化的过程就是空间金字塔池化(SPP)
综上所述,SPPNet的训练过程如下:
- 首先通过选择性搜索,对待检测的图片进行搜索出2000个候选窗口。这一步和R-CNN一样。
- 特征提取阶段:把整张待检测的图片,输入CNN中,进行一次性特征提取,得到feature maps,然后在feature maps中找到各个候选框的区域,再对各个候选框采用金字塔空间池化,提取出固定长度的特征向量。因为SPP-Net只需要一次对整张图片进行特征提取,速度会大大提升。
- 最后一步和R-CNN一样,采用SVM算法进行特征向量分类识别。
空间金字塔池化层代码实现:
#coding=utf-8
import math
import torch
import torch.nn.functional as F
# 构建SPP层(空间金字塔池化层)
class SPPLayer(torch.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):
num, c, h, w = x.size() # num:样本数量 c:通道数 h:高 w:宽
for i in range(self.num_levels):
level = i+1
kernel_size = (math.ceil(h / level), math.ceil(w / level))
stride = (math.ceil(h / level), math.ceil(w / level))
pooling = (math.floor((kernel_size[0]*level-h+1)/2), math.floor((kernel_size[1]*level-w+1)/2))
# 选择池化方式
if self.pool_type == 'max_pool':
tensor = F.max_pool2d(x, kernel_size=kernel_size, stride=stride, padding=pooling).view(num, -1)
else:
tensor = F.avg_pool2d(x, kernel_size=kernel_size, stride=stride, padding=pooling).view(num, -1)
# 展开、拼接
if (i == 0):
x_flatten = tensor.view(num, -1)
else:
x_flatten = torch.cat((x_flatten, tensor.view(num, -1)), 1)
3.Fast RCNN
Fast RCNN总体结构;
Fast RCNN相比于RCNN的改进之处主要有三点:
- RoIPooling:对于每一个虚线窗口内的特征图,,SPP Layer采用多个尺度的池化层进行池化操作;而RoI pooling层则采用一种尺度的池化层进行下采样,将每个RoI区域的卷积特征分成4*4个bin,然后对每个bin内采用max pooling,这样就得到一共16维的特征向量。如下图所示:
- 多任务损失函数(Multi-task Loss):Fast R-CNN统一了类别输出任务和候选框回归任务,有两个损失函数:分类损失和回归损失。分类采用softmax代替SVM进行分类,共输出N(类别)+1(背景)类。softmax由于引入了类间竞争,所以分类效果优于SVM,SVM在R-CNN中用于二分类。回归损失输出的是4*N(类别),4表示的是(x,y,w,h分别表示候选框的中心坐标和宽、高)。
- SVD对全连接层进行分解:由于一张图像约产生2000个RoI,将近一半多的时间用在全连接层计算,为了提高运算速度,可以用SVD(奇异值分解)对全连接层进行变换来提高运算速度。一个大的矩阵可以近似分解为三个小矩阵的乘积,分解后的矩阵的元素数目远小于原始矩阵的元素数目,从而达到减少计算量的目的。通过对全连接层的权值矩阵进行SVD分解,使得处理一张图像的速度明显提升。
综上所述,Fast RCNN的主要流程如下:
- 首先提取候选区域:利用Selective Search算法在输入图像中提取出候选区域,并把这些候选区域按照空间位置关系映射到最后的卷积特征层;
- 区域归一化:对于卷积特征层上的每个候选区域进行RoI Pooling操作,得到固定维度的特征;
- 分类与回归:将提取到的特征输入全连接层,然后用Softmax进行分类,对候选区域的位置进行回归。
4.Faster RCNN
Faster RCNN的总体结构如下;
相比于Fast RCNN,Faster RCNN主要的改进有两点:
- 产生建议窗口的CNN和目标检测的CNN共享
- 使用RPN(Region Proposal Network)代替原来的Selective Search方法产生建议窗口;
RPN(Region Proposal Network):RPN可以理解为一种全卷积网络,最终目的是前景背景分类 + 框位置的回归。之前得到的特征图有两个应用,一个是和ROIPooling结合使用、另一个是进行一次3x3的卷积后,进行一个18通道的1x1卷积,还有一个36通道的1x1卷积。
在Faster RCNN中,Anchor先验框的数量就是9,所以两个1x1卷积的结果实际上也就是:
- 9 x 4的卷积 用于预测用于预测proposal的中心锚点对应的proposal的坐标x,y和宽高w,h
- 9 x 2的卷积 用于预测proposal每一个网格点上每一个预测框内部是否包含了物体
RPN层代码实现:
class RegionProposalNetwork(nn.Module):
def __init__(
self, in_channels=512, mid_channels=512, ratios=[0.5, 1, 2],
anchor_scales=[8, 16, 32], feat_stride=16,
mode = "training",
):
super(RegionProposalNetwork, self).__init__()
self.anchor_base = generate_anchor_base(anchor_scales=anchor_scales, ratios=ratios)
# 步长,压缩的倍数
self.feat_stride = feat_stride
self.proposal_layer = ProposalCreator(mode)
# 每一个网格上默认先验框的数量
n_anchor = self.anchor_base.shape[0]
# 先进行一个3x3的卷积
self.conv1 = nn.Conv2d(in_channels, mid_channels, 3, 1, 1)
# 分类预测先验框内部是否包含物体
self.score = nn.Conv2d(mid_channels, n_anchor * 2, 1, 1, 0)
# 回归预测对先验框进行调整
self.loc = nn.Conv2d(mid_channels, n_anchor * 4, 1, 1, 0)
normal_init(self.conv1, 0, 0.01)
normal_init(self.score, 0, 0.01)
normal_init(self.loc, 0, 0.01)
def forward(self, x, img_size, scale=1.):
n, _, hh, ww = x.shape
# 对共享特征层进行一个3x3的卷积
h = F.relu(self.conv1(x))
# 回归预测
rpn_locs = self.loc(h)
rpn_locs = rpn_locs.permute(0, 2, 3, 1).contiguous().view(n, -1, 4)
# 分类预测
rpn_scores = self.score(h)
rpn_scores = rpn_scores.permute(0, 2, 3, 1).contiguous().view(n, -1, 2)
# 进行softmax
rpn_softmax_scores = F.softmax(rpn_scores, dim=-1)
rpn_fg_scores = rpn_softmax_scores[:, :, 1].contiguous()
rpn_fg_scores = rpn_fg_scores.view(n, -1)
rpn_scores = rpn_scores.view(n, -1, 2)
综上所述,Fast RCNN的步骤如下:
- 输入测试图像;
- 将整张图片输入CNN,进行特征提取;
- 用RPN先生成一堆Anchor box,对其进行裁剪过滤后通过softmax判断anchors属于前景(foreground)或者后景(background),即是物体or不是物体,所以这是一个二分类;同时,另一分支bounding box regression修正anchor box,形成较精确的proposal(注:这里的较精确是相对于后面全连接层的再一次box regression而言)
- 把建议窗口映射到CNN的最后一层卷积feature map上;
- 通过RoI pooling层使每个RoI生成固定尺寸的feature map;
- 利用Softmax Loss(探测分类概率) 和Smooth L1 Loss(探测边框回归)对分类概率和边框回归(Bounding box regression)联合训练
5.Feature Pyramid Networks(FPN)
下图展示了4种利用特征的形式:
- 图像金字塔,即将图像做成不同的scale,然后不同scale的图像生成对应的不同scale的特征。这种方法的缺点在于增加了时间成本。有些算法会在测试时候采用图像金字塔。
- 单一特征图:像SPP net,Fast RCNN,Faster RCNN是采用这种方式,即仅采用网络最后一层的特征。
- 多尺度特征融合:像SSD(Single Shot Detector)采用这种多尺度特征融合的方式,没有上采样过程,即从网络不同层抽取不同尺度的特征做预测,这种方式不会增加额外的计算量。作者认为SSD算法中没有用到足够低层的特征(在SSD中,最低层的特征是VGG网络的conv4_3)
- 本文作者是采用这种方式,顶层特征通过上采样和低层特征做融合,而且每层都是独立预测的。
FPN算法大致结构如下图:一个自底向上的线路,一个自顶向下的线路,横向连接(lateral connection)。图中放大的区域就是横向连接,这里1*1的卷积核的主要作用是减少卷积核的个数,也就是减少了feature map的个数,并不改变feature map的尺寸大小。
综上所述,FPN算法的主要流程如下:
- 将预处理过的图片送入预训练的特征网络中(ResNet),生成下采样网络(左侧三层);
- 采用上采样(upsampling)方法,而横向连接则是将上采样的结果和之前下采样对应的结果进行融合(merge)。在融合之后还会再采用3*3的卷积核对每个融合结果进行卷积
- 分别在右面的三层上面分别进行RPN操作(即一个3x3的卷积后面连接两个1x1的卷积分别用来分类和回归);将获得的候选ROI分别输入到4、5、6层分别进行ROI池化操作(即固定为7x7的特征);
- 连接两个1024层的全连接网络层,分别连接对应的分类层和回归层;
6.其他的变种