Semantic Segmentation --Improve Semantic Segmentation by Global Convolutional Network(GCN)论文解读

来源:互联网 发布:eraser软件 编辑:程序博客网 时间:2024/06/05 11:13

Large Kernel Matters —— GCN

论文全称:Large Kernel Matters——Improve Semantic Segmentation by Global Convolutional Network

作者栏里面有熟悉的Face++的Jian Sun~

原文地址:Large Kernel Matters —— GCN

实现代码:

  • PyTorch

Abstract

在现有的模型架构设计中有这样一个趋势:
堆叠小卷积核比大卷积核更有效。(主要说的是VGG的3×3和GoogleNet中的1×1)。但考虑到Semantic Segmentation需要逐像素分割预测,要同时完成分割和预测(classification and localization tasks simultaneously)。
考虑到这一问题,提出了Global Convolutional Network(GCN),同时给出了一个基于残差(residual,即Resnet的主要贡献)的boundary refinement(BR)模块用于细化物体边界,论文在PASCAL VOC 2012(82.2%)和Cityscapes(76.9% )上达到了state-of-the-art.

Introduction

论文上来先分析Semantic Segmentation。

我们要做的任务分成两部分:classification&localization.

论文指出这两部分在要义上是相互矛盾的(these two tasks are naturally contradictory),从而论文提出了一个改进架构Global Convolutional Network(GCN)

部分 任务 要义 GCN的设计 classification 每个像素有对应的语义信息,即分类要正确 要求模型对各种变换有不变形(例如翻转和旋转) 网络应该采用较大的核,使得feature map与像素分类层之间有密切的连接,从而增强处理不同变换的能力 localization 每个像素分类标签与对应的种类对齐 要求对变换敏感,可以精确分割像素 模型应该是完全卷积(没有FC或全局池化),保持定位信息

大致的示意图如下:

这里写图片描述

  • A. Classification : 多次提取高层次的feature map,然后做分类.
    例如:AlexNet,VGGNet,GoogleNet,ResNet等,整体呈现的是“圆锥形网络”(Cone-shaped),这样的feature map的空间上是粗糙的(高语义的),分类任务可通过后续接FC层或全局池化层,这样有模型能接收不同类型的输入,保持较强的鲁棒性。

  • B. Segmentation : 输入和输出大小相同(feature map尺度没变),全卷积层最后每个通道对应一个分类结果.
    这需要有较大的feature map保持空间信息。所有大多数模型例如:FCN,DeepLab,Deconv-Net采用桶状(Barrel-shaped)结构,使用Deconvolution, Unpooling 和Dilated-Convolution等技术反向生成高分辨率feature map.

  • C. GCN : 全卷积设计,最后分类使用了多通道的信息(和ShuffleNet有那么点神似).
    考虑到B中分类器和feature map不是全局连接,难以处理输入上的变换。例如下图,分类器与输入对象的红点对齐,如果接收野不够大(valid receptive filed,VRF),则很难分别出来.如果使用更大的feature map,则效果更差.
    这里写图片描述
    而GCN就是在使用全卷积的结构上尽可能的使用大的卷积核,从而达到分类和分割平衡。

总结一下Paper的Contributions:

  • 针对“classification” and “localization” 提出了GCN架构
  • 引入边界细化模块(Boundary Refinement block)进一步提高物体边界的划分性能
  • 在PASCAL VOC 2012(82.2%) and Cityscapes(76.9%)取得state-of-the-art.

以FCN为基础,从三个方面尝试提高semantic segmentation 性能:

  • Context Embedding : 上下文语义嵌入。这里列举了一些工作:ParseNet使用全局池化分支增加额外上下文信息;Dilated-Net使用空洞卷积增加上下文信息;Deeplab-V2使用Atrous Spatial Pyramid Pooling模块增加上下文信息.
  • Resolution Enlarging : FCN是使用deconvolution来提高层次的feature map的分辨率;在Deconv-Net和SegNet上采用unpooling来学习上采样;Deeplab和Dilated-Net提出了一个特殊的dilated convolution来扩大feature map的大小,从而达到更大的分辨图.
  • Boundary Alignment : 边界对齐的任务是优化分割对象的边界预测。许多方法是使用CRF(条件随机场)。后续的Deeplab提出的denseCRF等等一大堆的CRF改进版。

这里点出本文的看法:Semantic Segmentation是在大的feature map上的分类任务.(semantic segmentation is a classification task on large feature map)

Architecture

在GCN架构中,将核大小增加到feature map的空间大小,从而可以获取全局信息(Global Convolution)。这里没有直接使用大卷积核,而是采用GoogleNet的思想(可以参考我以前写的GoogleNet笔记Inception-V2),将大卷积核拆分为卷积组合,即GCN模块(下图F2.B),这里在卷积层后不使用非线性,保持计算复杂度为O(2k)

模型的整体架构如下(下图F2.A):
这里写图片描述

模型使用ResNet作为特征提取层(后面实验有介绍),使用FCN的结构作为分割架构。不同尺度的特征是从不同大小的feature map上提取的,对于不同层级的feature map使用GCN提取全局信息,同时高层次的feature map通过上采样来补充语义信息,最终融合得到预测图。这里提出了一个残差结构(residual structure)的BR模块(上图F2.C)学习边界信息。

Experiment

论文在PASCAL VOC 2012和Cityscapes上做了评估。

项目 属性 特征提取层 预训练的Resnet152权重 优化器 标准的SGD batch size 1 权重衰减 momentum 0.99 and weight decay 0.0005 数据增强 去均值,水平翻转 度量标准 标准平均IoU值 实现工具 Caffe

实验方案

将输入放缩到512×512,这样最终的feature map大小为16×16,论文比较了四种结构,示意图如下:

这里写图片描述

  • B:单纯的1×1卷积,提供一个baseline
  • A:GCN结构,使用参数k控制核大小,论文使用了不同k(3到15,使用奇数保持对齐),论证了论文思想。在k=15是接收野大概为16×16,达到了“全局卷积”,在实验结果上来看,性能随着内核大小k增加而增加,从而论证了GCN的设计思想。
    这里写图片描述
  • C:考虑到模型性能提升可能是因为参数增加,这里设计一个k×k的卷积核做对比,从结果来看,GCN结构比大卷积核效果好,可以看到随着k增加,大卷积核效果反倒下降了,这其中的原因可能是因为过拟合,在训练过程中,因为参数多,网络难以收敛,实际原因还有待研究
    这里写图片描述
  • D:对于大卷积核,可以使用小卷积核堆叠达到类似的接收野,为了等效比较,在堆叠小卷积时没用使用非线性(在VGG那些网络中使用非线性了,但是计算量也上去了),结果是GCN好一点。
    这里写图片描述
    同时还比较了堆叠小卷积核数量的影响,随着卷积核数目减少性能也减少了,结果还是GCN好一点:
    这里写图片描述

  • 其他实验:为了进一步分析GCN是如何影响分割结果的,即论证GCN是通过引入密集连接来提高分类结果,通过BR来细化边缘分割,论文将评分分为两部分:

    • boundary region:像素点接近分割边缘的部分,取distance<7
    • internal region: 除了boundary region剩余的像素

    实验结果如下(k=15):
    这里写图片描述
    可以看到GCN有效的提高了Internal的评分,BR提高了Boundary的评分.

预训练模型

考虑到大卷积核的优点,很自然的把GCN的思想应用到ResNet152上,论文提出了一个ResNet-GCN结构,如下图:

这里写图片描述

为了尽可能同条件比较,将Resnet-GCN精心选择k达到和原Resnet相似的计算量和参数量,先在ImageNet2015上预训练,再在PASCAL VOC 2012微调,考虑到Resnet152计算成本太高,这里选择Resnet50来比较,模型结构如下:
这里写图片描述

实验结果如下:

这里写图片描述

可以看到,在ImageNet上Resnet-GCN的结果稍差,但是在semantic segmentation上表现好了很多,可以有信心的得出GCN有助于提高分割性能

PASCAL VOC 2012

在PASCAL VOC 2012数据集上,

先使用MS COCO数据集预训练,训练分为三个阶段:

  • Stage-1: 将COCO,BSD,标准的PASCAL VOC 2012混合成一个109,892数据集训练
  • Stage-2: 使用BSD,PASCAL VOC 2012预训练调整模型
  • Stage-3: 只使用PASCAL VOC 2012

输入图片在Stage-1会填充到640×640,在Stage2-3会填充到512×512,实验结果如下:

这里写图片描述

GCN+BR效果较好,同时后添加CRF会进一步提升.

提交到PASCAL VOC 2012获得了state-of-the-art:

这里写图片描述

Cityscapes

Cityscapes的图片大小固定为1024×2048,这对于模型太大了,故先随机将图片裁剪成800×800来训练,同时将GCN的k从15提升到25,最后的特征图大小为25×25。训练一共分为2个阶段:

  • Stage-1 : 混合标注粗糙和精细的图片,一共22973张,一起训练
  • Stage-2 : 只对训练集上图片做微调

结果如下:
这里写图片描述

在测试阶段,将图片切分为4个1024×1024,融合预测分数.提交得到了state-of-the-art成绩:

这里写图片描述

Conclusion

给出分析结果:大核有助于提高分类结果,全卷积可以保持分割信息,使用BR可以细化分割结果。后面的实验给出了验证,实验效果还可以。
但是把,提交的结果中分成了多个训练阶段,其中各种的训练技巧不好复现啊。

模型代码分析

github上复现的模型代码: Pytorch

GCN模块的实现:

这里写图片描述

# many are borrowed from https://github.com/ycszen/pytorch-ss/blob/master/gcn.pyclass _GlobalConvModule(nn.Module):    def __init__(self, in_dim, out_dim, kernel_size):        super(_GlobalConvModule, self).__init__()        pad0 = (kernel_size[0] - 1) / 2        pad1 = (kernel_size[1] - 1) / 2        # kernel size had better be odd number so as to avoid alignment error        super(_GlobalConvModule, self).__init__()        self.conv_l1 = nn.Conv2d(in_dim, out_dim, kernel_size=(kernel_size[0], 1),                                 padding=(pad0, 0)) # 左kx1卷积        self.conv_l2 = nn.Conv2d(out_dim, out_dim, kernel_size=(1, kernel_size[1]),                                 padding=(0, pad1)) # 左1xk卷积        self.conv_r1 = nn.Conv2d(in_dim, out_dim, kernel_size=(1, kernel_size[1]),                                 padding=(0, pad1)) # 右1xk卷积        self.conv_r2 = nn.Conv2d(out_dim, out_dim, kernel_size=(kernel_size[0], 1),                                 padding=(pad0, 0)) # 右kx1卷积    def forward(self, x):        x_l = self.conv_l1(x)        x_l = self.conv_l2(x_l)        x_r = self.conv_r1(x)        x_r = self.conv_r2(x_r)        x = x_l + x_r   # sum操作        return x

BF模块实现:

这里写图片描述

class _BoundaryRefineModule(nn.Module):    def __init__(self, dim):        super(_BoundaryRefineModule, self).__init__()        self.relu = nn.ReLU(inplace=True)        self.conv1 = nn.Conv2d(dim, dim, kernel_size=3, padding=1)  # 分支3x3卷积        self.conv2 = nn.Conv2d(dim, dim, kernel_size=3, padding=1)  # 分支3x3卷积    def forward(self, x):        residual = self.conv1(x)        residual = self.relu(residual)  # Conv + ReLU        residual = self.conv2(residual) # Conv        out = x + residual  # sum操作        return out

基于Resnet152的整体架构

这里写图片描述

class GCN(nn.Module):    def __init__(self, num_classes, input_size, pretrained=True):        super(GCN, self).__init__()        self.input_size = input_size        resnet = models.resnet152()        if pretrained:            resnet.load_state_dict(torch.load(res152_path))        self.layer0 = nn.Sequential(resnet.conv1, resnet.bn1, resnet.relu)        self.layer1 = nn.Sequential(resnet.maxpool, resnet.layer1)        self.layer2 = resnet.layer2        self.layer3 = resnet.layer3        self.layer4 = resnet.layer4        # 所有GCN模块        self.gcm1 = _GlobalConvModule(2048, num_classes, (7, 7))        self.gcm2 = _GlobalConvModule(1024, num_classes, (7, 7))        self.gcm3 = _GlobalConvModule(512, num_classes, (7, 7))        self.gcm4 = _GlobalConvModule(256, num_classes, (7, 7))        # 所有BR模块        self.brm1 = _BoundaryRefineModule(num_classes)        self.brm2 = _BoundaryRefineModule(num_classes)        self.brm3 = _BoundaryRefineModule(num_classes)        self.brm4 = _BoundaryRefineModule(num_classes)        self.brm5 = _BoundaryRefineModule(num_classes)        self.brm6 = _BoundaryRefineModule(num_classes)        self.brm7 = _BoundaryRefineModule(num_classes)        self.brm8 = _BoundaryRefineModule(num_classes)        self.brm9 = _BoundaryRefineModule(num_classes)        initialize_weights(self.gcm1, self.gcm2, self.gcm3, self.gcm4, self.brm1, self.brm2, self.brm3,                           self.brm4, self.brm5, self.brm6, self.brm7, self.brm8, self.brm9)    def forward(self, x):        # if x: 512        fm0 = self.layer0(x)  # 256        fm1 = self.layer1(fm0)  # 128        fm2 = self.layer2(fm1)  # 64        fm3 = self.layer3(fm2)  # 32        fm4 = self.layer4(fm3)  # 16        gcfm1 = self.brm1(self.gcm1(fm4))  # 16        gcfm2 = self.brm2(self.gcm2(fm3))  # 32        gcfm3 = self.brm3(self.gcm3(fm2))  # 64        gcfm4 = self.brm4(self.gcm4(fm1))  # 128        # 上采样融合输出        fs1 = self.brm5(F.upsample_bilinear(gcfm1, fm3.size()[2:]) + gcfm2)  # 32        fs2 = self.brm6(F.upsample_bilinear(fs1, fm2.size()[2:]) + gcfm3)  # 64        fs3 = self.brm7(F.upsample_bilinear(fs2, fm1.size()[2:]) + gcfm4)  # 128        fs4 = self.brm8(F.upsample_bilinear(fs3, fm0.size()[2:]))  # 256        out = self.brm9(F.upsample_bilinear(fs4, self.input_size))  # 512        return out
阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 卖烧纸 寒衣烧纸 烧纸怎么写 烧纸包装袋 七七烧纸 十一烧纸 有事请烧纸 寒食烧纸 给活人烧纸 人死后烧纸 梦到烧纸 烧纸的来历 梦见烧纸车 五七烧纸有什么讲究 梦见给死人烧纸 梦见坟前烧纸 梦见死人烧纸 写活人名字烧纸会怎样 阴历十月一烧纸有什么规矩 女人梦见烧纸什么预兆 烧纸图片搞笑 给死人烧纸的表情包 给老人烧纸写法的图片 烧纸图片搞笑表情包 给去世的人烧纸怎么写 烧纸批发价格 烧纸表文怎么写 环保烧纸造纸机 印烧纸的机器多少钱 上坟烧纸图片 批发烧纸多少钱一吨 给去世的人烧纸讲究 烧纸批发一捆多少钱 烧纸生产设备 烧纸印刷机价格 什么时候烧纸 烧纸造纸厂设备 来月经可以烧纸吗 给死人烧纸怎么写 小事烧纸大事挖坟 烧纸制作设备