R-CNN是目标检测领域中十分经典的方法,相比于传统的手工特征,R-CNN将卷积神经网络引入,用于提取深度特征,后接一个分类器判决搜索区域是否包含目标及其置信度,取得了较为准确的检测结果。Fast R-CNN和Faster R-CNN是R-CNN的升级版本,在准确率和实时性方面都得到了较大提升。在Fast R-CNN中,首先需要使用Selective Search的方法提取图像的候选目标区域(Proposal)。而新提出的Faster R-CNN模型则引入了RPN网络(Region Proposal Network),将Proposal的提取部分嵌入到内部网络,实现了卷积层特征共享,Fast R-CNN则基于RPN提取的Proposal做进一步的分类判决和回归预测,因此,整个网络模型可以完成端到端的检测任务,而不需要先执行特定的候选框搜索算法,显著提升了算法模型的实时性。
Faster R-CNN模型主要由两个模块组成:RPN候选框提取模块和Fast R-CNN检测模块,如下图所示,又可细分为4个部分;Conv Layer,Region Proposal Network(RPN),RoI Pooling,Classification and Regression。
Faster R-CNN网络模型
下图为Faster R-CNN测试网络结构(网络模型文件为faster_rcnn_test.pt),可以清楚地看到图像在网络中的前向计算过程。对于一幅任意大小P×Q
的图像,首先缩放至固定大小M×N(源码中是要求长边不超过1000,短边不超过600),然后将缩放后的图像输入至采用VGG16模型的Conv Layer中,最后一个feature map为conv5-3,特征数(channels)为512。RPN网络在特征图conv5-3上执行3×3
卷积操作,后接一个512维的全连接层,全连接层后接两个子连接层,分别用于anchors的分类和回归,再通过计算筛选得到proposals。RoIs Pooling层则利用Proposal从feature maps中提取Proposal feature进行池化操作,送入后续的Fast R-CNN网络做分类和回归。RPN网络和Fast R-CNN网络中均有分类和回归,但两者有所不同,RPN中分类是判断conv5-3中对应的anchors属于目标和背景的概率(score),并通过回归获取anchors的偏移和缩放尺度,根据目标得分值筛选用于后续检测识别的Proposal;Fast R-CNN是对RPN网络提取的Proposal做分类识别,并通过回归参数调整得到目标(Object)的精确位置。具体的训练过程会在后面详述。接下来会重点介绍RPN网络和Fast R-CNN网络这两个模块,包括RPN网络中引入的Anchor机制、训练数据的生成、分类和回归的损失函数(Loss Function)计算以及RoI Pooling等。
Fast R-CNN test网络结构
传统的目标检测方法中生成候选框都比较耗时,例如使用滑动窗口加图像金字塔的方式遍历图像,获取多尺度的候选区域;以及R-CNN、Fast R-CNN中均使用到的Selective Search的方法生成候选框。而Faster R-CNN则直接使用RPN网络,将检测框Proposal的提取嵌入到网络内部,通过共享卷积层参数的方式提升了Proposal的生成速度。
Anchor
Anchor是RPN网络中一个较为重要的概念,传统的检测方法中为了能够得到多尺度的检测框,需要通过建立图像金字塔的方式,对图像或者滤波器(滑动窗口)进行多尺度采样。RPN网络则是使用一个3×3
的卷积核,在最后一个特征图(conv5-3)上滑动,将卷积核中心对应位置映射回输入图像,生成3种尺度(scale){1282,2562,5122}和3种长宽比(aspect ratio){1:1,1:2,2:1}共9种Anchor,如下图所示。特征图conv5-3每个位置都对应9个anchors,如果feature map的大小为W×H,则一共有W×H×9
个anchors,滑动窗口的方式保证能够关联conv5-3的全部特征空间,最后在原图上得到多尺度多长宽比的anchors。
Anchor示意图
最后一个feature map后面会接一个全连接层,如下图所示,全连接的维数和feature map的特征数(channels)相同。对于原论文中采用的ZF模型,conv5的特征数为256,全连接层的维数也为256;对于VGG模型,conv5-3的特征数为512,全连接的的维数则为512,相当于feature map上的每一个点都输出一个512维的特征向量。
RPN网络结构
关于anchors还有几点需要说明:
训练样本的生成
一般而言,特征图conv5-3的实际尺寸大致为60×40
,那么一共可以生成60×40×9≈20k
个anchors,显然不会将所有anchors用于训练,而是筛选一定数量的正负样本。对于数据集中包含有人工标定ground truth的图像,考虑一张图像上所有anchors:
Multi-task Loss Function
由于涉及到分类和回归,所以需要定义一个多任务损失函数(Multi-task Loss Function),包括Softmax Classification Loss和Bounding Box Regression Loss,公式定义如下:
L({pi},{ti})=1NclsΣiLcls(pi,p∗i)+λ1NregΣip∗iLreg(ti,t∗i)
Softmax Classification:对于RPN网络的分类层(cls),其向量维数为2k = 18,考虑整个特征图conv5-3,则输出大小为W×H×18,正好对应conv5-3上每个点有9个anchors,而每个anchor又有两个score(fg/bg)输出,对于单个anchor训练样本,其实是一个二分类问题。为了便于Softmax分类,需要对分类层执行reshape操作,这也是由底层数据结构决定的。在caffe中,Blob的数据存储形式为Blob=[batch_size,channel,height,width],而对于分类层(cls),其在Blob中的实际存储形式为[1,2k,H,W],而Softmax针对每个anchor进行二分类,所以需要在分类层后面增加一个reshape layer,将数据组织形式变换为[1,2,k∗H,W]
,之后再reshape回原来的结构,caffe中有对softmax_loss_layer.cpp的reshape函数做如下解释:
1 2 3 4 |
"Number of labels must match number of predictions; " "e.g., if softmax axis == 1 and prediction shape is (N, C, H, W), " "label count (number of labels) must be N*H*W, " "with integer values in {0, 1, ..., C-1}."; |
在上式中,pi
为样本分类的概率值,p∗i为样本的标定值(label),anchor为正样本时p∗i为1,为负样本时p∗i为0,Lcls为两种类别的对数损失(log loss)。
Bounding Box Regression:RPN网络的回归层输出向量的维数为4k = 36,回归参数为每个样本的坐标[x,y,w,h],分别为box的中心位置和宽高,考虑三组参数预测框(predicted box)坐标[x,y,w,h],anchor坐标[xa,ya,wa,ha],ground truth坐标[x∗,y∗,w∗,h∗],分别计算预测框相对anchor中心位置的偏移量以及宽高的缩放量{t},ground truth相对anchor的偏移量和缩放量{t∗}
tx=(x−xa)/wa, ty=(y−ya)/ha, tw=log(w/wa), th=log(h/ha) (1)
t∗x=(x∗−xa)/wa, t∗y=(y∗−ya)/ha, t∗w=log(w∗/wa), t∗h=log(h∗/ha) (2)
回归目标就是让{t}尽可能地接近{t∗},所以回归真正预测输出的是{t},而训练样本的标定真值为{t∗}。得到预测输出{t}后,通过上式(1)即可反推获取预测框的真实坐标。在损失函数中,回归损失采用Smooth L1函数
SmoothL1(x)={0.5x2 |x|≤1|x|−0.5 otherwise
Lreg=SmoothL1(t−t∗)
Smooth L1损失函数曲线如下图所示,相比于L2损失函数,L1对离群点或异常值不敏感,可控制梯度的量级使训练更易收敛。
Smooth L1损失函数
在损失函数中,p∗iLreg
这一项表示只有目标anchor(p∗i=1)才有回归损失,其他anchor不参与计算。这里需要注意的是,当样本bbox和ground truth比较接近时(IoU大于某一阈值),可以认为上式的坐标变换是一种线性变换,因此可将样本用于训练线性回归模型,否则当bbox与ground truth离得较远时,就是非线性问题,用线性回归建模显然不合理,会导致模型不work。分类层(cls)和回归层(reg)的输出分别为{p}和{t},两项损失函数分别由Ncls和Nreg以及一个平衡权重λ归一化。分类损失的归一化值为minibatch的大小,即Ncls=256;回归损失的归一化值为anchor位置的数量,即Nreg≈2400;λ
一般取值为10,这样分类损失和回归损失差不多是等权重的。
Proposal的生成
Proposal的生成就是将图像输入到RPN网络中进行一次前向(forward)计算,处理流程如下:
对于RPN网络中生成的proposal,需要送入Fast R-CNN网络做进一步的精确分类和坐标回归,但proposal的尺寸可能大小不一,所以需要做RoI Pooling,输出统一尺寸的特征,再与后面的全连接层相连。
RoI Pooling
对于传统的卷积神经网络,当网络训练好后输入图像的尺寸必须是固定值,同时网络输出的固定大小的向量或矩阵。如果输入图像大小不统一,则需要进行特殊处理,如下图所示:
crop与warp操作
可以从图中看出,crop操作破坏了图像的完整结构,warp操作破坏了图像的原始形状信息,两种方法的效果都不太理想。RPN网络生成的proposal也存在尺寸不一的情况,但论文中提出了RoI Pooling的方法解决这个问题。
RoI Pooling结合特征图conv5-3和proposal的信息,proposal在输入图像中的坐标[x1,y1,x2,y2]
对应M×N尺度,将proposal的坐标映射到M16×N16大小的conv5-3中,然后将Proposal在conv5-3的对应区域水平和竖直均分为7等份,并对每一份进行Max Pooling或Average Pooling处理,得到固定大小(7×7
)输出的池化结果,实现固定长度输出(fixed-length output),如下图所示。
RoI Pooling示意图
Classification and Regression
RoI Pooling层后接多个全连接层,最后为两个子连接层——分类层(cls)和回归层(reg),如下图所示,和RPN的输出类似,只不过输出向量的维数不一样。如果类别数为N+1(包括背景),分类层的向量维数为N+1,回归层的向量维数则为4(N+1)。还有一个关键问题是RPN网络输出的proposal如何组织成Fast R-CNN的训练样本:
在源码实现中,用于训练Fast R-CNN的Proposal除了RPN网络生成的,还有图像的ground truth,这两者归并到一起,然后通过筛选组成minibatch用于迭代训练。Fast R-CNN的损失函数也与RPN类似,二分类变成了多分类,背景同样不参与回归损失计算,且只考虑proposal预测为标签类的回归损失。
Classification and Regression
对于提取proposals的RPN,以及分类回归的Fast R-CNN,如何将这两个网络嵌入到同一个网络结构中,训练一个共享卷积层参数的多任务(Multi-task)网络模型。源码中有实现交替训练(Alternating training)和端到端训练(end-to-end)两种方式,这里介绍交替训练的方法。
由训练流程可知,第4步训练RPN网络和第6步训练Fast R-CNN网络实现了卷积层参数共享。总体上看,训练过程只循环了2次,但每一步训练(M1,M2,M3,M4)都迭代了多次(e.g. 80k,60k)。对于固定卷积层参数,只需将学习率(learning rate)设置为0即可。
以上关于RPN的训练,Proposal的生成,以及Fast R-CNN的训练做了的详细讲解,接下来结合网络模型图和部分源码,对这些模块做进一步的分析。
train RPN
训练RPN的网络结构如下图所示,首先加载参数文件,并改动一些参数适应当前训练任务。在train_rpn函数中调用get_roidb、get_imdb、get_train_imdb_roidb等获取训练数据集,并通过调用gt_roidb和prepare_roidb方法对训练数据进行预处理,为样本增添一些属性,数据集roidb中的每个图像样本,主要有以下属性:
1 2 3 4 5 6 7 8 9 10 |
'image':图像存储路径 'width':图像宽 'height':图像高 'boxes':图像中bbox(groundtruth or proposal)的坐标[x1,y1,x2,y2] 'gt_classes':每个bbox对应的类索引(1~20) 'gt_overlaps':二维数组,shape=[num_boxes * num_classes],每个bbox(ground truth)对应的类索引处取值为1,其余为0 'flipped':取值为True/False,用于标记有无将图像水平翻转 'seg_area':bbox的面积 'max_classes':bbox与所有ground truth的重叠比例IoU最大的类索引(gt_overlaps.argmax(axis=1)) 'max_overlaps':bbox与所有ground truth的IoU最大值(gt_overlaps.max(axis=1)) |
train_rpn_model
获取数据集roidb中字典的属性后,设置输出路径output_dir,用来保存中间训练结果,然后调用train_net函数。在train_net函数中,首先调用filter_roidb,滤除掉既没有前景又没有背景的roidb。然后调用layer.py中的set_roidb方法,打乱训练样本roidb的顺序,将roidb中长宽比近似的图像放在一起。之后开始训练模型train_model,这里需要实例化每个层,对于第一层RoIDataLayer,通过setup方法进行实例化,并且在训练过程中通过forward方法,调用get_minibatch函数,获取每一次迭代训练的数据,在读取数据时,主要获取了3个属性组成Layer中的Blob
1 2 3 |
'data':单张图像数据im_blob=[1,3,H,W] 'gt_boxes':一幅图像中所有ground truth的坐标和类别[x1,y1,x2,y2,cls] 'im_info':图像的宽高和缩放比例 height,width,scale = [[im_blob.shape[2], im_blob.shape[2], im_scale[0]]] |
从网络结构图中可以看出,input-data(RoIDataLayer)的下一层是rpn-data(AnchorTargetLayer),rpn-data计算所有anchors与ground truth的重叠比例IoU,从中筛选出一定数量(256)的正负样本组成一个minibatch,用于RPN网络的训练,这一层的输出有如下属性:
1 2 3 4 5 |
'rpn_label':每个anchor对应的类别(1——fg,0——bg,-1——ignored),shape=[1,1,A*height,width] 'rpn_bbox_targets':anchor与ground truth的回归参数[dx,dy,dw,dh],shape=[1,A*4,height,width] 'rpn_box_inside_targets':回归损失函数中的样本权值,正样本为1,负样本为0,相当于损失函数中的p*,shape=[1,A*4,height,width] 'rpn_box_outside_targets':分类损失函数和回归损失函数的平衡权重,相当于λ,shape=[1,A*4,height,width] 注:height、width为特征图conv5-3的高宽,A=9为Anchor种数 |
对于分类损失rpn_loss_cls,输入的rpn_cls_scors_reshape和rpn_labels分别对应p
与p∗;对于回归损失,输入的rpn_bbox_pred和rpn_bbox_targets分别对应{t}与{t∗},pn_bbox_inside_weigths对应p∗,rpn_bbox_outside_weights对应λ
。
generate proposals
Proposal的生成只需将图像输入到RPN网络中,进行前向(forward)计算然后经过筛选即可得到,网络结构如下图所示
generate proposals
从rpn_proposals = imdb_proposals(rpn_net, imdb)开始,使用im = cv2.imread(imdb.image_path_at(i))读入图片数据,调用 im_proposals生成单张图片的rpn proposals,以及得分。im_proposals函数会调用网络的forward方法,从而得到想要的boxes和scores,最后将获取的proposal保存在python pickle文件中。
train Fast R-CNN
训练Fast R-CNN的网络结构如下图所示,首先设置参数适应训练任务,在预处理数据时,调用的不再是gt_roidb方法,而是rpn_roidb,通过使用类imdb的静态方法merge_roidb,将rpn_roidb和gt_roidb归并为一个roidb,因此数据集中的’boxes’属性除了包含ground truth,还有RPN网络生成的proposal,可通过上一步保存的文件直接读取。通过add_bbox_regression_targets方法给roidb的样本增添了额外的属性’bbox_targets’,用于表示回归参数的标定值。属性’gt_overlaps’是所有proposal与ground truth通过计算IoU得到的。最后就是调用get_minibatch方法从2张图像中选取128个proposal作为一次迭代的训练样本,读取数据时,获取如下属性组成Layer中的Blob
1 2 3 4 5 6 |
'data':图像数据 'rois':proposals的坐标[batch_inds,x1,y1,x2,y2] 'label':proposals对应的类别(0~20) 'bbox_targets':proposal回归参数的标定值,shape = [128, 4(N+1)] 'box_inside_targets':回归损失函数中的样本权值,正样本为1,负样本为0,相当于损失函数中的p* 'rpn_box_outside_targets':分类损失函数和回归损失函数的平衡权重,相当于λ |
train_fast_rcnn_model
损失函数的计算与RPN网络类似。在Faster R-CNN中,自定义的Python Layer包括RoIDataLayer、AnchorTargetLay、ProposalLayer,都只实现了前向计算forward,因为这些Layer的作用是获取用于训练网络的数据,而对网络本身没有贡献任何权值参数,也不传播梯度值,因此不需要实现反向传播backward。
github:https://github.com/jwyang/faster-rcnn.pytorch
blog:www.telesens.co/2018/03/11/object-detection-and-classification-using-r-cnns/#ITEM-1455-2