Faster RCNN中RPN理解(需要训练得到粗略proposals的)、附画图代码。biu~

fast+rpn=faster rcnn

Faster RCNN中RPN理解(需要训练得到粗略proposals的)、附画图代码。biu~_第1张图片

之前已经用过RPN,因为重心一直在修改我自己的网络结构上,这次重新涉及到此部分的优化(Guided Anchoring: 物体检测器也能自己学 Anchor),重新审视了一下RPN,记录一下。

如果把RPN看作一个黑盒子的话,最关心的问题是,输入和输出。RPN输入的是一张图片,输出输出一系列的矩形object proposals。RPN其实就像个“内嵌的”的网络似的,这也正是“两阶段物体检测”的体现之处,进行了初步、首阶段的学习。

RPN相当于是一个小的、粗糙的目标检测cnn模型,只不过它不具体识别/分类,它的识别/分类是:是前景还是背景;定位是:根据anchor使得检测的框靠近gt_box(回归:做怎样的逼近才能使得anchor逼近gt-boxes)。这时候结果不是很准确,只能大致确定框的位置,后面fast rcnn会进一步精确。这个RPN实际上就是一个attention机制,这也是两阶段法比一阶段法(YOLO等)准确的原因)

训练步骤:

1.将图片输入到VGG或resnet等的可共享的卷积层中,得到最后可共享的卷积层特征图FM(feature map)。

2.将FM送入RPN。(先3*3filter滑窗生成新的FM(intermediate layer),再送入两个1*1conv

    2.1在滑动窗口的每个像素点根据感受野都对应的原图上的一片区域,这片区域对目标的覆盖力、形状多样性可能没有那么好,故根据此区域的中心点(锚点)在原图设置9/15个矩形窗口(eg:3种长宽比*3种尺度),即锚框、anchor。至于这里为什么要在原图上,是因为最后求出来的锚框要跟原图的标定框最小梯度下降。

   2.2将卷积的结果和锚点分别输入到两个小的1*1的网络中reg(回归,求目标框的位置)和cls(分类,确定该框中是不是目标)

3.训练集标记好了每个框的位置,和reg输出的框的位置比较,用梯度下降来训练网络。

4.得到的proposals将会根据strides被部署在一幅尺寸为w×h的特征图上(映射),注意这个时候映射出的是一块区域了,因为原来是的根据感受野中心点扩的9个框,回归后某个、某几个框映射过去是一个区域。比如:

# anchors 尺寸  3种尺寸大小,3种比例。故一个锚点生成9个anchors
scales = [2,4,8]#根据这个生成9种尺寸,但面积都为4,16,64
# scales = [32,64,128,256,512]
ratios = [0.5, 1.0, 2.0]

注意:anchors是在原图上的。在FM上对点做信息抽取时候,相当于抽取该点在原图上对应的一堆anchors的信息(分类和回归信息)。FM一个点对应原图的感受野,围绕感受野中心做的一堆anchors的信息。也就是说特征图中的每个位置都与具有预定义的尺度和纵横比的k个anchors相关联。

Faster RCNN中RPN理解(需要训练得到粗略proposals的)、附画图代码。biu~_第2张图片

图片节选自https://www.bilibili.com/video/av21846607/?p=6(视频不建议看,建议看这个系列:https://www.bilibili.com/video/av29987414)

ROI Pooling : anchor 到FM上的ROI进行pooling

Region Proposal Networks(RPN)

(RPN是faster-Rcnn的一部分,faster-Rcnn论文原文自行找)

RPN网络把一个任意尺度的图片作为输入,输出一系列的矩形object proposals,每个object proposals都带一个objectness score。用一个全卷积网络来模拟这个过程,这一小节描述它。因为最终目标是与Fast R-CNN目标检测网络来共享计算,所以假设两个网有一系列相同的卷积层。研究了the Zeiler and Fergus model(ZF,它有5个可共享的卷积层,以及the Simonyan and Zisserman model(VGG-16,它有13个可共享的卷积层。

为了生成region proposals,在卷积的feature map上滑动一个小的网络,这个feature map 是特征提取层最后一个共享卷积层的输出。这个小网络需要输入卷积feature map的一个n*n窗口。每个滑动窗口都映射到一个低维特征(resnet+fpn中是256维,VGG是512维,后面跟一个ReLU激活函数)。这个特征被输入到两个并列全连接层中(一个box-regression层(reg),一个box-classification层(cls))。在这篇论文中使用了n=3,使输入图像上有效的接受域很大(ZF 171个像素,VGG 228个像素)。这个迷你网络在图3(左)的位置上进行了说明。注意,由于迷你网络以滑动窗口的方式运行,所以全连接层在所有空间位置共享。这个体系结构是用一个n*n的卷积层来实现的,后面是两个1*1的并列卷积层(分别是reg和cls)。

要保证下面三个layer的尺寸一致性(空间位置一致,有助于后面直接找到anchor),也就是:

假设:FM尺寸W,H,C,则intermediate layer:经过3*3*C→W,H,1,然后cls layer经过1*1*2k→W,H,2k;reg layer经过1*1*4k→W,H,4K。如图:

Faster RCNN中RPN理解(需要训练得到粗略proposals的)、附画图代码。biu~_第3张图片

 

Faster RCNN中RPN理解(需要训练得到粗略proposals的)、附画图代码。biu~_第4张图片

1.1Anchors(锚点)

在每个滑动窗口位置,同时预测多个region proposals,其中每个位置的最大可能建议的数量表示为k。所以reg层有4 k输出来编码k个box的坐标(可能是一个角的坐标(x,y)+width+height),cls层输出2 k的分数来估计每个proposal是object的概率或者不是的概率。这k个proposals是k个参考框的参数化,把这些proposals叫做Anchors(锚点)。锚点位于问题的滑动窗口中,并与比例和纵横比相关联。默认情况下,使用3个尺度和3个纵横比,在每个滑动位置上产生k=9个锚点。对于W *H大小的卷积特性图(通常为2,400),总共有W*H*k个锚点。

平移不变性的锚点

RPN的一个重要特性是是平移不变性,锚点本身和计算锚点的函数都是平移不变的。如果在图像中平移一个目标,那么proposal也会跟着平移,这时,同一个函数需要能够在任何位置都预测到这个proposal。我们的方法可以保证这种平移不变性。作为比较,the MultiBox method使用k聚类方法生成800个锚点,这不是平移不变的。因此,MultiBox并不保证当一个对象被平移式,会生成相同的proposal。

平移不变性也减少了模型的尺寸,当锚点数k=9时,因为MultiBox锚点不具有平移不变性,所有它要求一个(4+1)×800维的全连接输出层,而这篇论文的方法有一个(4+2)*9维的卷积输出层。因此,输出层的参数比MultiBox少两个数量级(原文有具体的数,感觉用处不大,没有具体翻译)。如果考虑到feature projection层,建议层仍然比MultiBox的参数少了一个数量级。这里的建议层有一个数量级的参数减少(使用GoogLeNet 的MultiBox有27百万,而使用VGG-16的RPN有2.4百万),因此在想PASCAL VOC这样的小数据集上有更少过拟合的风险。

多尺度锚点作为回归参考

  锚点设计提出了一种解决多个尺度(和纵横比)的新方案。如图所示,有两种流行的多尺度预测方法

Faster RCNN中RPN理解(需要训练得到粗略proposals的)、附画图代码。biu~_第5张图片

(a.建立了图像和特征图的金字塔,分类器在所有的尺度上运行。b. 具有多个尺寸/大小的过滤器金字塔在feature map上运行。c. 我们在回归函数中使用了参考框的金字塔。)

第一种方法是基于图像/特征金字塔,例如,在DPM和基于cnn方法的方法。这些图像在多个尺度上进行了调整,并且为每个尺度计算特征图(占用或深度卷积特性)(图1(a))。这种方法通常很有用,但很耗费时间。第二种方法是在feature map上使用多个尺度(和/或方面比率)的滑动窗口。例如,在DPM中,不同方面比率的模型分别使用不同的过滤大小(如5 7和7 5)进行单独训练。如果这种方法用于处理多个尺度,它可以被认为是一个过滤器金字塔(图1(b))。第二种方法通常是与第一种方法共同使用的

相比而言,基于锚点的方法建立在锚点金字塔上,这更节省成本。RPN对多个尺度和纵横比的锚点框进行了分类和回归。它只依赖于单一尺度的图像和feature map,并使用单一大小的过滤器(feature map上的滑动窗口)。实验展示了这个方案对处理多个尺度和大小的影响(表8)。

Faster RCNN中RPN理解(需要训练得到粗略proposals的)、附画图代码。biu~_第6张图片

由于这种多尺度设计是基于锚点的,可以简单地使用在单尺度图像上计算的卷积特性,也可以使用Fast R-CNN检测器。多尺度锚点的设计是共享特性的关键组成,使处理尺度时不需要额外的成本。

1.2损失函数

为了训练RPNs,(既然训练就要样本,小网络已经有了,那么接下来)

(1)样本制作:

将一个二进制类标签(是否是object)分配给每个锚点。会给这两种锚点设置成正标签:1)跟真值框的交并比最高的。2)跟真值框的交并比大于0.7的。因此,一个真值框可以对应多个正标签的锚点。通常第二个条件足以确定正样本,我们还用第一种情况的原因是,有的时候第二种情况找不到正样本。如果一个锚点跟所有真值框的交并比小于0.3,那马我们就把它设为负样本。正负样本之间的这些样本对训练没有贡献。

(2)损失函数:

在fast R-CNN的多任务损失之后最小化一个目标函数,一张图片的损失函数定义为:

Faster RCNN中RPN理解(需要训练得到粗略proposals的)、附画图代码。biu~_第7张图片

i 是每个小批量中锚点的序号,p是锚点i是目标的概率,p*是标签(只能是0或1),t是预测框的4个参数,t*是标定框的参数;L_{cls}是分类损失函数,L_{reg}是回归损失函数;p_{i}^{*}L_{reg}表示回归只对正样本进行(负样本p_{i}^{*}=0)。cls和reg分别输出p^{_{i}}t^{_{i}}

这两部分由N^{_{cls}}(小批量的大小决定,这里是~2000个proposals,只选256个进行训练)和Nreg(锚点位置数量决定,这里是2400)进行规范化,并通过一个平衡参数λ进行加权。默认情况下,设置λ=10,因此cls和reg部分的权重大致相同。作者通过实验证明,结果对在大范围内λ的值不敏感。作者还注意到,上面的标准化是不需要的,可以被简化。λ=9.375完全相同。

对于边界框回归,采用了以下4个坐标的参数化:

 x,y,w,h表示框的中心坐标以及它的宽度和高度。变量x、x{_{a}}和x*分别代表预测的框、锚点框和真值框(y、w、h类似)。这可以认为是锚点框向附近真值框回归。是一种微调的感觉。

用与以前基于ROI(感兴趣的区域)方法不同的方法实现了有界的回归。原来的方法在从任意大小的RoIs中集合的特性上执行有界的回归,并且所有区域大小都共享回归权重。在作者的公式中,用于回归的特性在feature maps上具有相同的空间大小(3*3)。为了计算不同的尺寸,学习了一组k个有界的回归每一个回归器负责一个尺度和一个纵横比,而k个回归器不共享权重。因此,由于锚点的设计,仍然可以预测各种大小的框,即使这些特征是固定的大小/比例。

1.3 训练RPNs

RPN可以通过反向传播和随机梯度下降来端到端训练。遵循以图像为中心的采样策略,开始训练这个网络。每个小批次都来自同一张照片,照片包含许多正负锚点示例。对所有锚点的损失函数进行优化是可行的,但这将偏向于负样本,因为它们多。相反,在一个图像中随机抽取256个锚点来计算一个小批的损失函数,其中采样的正和负锚点的比例是1:1。如果一个图像中有少于128个正的样本,用负样本填充。

随机地初始化所有的新层,方法是用标准偏差0.01的零均值高斯分布来初始化权重。通常,所有其他层(共享卷积层)都是通过预先培训练一个用于ImageNet分类的模型来初始化的。对ZF网络的所有层进行调优,并对VGG网络的conv3_1层进行调优,以节省内存。对60 k小批量的学习速率为0.001,在PASCAL VOC数据集上的下一个20 k小批量的学习速率是0.0001。使用0.9的步长和0.0005的重量衰减。用Caffe实现的。

初步会根据概率筛选一部分,余下得分高、高度重叠的proposals,再用nms进行循环抑制。

参考:

Faster RCNN中RPN理解(需要训练得到粗略proposals的)、附画图代码。biu~_第8张图片

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches

# F_X,F_Y = np.meshgrid(f_x,f_y)
# print(F_X,'\n',F_Y)

# scales,ratios = np.meshgrid(scales,ratios)
# print(scales.flatten(),'\n',ratios.flatten())

def anchor_gen(size_x,size_y,rpn_stride,scales,ratios):
    ## 从坐标向量返回坐标矩阵,shape:(len(a),len(b))。 可以debug看下怎么排的
    # len:5,3 →shape: (3, 5)  (3, 5) 把scales的值按行排列(横着排),共len(ratios)行;ratios按列排列,共len(scales)列
    scales, ratios = np.meshgrid(scales, ratios)
    scales, ratios = scales.flatten(),ratios.flatten()#len(scales)*len(ratios);len(ratios)*len(scales)
    # 根据尺寸、比例生成的anchors框的尺寸。sqrt是因为这里是算边的。scales是面积
    scalesX = scales / np.sqrt(ratios)
    scalesY = scales * np.sqrt(ratios)
    print(scalesX.shape,'\n',scalesY.shape)# 用他俩对应位置组合就能组合出anchors
    # 映射到原图的坐标点即锚点,再组合
    shiftX = np.arange(0,size_x) * rpn_stride
    shiftY = np.arange(0,size_y) * rpn_stride
    print('shiftX,shiftY',shiftX.shape,shiftY.shape)  # (40,) (24,)
    # 锚点们 anchors
    shiftX,shiftY = np.meshgrid(shiftX,shiftY)        #
    print('shiftX,shiftY',shiftX.shape,shiftY.shape)  #(24, 40) (24, 40)
    # 锚点+尺寸+比例 生成的anchors
    centerX,anchorX = np.meshgrid(shiftX,scalesX)# (24, 40);15
    centerY,anchorY = np.meshgrid(shiftY,scalesY)# : (15, 960)
    print('centerX.shape:',centerX.shape,'centerY.shape:',centerY.shape,"total num of anchors:",centerX.shape[0]*centerX.shape[1]) #(16, 16)
    # 中心点和尺寸融合
    anchor_center = np.stack([centerY,centerX],axis=2).reshape(-1,2)
    anchor_size = np.stack([anchorY,anchorX],axis=2).reshape(-1,2)
    # 左上顶点、右上顶点
    boxes = np.concatenate([anchor_center-0.5*anchor_size,anchor_center+0.5*anchor_size],axis=1)
    return boxes


if __name__ == "__main__":
    img = np.ones((128,128,3))

    #共享FM的尺寸,影响anchors总个数。scales和ratios影响FM上一个点代表的anchors个数
    size_x = 16
    size_y = 16
    # 缩放倍数 8
    rpn_stride = img.shape[0]//size_x  #/mask第四层是32,(1333, 800)。image_size = 16 * 8
    # anchors 尺寸  5种尺寸大小,3种比例。故一个锚点生成15个anchors
    #根据这个生成9种尺寸,但面积为16,64,128...
    scales = [4, 8, 16, 32,64]
    ratios = [0.5, 1.0, 2.0]

    f_x = np.arange(size_x) * rpn_stride
    f_y = np.arange(size_y) * rpn_stride
    anchors = anchor_gen(size_x,size_y,rpn_stride,scales,ratios)#: (14400, 4)
    plt.figure(figsize=(10,10))
    plt.imshow(img)
    Axs = plt.gca()# get current axs得到当前的接口
    for i in range(anchors.shape[0]):
        box = anchors[i]
        rec = patches.Rectangle((box[0],box[1]),box[2]-box[0],box[3]-box[1],edgecolor='r',facecolor="none")
        Axs.add_patch(rec)
    plt.show()

 

https://www.cnblogs.com/jiangnanyanyuchen/p/9433791.html

https://www.cnblogs.com/hellcat/p/9811301.html

你可能感兴趣的:(姿态估计逐步)