作者:贺弘 谦言 临在

导言

图画切割(Image Segmentation)是指对图片进行像素级的分类,根据分类粒度的不同能够分为语义切割(Semantic Segmentation)、实例切割(Instance Segmentation)、全景切割(Panoptic Segmentation)三类。图画切割是核算机视觉中的首要研究方向之一,在医学图画分析、自动驾驶、视频监控、增强实际、图画压缩等范畴有重要的运用价值。咱们在EasyCV结构中对这三类切割SOTA算法进行了集成,并供给了相关模型权重。经过EasyCV能够轻松猜测图画的切割谱以及练习定制化的切割模型。本文首要介绍怎么运用EasyCV实实际例切割、全景切割和语义切割,及相关算法思维。

运用EasyCV猜测切割图

EasyCV供给了在coco数据集上练习的实例切割模型和全景切割模型以及在ADE20K上练习的语义切割模型,参考EasyCV quick start(github.com/alibaba/Eas…)完成依赖环境的装备后,能够直接运用这些模型完成对图画的切割谱猜测,相关模型链接在reference中给出。

实例切割猜测

由于该示例中的mask2fromer算法运用了Deformable attention (在DETR系列算法中运用该算子能够有用提高算法收敛速度和核算功率),需求额定对该算子进行编译

cd thirdparty/deformable_attention
python setup.py build install

经过Mask2formerPredictor猜测图画实例切割图

import cv2
from easycv.predictors.segmentation import Mask2formerPredictor
predictor = Mask2formerPredictor(model_path='mask2former_instance_export.pth',task_mode='instance')
img = cv2.imread('000000123213.jpg')
predict_out = predictor(['000000123213.jpg'])
instance_img = predictor.show_instance(img, **predict_out[0])
cv2.imwrite('instance_out.jpg',instance_img)

输出成果如下图:

使用EasyCV Mask2Former轻松实现图像分割

使用EasyCV Mask2Former轻松实现图像分割

全景切割猜测

经过Mask2formerPredictor猜测图画全景切割图

import cv2
from easycv.predictors.segmentation import Mask2formerPredictor
predictor = Mask2formerPredictor(model_path='mask2former_pan_export.pth',task_mode='panoptic')
img = cv2.imread('000000123213.jpg')
predict_out = predictor(['000000123213.jpg'])
pan_img = predictor.show_panoptic(img, **predict_out[0])
cv2.imwrite('pan_out.jpg',pan_img)

输出成果如下图:

使用EasyCV Mask2Former轻松实现图像分割

使用EasyCV Mask2Former轻松实现图像分割

语义切割猜测

经过Mask2formerPredictor猜测图画语义切割图

import cv2
from easycv.predictors.segmentation import Mask2formerPredictor
predictor = Mask2formerPredictor(model_path='mask2former_semantic_export.pth',task_mode='semantic')
img = cv2.imread('000000123213.jpg')
predict_out = predictor(['000000123213.jpg'])
semantic_img = predictor.show_panoptic(img, **predict_out[0])
cv2.imwrite('semantic_out.jpg',semantic_img)

使用EasyCV Mask2Former轻松实现图像分割

使用EasyCV Mask2Former轻松实现图像分割

示例图片来历:cocodataset

在阿里云机器学习渠道PAI上运用Mask2Former模型

PAI-DSW(Data Science Workshop)是阿里云机器学习渠道PAI开发的云上IDE,面向各类开发者,供给了交互式的编程环境。在DSW Gallery中(链接),供给了各种Notebook示例,便利用户轻松上手DSW,搭建各种机器学习运用。咱们也在DSW Gallery中上架了Mask2Former进行图画切割的Sample Notebook(见下图),欢迎我们体验!

使用EasyCV Mask2Former轻松实现图像分割

Mask2Former算法解读

上述比如中选用的模型是根据Mask2former完成的,Mask2former是一个统一的切割架构,能够同时进行语义切割、实例切割以及全景切割,而且获得SOTA的成果,在COCO数据集上全景切割精度57.8 PQ,实例切割精度达50.1 AP,在ADE20K数据集上语义切割精度达57.7 mIoU。

使用EasyCV Mask2Former轻松实现图像分割

核心思维

Mask2Former选用mask classification的方法来进行切割,即经过模型去猜测一组二值mask再组合成最终的切割图。每个二值mask能够代表类别或实例,就能够完成语义切割、实例切割等不同的切割使命。

在mask classsification使命中,一个比较核心的问题是怎么去找到一个好的方法学习二值Mask。如从前的作业 Mask R-CNN经过bounding boxes来约束特征区域,在区域内猜测各自的切割谱。这种方法也导致Mask R-CNN只能进行实例切割。Mask2Former参考DETR的方法,经过一组固定数量的特征向量(object query)去表明二值Mask,经过Transformer Decoder进行解码去猜测这一组Mask。(ps:关于DETR的解读能够参考:根据EasyCV复现DETR和DAB-DETR,Object Query的正确打开方法)

在DETR系列的算法中,有一个比较重要的缺陷是在Transformer Decoder中的cross attention中会对全局的特征进行处理,导致模型很难重视到真正想要重视的区域,会下降模型的收敛速度和最终的算法精度。关于这个问题Mask2former提出了Transformer Decoder with mask attention,每个Transformer Decoder block 会去猜测一个attention mask并以0.5为阈值进行二值化,然后将这个attentino mask作为下一个block的输入,让attention模块核算时只重视在mask的远景部分。

模型结构

使用EasyCV Mask2Former轻松实现图像分割

Mask2Former由三个部分组成:

  1. Backbone(ResNet、Swin Transformer)从图片中抽取低分辨率特征
  2. Pixel Decoder 从低分辩率特征中逐渐进行上采样解码,获得从低分辨率到高分辨率的特征金字塔,循环的作为Transformer Decoder中V、K的输入。经过多标准的特征来保证模型对不同标准的方针的猜测精度。

其中一层的Trasformer代码如下所示(ps:为了进一步加速模型的收敛速度,在Pixel Decoder中选用了Deformable attention模块):

class MSDeformAttnTransformerEncoderLayer(nn.Module):
    def __init__(self,
                 d_model=256,
                 d_ffn=1024,
                 dropout=0.1,
                 activation='relu',
                 n_levels=4,
                 n_heads=8,
                 n_points=4):
                     super().__init__()
                     # self attention
                     self.self_attn = MSDeformAttn(d_model, n_levels, n_heads, n_points)
                     self.dropout1 = nn.Dropout(dropout)
                     self.norm1 = nn.LayerNorm(d_model)
                     # ffn
                     self.linear1 = nn.Linear(d_model, d_ffn)
                     self.activation = _get_activation_fn(activation)
                     self.dropout2 = nn.Dropout(dropout)
                     self.linear2 = nn.Linear(d_ffn, d_model)
                     self.dropout3 = nn.Dropout(dropout)
                     self.norm2 = nn.LayerNorm(d_model)
    @staticmethod
    def with_pos_embed(tensor, pos):
        return tensor if pos is None else tensor + pos
    def forward_ffn(self, src):
        src2 = self.linear2(self.dropout2(self.activation(self.linear1(src))))
        src = src + self.dropout3(src2)
        src = self.norm2(src)
        return src
    def forward(self,
                src,
                pos,
                reference_points,
                spatial_shapes,
                level_start_index,
                padding_mask=None):
                    # self attention
                    src2 = self.self_attn(
                        self.with_pos_embed(src, pos), reference_points, src,
                        spatial_shapes, level_start_index, padding_mask)
                    src = src + self.dropout1(src2)
                    src = self.norm1(src)
                    # ffn
                    src = self.forward_ffn(src)
                    return src
  1. Transformer Decoder with mask attention 经过Object query和Pixel Decoder中得到的Multi-scale feature去逐层去refine二值mask图,得到最终的成果。

其中核心的mask cross attention,会将前一层的猜测的mask作为MultiheadAttention的atten_mask输入,以此来将注意力的核算约束在这个query重视的远景中。详细完成代码如下:

class CrossAttentionLayer(nn.Module):
    def __init__(self,
                 d_model,
                 nhead,
                 dropout=0.0,
                 activation='relu',
                 normalize_before=False):
        super().__init__()
        self.multihead_attn = nn.MultiheadAttention(
            d_model, nhead, dropout=dropout)
        self.norm = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(dropout)
        self.activation = _get_activation_fn(activation)
        self.normalize_before = normalize_before
        self._reset_parameters()
    def _reset_parameters(self):
        for p in self.parameters():
            if p.dim() > 1:
                nn.init.xavier_uniform_(p)
    def with_pos_embed(self, tensor, pos: Optional[Tensor]):
        return tensor if pos is None else tensor + pos
    def forward_post(self,
                     tgt,
                     memory,
                     memory_mask: Optional[Tensor] = None,
                     memory_key_padding_mask: Optional[Tensor] = None,
                     pos: Optional[Tensor] = None,
                     query_pos: Optional[Tensor] = None):
        tgt2 = self.multihead_attn(
            query=self.with_pos_embed(tgt, query_pos),
            key=self.with_pos_embed(memory, pos),
            value=memory,
            attn_mask=memory_mask,
            key_padding_mask=memory_key_padding_mask)[0]
        tgt = tgt + self.dropout(tgt2)
        tgt = self.norm(tgt)
        return tgt
    def forward_pre(self,
                    tgt,
                    memory,
                    memory_mask: Optional[Tensor] = None,
                    memory_key_padding_mask: Optional[Tensor] = None,
                    pos: Optional[Tensor] = None,
                    query_pos: Optional[Tensor] = None):
        tgt2 = self.norm(tgt)
        tgt2 = self.multihead_attn(
            query=self.with_pos_embed(tgt2, query_pos),
            key=self.with_pos_embed(memory, pos),
            value=memory,
            attn_mask=memory_mask,
            key_padding_mask=memory_key_padding_mask)[0]
        tgt = tgt + self.dropout(tgt2)
        return tgt
    def forward(self,
                tgt,
                memory,
                memory_mask: Optional[Tensor] = None,
                memory_key_padding_mask: Optional[Tensor] = None,
                pos: Optional[Tensor] = None,
                query_pos: Optional[Tensor] = None):
        if self.normalize_before:
            return self.forward_pre(tgt, memory, memory_mask,
                                    memory_key_padding_mask, pos, query_pos)
        return self.forward_post(tgt, memory, memory_mask,
                                 memory_key_padding_mask, pos, query_pos)

Tricks

1.efficient multi-scale strategy

在pixel decoder中会解码得到标准为原图1/32、1/16、1/8的特征金字塔顺次作为对应transformer decoder block的K、V的输入。参照deformable detr的做法,对每个输入都加上了sinusoidal positional embedding和learnable scale-level embedding。按分辨率从低到高的循序顺次输入,并循环L次。

2.PointRend

经过PointRend的方法来节省练习过程中的内存消耗,首要体现在两个部分a.在运用匈牙利算法匹配猜测mask和真值标签时,经过均匀采样的K个点集替代完好的mask图来核算match cost b.在核算损失时按照importance sampling策略采样的K个点集替代完好的mask图来核算loss(ps实验证明根据pointreind方法来核算损失能够有用提高模型精度)

3.Optimization improvements

  • 更换了self-attention和cross-attention的次序。self-attention->cross-attention变成cross-attention->self-attention。
  • 让query变成可学习的参数。让query进行监督学习能够起到相似region proposal的效果。经过实验能够证明可学习的query能够发生mask proposal。
  • 去掉了transformer deocder中的dropout操作。经过实验发现这个操作会下降精度。

复现精度

实例切割及全景切割在COCO上的复现精度,实验在单机8卡A100环境下进行(ps :关于实例切割复现精度问题在官方repo issue 46中有提及)

Model PQ Box mAP Mask mAP memory train_time
mask2former_r50_instance_official 43.7
mask2former_r50_8xb2_epoch50_instance 46.09 43.26 13G 3day2h
mask2former_r50_panoptic_official 51.9 41.7
mask2former_r50_8xb2_epoch50_panoptic 51.64 44.81 41.88 13G 3day4h

语义切割在ADE20K数据集上进行复现

Model mIoU train memory train_time
mask2former_r50_semantic_official 47.2
mask2former_r50_8xb2_e127_samantic 47.03 5.6G 15h35m

运用EasyCV练习切割模型

关于特定场景的切割,能够运用EasyCV结构和相应数据练习定制化的切割模型。这里以实例切割为比如,介绍练习流程。

一、数据准备

目前EasyCV支撑COCO方法的数据格式,咱们供给了示例COCO数据用于快速走通流程。

wget http://pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/data/small_coco_demo/small_coco_demo.tar.gz && tar -zxf small_coco_demo.tar.gz
mkdir -p data/  && mv small_coco_demo data/coco

二、模型练习

在EasyCV的config文件夹下,咱们供给了mask2former的数据处理和模型练习及验证的装备文件(configs/segmentation/mask2former/mask2former_r50_8xb2_e50_instance.py),根据需求修改猜测的类别、数据途径。

执行练习命令,如下所示:

#单机八卡
python -m torch.distributed.launch --nproc_per_node=8 --master_port 11111 tools/train.py \
                                        configs/segmentation/mask2former/mask2former_r50_8xb2_e50_instance.py \
                                        --launcher pytorch \
                                        --work_dir experiments/mask2former_instance \
                                        --fp16 

模型导出,将config文件保存到模型中,以便在predictor中得到模型和数据处理的装备,导出后的模型就可直接用于切割图的猜测。

python tools/export.py configs/segmentation/mask2former/mask2former_r50_8xb2_e50_instance.py epoch_50.pth mask2former_instance_export.pth

Reference

实例切割模型:pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/EasyCV/mode…

全景切割模型:pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/EasyCV/mode…

语义切割模型:pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/EasyCV/mode…

EasyCV往期分享

EasyCV开源地址:github.com/alibaba/Eas…

EasyCV DataHub 供给多范畴视觉数据集下载,助力模型出产 zhuanlan.zhihu.com/p/572593950

EasyCV带你复现更好更快的自监督算法-FastConvMAE zhuanlan.zhihu.com/p/566988235

根据EasyCV复现DETR和DAB-DETR,Object Query的正确打开方法 zhuanlan.zhihu.com/p/543129581

根据EasyCV复现ViTDet:单层特征逾越FPN zhuanlan.zhihu.com/p/528733299

MAE自监督算法介绍和根据EasyCV的复现 zhuanlan.zhihu.com/p/515859470

EasyCV开源|开箱即用的视觉自监督+Transformer算法库 zhuanlan.zhihu.com/p/50521999

END

EasyCV会持续进行SOTA论文复现进行系列的作业介绍,欢迎我们重视和运用,欢迎我们各种维度的反应和改善建议以及技术讨论,同时咱们非常欢迎和期待对开源社区建造感兴趣的同行一起参与共建。