<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> <path stroke-linecap="round" d="M5,0 0,2.5 5,5z" id="raphael-marker-block" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);"></path> </svg> <h4>一、PointRend理解</h4>
讯享网
PointRend的提出是为了解决Mask RCNN分割精度不高的问题,个人建议想要学习这个网络的同学可以从语义分割模型上着手,毕竟Mask RCNN相比于语义分割模型还是比较复杂的,但是PointRend的思想是一样的。PoinRend语义分割
那么PointRend网络到底是如何解决分割结果精细度不高的问题呢?让我们来回顾一下语义分割领域最经典的FCN模型。
FCN语义分割思想:对原始图像经过不断的卷积和池化,得到高密度特征,然后直接上采样到与图像大小一致的结果,这样必然会导致分割结果很粗糙,尤其是边界部分误差很大。
之后大部分的语义分割模型都是基于这个框架来做的,只是在最后上采样部分做了修改,如Unet的decoder和deeplabV3+的decoder上采样。
PointRend核心思想:语义分割经过多次池化降采样后,直接预测分类结果,而不是上采样到跟原图像同样尺寸。在粗糙的分割结果中选择分割精度不高的点,然后在这些点上结合粗糙和精细的特征训练MLP模型,对这些点进行重新预测,用重新预测的结果替换原来预测的粗糙结果。
1.1 语义分割模型训练
首先训练一个标准的语义分割网络训练(这里以deeplabv3为例),将上采样部分替换为3X3的卷积,最终层的通道数为分类数,把这个作为粗分割结果。如下图
1.2、选点
讯享网
推理时选点的策略
1.3、特征提取
在精细特征图(deeplabV3中的res2输出结果)和粗糙特预测结果上提取步骤2中选出的N个点所在位置的特征,然后合并。
1.4、训练MLP,替换。
用上面提取特征作为样本训练一个MLP模型(实际实现是1x1卷积)。即可得到这些点重新预测的结果,在推理时,通过迭代上采样的方式逐步的替换这些精度不高的点,知道尺寸跟原图大小一致。
要点总结:训练和推理阶段的两处不同
1、选点策略不同
2、点替换策略不同
二、PointRend代码解释(为了节省空间,我删除了源码中大段的注释)
PointRend整体逻辑梳理
讯享网
PointRend()类初始化就两部分,backbone和head。其中backbone就是deeplabv3,head是PointHead,即选择点重新训练的过程。重点在PointHead:
讯享网

deeplabV3输出的粗糙的分割结果每次上采样2倍,然后执行一次选点、特征提取、mlp预测的过程(跟训练时的流程一致,只是选点策略有区别),替换所选出的点上的分割结果,重复执行这个操作,直到分割结果的尺寸与原图尺寸大小一致。
这里有个问题,为啥PointRend的训练和推理的前向过程不一样呢?
2.1、选点策略
这一部分是选点的具体实现细节。
讯享网
可以看到推理和训练的选点策略是分开的,通过模型的training参数来判断当前是推理还是训练阶段。结合第1.2节介绍的选点策略,我们先来看训练阶段如何实现选点。
选点之前先对mask(deeplabV3 输出的粗糙分割结果)做了排序,这步的作用是为了确定所选点的不确定性大小,在选点的第二步会用到。
训练阶段选点第一步是Over generation,对应下面这两句代码:
讯享网
Point_sample()函数的核心是调用了torch的grid_sample()插值函数,主要是通过插值的方法获得指定点的值。
讯享网
先来看输入的参数,第一个参数input对应的是mask,也就是deeplabV3输出的粗糙的分割结果,第二个参数是要插值的点,输入的是2.0*point_coords-1.0,其中point_coords是之前随机生成的k*N (一张图片)个二维坐标点。那为啥要做一下这个线性变换呢?其实这是跟torch的grid_sample()原理有关。
grid_sample()输入的插值点的坐标是相对坐标,相对于mask的位置,其中左上角坐标是(-1,-1),右下角坐标是(1,1)。所以传入的坐标范围要在[-1,1]之间。具体原理可以另外一篇文章:PyTorch中grid_sample的使用方法。
从这里我们可以看到代码实现中所选的点坐标都是相对位置,这样才能将在不同尺寸的特征图上提取的特征合并。
然后是第二步:Import sampling,对应的代码如下
这是很关键的一步操作,来逐句看一下是怎么实现的
讯享网
第一类得分减去第二类得分(mask 已经在每个像素上按得分结果降序排列了),取反,然后取前β*N个点,就这?这样就取到了前β*N个最不确定的点?为啥要这么做呢?
我们来分析一下,我们知道1个像素点只对应1个类别,如果该像素对应的两个类别分数都很高或者说得分最高的两类分数很接近,说明它可能是边界点,也说明这个点的分割结果不确定性很高。
举个例子,假设分割任务有5类,像素A、B、C的分割得分如下表
我们知道这三个像素都会被认定为是第5类,但是分类正确的可能性应该是A>B>C。我们按照代码中的不确定性计算方法得到uncertain_map_A=-0.55,uncertain_map_B=-0.15,uncertain_map_B=-0.01,明显uncertain_map_C要远大于uncertain_map_A和uncertain_map_B,因此在选点的时候,C会被选出来作为不确定性高的点。

第三步:Coverage,对应代码如下
很简单,剩下的(1-β)*N个点随机生成即可。最后将importance点和coverage点合并返回即可。
讯享网
然后我们再来看一下推理时如何选点。
与训练不同的是,推理时是直接选出N个不确定的点,点的不确定性的规则训练时一样。
注意:所选的点的坐标都是相对坐标
2.2、特征提取
2.1节获得了所需要的N个点,接下来就是获取这些点对应的精细层特征和粗糙层特征,用的方法也是torch的grid_sample()。代码如下
讯享网
2.3、预测分类
得到所选的N个点的特征值之后,执行MLP操作,其实就是1X1的卷积。rend保存的就是这N个点新的分类结果。
其他
讯享网
net就是PoinRend网络,result为前向输出结果,里面包含了“coarse”,“points”,“rend”。
讯享网
推理前记得加上
这句是将网络中的training参数设置为False。
3、PointRend如何在Mask RCNN上使用
理解了一、二节介绍的PointRend网络在语义分割上使用的原理和实现细节后再来看PointRend如何在Mask Rcnn上使用应该会简单很多了。
其实还是backbone和pointHead,只不过backbone不一样了。(这里假定大家已经了解了Mask RCNN的原理)。
来看一下PointRend原文里给的结构图
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/158322.html