实例分割算法笔记:yolact论文详解(1)

632 阅读5分钟

原文: yolact

代码yoact/yolact_edge

总述:yolact是基于这样的一种思路:把实例分割当作两个并行的任务:一个是生成一个全图的非局部的原型掩膜字典(我的理解所谓原型字典应该就是两个语义信息对应的两个掩膜值,即前背景两个信息)。另一个是对每一个实例预测一个线性结合的参数掩膜集。然后结合这两个组件完成实例分割。具体的,对每一个实例,使用原型mask和该实例对应的coefficient做线性运算,并且利用该实例的预测的边界框剪切该实例。通过验证表明,利用yolact这种方式,网络会自行学习如何定位实例的掩膜,并且在视觉,空间和语义相似的实例间在原型种看起来有很大不同。

1.网络设计

yolact是在现有的一阶段目标检测网络的基础上增加了一个掩膜预测分支,如同mask-rcnn是在faster r-cnn的基础上做的一样,唯一的区别在于没有特征池化的操作。这里把任务分成了两个并行的子任务:

(1)prototype mask:该分支使用fcn生成一个雏形的mask,这一阶段尚未具备区分实例的能力(真正实现实例的区分要结合检测信息后才能得到);

image.png (2)目标检测分支:该分支对每个边界框进行预测,得到一个对应的掩膜,由此可以得到图中实例的位置信息。该信息结合掩膜分支得到的雏形mask,就可以得到实例分割结果(既具备了掩膜的信息又包含位置信息)。

1.1 prototype mask生成

在这个分支中会产生K个prototype mask,产生雏形掩膜的网络可以参考fcn,输入来自P3,输出通道数为K(这里K=4)。

注意: (1)该分支并未计算损失,而是通过最终的掩膜的损失来间接监督优化该处的掩膜信息;

(2)该分支采用P3即浅层的特征预测掩膜,充分考虑浅层特征保存较为完整,分辨率更高,有利于提高掩膜预测结果和在小目标上的结果。

image.png

1.2 mask coefficients 常见的目标检测具有两个分支,一个用来预测N类目标的置信度,另一个用来预测边界框。yolact在此基础上引入了第三个分支——预测K个掩膜的mask coefficients(掩膜的置信度系数掩膜)。

image.png

1.3雏形掩膜和掩膜系数分支结合

这里是将生成的prototype mask与mask coefficient进行线性操作,之后使用sigmoid激活函数。

image.png

2.疑惑

Q1:prototype分支生成了K个全图的雏形mask,这几个mask怎么用?(是卷积成一个mask和maskcoefficient做线性运算吗?还是怎么样)

Q2:coefficients mask对应每一个anchor生成K个mask coefficients,然后又说什么对每个anchor取而代之生成K+C个系数,而是生成了K+C+4个系数?这个生成的参数的个数是随便取的吗?为什么另外加的K,C这俩个有特殊含义的个数?真真是搞玄学了????

Q3:如下图所示,prototype分支要和coefficient mask分支做线性运算,必然是两个分支有具有相同的通道数,即prototype生成K个mask,coefficient每个anchor也要生成K个,不然这个矩阵怎么运算?????

image.png

----------------------------------------2022-06-16---------------------------------------------

3.代码细节

3.1预测头

(1)
yolact的预测头参考了dssd的头,实际上对比ssd的预测头也是相差无几的。根据代码可以发现,预测头是在五个尺度的特征图上进行的,这五个特征图的尺度分别为69、35、18、9和5,其中原始输入数据大小为550X550。在 这五个头上分别用预设大小的priors对各个特征图进行卷积,每个特征图分别的每个进行num_classesHfWf,其中num_classes是类别数,Hf和Wf是各个尺度特征图的尺度大小。比如我这里类别数是3,则总的num_priors个数为(6969+3535+1818+99+5*5)*num_classes(3)=19248。 image.png

(2)预测头在FPN的多尺度特征图上进行,如上图所示,存在五个尺度的特征图,首先对每个尺度的特征图进行一个same的卷积(不改变尺度大小),然后分别预测bbox、conf和mask。 其中对原始输入特征图进行卷积的是upfeature(一个卷积一个激活函数,输入输出尺度和通道数完全一致); bbox、conf和mask分别采用对应的卷积层进行预测(尺度都保持一致,但是输出通道根据不同的任务分支具有不同的通道);

image.png

(3)共享头

yolact采用了共享头的方式,所谓共享头,就是从FPN的多个尺度上(5个尺度)只采用一个头来学习。不是很确定这种方式在算法精度上有什么优势,但是显然的是会有显著的提升的,毕竟节约了N-1个独立的头的计算复杂度。那么问题来了,预测推理阶段一个头在多个尺度上重复计算是否算是一种资源浪费呢?暂不清楚,边看边记录......

    self.prediction_layers = nn.ModuleList()
    cfg.num_heads = len(self.selected_layers)

    for idx, layer_idx in enumerate(self.selected_layers):
        # If we're sharing prediction module weights, have every module's parent be the first one
        parent = None
        if cfg.share_prediction_module and idx > 0:
            parent = self.prediction_layers[0]

        pred = PredictionModule(src_channels[layer_idx], src_channels[layer_idx],
                                aspect_ratios = cfg.backbone.pred_aspect_ratios[idx],
                                scales        = cfg.backbone.pred_scales[idx],
                                parent        = parent,
                                index         = idx)
        self.prediction_layers.append(pred)