概述
Fast RCNN几乎已经是端到端的模型了,并且相较于RCNN速度上有了很大的提升,但是距离实时的检测还有一定距离,当前速度的瓶颈来自于候选框的生成,因为他是跑在CPU上的。所以本文就提出了一个Region Proposal Network(RPN),用于生成候选框,并且,在其中引入了anchor机制,解决多尺度检测的问题。也就是说,Faster RCNN=RPN+Fast RCNN
细节
网络结构
流程也和Fast RCNN几乎一样,唯一的区别就是候选框的生成方式
流程概述:
- 通过网络对原图进行特征提取
- RPN网络生成候选框
- 将候选框投影到特征图中,得到候选框特征,并对其进行ROI池化,得到固定尺寸的候选框特征
- 基于候选框特征进行分类和bbox回归(得到更加精确的检测框)
下图是一张更加具体一点的网络结构图(骨干网络用的是VGG16,输出的尺寸是
(
M
/
16
,
N
/
16
)
(M/16,N/16)
(M/16,N/16),这个网络的详情可以看这里):
RPN
PRN网络主要是用于候选框的生成,代替之前的选择性搜索方法,极大的提高了候选框生成的速度。它主要包括anchor生成、anchor分类、bbox回归微调anchor位置得到候选框这几个步骤。
理论上讲:
在RPN之前我们已经对原图进行特征提取得到一个特征图,接着我们在特征图上进行滑动窗口,我们通过窗口的中心点像素的坐标计算它在原图中的像素位置,然后在这个位置上产生k个anchor(默认k=9)。然后我们再基于滑动窗口得到的特征,得到2k个anchor的分类情况和4k个位置回归参数,前者指的是当前位置的k个anchor是前景和是背景的概率,后者指的是当前位置的k个anchor进行位置调整需要的四个参数。注:
这里的256-d是因为原论文采用的是ZF网络,输出特征图的通道数是256,这里表示的是一个窗口会生成一个
1
∗
1
∗
256
1*1*256
1∗1∗256的向量
实际上讲:
在RPN之前我们已经对原图进行特征提取得到一个特征图,接着我们对他进行一个3x3的same卷积(相当于是3x3的滑动窗口了,滑动窗口得到的特征就是卷积核一次运算得到的结果,并且卷积前后特征图尺寸不变),然后就是两路分支,一路使用1x1卷积将通道数调整为2k,经过softmax得到2k个类别概率,另一路使用1x1卷积将通道数调整为4k,使用全连接层得到4k个位置回归参数。
有了上述的这些,我们就能得到我们需要的候选框了,操作如下:
- 使用4k个位置回归参数对anchor进行位置调整
- 根据2k个类别概率得到所有的positive anchor,也就是将不包含前景目标的anchor去掉。对这些包含前景目标的anchor按照softmax中的得分进行排序,选取前N(如6000)个anchor
- 剔除掉超过图片边界的anchor,剔除掉尺寸非常小的anchor
- 对剩余的anchor进行NMS,最终留下的大概就是2000个左右的anchor,这些anchor就可以称作候选框了。
anchor:
主要是用来解决多尺度预测的问题。以往
解决多尺度问题的方法有图像金字塔(使用不同尺度的图像构建多个模型,让他们去检测不同尺度的对象)、卷积核设计(特征提取的时候使用不同尺度、不同尺寸的卷积核,希望得到更加具有表征能力的特征)。本文
的anchor就是预设一组或者多组不同尺寸不同长宽比例的参考框,它的出现使得模型对一张图片只需要检测一次,就能得到对于不同尺寸和不同长宽比目标的优秀检测效果。具体的话
,anchor就是一个预先设定好的矩形框,用一个四元式
(
x
1
,
y
1
,
x
2
,
y
2
)
(x_1,y_1,x_2,y_2)
(x1,y1,x2,y2)表示矩阵,包含矩形框左上角和右下角的坐标。
anchor产生:
我们以原图中某一个像素点为中心,按照不同的尺度和横纵比得到若干个不同的矩形框,就是anchor。比如我们设置尺度和横纵比分别为
[
8
,
16
,
32
]
、
[
0.5
,
1
,
2
]
[8,16,32]、[0.5,1,2]
[8,16,32]、[0.5,1,2],使vgg16作为骨干网络。那么输入图像尺寸是
800
∗
600
800*600
800∗600,得到的特征图是尺寸是
c
e
i
l
(
800
/
16
)
∗
c
e
i
l
(
600
/
16
)
=
50
∗
38
ceil(800/16)*ceil(600/16)=50*38
ceil(800/16)∗ceil(600/16)=50∗38,每一点对应原图的感受野是
16
∗
16
16*16
16∗16,所以按照比例得到的面积是:
[
16
∗
8
∗
16
∗
8
,
16
∗
16
∗
16
∗
16
,
16
∗
32
∗
16
∗
32
]
[16*8*16*8,16*16*16*16,16*32*16*32]
[16∗8∗16∗8,16∗16∗16∗16,16∗32∗16∗32]也就是
[
128
∗
128
,
156
∗
156
,
512
∗
512
]
[128*128,156*156,512*512]
[128∗128,156∗156,512∗512]。现在有了三个面积,还有三个纵横比,我们就能得到9种组合就是9个anchor。上面我们提到,是将特征图上的点映射到原图中,然后产生anchor的,也就是说我们总共会产生
50
∗
38
∗
9
50*38*9
50∗38∗9个anchor。注:
vgg中最后输出特征图的感受野是
228
∗
228
228*228
228∗228但是最大框的面积会达到
512
∗
512
512*512
512∗512,超过我们的感受野了,作者认为这个是ok的,因为只知道目标的部分也是可以实现对整个目标的预测的,而最终的实验结果也说明了这点。
但是
,产生了这么多的anchor,我们都用来训练吗?这就产生了正负样本的问题。正样本选择有两种标准,对于每一个gt box,选取与其IoU最大的anchor作为正样本,或者如果一个anchor至少与任意一个ground-truth box的IoU大于0.7,则认为这个anchor为正样本,打上标签为1。如果IoU小于0.3,则认为这个anchor为负样本,打上标签为0。那些IoU值位于(0.3,0.7)区间的anchor不参与训练,也就没有标签。正样本的第二条其实就够了,加上第一条能保证一定会有一定的正样本存在。
计算特征图上像素点在原图中的位置:
原图width/特征图的width=stride_x,然后得到特征图中当前像素的x坐标,x * stride_x就得到了原图上对应点的x坐标,y坐标同理
bbox回归
我们可以发现Faster RCNN中有两次bbox回归,一次是将anchor的位置进行微调,另一次是对候选框进行微调,两次的操作其实是一样的,就是根据模型输出的四个位置参数调整原来的边框,让它和gt box更加接近。同样的操作在Fast RCNN和RCNN中也出现了。
我们知道一个框可以用四个参数来表示,分别是中心点的xy坐标以及框的宽和高,我们假设原来的框是 [ A x , A y , A w , A h ] [A_x,A_y,A_w,A_h] [Ax,Ay,Aw,Ah],而真实的表框gt box是 [ G x , G y , G w , G h ] [G_x,G_y,G_w,G_h] [Gx,Gy,Gw,Gh],我们需要寻找一个变换F,使得 F ( A x , A y , A w , A h ) = [ G x " , G y " , G w " , G h " ] F(A_x,A_y,A_w,A_h)=[G^"_x,G^"_y,G^"_w,G^"_h] F(Ax,Ay,Aw,Ah)=[Gx",Gy",Gw",Gh"]并且有 [ G x " , G y " , G w " , G h " ] ≈ [ G x , G y , G w , G h ] [G^"_x,G^"_y,G^"_w,G^"_h]\approx[G_x,G_y,G_w,G_h] [Gx",Gy",Gw",Gh"]≈[Gx,Gy,Gw,Gh]
一个简单的思路就是先平移再缩放,我们使用四个参数
[
t
x
,
t
y
,
t
w
,
t
h
]
[t_x,t_y,t_w,t_h]
[tx,ty,tw,th]就可以描述这个变化,如下:
G
x
"
=
A
w
∗
t
x
+
A
x
G^"_x=A_w*t_x+A_x
Gx"=Aw∗tx+Ax
G
y
"
=
A
h
∗
t
y
+
A
y
G^"_y=A_h*t_y+A_y
Gy"=Ah∗ty+Ay
G
w
"
=
A
w
∗
e
x
p
(
t
w
)
G^"_w=A_w*exp(t_w)
Gw"=Aw∗exp(tw)
G
h
"
=
A
h
∗
e
x
p
(
t
h
)
G^"_h=A_h*exp(t_h)
Gh"=Ah∗exp(th)
也就是说,只要模型能够输出合适的参数
[
t
x
,
t
y
,
t
w
,
t
h
]
[t_x,t_y,t_w,t_h]
[tx,ty,tw,th],我们就可以实现对原有框的调整,使他更加接近gt box。那么我们怎么得到监督值呢?其实也简单,就是对上面式子的变形,训练的时候,我们有gt box的信息,有anchor box的信息,就可以得到变换F中的四个参数了,然后用这些真实的参数做监督,强迫网络进行学习。那么当推理的时候,网络就会输出较为合适的变换参数,我们也就可以完成对原边框的微调了。
t
x
=
(
G
x
"
−
A
x
)
/
A
w
t_x=(G^"_x-A_x)/A_w
tx=(Gx"−Ax)/Aw
t
y
=
(
G
y
"
−
A
y
)
/
A
h
t_y=(G^"_y-A_y)/A_h
ty=(Gy"−Ay)/Ah
t
w
=
l
o
g
(
G
w
"
/
A
w
)
t_w=log(G^"_w/A_w)
tw=log(Gw"/Aw)
t
h
=
l
o
g
(
G
h
"
/
A
h
)
t_h=log(G^"_h/A_h)
th=log(Gh"/Ah)
损失函数与训练
Faster RCNN论文中的训练步骤是先训练RPN网络,然后再训练整个Fast RCNN网络。但是两个部分的形式其实差不多,都是分类损失+回归损失,前者用交叉熵损失函数,后者用smooth L1损失函数,虽然长得和Fast RCNN不太一样,但其实是一样的。具体点可以看之前的分析:链接
在训练RPN的时候,一张图片随机选取256个anchors计算loss,正样本anchor和负样本anchor的比例是1:1,如果正样本的个数小于128,那么就用负样本来填充。
训练Faster RCNN作者没提,估计是和Fast RCNN中类似吧。