• rcnn ->fast rcnn->faster rcnn物体检测论文


    faster rcnn中的rpn网络:

    特征可以看做一个尺度51*39的256通道图像,对于该图像的每一个位置,考虑9个可能的候选窗口:三种面积{1282,2562,5122}×三种比例{1:1,1:2,2:1}。这些候选窗口称为anchors。下图示出51*39个anchor中心,以及9种anchor示例。 

    讲解RPN网络比较好的博客:https://blog.csdn.net/sloanqin/article/details/51545125

    RCNN的缺点:

    1、图像wrap的问题

    2、计算量大

    FAST RCNN

    1、ROI pooling就是不同大小feature resize到一个固定大小

    2、Train end to end,multi task loss

    梯度如何从唯一大小回传到任意大小,如果有影响,相当于y=x,如果有影响就累加

    FASTER RCNN

    1、test的时候要做NMS,RPN

    2、Joint trainning

    首先rcnn,和fast rcnn都比较好理解

    fast rcnn其实就是roi+clc+bbox regression

    faster rcnn是rpn(region proposal network)+ fast rcnn,最后两个网络是迭代训练

    本弱找到一篇讲解faster rcnn比较好都博文,所以就速速搬过来了:

    缩进经过RCNN和Fast RCNN的积淀,Ross B. Girshick在2016年提出了新的Faster RCNN,在结构上,Faster RCN已经将特征抽取(feature extraction),proposal提取,bounding box regression(rect refine),classification都整合在了一个网络中,使得综合性能有较大提高,在检测速度方面尤为明显。

    图1 Faster CNN基本结构(来自原论文)

    缩进依作者看来,如图1,Faster RCNN其实可以分为4个主要内容:

    1. Conv layers。作为一种CNN网络目标检测方法,Faster RCNN首先使用一组基础的conv+relu+pooling层提取image的feature maps。该feature maps被共享用于后续RPN层和全连接层。
    2. Region Proposal Networks。RPN网络用于生成region proposals。该层通过softmax判断anchors属于foreground或者background,再利用bounding box regression修正anchors获得精确的proposals。
    3. Roi Pooling。该层收集输入的feature maps和proposals,综合这些信息后提取proposal feature maps,送入后续全连接层判定目标类别。
    4. Classification。利用proposal feature maps计算proposal的类别,同时再次bounding box regression获得检测框最终的精确位置。

    所以本文以上述4个内容作为切入点介绍Faster RCNN网络。

    缩进图2展示了Python版本中的VGG16模型中的faster_rcnn_test.pt的网络结构,可以清晰的看到该网络对于一副任意大小PxQ的图像,首先缩放至固定大小MxN,然后将MxN图像送入网络;而Conv layers中包含了13个conv层+13个relu层+4个pooling层;RPN网络首先经过3x3卷积,再分别生成foreground anchors与bounding box regression偏移量,然后计算出proposals;而Roi Pooling层则利用proposals从feature maps中提取proposal feature送入后续全连接和softmax网络作classification(即分类proposal到底是什么object)。

    path:${py-faster-rcnn-root}/models/pascal_voc/VGG16/faster_rcnn_alt_opt/faster_rcnn_test.pt

    图2 faster_rcnn_test.pt网络结构(放大网页看大图)

    1 Conv layers

    缩进Conv layers包含了conv,pooling,relu三种层。以python版本中的VGG16模型中的faster_rcnn_test.pt的网络结构为例,如图2,Conv layers部分共有13个conv层,13个relu层,4个pooling层。这里有一个非常容易被忽略但是又无比重要的信息,在Conv layers中:

    1. 所有的conv层都是:kernel_size=3,pad=1
    2. 所有的pooling层都是:kernel_size=2,stride=2

    为何重要?在Faster RCNN Conv layers中对所有的卷积都做了扩边处理(pad=1,即填充一圈0),导致原图变为(M+2)x(N+2)大小,再做3x3卷积后输出MxN。正是这种设置,导致Conv layers中的conv层不改变输入和输出矩阵大小。如图3:

    图3

    类似的是,Conv layers中的pooling层kernel_size=2,stride=2。这样每个经过pooling层的MxN矩阵,都会变为(M/2)*(N/2)大小。综上所述,在整个Conv layers中,conv和relu层不改变输入输出大小,只有pooling层使输出长宽都变为输入的1/2。

    缩进那么,一个MxN大小的矩阵经过Conv layers固定变为(M/16)x(N/16)!这样Conv layers生成的featuure map中都可以和原图对应起来。

    2 Region Proposal Networks(RPN)

    缩进经典的检测方法生成检测框都非常耗时,如OpenCV adaboost使用滑动窗口+图像金字塔生成检测框;或如RCNN使用SS(Selective Search)方法生成检测框。而Faster RCNN则抛弃了传统的滑动窗口和SS方法,直接使用RPN生成检测框,这也是Faster RCNN的巨大优势,能极大提升检测框的生成速度。

    图4 RPN网络结构

    上图4展示了RPN网络的具体结构。可以看到RPN网络实际分为2条线,上面一条通过softmax分类anchors获得foreground和background(检测目标是foreground),下面一条用于计算对于anchors的bounding box regression偏移量,以获得精确的proposal。而最后的Proposal层则负责综合foreground anchors和bounding box regression偏移量获取proposals,同时剔除太小和超出边界的proposals。其实整个网络到了Proposal Layer这里,就完成了相当于目标定位的功能。

    2.1 多通道图像卷积基础知识介绍

    缩进在介绍RPN前,还要多解释几句基础知识,已经懂的看官老爷跳过就好。
    1. 对于单通道图像+单卷积核做卷积,第一章中的图3已经展示了;
    2. 对于多通道图像+多卷积核做卷积,计算方式如下:
    图5 多通道+多卷积核做卷积示意图(摘自Theano教程)
    缩进如图5,输入图像layer m-1有4个通道,同时有2个卷积核w1和w2。对于卷积核w1,先在输入图像4个通道分别作卷积,再将4个通道结果加起来得到w1的卷积输出;卷积核w2类似。所以对于某个卷积层,无论输入图像有多少个通道,输出图像通道数总是等于卷积核数量!
    缩进对多通道图像做1x1卷积,其实就是将输入图像于每个通道乘以卷积系数后加在一起,即相当于把原图像中本来各个独立的通道“联通”在了一起。

    2.2 anchors

    缩进提到RPN网络,就不能不说anchors。所谓anchors,实际上就是一组由rpn/generate_anchors.py生成的矩形。直接运行作者demo中的generate_anchors.py可以得到以下输出:

    [python] view plain copy
     
    1. [[ -84.  -40.   99.   55.]  
    2.  [-176.  -88.  191.  103.]  
    3.  [-360. -184.  375.  199.]  
    4.  [ -56.  -56.   71.   71.]  
    5.  [-120. -120.  135.  135.]  
    6.  [-248. -248.  263.  263.]  
    7.  [ -36.  -80.   51.   95.]  
    8.  [ -80. -168.   95.  183.]  
    9.  [-168. -344.  183.  359.]]  

    其中每行的4个值[x1,y1,x2,y2]代表矩形左上和右下角点坐标。9个矩形共有3种形状,长宽比为大约为:height = [1:1, 1:2, 2:1]三种,如图6。实际上通过anchors就引入了检测中常用到的多尺度方法。

    图6 anchors示意图

    注:关于上面的anchors size,其实是根据检测图像设置的。在python demo中,会把任意大小的输入图像reshape成800x600(即图2中的M=800,N=600)。再回头来看anchors的大小,anchors中长宽1:2中最大为352x704,长宽2:1中最大736x384,基本是cover了800x600的各个尺度和形状。

    那么这9个anchors是做什么的呢?借用Faster RCNN论文中的原图,如图7,遍历Conv layers计算获得的feature maps,为每一个点都配备这9种anchors作为初始的检测框。这样做获得检测框很不准确,不用担心,后面还有2次bounding box regression可以修正检测框位置。

    图7

    解释一下上面这张图的数字。

    1. 在原文中使用的是ZF model中,其Conv Layers中最后的conv5层num_output=256,对应生成256张特征图,所以相当于feature map每个点都是256-d
    2. 在conv5之后,做了rpn_conv/3x3卷积且num_output=256,相当于每个点又融合了周围3x3的空间信息(猜测这样做也许更鲁棒?反正我没测试),同时256-d不变(如图4和图7中的红框)
    3. 假设一共有k个anchor,而每个anhcor要分foreground和background,所以cls=2k scores;而每个anchor都有[x, y, w, h]对应4个偏移量,所以reg=4k coordinates
    4. 补充一点,这k个anchor全部拿去训练太多了,训练程序会随机选取k中256个合适的anchors进行训练,这个256与上面的256-d不同(什么是合适的anchors下文5.1有解释)

    注意,在本文讲解中使用的VGG conv5 num_output=512,所以是512d,其他类似.....

    2.3 softmax判定foreground与background

    缩进一副MxN大小的矩阵送入Faster RCNN网络后,到RPN网络变为(M/16)x(N/16),不妨设W=M/16,H=N/16。在进入reshape与softmax之前,先做了1x1卷积,如图8:
    图8 RPN中判定fg/bg网络结构
    该1x1卷积的caffe prototxt定义如下:
    [cpp] view plain copy
     
    1. layer {  
    2.   name: "rpn_cls_score"  
    3.   type: "Convolution"  
    4.   bottom: "rpn/output"  
    5.   top: "rpn_cls_score"  
    6.   convolution_param {  
    7.     num_output: 18   # 2(bg/fg) * 9(anchors)  
    8.     kernel_size: 1 pad: 0 stride: 1  
    9.   }  
    10. }  
    可以看到其num_output=18,也就是经过该卷积的输出图像为WxHx18大小(注意第二章开头提到的卷积计算方式)。这也就刚好对应了feature maps每一个点都有9个anchors,同时每个anchors又有可能是foreground和background,所有这些信息都保存WxHx(9x2)大小的矩阵。为何这样做?后面接softmax分类获得foreground anchors,也就相当于初步提取了检测目标候选区域box(一般认为目标在foreground anchors中)。
    缩进那么为何要在softmax前后都接一个reshape layer?其实只是为了便于softmax分类,至于具体原因这就要从caffe的实现形式说起了。在caffe基本数据结构blob中以如下形式保存数据:
    blob=[batch_size, channel,height,width]
    对应至上面的保存bg/fg anchors的矩阵,其在caffe blob中的存储形式为[1, 2*9, H, W]。而在softmax分类时需要进行fg/bg二分类,所以reshape layer会将其变为[1, 2, 9*H, W]大小,即单独“腾空”出来一个维度以便softmax分类,之后再reshape回复原状。贴一段caffe softmax_loss_layer.cpp的reshape函数的解释,非常精辟:
    [cpp] view plain copy
     
    1. "Number of labels must match number of predictions; "  
    2. "e.g., if softmax axis == 1 and prediction shape is (N, C, H, W), "  
    3. "label count (number of labels) must be N*H*W, "  
    4. "with integer values in {0, 1, ..., C-1}.";  
    综上所述,RPN网络中利用anchors和softmax初步提取出foreground anchors作为候选区域。

    2.4 bounding box regression原理

    缩进介绍bounding box regression数学模型及原理。如图9所示绿色框为飞机的Ground Truth(GT),红色为提取的foreground anchors,那么即便红色的框被分类器识别为飞机,但是由于红色的框定位不准,这张图相当于没有正确的检测出飞机。所以我们希望采用一种方法对红色的框进行微调,使得foreground anchors和GT更加接近。

    图9

    缩进对于窗口一般使用四维向量(x, y, w, h)表示,分别表示窗口的中心点坐标和宽高。对于图 10,红色的框A代表原始的Foreground Anchors,绿色的框G代表目标的GT,我们的目标是寻找一种关系,使得输入原始的anchor A经过映射得到一个跟真实窗口G更接近的回归窗口G',即:给定A=(Ax, Ay, Aw, Ah),寻找一种映射f,使得f(Ax, Ay, Aw, Ah)=(G'x, G'y, G'w, G'h),其中(G'x, G'y, G'w, G'h)≈(Gx, Gy, Gw, Gh)。

    图10

    那么经过何种变换才能从图6中的A变为G'呢? 比较简单的思路就是:

    缩进 1. 先做平移

    缩进 2. 再做缩放

    缩进观察上面4个公式发现,需要学习的是dx(A),dy(A),dw(A),dh(A)这四个变换。当输入的anchor与GT相差较小时,可以认为这种变换是一种线性变换, 那么就可以用线性回归来建模对窗口进行微调(注意,只有当anchors和GT比较接近时,才能使用线性回归模型,否则就是复杂的非线性问题了)。对应于Faster RCNN原文,平移量(tx, ty)与尺度因子(tw, th)如下:

    缩进接下来的问题就是如何通过线性回归获得dx(A),dy(A),dw(A),dh(A)了。线性回归就是给定输入的特征向量X, 学习一组参数W, 使得经过线性回归后的值跟真实值Y(即GT)非常接近,即Y=WX。对于该问题,输入X是一张经过num_output=1的1x1卷积获得的feature map,定义为Φ;同时还有训练传入的GT,即(tx, ty, tw, th)。输出是dx(A),dy(A),dw(A),dh(A)四个变换。那么目标函数可以表示为:

    其中Φ(A)是对应anchor的feature map组成的特征向量,w是需要学习的参数,d(A)是得到的预测值(*表示 x,y,w,h,也就是每一个变换对应一个上述目标函数)。为了让预测值(tx, ty, tw, th)与真实值最小,得到损失函数:

    函数优化目标为:

    2.5 对proposals进行bounding box regression

    缩进在了解bounding box regression后,再回头来看RPN网络第二条线路,如图11。

    图11 RPN中的bbox reg

    先来看一看上图11中1x1卷积的caffe prototxt定义:

    [cpp] view plain copy
     
    1. layer {  
    2.   name: "rpn_bbox_pred"  
    3.   type: "Convolution"  
    4.   bottom: "rpn/output"  
    5.   top: "rpn_bbox_pred"  
    6.   convolution_param {  
    7.     num_output: 36   # 4 * 9(anchors)  
    8.     kernel_size: 1 pad: 0 stride: 1  
    9.   }  
    10. }  

    可以看到其num_output=36,即经过该卷积输出图像为WxHx36,在caffe blob存储为[1, 36, H, W]。与上文中fg/bg anchors存储为[1, 18, H, W]类似,这里相当于feature maps每个点都有9个anchors,每个anchors又都有4个用于回归的[dx(A),dy(A),dw(A),dh(A)]变换量。利用上面的的计算公式即可从foreground anchors回归出proposals。

    2.6 Proposal Layer

    缩进Proposal Layer负责综合所有[dx(A),dy(A),dw(A),dh(A)]变换量和foreground anchors,计算出精准的proposal,送入后续RoI Pooling Layer。还是先来看看Proposal Layer的caffe prototxt定义:
    [cpp] view plain copy
     
    1. layer {  
    2.   name: 'proposal'  
    3.   type: 'Python'  
    4.   bottom: 'rpn_cls_prob_reshape'  
    5.   bottom: 'rpn_bbox_pred'  
    6.   bottom: 'im_info'  
    7.   top: 'rois'  
    8.   python_param {  
    9.     module: 'rpn.proposal_layer'  
    10.     layer: 'ProposalLayer'  
    11.     param_str: "'feat_stride': 16"  
    12.   }  
    13. }  
    Proposal Layer有3个输入:fg/bg anchors分类器结果rpn_cls_prob_reshape,对应的bbox reg的[dx(A),dy(A),dw(A),dh(A)]变换量rpn_bbox_pred,以及im_info;另外还有参数feat_stride=16,这和图4是对应的。
    缩进首先解释im_info。对于一副任意大小PxQ图像,传入Faster RCNN前首先reshape到固定MxN,im_info=[M, N, scale_factor]则保存了此次缩放的所有信息。然后经过Conv Layers,经过4次pooling变为WxH=(M/16)x(N/16)大小,其中feature_stride=16则保存了该信息。所有这些数值都是为了将proposal映射回原图而设置的,如图12,毕竟检测就是为了在原图上画一个框而已~
    图12
    缩进Proposal Layer forward(caffe layer的前传函数)按照以下顺序依次处理:
    1. 再次生成anchors,并对所有的anchors做bbox reg位置回归(注意这里的anchors生成顺序和之前是即完全一致的)
    2. 按照输入的foreground softmax scores由大到小排序anchors,提取前pre_nms_topN(e.g. 6000)个anchors。即提取修正位置后的foreground anchors
    3. 利用feat_stride和im_info将anchors映射回原图,判断fg anchors是否大范围超过边界,剔除严重超出边界fg anchors。
    4. 进行nms(nonmaximum suppression,非极大值抑制)
    5. 再次按照nms后的foreground softmax scores由大到小排序fg anchors,提取前post_nms_topN(e.g. 300)结果作为proposal输出。
    之后输出proposal=[x1, y1, x2, y2],注意,由于在第三步中将anchors映射回原图判断是否超出边界,所以这里输出的proposal是对应MxN输入图像尺度的,这点在后续网络中有用。另外我认为,严格意义上的检测应该到此就结束了,后续部分应该属于识别了~
     
    RPN网络结构就介绍到这里,总结起来就是:
    生成anchors -> softmax分类器提取fg anchors -> bbox reg回归fg anchors -> Proposal Layer生成proposals
     

    3 RoI pooling

    缩进而RoI Pooling层则负责收集proposal,并计算出proposal feature maps,送入后续网络。从图3中可以看到Rol pooling层有2个输入:

    1. 原始的feature maps
    2. RPN输出的proposal boxes(大小各不相同)

    3.1 为何需要RoI Pooling

    缩进先来看一个问题:对于传统的CNN(如AlexNet,VGG),当网络训练好后输入的图像尺寸必须是固定值,同时网络输出也是固定大小的vector or matrix。如果输入图像大小不定,这个问题就变得比较麻烦。有2种解决办法:

    1. 从图像中crop一部分传入网络
    2. 将图像warp成需要的大小后传入网络

    图13 crop与warp破坏图像原有结构信息

    两种办法的示意图如图13,可以看到无论采取那种办法都不好,要么crop后破坏了图像的完整结构,要么warp破坏了图像原始形状信息。回忆RPN网络生成的proposals的方法:对foreground anchors进行bound box regression,那么这样获得的proposals也是大小形状各不相同,即也存在上述问题。所以Faster RCNN中提出了RoI Pooling解决这个问题(需要说明,RoI Pooling确实是从SPP发展而来,但是限于篇幅这里略去不讲,有兴趣的读者可以自行查阅相关论文)。

    3.2 RoI Pooling原理

    缩进分析之前先来看看RoI Pooling Layer的caffe prototxt的定义:

    [cpp] view plain copy
     
    1. layer {  
    2.   name: "roi_pool5"  
    3.   type: "ROIPooling"  
    4.   bottom: "conv5_3"  
    5.   bottom: "rois"  
    6.   top: "pool5"  
    7.   roi_pooling_param {  
    8.     pooled_w: 7  
    9.     pooled_h: 7  
    10.     spatial_scale: 0.0625 # 1/16  
    11.   }  
    12. }  

    其中有新参数pooled_w=pooled_h=7,另外一个参数spatial_scale=1/16应该能够猜出大概吧。

    缩进RoI Pooling layer forward过程:在之前有明确提到:proposal=[x1, y1, x2, y2]是对应MxN尺度的,所以首先使用spatial_scale参数将其映射回MxN大小的feature maps尺度(这里来回多次映射,是有点绕);之后将每个proposal水平和竖直都分为7份,对每一份都进行max pooling处理。这样处理后,即使大小不同的proposal,输出结果都是7x7大小,实现了fixed-length output。

     

    图14 proposal示意图

    4 Classification

    缩进Classification部分利用已经获得的proposal feature maps,通过full connect层与softmax计算每个proposal具体属于那个类别(如人,车,电视等),输出cls_prob概率向量;同时再次利用bounding box regression获得每个proposal的位置偏移量bbox_pred,用于回归更加精确的目标检测框。Classification部分网络结构如图15。
    图15 Classification部分网络结构图
     

    从PoI Pooling获取到7x7=49大小的proposal feature maps后,送入后续网络,可以看到做了如下2件事:

    1. 通过全连接和softmax对proposals进行分类,这实际上已经是识别的范畴了
    2. 再次对proposals进行bounding box regression,获取更高精度的rect box
    这里来看看全连接层InnerProduct layer,如图16,

    图16 全连接层示意图

    其计算公式如下:

    其中W和bias B都是预先训练好的,即大小是固定的,当然输入X和输出Y也就是固定大小。所以,这也就印证了之前Poi Pooling的必要性。到这里,我想其他内容已经很容易理解,不在赘述了。

     
     

    5 Faster RCNN训练

    缩进Faster CNN的训练,是在已经训练好的model(如VGG_CNN_M_1024,VGG,ZF)的基础上继续进行训练。实际中训练过程分为6个步骤:
    1. 在已经训练好的model上,训练RPN网络,对应stage1_rpn_train.pt
    2. 利用步骤1中训练好的RPN网络,收集proposals,对应rpn_test.pt
    3. 第一次训练Fast RCNN网络,对应stage1_fast_rcnn_train.pt
    4. 第二训练RPN网络,对应stage2_rpn_train.pt
    5. 再次利用步骤4中训练好的RPN网络,收集proposals,对应rpn_test.pt
    6. 第二次训练Fast RCNN网络,对应stage2_fast_rcnn_train.pt

    可以看到训练过程类似于一种“迭代”的过程,不过只循环了2次。至于只循环了2次的原因是应为作者提到:"A similar alternating training can be run for more iterations, but we have observed negligible improvements",即循环更多次没有提升了。接下来本章以上述6个步骤讲解训练过程。

    5.1 训练RPN网络

    缩进在该步骤中,首先读取RBG提供的预训练好的model(本文使用VGG),开始迭代训练。来看看stage1_rpn_train.pt网络结构,如图17。

    图17 stage1_rpn_train.pt

    (考虑图片大小,Conv Layers中所有的层都画在一起了,如红圈所示,后续图都如此处理)

    与检测网络类似的是,依然使用Conv Layers提取feature maps。整个网络使用的Loss如下:

    上述公式中,i表示anchors index,pi表示foreground softmax predict概率,pi*代表对应的GT predict概率(即当第i个anchor与GT间IoU>0.7,认为是该anchor是foreground,pi*=1;反之IoU<0.3时,认为是该anchor是background,pi*=0;至于那些0.3<IoU<0.7的anchor则不参与训练);t代表predict bounding box,t*代表对应foreground anchor对应的GT box。可以看到,整个Loss分为2部分:

    1. cls loss,即rpn_cls_loss层计算的softmax loss,用于分类anchors为forground与background的网络训练
    2. reg loss,即rpn_loss_bbox层计算的soomth L1 loss,用于bounding box regression网络训练。注意在该loss中乘了pi*,相当于只关心foreground anchors的回归(其实在回归中也完全没必要去关心background)。

    缩进由于在实际过程中,Ncls和Nreg差距过大,用参数λ平衡二者(如Ncls=256,Nreg=2400时设置λ=10),使总的网络Loss计算过程中能够均匀考虑2种Loss。这里比较重要是Lreg使用的soomth L1 loss,计算公式如下:

    缩进了解数学原理后,反过来看图17:

    1. 对于rpn_loss_cls,输入的rpn_cls_scors_reshape和rpn_labels分别对应p与p*,Ncls参数隐含在p与p*的blob的大小中
    2. 对于rpn_loss_bbox,输入的rpn_bbox_pred和rpn_bbox_targets分别对应t于t*,rpn_bbox_inside_weigths对应p*,rpn_bbox_outside_weights对应λ,Nreg同样隐含在blob大小中

    这样,公式与代码就完全对应了。

    5.2 通过训练好的RPN网络收集proposals

    缩进在该步骤中,利用之前的RPN网络,获取proposal rois,同时获取foreground softmax probability,如图18,然后将获取的信息保存在python pickle文件中。该网络本质上和检测中的RPN网络一样,没有什么区别。

    图18 rpn_test.pt

    5.3 训练Fast RCNN网络

    缩进读取之前保存的pickle文件,获取proposals与foreground probability。从data层输入网络。然后:

    1. 将提取的proposals作为rois传入网络,如图19蓝框
    2. 将foreground probability作为bbox_inside_weights传入网络,如图19绿框
    3. 通过caffe blob大小对比,计算出bbox_outside_weights(即λ),如图19绿框

    这样就可以训练最后的识别softmax与最终的bounding regression了,如图19。

    图19 stage1_fast_rcnn_train.pt

    之后的训练都是大同小异,不再赘述了。

    RPN网络:

      name: 'input-data' #这一层就是最开始数据输入
      type: 'Python'
      top: 'data' # top表示该层的输出,所以可以看到这一层输出三组数据,data,真值框gt_boxes,和相关信息im_info
      top: 'im_info' # 这些都是存储在矩阵中的
      top: 'gt_boxes'
      python_param {
        module: 'roi_data_layer.layer'
        layer: 'RoIDataLayer'
        param_str: "'num_classes': 21"
      }
    }
     
    #========= conv1-conv5 ============
     
    layer {
    	name: "conv1"
    	type: "Convolution"
    	bottom: "data" # 输入data
    	top: "conv1" # 输出conv1,这里conv1就代表了这一层输出数据的名称,存储在对应的矩阵中
    	param { lr_mult: 1.0 }
    	param { lr_mult: 2.0 }
    	convolution_param {
    		num_output: 96
    		kernel_size: 7
    		pad: 3  # 这里可以看到卷积1层 填充了3个像素
    		stride: 2
    	}
    }
    layer {
    	name: "relu1"
    	type: "ReLU"
    	bottom: "conv1"
    	top: "conv1"
    }
    layer {
    	name: "norm1"
    	type: "LRN"
    	bottom: "conv1"
    	top: "norm1" # 做归一化操作,通俗点说就是做个除法
    	lrn_param {
    		local_size: 3
    		alpha: 0.00005
    		beta: 0.75
    		norm_region: WITHIN_CHANNEL
        engine: CAFFE
    	}
    }
    layer {
    	name: "pool1"
    	type: "Pooling"
    	bottom: "norm1"
    	top: "pool1"
    	pooling_param {
    		kernel_size: 3
    		stride: 2
    		pad: 1 # 池化的时候,又做了填充
    		pool: MAX
    	}
    }
    layer {
    	name: "conv2"
    	type: "Convolution"
    	bottom: "pool1"
    	top: "conv2"
    	param { lr_mult: 1.0 }
    	param { lr_mult: 2.0 }
    	convolution_param {
    		num_output: 256
    		kernel_size: 5
    		pad: 2
    		stride: 2
    	}
    }
    layer {
    	name: "relu2"
    	type: "ReLU"
    	bottom: "conv2"
    	top: "conv2"
    }
    layer {
    	name: "norm2"
    	type: "LRN"
    	bottom: "conv2"
    	top: "norm2"
    	lrn_param {
    		local_size: 3
    		alpha: 0.00005
    		beta: 0.75
    		norm_region: WITHIN_CHANNEL
        engine: CAFFE
    	}
    }
    layer {
    	name: "pool2"
    	type: "Pooling"
    	bottom: "norm2"
    	top: "pool2"
    	pooling_param {
    		kernel_size: 3
    		stride: 2
    		pad: 1
    		pool: MAX
    	}
    }
    layer {
    	name: "conv3"
    	type: "Convolution"
    	bottom: "pool2"
    	top: "conv3"
    	param { lr_mult: 1.0 }
    	param { lr_mult: 2.0 }
    	convolution_param {
    		num_output: 384
    		kernel_size: 3
    		pad: 1
    		stride: 1
    	}
    }
    layer {
    	name: "relu3"
    	type: "ReLU"
    	bottom: "conv3"
    	top: "conv3"
    }
    layer {
    	name: "conv4"
    	type: "Convolution"
    	bottom: "conv3"
    	top: "conv4"
    	param { lr_mult: 1.0 }
    	param { lr_mult: 2.0 }
    	convolution_param {
    		num_output: 384
    		kernel_size: 3
    		pad: 1
    		stride: 1
    	}
    }
    layer {
    	name: "relu4"
    	type: "ReLU"
    	bottom: "conv4"
    	top: "conv4"
    }
    layer {
    	name: "conv5"
    	type: "Convolution"
    	bottom: "conv4"
    	top: "conv5"
    	param { lr_mult: 1.0 }
    	param { lr_mult: 2.0 }
    	convolution_param {
    		num_output: 256
    		kernel_size: 3
    		pad: 1
    		stride: 1
    	}
    }
    layer {
    	name: "relu5"
    	type: "ReLU"
    	bottom: "conv5"
    	top: "conv5"
    }
     
    #========= RPN ============
    # 到我们的RPN网络部分了,前面的都是共享的5层卷积层的部分
    layer {
      name: "rpn_conv1"
      type: "Convolution"
      bottom: "conv5"
      top: "rpn_conv1"
      param { lr_mult: 1.0 }
      param { lr_mult: 2.0 }
      convolution_param {
        num_output: 256
        kernel_size: 3 pad: 1 stride: 1 #这里作者把每个滑窗3*3,通过3*3*256*256的卷积核输出256维,完整的输出其实是12*12*256,
        weight_filler { type: "gaussian" std: 0.01 }
        bias_filler { type: "constant" value: 0 }
      }
    }
    layer {
      name: "rpn_relu1"
      type: "ReLU"
      bottom: "rpn_conv1"
      top: "rpn_conv1"
    }
    layer {
      name: "rpn_cls_score"
      type: "Convolution"
      bottom: "rpn_conv1"
      top: "rpn_cls_score"
      param { lr_mult: 1.0 }
      param { lr_mult: 2.0 }
      convolution_param {
        num_output: 18   # 2(bg/fg) * 9(anchors)
        kernel_size: 1 pad: 0 stride: 1 #这里看的很清楚,作者通过1*1*256*18的卷积核,将前面的256维数据转换成了18个输出
        weight_filler { type: "gaussian" std: 0.01 }
        bias_filler { type: "constant" value: 0 }
      }
    }
    layer {
      name: "rpn_bbox_pred"
      type: "Convolution"
      bottom: "rpn_conv1"
      top: "rpn_bbox_pred"
      param { lr_mult: 1.0 }
      param { lr_mult: 2.0 }
      convolution_param {
        num_output: 36   # 4 * 9(anchors)
        kernel_size: 1 pad: 0 stride: 1 <span style="font-family: Arial, Helvetica, sans-serif;">#这里看的很清楚,作者通过1*1*256*36的卷积核,将前面的256维数据转换成了36个输出</span>
        weight_filler { type: "gaussian" std: 0.01 }
        bias_filler { type: "constant" value: 0 }
      }
    }
    layer {
       bottom: "rpn_cls_score"
       top: "rpn_cls_score_reshape" # 我们之前说过,其实这一层是12*12*256的,所以后面我们要送给损失函数,需要将这个矩阵reshape一下,我们需要的是144个滑窗,每个对应的256的向量
       name: "rpn_cls_score_reshape"
       type: "Reshape"
       reshape_param { shape { dim: 0 dim: 2 dim: -1 dim: 0 } }
    }
    layer {
      name: 'rpn-data'
      type: 'Python'
      bottom: 'rpn_cls_score'
      bottom: 'gt_boxes'
      bottom: 'im_info'
      bottom: 'data'
      top: 'rpn_labels'
      top: 'rpn_bbox_targets'
      top: 'rpn_bbox_inside_weights'
      top: 'rpn_bbox_outside_weights'
      python_param {
        module: 'rpn.anchor_target_layer'
        layer: 'AnchorTargetLayer'
        param_str: "'feat_stride': 16"
      }
    }
    layer {
      name: "rpn_loss_cls"
      type: "SoftmaxWithLoss" # 很明显这里是计算softmax的损失,输入labels和cls layer的18个输出(中间reshape了一下),输出损失函数的具体值
      bottom: "rpn_cls_score_reshape"
      bottom: "rpn_labels"
      propagate_down: 1
      propagate_down: 0
      top: "rpn_cls_loss"
      loss_weight: 1
      loss_param {
        ignore_label: -1
        normalize: true
      }
    }
    layer {
      name: "rpn_loss_bbox"
      type: "SmoothL1Loss" # 这里计算的框回归损失函数具体的值
      bottom: "rpn_bbox_pred"
      bottom: "rpn_bbox_targets"
      bottom: "rpn_bbox_inside_weights"
      bottom: "rpn_bbox_outside_weights"
      top: "rpn_loss_bbox"
      loss_weight: 1
      smooth_l1_loss_param { sigma: 3.0 }
    }
     
    #========= RCNN ============
    # Dummy layers so that initial parameters are saved into the output net
     
    layer {
      name: "dummy_roi_pool_conv5"
      type: "DummyData"
      top: "dummy_roi_pool_conv5"
      dummy_data_param {
        shape { dim: 1 dim: 9216 }
        data_filler { type: "gaussian" std: 0.01 }
      }
    }
    layer {
      name: "fc6"
      type: "InnerProduct"
      bottom: "dummy_roi_pool_conv5"
      top: "fc6"
      param { lr_mult: 0 decay_mult: 0 }
      param { lr_mult: 0 decay_mult: 0 }
      inner_product_param {
        num_output: 4096
      }
    }
    layer {
      name: "relu6"
      type: "ReLU"
      bottom: "fc6"
      top: "fc6"
    }
    layer {
      name: "fc7"
      type: "InnerProduct"
      bottom: "fc6"
      top: "fc7"
      param { lr_mult: 0 decay_mult: 0 }
      param { lr_mult: 0 decay_mult: 0 }
      inner_product_param {
        num_output: 4096
      }
    }
    layer {
      name: "silence_fc7"
      type: "Silence"
      bottom: "fc7"
    

      

    对于每个3x3的窗口,作者就计算这个滑动窗口的中心点所对应的原始图片的中心点。然后作者假定,这个3x3窗口,是从原始图片上通过SPP池化得到的,而这个池化的区域的面积以及比例,就是一个个的anchor。换句话说,对于每个3x3窗口,作者假定它来自9种不同原始区域的池化,但是这些池化在原始图片中的中心点,都完全一样。这个中心点,就是刚才提到的,3x3窗口中心点所对应的原始图片中的中心点。如此一来,在每个窗口位置,我们都可以根据9个不同长宽比例、不同面积的anchor,逆向推导出它所对应的原始图片中的一个区域,这个区域的尺寸以及坐标,都是已知的。而这个区域,就是我们想要的 proposal。所以我们通过滑动窗口和anchor,成功得到了 51x39x9 个原始图片的proposal。接下来,每个proposal我们只输出6个参数:每个 proposal 和 ground truth 进行比较得到的前景概率和背景概率(2个参数)(对应图上的 cls_score);由于每个 proposal 和 ground truth 位置及尺寸上的差异,从 proposal 通过平移放缩得到 ground truth 需要的4个平移放缩参数(对应图上的 bbox_pred)。

    所以根据我们刚才的计算,我们一共得到了多少个anchor box呢?

    51 x 39 x 9 = 17900

    约等于 20 k

  • 相关阅读:
    添加gitignore文件后使其生效
    git提交时如何忽略一些文件
    苹果:Safari 4 下载量三天内达 1100 万次
    Linux Multitouch 技术展示
    HTML 5 来了,不需要 Flash 插件的 Youtube
    JSR 299(Java EE 平台的上下文与依赖注入)最终建议草案
    JSR 330(Java 的依赖注入)通过 JCP 接受
    HTML 5 来了,不需要 Flash 插件的 Youtube
    22 条经典的编程引言
    JSR 330(Java 的依赖注入)通过 JCP 接受
  • 原文地址:https://www.cnblogs.com/wuxiangli/p/7066707.html
Copyright © 2020-2023  润新知