【语义分割网络系列】五、PSPNet


参考资料

论文

  PSPNet: Pyramid Scene Parsing Network

博客

  PSPNet:Pyramid Scene Parsing Network

  PSPNet模型学习笔记

  Semantic Segmentation–Pyramid Scene Parsing Network(PSPNet)论文解读


第1章 前言

PSPNet的主要优点如下:

  • 金字塔池化实现多尺度特征融合,提升全局范围信息;
  • 修改Resnet-101 为 ResNet-103、辅助 loss;

 PSPNet是在FCN上的改进,引入更多的上下文信息进行解决,当分割层有更多全局信息时,出现误分割的概率就会低一些,这种思想目前在许多图像领域都有所应用,引入更多上下文信息的方式也很多,比如:增大感受野,这种方式最直观,视野越广,看到的东西也越多,而增大感受野也有许多方式,比如:

  • 空洞卷积(dilated convolution):这是在deeplab算法上成功应用的实现方式;
  • 金字塔池化操作(spatial pyramid pooling):PSPNet的金字塔池化操作也是增加感受野的方式;

 场景解析(Scene Parsing)的难度与场景的标签密切相关。先大多数先进的场景解析框架大多数基于FCN,但FCN存在的几个问题:

在这里插入图片描述

  • Mismatched Relationship(错误匹配):上下文关系匹配对理解复杂场景很重要,例如在上图第一行,在水面上的大很可能是“boat”,而不是“car”。虽然“boat和“car”很像。FCN缺乏依据上下文推断的能力。
  • Confusion Categories(相似的标签): 许多标签之间存在关联,可以通过标签之间的关系弥补。上图第二行,把摩天大厦的一部分识别为建筑物,这应该只是其中一个,而不是二者。这可以通过类别之间的关系弥补。
  • Inconspicuous Classes(小目标丢失):模型可能会忽略小的东西,而大的东西可能会超过FCN接收范围,从而导致不连续的预测。如上图第三行,枕头与被子材质一致,被识别成到一起了。为了提高不显眼东西的分割效果,应该注重小面积物体。

 使用FCN的global average pooling可以提高语义分割结果。然而,我们的实验表明,global average pooling 对于具有挑战性的ADE20K数据来说不够具有代表性。因此,与这种方法不同,我们利用 pyramid scene parsing network ,通过基于不同区域的上下文聚合来利用全局上下文信息


第2章 PSPNet网络结构

2.1 Pyramid Pooling Module(金字塔池化模块)

 有效获得全局语境信息,一般有两种方式:

(1)全局平均池化(GAP global average pooling):是一个利用全局信息的baseline model,通常用于图像分类,也有应用到语义分割的model。但是由于ADE20K的场景复杂性,这种策略不足以覆盖必需的信息。而且像素会被标记,直接融合产生一个向量这会损失空间关系并造成歧义。一个基于子区域的全局上下文信息表示对区分类别是有益的。

(2)空间金字塔池化(SPP):生成的不同级别的特征图最终被展平并拼接起来,然后输入到全连接层中进行分类。该全局先验模块是为消除CNN进行图像分类时需输入固定尺寸图像的这一约束而设计的。为了进一步避免丢失表征不同子区域之间关系的语境信息,我们提出了一个包含不同尺度、不同子区域间关系的分层全局信息。将该金字塔池化模块的输出作为深度神经网络最终的特征图,并称其为Pyramid Pooling Module(全局场景先验信息)


 PSP结构的功能是将获取到的特征层划分成不同大小的网格,每个网格内部各自进行平均池化。实现聚合不同区域的上下文信息,从而提高获取全局信息的能力。

 在PSPNet中,PSP结构典型情况下,会将输入进来的特征层划分成6x6,3x3,2x2,1x1的网格,对应了图片中的绿色、蓝色、橙色、红色的的输出

  • 红色:将输入进来的特征层整个进行平均池化。
  • 橙色:将输入进来的特征层划分为2×2个子区域,然后对每个子区域进行平均池化。
  • 蓝色:将输入进来的特征层划分为3×3个子区域,然后对每个子区域进行平均池化。
  • 绿色:将输入进来的特征层划分为6×6个子区域,然后对每个子区域进行平均池化。

在这里插入图片描述

金字塔池化(Pyramid pooling)融合了四个比例的特征:最粗糙的 1 × 1 1\times1 1×1 是全局尺度的池化,剩下的层次会将图像分为不同子区域,形成不同区域的信息表示

 金字塔池模块中不同level的输出包含比例不同的feature map(比如输入的维度是 2048 ,有四个层次的金字塔,那么输出的维度则为 2048/4=512 )。

 为了保持全局特征的权重,若如果金字塔的数量为 N ,则在每个金字塔级别之后使用 1 × 1 1×1 1×1 卷积层将上下文表示的维度减小到原先的 1 N \frac{1}{N} N1 。然后直接对feature map进行双线性插值,恢复到输入的长宽上。最后,将不同level的特征拼接起来作为金字塔池化的全局特征。

金字塔池化的每个level的大小和总共的level都是可以调整,这跟输入的feature map有关。因此,不同大小应在表示上保持合理的区别。 我们的金字塔池模块是一个四级模块,其大小分别为 1 × 1 1×1 1×1 2 × 2 2×2 2×2 3 × 3 3×3 3×3 6 × 6 6×6 6×6

整体结构如下

  • 基础层经过预训练的模型(ResNet101)和空洞卷积策略提取feature map,提取后的feature map是输入的 1 8 \frac{1}{8} 81大小;
  • feature map经过Pyramid Pooling Module得到融合的带有整体信息的feature,在上采样与池化前的feature map相concat;
  • 最后过一个卷积层得到最终输出;

2.2 基于ResNet的深度监督网络

 深度的预训练网络可带来良好的性能。 但是,网络深度的增加可能会带来一些优化困难。 ResNet通过在每个块中跳接(skip connection)来解决此问题。ResNet较深的层主要根据先前的残差来学习残差。

 相反地,我们建议在监督下产生额外的损失而产生初始结果,然后在损失最终的情况下学习残差。因此,将深度网络的优化分解为两个,每个问题都更易于解决。

在这里插入图片描述

 在上图中,给出了我们增加辅助损失函数后的ResNet101模型的一个示例。除了使用softmax loss训练最终分类器的主要分支外,在第四阶段(即res4b22残差块)后应用另一个分类器。误差反向传播会阻塞辅助损失函数传递到较浅的网络层,我们让这两个损失函数通过在其之前的所有网络层。辅助损失函数有助于优化学习过程,而主分支损失函数承担起了最大的责任。最后,我们还增加权重以平衡辅助损失函数。即两个loss一起传播,使用不同的权重,共同优化参数。后续的实验证明这样做有利于快速收敛

 但是在测试阶段,我们放弃了这个辅助分支,只使用经过良好优化的主分支进行最终预测。但是,这种残差全卷积网络的损失函数训练策略与预先训练的ResNet模型相结合后,在不同的实验参数设置下都着实有效,这表明了该学习策略的普适性。


第3章 训练细节

 论文在ImageNet scene parsing challenge 2016, PASCAL VOC 2012,Cityscapes 三个数据集上做了实验。

项目设置
学习率采用“poly”策略,即 l r = l r b a s e × ( 1 − i t e r m a x i t e r ) p o w e r lr=lr_{base}\times(1-\frac{iter}{max_{iter}})^{power} lr=lrbase×(1maxiteriter)power,设置 l r b a s e = 0.01 , p o w e r = 0.9 lr_{base}=0.01,power=0.9 lrbase=0.01,power=0.9
迭代次数ImageNet上设置150K,PASCAL VOC设置30K,Cityscapes设置90K
数据增强随机翻转、尺寸在0.5到2之间缩放、角度在-10到10之间旋转、随机的高斯滤波
batchsize设置batch=16
训练分支网络设置辅助loss的权重为0.4

第4章 PSP与SPP的异同

(1)任务不同

SPP即空间金字塔池化(Spatial Pyramid Pooling):主要做图像分类和目标检测;

PSP即金字塔场景解析(Pyramid Scene Parsing):主要做语义分割,还需要上采样,进行逐像素的分类,讲究对不同的场景进行解析;


(2)包含关系

 PSP是针对整个网络而言的,整个网络的目的是要在进行语义分割的时候结合场景分析,整个网络称之为PSPNet,而PSPNet这个大的网络里面核心的构建是SPP,PSP是全局的网络架构,而SPP只是网络中的核心构成单元


(3)思路类似

 二者都利用全局场景类别线索,广泛使用了空间金字塔池化技术,使得考虑浅层细小目标以及深层宏观整体目标考虑进来,使得最终的预测结果更为可靠。


第5章 Pytorch实现PSPNet

参考资料

  憨批的语义分割重制版3——Pytorch 搭建自己的PSPNet语义分割平台


 和论文的在backbone上有所不同,主要是品味金字塔池化模块的代码。

import torch
import torchvision.models as models
import torch.nn as nn
from torch.nn import functional as F


class PyramidPool(nn.Module):
    def __init__(self, in_channels, out_channels, pool_size):
        super(PyramidPool, self).__init__()
        self.features = nn.Sequential(
            nn.AdaptiveAvgPool2d(pool_size),
            nn.Conv2d(in_channels, out_channels, 1, bias=True),
            nn.BatchNorm2d(out_channels, momentum=0.95),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        size = x.shape
        output = F.upsample_bilinear(self.features(x), size[2:])
        return output


def initialize_weights(*models):
    for model in models:
        for module in model.modules():
            if isinstance(module, nn.Conv2d) or isinstance(module, nn.Linear):
                nn.init.kaiming_normal(module.weight)
                if module.bias is not None:
                    module.bias.data.zero_()
            elif isinstance(module, nn.BatchNorm2d):
                module.weight.data.fill_(1)
                module.bias.data.zero_()


class PSPNet(nn.Module):
    def __init__(self, num_classes, pretrained=True):
        super(PSPNet, self).__init__()
        print("initializing model")

        self.resnet = models.resnet50(pretrained=pretrained)

        self.layer5a = PyramidPool(2048, 512, 1)
        self.layer5b = PyramidPool(2048, 512, 2)
        self.layer5c = PyramidPool(2048, 512, 3)
        self.layer5d = PyramidPool(2048, 512, 6)

        self.final = nn.Sequential(
            nn.Conv2d(4096, 512, 3, padding=1, bias=False),
            nn.BatchNorm2d(512, momentum=.95),
            nn.ReLU(inplace=True),
            nn.Dropout(.1),
            nn.Conv2d(512, num_classes, 1),
        )

        initialize_weights(self.layer5a, self.layer5b, self.layer5c, self.layer5d, self.final)

    def forward(self, x):
        size = x.size()
        x = self.resnet.conv1(x)
        x = self.resnet.bn1(x)
        x = self.resnet.relu(x)
        x = self.resnet.layer1(x)
        x = self.resnet.layer2(x)
        x = self.resnet.layer3(x)
        x = self.resnet.layer4(x)

        x = self.final(torch.cat([
            x,
            self.layer5a(x),
            self.layer5b(x),
            self.layer5c(x),
            self.layer5d(x),
        ], 1))

        return F.upsample_bilinear(x, size[2:])

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

travellerss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值