DeepLab-v3

深度学习语义分割:DeepLabV3改进与多尺度处理

一、待解决的问题

  • 连续下采样和重复池化,导致最后特征图分辨率低
  • 图像中存在多尺度问题
  • 空间不变性导致细节信息丢失(v3未采用CRF)

二、创新点

  • 增加多尺度分割物体的模块,设计了串行和并行的空洞卷积模块,采用不同的膨胀率atrous rates来获取多尺度的信息
  • 优化ASPP模块,加入1*1卷积和编码了全局信息的图像层特征image pooling,进一步挖掘不同尺寸的特征,提升分割效果。
  • 不再需要DenseCRF作为后处理

三、具体细节

1.总结多尺度问题的四个解决方案:

在这里插入图片描述

  • 方案a: 图像金字塔,把图像Resize到不同尺寸,输入到同一个网络中,最后把不同尺寸输入图像得到的特征图Merge到一起作为输出;
  • 方案b:编解码结构,在解码网络不同阶段融合编码网络不同阶段的特征(同时保证低维度的信息不丢失)
  • 方案c:在原始网络输出后添加额外的context后处理模块,例如DenseCRF。或者串联不同膨胀率的空洞卷积模块,来获取不同尺度的信息(图c为后者)。
  • 方案d:ASPP,空洞卷积空间金字塔池化,在输入特征上平行地获取不同尺度的信息,融合后最为输出。
2.更深结构下的空洞卷积

在这里插入图片描述
图a在更深的网络后,连续的下采样虽然增加了感受野,但是会导致最后特征图的分辨率很低,丢失了大量的细节信息,不利于语义分割。
图b采用串联空洞卷积,保持输出步幅在out_stride=16的情况下,保证分辨率,逐步增加感受野。

3.优化ASPP

作者通过实验发现,膨胀率越大,卷积核中的有效权重越少,当膨胀率足够大时,只有卷积核最中间的权重有效,即退化成了1x1卷积核,并不能获取到全局的context信息。3x3的卷积核中有效权重与膨胀率的对应如下:
在这里插入图片描述
为了克服这个问题,作者考虑使用图片级特征。具体来说,在模型最后的特征映射上应用全局平均,将结果经过1×1的卷积,再双线性上采样得到所需的空间维度。最终,作者改进的ASPP包括:

  • 使用了Multi-Grid策略,即在模型后端多加几层不同rate的空洞卷积。每个block中的三个卷积有各自unit rate,例如Multi Grid = (1, 2, 4),block的dilate rate=2,则block中每个卷积的实际膨胀率=2* (1, 2, 4)=(2,4,8)
  • 一个1×1卷积和三个3×3的采样率为rates={6,12,18}的空洞卷积
  • 将BN层加到了ASPP模块中
  • 图像级特征,即将特征做全局平均池化,如下图(b)部分Image Pooling
  • 得到的图像级特征Concat后输入到一个 1×1 convolution with 256 filters(1*1 256滤波器),然后将特征进行双线性上采样(bilinearly upsample)到特定的空间维度。
    在这里插入图片描述
    从代码中看上述几点的具体实现,见注释:
class ASPPPooling(nn.Sequential):
   	def __init__(self, in_channels, out_channels):
        super(ASPPPooling, self).__init__(
        nn.AdaptiveAvgPool2d(1),
        nn.Conv2d(in_channels, out_channels, 1, bias=False),
        nn.BatchNorm2d(out_channels),
        nn.ReLU())

class _ResLayer(nn.Sequential):
    """
    Residual layer with multi grids
    """

    def __init__(self, n_layers, in_ch, out_ch, stride, dilation, multi_grids=None):
        super(_ResLayer, self).__init__()

        if multi_grids is None:
            multi_grids = [1 for _ in range(n_layers)]
        else:
            assert n_layers == len(multi_grids)

        # Downsampling is only in the first block
        for i in range(n_layers):
            self.add_module(
                "block{}".format(i + 1),
                _Bottleneck(
                    in_ch=(in_ch if i == 0 else out_ch),
                    out_ch=out_ch,
                    stride=(stride if i == 0 else 1),
                    dilation=dilation * multi_grids[i],
                    downsample=(True if i == 0 else False),
                ),
            )
            
class _ASPP(nn.Module):
    """
    Atrous spatial pyramid pooling with image-level feature
    """

    def __init__(self, in_ch, out_ch, rates):
        super(_ASPP, self).__init__()
        self.stages = nn.Module()
        self.stages.add_module("c0", _ConvBnReLU(in_ch, out_ch, 1, 1, 0, 1))   # 1 * 1 卷积
        for i, rate in enumerate(rates):
            self.stages.add_module(
                "c{}".format(i + 1),
                _ConvBnReLU(in_ch, out_ch, 3, 1, padding=rate, dilation=rate), 
            ) # rates = [6, 12, 18] 空洞卷积, BN层添加到ASPP中(v2只有卷积层)
        self.stages.add_module("imagepool", ASPPPooling(in_ch, out_ch)) # globel image pooling, 其输入也是网络提取到的特征

    def forward(self, x):
        return torch.cat([stage(x) for stage in self.stages.children()], dim=1)
        
class DeepLabV3(nn.Sequential):
    """
    DeepLab v3: Dilated ResNet with multi-grid + improved ASPP
    """

    def __init__(self, n_classes, n_blocks, atrous_rates, multi_grids, output_stride):
        super(DeepLabV3, self).__init__()

        # Stride and dilation
        if output_stride == 8:
            s = [1, 2, 1, 1]
            d = [1, 1, 2, 4]
        elif output_stride == 16:
            s = [1, 2, 2, 1]
            d = [1, 1, 1, 2]

        ch = [64 * 2 ** p for p in range(6)]
        self.add_module("layer1", _Stem(ch[0]))
        self.add_module("layer2", _ResLayer(n_blocks[0], ch[0], ch[2], s[0], d[0]))
        self.add_module("layer3", _ResLayer(n_blocks[1], ch[2], ch[3], s[1], d[1]))
        self.add_module("layer4", _ResLayer(n_blocks[2], ch[3], ch[4], s[2], d[2]))
        self.add_module(
            "layer5", _ResLayer(n_blocks[3], ch[4], ch[5], s[3], d[3], multi_grids)
        ) # output_stride=16时,d[3] = 2,通过multi_grids,实际bootleneck中空洞卷积dilate rate = d[3] * multi_grides[i]
        self.add_module("aspp", _ASPP(ch[5], 256, atrous_rates))
        concat_ch = 256 * (len(atrous_rates) + 2)
        self.add_module("fc1", _ConvBnReLU(concat_ch, 256, 1, 1, 0, 1)) # 1 * 1 256滤波器
        self.add_module("fc2", nn.Conv2d(256, n_classes, kernel_size=1))

model = DeepLabV3(
        n_classes=21,
        n_blocks=[3, 4, 23, 3],  # 4个block对应的bottleneck数量
        atrous_rates=[6, 12, 18], # ASPP空洞卷积rates
        multi_grids=[1, 2, 4],   # 最后一个block中每个bottleneck对应的grid
        output_stride=16,
    )
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值