论文原文:Mask R-CNN
1. RoI Align方法
1.1 RoI Pooling局限性分析
在常见的两级检测框架(比如Fast-RCNN,Faster-RCNN,RFCN)中,ROI Pooling 的作用是根据预选框的位置坐标在特征图中将相应区域池化为固定尺寸的特征图,以便进行后续的分类和包围框回归操作。由于预选框的位置通常是由模型回归得到的,一般来讲是浮点数,而池化后的特征图要求尺寸固定。故ROI Pooling这一操作存在两次量化( quantization q u a n t i z a t i o n )的过程。
- 将候选框边界量化为整数点坐标值。
- 将量化后的边界区域平均分割成 k×k k × k 个单元(bin),对每一个单元的边界进行量化。
事实上,经过上述两次量化,此时的候选框已经和最开始回归出来的位置有一定的偏差,这个偏差会影响检测或者分割的准确度。在论文里,作者把它总结为”不匹配问题(misalignment)”。
下面我们用直观的例子具体分析一下上述区域不匹配问题。如 图1 所示,这是一个Faster-RCNN检测框架。输入一张800*800的图片,图片上有一个665*665的包围框(框着一只狗)。图片经过主干网络提取特征后,特征图缩放步长(stride)为32。因此,图像和包围框的边长都是输入时的1/32。800正好可以被32整除变为25。但665除以32以后得到20.78,带有小数,于是ROI Pooling 直接将它量化成20。接下来需要把框内的特征池化
7×7
7
×
7
的大小,因此将上述包围框平均分割成
7×7
7
×
7
个矩形区域。显然,每个矩形区域的边长为2.86,又含有小数。于是ROI Pooling 再次把它量化到2。经过这两次量化,候选区域已经出现了较明显的偏差(如图中绿色部分所示)。更重要的是,该层特征图上0.1个像素的偏差,缩放到原图就是3.2个像素。那么0.8的偏差,在原图上就是接近30个像素点的差别,这一差别不容小觑。
1.2 2. ROI Align 的主要思想和具体方法
为了解决ROI Pooling的上述缺点,作者提出了ROI Align这一改进的方法(如图2)。ROI Align的思路很简单:取消量化操作,使用双线性内插的方法获得坐标为浮点数的像素点上的图像数值,从而将整个特征聚集过程转化为一个连续的操作,。值得注意的是,在具体的算法操作上,ROI Align并不是简单地补充出候选区域边界上的坐标点,然后将这些坐标点进行池化,而是重新设计了一套比较优雅的流程,如 图3 所示:
- 遍历每一个候选区域,保持浮点数边界不做量化。
- 将候选区域分割成k x k个单元,每个单元的边界也不做量化。
- 在每个单元中计算固定四个坐标位置,用双线性内插的方法计算出这四个位置的值,然后进行最大池化操作。
详细过程如下:
假设我们有一个 128x128 的图像,25x25 的特征图,想要找出与原始图像左上角 15x15 位置对应的特征区域,怎么在特征图上选取像素?
我们知道原始图像的每一个像素与特征图上的 25/128 个像素对应。为了在原始图像选取 15 个像素,在特征图上我们需要选择 15 * 25/128 ~= 2.93 个像素。
对于这种情形,RoIPool 会舍去零头选择两个像素,导致排列问题。但在 RoIAlign,这种去掉小数点之后数字的方式被避免,而是使用双线性插值(bilinear interpolation)准确获得 2.93 像素位置的信息。在高层级,这避免了排列错误。
在 mask 生成之后,Mask R-CNN 把它们与 Faster R-CNN 的分类、选框结合起来,生成相当精确的分割。
总之,RoI Align的方法就是,做pooling的时候,不在是在类似 2×2 2 × 2 的矩形框里面做了,如果是小数,比如 2.7×2.7 2.7 × 2.7 的情况,理论上没法获得 2.7 2.7 处的像素值,但是使用双线性差值的方法能够获得 (2.7,2.7) ( 2.7 , 2.7 ) 处的像素值。最后其实做的是一个 3×3 3 × 3 的pooling操作。
object detection对于这样的误差不是特别敏感,但是segmentation是基于单个像素的,对于这种像素级别的误差非常敏感,因此RoI Align对于segmentation非常有用。
2. Mask R-CNN网络结构
2.1 整体框架
整体框架有两种:
前面的backbone可以用resnet和FPN。
2.2 将掩模预测和分类预测拆解
该框架对每个类别独立地预测一个二值掩模,没有引入类间竞争,每个二值掩模的类别依靠网络RoI分类分支给出的分类预测结果。这与FCNs不同,FCNs是对每个像素进行多类别分类,它同时进行分类和分割,基于实验结果表明这样对于对象实例分割会得到一个较差的性能。
下面介绍一下更多的细节,在训练阶段,作者对于每个采样的RoI定义一个多任务损失函数
L=Lcls+Lbox+Lmask
L
=
L
c
l
s
+
L
b
o
x
+
L
m
a
s
k
,前两项不过多介绍。掩模分支对于每个RoI会有一个
Km2
K
m
2
维度的输出,它编码了
K
K
个分辨率为的二值掩模,分别对应着K个类别。因此作者利用了a per-pixel sigmoid,并且定义
Lmask
L
m
a
s
k
为平均二值交叉熵损失(the average binary cross-entropy loss)。对于一个属于第k个类别的RoI,
Lmask
L
m
a
s
k
仅仅考虑第k个
mask
m
a
s
k
(其他的掩模输入不会贡献到损失函数中)。这样的定义会允许对每个类别都会生成掩模,并且不会存在类间竞争。
2.3 掩模的表示
一个掩模编码了一个输入对象的空间布局。作者使用了一个FCN来对每个RoI预测一个 m×m m × m 的掩模,这保留了空间结构信息。