有关于Mask-RCNN的个人理解
背景
在前面几篇博客中我讲述了许多的目标检测以及目标分割的文章,但它们都是独立的任务网络,而在本文中作者设计 了一种能够执行多任务的网络结构,检测+实例分割(与语义分割不同)。
思想
利用Faster-RCNN+FPN结构实现检测与分割,并且在检测(类别+box位置参数)与分割的输入是相同的,即它们的网络分支处于一种并联的状态。同时为了实现实例分割的效果,训练与测试的时候,mask分支的预测都是依据检测分支中的类别预测相关的。
模型结构
模型结构:上游采用与Faster-RCNN中一样的网络结构(存在RPN),中间的感兴趣特征层ROIPooling作者稍有改动(ROIAlign:重点,在后面部分将会详细介绍怎么计算),下游添加了一个mask分支,预测K(代表的是总的类别个数)个m * m(由ROIAlign输出的特征图尺寸,默认为14 * 14或28 * 28大小)的概率图。另外这mask分支的输出最后会经过Sigmoid函数(避免类别竞争),而不是FCN中使用的Softmax函数(存在类别竞争),除了可以避免类别竞争外,其实我认为最大的原因是:关于ROI的输入已经在检测分支预测过类别概率(已经进行过了一次类别竞争,无需在竞争比重),我认为这是一个像素级别的类别精细模块(相对于每一个ROI特征图)。两种分支结构,含义都是一样的。
- ROIAlign
关于该层主要是解决原ROIPooling层使得特征点信息无法对应的问题,即在原本的ROIPooling中存在两次量化的过程,都是以取整的方式来决定边界像素点的位置,这种问题在检测问题中并不会有什么影响,但对分割任务影响是很大的(由于分割是像素界级别的任务,过大的位置偏差会造成很大的偏差)。举一个例子:首先原输入图像尺寸为16 * 16(左上角坐标=(0,0),右上角=(16,16)),经过Backbone变成8 * 8(左上角=(0,0),右下角=(8,8)),现经过RPN模块生成一个665 * 665(左上角坐标位置=(1,1),右下角=(14,14))的候选区域,然后我们需要将此候选区域映射到原图上得到相应的特征图。ROIPooling的做法是:【1/】=0,【14/2】=7,向下取整,因此会得到333* 333大小的特征图(左上角=(0,0),右下角=(7,7)),而我们知道我们的候选区域的尺寸为13 * 13,将13/2=6.5,即最右边-最左边的差值应该是6.5,但此时却是7,出现了0.5的偏差,这在分割任务中是不允许的。同时如果接下来我们需要将得到的特征图变为分成2 * 2大小的特征图,按照上述ROIPooling做法:【0/2】=0,【7/2】=3,则会被分成这四个区域(0,0)->(3,3),(3,0)->(7,3),(0,3)->(3,7),(3,3)->(7,7),然后再在这四部分取最大(最大池化),最终得到2 * 2大小的特征图,这就是完整的ROIPooling做法。
然而作者发现上述存在边界误差问题,不利于分割任务,因此对该模块的计算方式进行了修改。首先:1/2=0.5,14/2=7,不再进行取整,保持左上角与右下角的边界浮点数坐标,两者差值为6.5,与之前的最右边-最左边差值相同,解决了第一次量化造成的误差,随后7/2=3.5,现解释一下3.25的含义代表我们会将第一次量化的得到的特征图(0.5,7)划分为四等分,称划分的区域为grid(网格),每个网格的坐标范围分别为(0.5,0.5)->(3.75,3.75),(3.75,0.5)->(7,3.75),(0.5,3.75)->(3.75,7),(3.75,3.75)->(7,7),每个grid的范围等于6.5/2=3.25,解决了第二次量化误差的问题。随后不是像ROIPooling一样直接取最大值,而是再将这四个grid每个都平分成四个 区域,3.25/2=1.625,那么这四个grid将会被拓展成八个小区域:
第一个grid对应的四个区域(0.5,0.5)->(2.125,2.125),(2.125,0.5)->(3.75,2.125),(0.5,2.125)->(2.125,3.75),(2.125,2.125)->(3.75,3.75);
第二个grid对应的四个区域(3.75,0.5)->(5.375,2.125),(5.375,0.5)->(7,2.125),(3.75,2.125)->(5.375,3.75),(5.375,5.375)->(7,7);
第三个grid对应的四个区域(0.5,3.75)->(2.125,5.375),(2.125,3.75)->(3.75,5.375),(0.5,5.375)->(2.125,7),(2.125,5.375)->(3.75,7);
第四个grid对应的四个区域(3.75,3.75)->(5.375,5.375),(5.375,3.75)->(7,5.375),(3.75,5.375)->(5.375,7),(5.375,5.375)->(7,7);
这样我们就可以得到相应的16个区域,然后我们再去计算16个区域的中心点坐标,例如第一个grid对应的第一个区域的中心点坐标=(1.3125,1.3125),则我们会将此中心点坐标映射到(0,0)->(7,7)的特征图上,选取其周围最近的四个像素值,即该点的(0,0)->(2,2)这四个像素值,然后将此四个值利用双线性差值的方法计算得到该点的像素值,其他中心点同理。于是我们就会得到每个grid对应的四个采样点,然后对每个grid区域内的四个采样点使用最大池化或者均值池化得到1个像素点,最后我们即可以得到2 * 2尺寸大小的特征图(即分成了四个部分),此过程与ROIPooling层是完全不同的,核心在于每个grid像素点是由四个采样点得到的,而采样点是通过周围四个像素点双线性插值计算得到的,以下为该操作的完整图示。
- mask分支
两种结构,输出K个固定尺寸大小的对应k类的概率图(像素值由Sigmoid得到),采用全卷积的方式,没有全连接层。
训练阶段
模型的训练相对于原本的检测网络多了一个mask损失,因此我只介绍mask损失是如何计算的,以下为总损失公式。
- L m a s k L_{mask} Lmask:只计算正样本的掩膜损失,而且会依据类别分支得到的类别k,选择mask分支输出的第k个概率图。然后我们会再把该m * m固定尺寸的概率图缩放到与对应ROI相同的尺寸,再设定阈值(默认为0.5,将概率值变换成0或1),最后与真实的ROI对应的mask计算掩膜损失。
测试阶段
检测阶段与Faster一样使用NMS方法,而在mask分支,选择类别分支预测的第k个类别的mask,最后进行染色。
总结
如理解存在纰漏,敬请指正,谢谢!