先回归一下: R-CNN
, SPP-net
R-CNN
和SPP-net
在训练时pipeline
是隔离的:提取proposal
,CNN
提取特征,SVM
分类,bbox regression
。
Fast R-CNN
两大主要贡献点 :
- 实现大部分
end-to-end
训练(提proposal
阶段除外): 所有的特征都暂存在显存中,就不需要额外的磁盘空间。
joint training
(SVM
分类,bbox
回归联合起来在CNN
阶段训练)把最后一层的Softmax
换成两个,一个是对区域的分类Softmax
(包括背景),另一个是对bounding box
的微调。这个网络有两个输入,一个是整张图片,另一个是候选proposals
算法产生的可能proposals
的坐标。(对于SVM
和Softmax
,论文在SVM
和Softmax
的对比实验中说明,SVM
的优势并不明显,故直接用Softmax
将整个网络整合训练更好。对于联合训练: 同时利用了分类的监督信息和回归的监督信息,使得网络训练的更加鲁棒,效果更好。这两种信息是可以有效联合的。) - 提出了一个
RoI
层,算是SPP
的变种,SPP
是pooling
成多个固定尺度,RoI
只pooling
到单个固定的尺度 (论文通过实验得到的结论是多尺度学习能提高一点点mAP
,不过计算量成倍的增加,故单尺度训练的效果更好。)
其它贡献点:
- 指出
SPP-net
训练时的不足之处,并提出新的训练方式,就是把同张图片的prososals
作为一批进行学习,而proposals
的坐标直接映射到conv5
层上,这样相当于一个batch
一张图片的所以训练样本只卷积了一次。文章提出他们通过这样的训练方式或许存在不收敛的情况,不过实验发现,这种情况并没有发生。这样加快了训练速度。 (实际训练时,一个batch
训练两张图片,每张图片训练64个RoIs(Region of Interest
))
注意点:
- 论文在回归问题上并没有用很常见的2范数作为回归,而是使用所谓的鲁棒L1范数作为损失函数。
- 论文将比较大的全链接层用
SVD
分解了一下使得检测的时候更加迅速。虽然是别人的工作,但是引过来恰到好处(矩阵相关的知识是不是可以在检测中发挥更大的作用呢?)。
ROI Pooling
与SPP
的目的相同:如何把不同尺寸的ROI
映射为固定大小的特征。ROI
就是特殊的SPP
,只不过它没有考虑多个空间尺度,只用单个尺度(下图只是大致示意图)。
ROI Pooling
的具体实现可以看做是针对ROI
区域的普通整个图像feature map
的Pooling
,只不过因为不是固定尺寸的输入,因此每次的pooling
网格大小得手动计算,比如某个ROI
区域坐标为
(
x
1
,
y
1
,
x
2
,
y
2
)
(x_1,y_1,x_2,y_2)
(x1,y1,x2,y2),那么输入size
为
(
y
2
−
y
1
)
⋅
(
x
2
−
x
1
)
(y_2-y_1)\cdot(x_2-x_1)
(y2−y1)⋅(x2−x1),如果pooling
的输出size
为
p
o
o
l
e
d
h
e
i
g
h
t
⋅
p
o
o
l
e
d
w
i
d
t
h
pooledheight\cdot pooledwidth
pooledheight⋅pooledwidth那么每个网格的size
为
(
(
y
2
−
y
1
)
/
p
o
o
l
e
d
h
e
i
g
h
t
⋅
(
x
2
−
x
1
)
/
p
o
o
l
e
d
w
i
d
t
h
)
((y_2-y_1)/pooledheight\cdot (x_2-x_1)/pooledwidth)
((y2−y1)/pooledheight⋅(x2−x1)/pooledwidth)。
Bounding-box Regression
有了ROI Pooling
层其实就可以完成最简单粗暴的深度对象检测了,也就是先用selective search
等proposal
提取算法得到一批box
坐标,然后输入网络对每个box
包含一个对象进行预测,此时,神经网络依然仅仅是一个图片分类的工具而已,只不过不是整图分类,而是ROI
区域的分类,显然大家不会就此满足,那么,能不能把输入的box
坐标也放到深度神经网络里然后进行一些优化呢?rbg
大神于是又说了"yes"。在Fast-RCNN
中,有两个输出层:第一个是针对每个ROI
区域的分类概率预测
p
=
(
p
0
,
p
1
,
⋅
⋅
⋅
,
p
K
)
p=(p_0,p_1,\cdot \cdot \cdot ,p_K )
p=(p0,p1,⋅⋅⋅,pK),第二个则是针对每个ROI
区域坐标的偏移优化
t
k
=
(
t
x
k
,
t
y
k
,
t
w
k
,
t
h
k
)
,
0
≤
k
≤
K
t^k=(t_x^k,t_y^k,t_w^k,t_h^k),0\leq k \leq K
tk=(txk,tyk,twk,thk),0≤k≤K 是多类检测的类别序号。这里我们着重介绍第二部分,即坐标偏移优化。
假设对于类别
k
∗
k^*
k∗,在图片中标注了一个groundtruth
坐标:
t
∗
=
(
t
x
∗
,
t
y
∗
,
t
w
∗
,
t
h
∗
)
t^*=(t_x^*,t_y^*,t_w^*,t_h^*)
t∗=(tx∗,ty∗,tw∗,th∗),而预测值为
t
=
(
t
x
,
t
y
,
t
w
,
t
h
)
t=(t_x,t_y,t_w,t_h)
t=(tx,ty,tw,th)
,二者理论上越接近越好,这里定义损失函数:
这里, s m o o t h L 1 ( x ) smooth_{L_1}(x) smoothL1(x) 中的x即为 t i − t i ∗ t_i - t_i^* ti−ti∗
t
i
−
t
i
∗
t_i - t_i^*
ti−ti∗ 即对应坐标的差距。该函数在 (−1,1) 之间为二次函数,而其他区域为线性函数,作者表示这种形式可以增强模型对异常数据的鲁棒性,整个函数在matplotlib
中画出来是这样的
对应的代码在smooth_L1_loss_layer.cu
中。
参考:
- 《Fast R-CNN》
- 目标检测–从RCNN到Faster RCNN 串烧
- RCNN, Fast-RCNN, Faster-RCNN的一些事