实时语义分割---PIDNet论文笔记

这篇具有很好参考价值的文章主要介绍了实时语义分割---PIDNet论文笔记。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

PIDNet是2023年发表在CVPR上的实时语义分割网络,在推理速度和准确性之间实现了最佳平衡,其中该系列的PIDNet-S在Cityscapes 测试集上达到93.2 FPS + 78.6% mIOU。

论文和开源代码在这里。

解决的问题:传统双分支网络低层的细节信息和高层语义信息直接融合,会导致细节特征很容易被上下文信息淹没,即文中的overshoot。

思路:提出一种三分支网络架构,分别解析细节、上下文和边界信息,并设计边界注意力引导融合模块(Bag)融合三个分支的特征。实时语义分割---PIDNet论文笔记

图1 Cityscapes测试集中实时分割模型推理速度与准确度之间的权衡

为了在推理速度和准确度之间取得最佳平衡,研究人员投入了大量精力来重新设计网络架构,可以概括为:轻量级编码器和解码器(卷积分解及分组卷积)、多尺度输入以及双分支网络。具体而言,SwiftNet使用一个低分辨率输入来获得高级语义,使用另一个高分辨率输入来为其轻量级解码器提供足够的细节。DFANet通过修改基于深度可分离卷积的Xception的架构,引入了一种轻量级主干,并缩减了输入大小以提高推理速度。ShuffleSeg采用Shuffle Net作为其主干,它结合了通道混洗和分组卷积,以降低计算成本。然而,这些网络中的大多数仍然是编码器-解码器架构的形式,并且需要信息流通过深层编码器,然后反向通过解码器,给这些模型带来了很大的延迟。此外,由于GPU上深度可分离卷积的优化还不成熟,传统卷积尽管具有更多FLOP和参数,但速度更快。

在语义分割任务中,上下文依赖性可以通过大的感受野来提取,而精确的边界和小范围物体识别则依赖于空间细节信息。在双分支网络如DDRNet中,细节分支的输出分辨率为上下文分支的8倍(BiSeNet中为4倍),它们的直接融合将不可避免地导致过冲现象(overshoot),即物体边界很容易被其周围的像素淹没,小规模物体可能被相邻的大物体淹没。文中用下图来解释过冲现象:
实时语义分割---PIDNet论文笔记

图2 动态系统(左)和图像分割(右)的过冲问题。左图为二阶系统的PI和PID控制器的阶跃响应,右图从第一行到最后一行分别从groundtruth、DDRNet-23和ADB-Bag-DDRNet23的输出中裁剪

PID控制器包含3个具有互补功能的组件:比例(P)控制器表示当前误差,积分(I)控制器累积先前误差,微分(D)控制器预测未来误差变化。在双分支网络中,上下文分支通过级联卷积层或池化层(其实也就是下采样操作)不断聚合从局部到全局的语义信息,以解析像素之间的长距离依赖关系,而细节分支特征图维持高分辨率以保留单个像素的位置信息。因此细节分支和上下文分支可以被视为空间域中的比例和积分控制器,这解释了分割任务中存在过冲问题的原因。

PIDNet网络结构

实时语义分割---PIDNet论文笔记

图3 PIDNet网络结构

PIDNet主要包含以下结构:

  1. 绿色的比例(P)分支解析并保存高分辨率特征图中的细节信息;
  2. 蓝色的积分(I)分支聚合上下文信息,以解析远程依赖关系;
  3. 灰色的导数(D)分支提取高频特征以预测边界区域。

PIDNet的骨干网络采用级联的残差块,也就是ResNet中的BasicBlock和Bottleneck,首先我们介绍P分支中的Pag模块。

Pag:选择性学习高级语义

在PIDNet中,I分支提供的丰富和准确的语义信息对于P分支的细节解析至关重要,P分支包含相对较少的层和通道。因此,可以将I分支作为其他两个分支的备份,使其能够向它们提供所需的信息。
实时语义分割---PIDNet论文笔记

图4 Pag模块结构

如上图,先对I分支和P分支的输入y和x分别做1*1卷积和BN,再将y上采样(因为特征图y的大小为原图的1/16,x为1/8),两者相乘后将结果做逐通道求和,再经过sigmoid得到结果σ,得到Pag的输出为σ * y + (1 - σ) *x
代码实现:

class PagFM(nn.Module):
    def __init__(self, in_channels, mid_channels, after_relu=False, with_channel=False, BatchNorm=nn.BatchNorm2d):
        super(PagFM, self).__init__()
        self.with_channel = with_channel
        self.after_relu = after_relu
        self.f_x = nn.Sequential(
                                nn.Conv2d(in_channels, mid_channels, 
                                          kernel_size=1, bias=False),
                                BatchNorm(mid_channels)
                                )
        self.f_y = nn.Sequential(
                                nn.Conv2d(in_channels, mid_channels, 
                                          kernel_size=1, bias=False),
                                BatchNorm(mid_channels)
                                )
        if with_channel:
            self.up = nn.Sequential(
                                    nn.Conv2d(mid_channels, in_channels, 
                                              kernel_size=1, bias=False),
                                    BatchNorm(in_channels)
                                   )
        if after_relu:
            self.relu = nn.ReLU(inplace=True)
        
    def forward(self, x, y):
        input_size = x.size()
        if self.after_relu:
            y = self.relu(y)
            x = self.relu(x)
        
        y_q = self.f_y(y)
        y_q = F.interpolate(y_q, size=[input_size[2], input_size[3]],
                            mode='bilinear', align_corners=False)##上采样到与x分辨率相同
        x_k = self.f_x(x)
        
        if self.with_channel:
            sim_map = torch.sigmoid(self.up(x_k * y_q))
        else:
            sim_map = torch.sigmoid(torch.sum(x_k * y_q, dim=1).unsqueeze(1)) 
        ##dim=1:逐通道相加,假设x_k * y_q的shape为[4, 32, 32, 64],相加后shape变为[4, 32, 64],再通过unsqueeze(1)升维为[4, 1, 32, 64]
        
        y = F.interpolate(y, size=[input_size[2], input_size[3]],
                            mode='bilinear', align_corners=False)##上采样到与x分辨率相同
        x = (1-sim_map)*x + sim_map*y
        
        return x

PAPPM:上下文特征快速聚合

作者改进了DDRNet中用于聚合不同尺度上下文信息的DAPPM模块,改变DAPPM中的连接并使其并行化,同时缩减了每个尺度的通道数以提高推理速度,提出一种新的上下文信息聚合模块,称为并行聚合PPM(PAPPM)。
实时语义分割---PIDNet论文笔记

图5 DDRNet中的DAPPM模块(左)与PIDNet中的PAPPM模块(右)

代码实现:

class PAPPM(nn.Module):
    def __init__(self, inplanes, branch_planes, outplanes, BatchNorm=nn.BatchNorm2d):
        super(PAPPM, self).__init__()
        bn_mom = 0.1
        self.scale1 = nn.Sequential(nn.AvgPool2d(kernel_size=5, stride=2, padding=2),
                                    BatchNorm(inplanes, momentum=bn_mom),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
                                    )
        self.scale2 = nn.Sequential(nn.AvgPool2d(kernel_size=9, stride=4, padding=4),
                                    BatchNorm(inplanes, momentum=bn_mom),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
                                    )
        self.scale3 = nn.Sequential(nn.AvgPool2d(kernel_size=17, stride=8, padding=8),
                                    BatchNorm(inplanes, momentum=bn_mom),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
                                    )
        self.scale4 = nn.Sequential(nn.AdaptiveAvgPool2d((1, 1)),
                                    BatchNorm(inplanes, momentum=bn_mom),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
                                    )##全局平均池化

        self.scale0 = nn.Sequential(
                                    BatchNorm(inplanes, momentum=bn_mom),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(inplanes, branch_planes, kernel_size=1, bias=False),
                                    )##scale0不做池化
        
        self.scale_process = nn.Sequential(
                                    BatchNorm(branch_planes*4, momentum=bn_mom),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(branch_planes*4, branch_planes*4, kernel_size=3, padding=1, groups=4, bias=False),
                                    )

      
        self.compression = nn.Sequential(
                                    BatchNorm(branch_planes * 5, momentum=bn_mom),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(branch_planes * 5, outplanes, kernel_size=1, bias=False),
                                    )
        
        self.shortcut = nn.Sequential(
                                    BatchNorm(inplanes, momentum=bn_mom),
                                    nn.ReLU(inplace=True),
                                    nn.Conv2d(inplanes, outplanes, kernel_size=1, bias=False),
                                    )


    def forward(self, x):
        width = x.shape[-1]
        height = x.shape[-2]        
        scale_list = []

        x_ = self.scale0(x)
        scale_list.append(F.interpolate(self.scale1(x), size=[height, width],
                        mode='bilinear', align_corners=algc)+x_)
        scale_list.append(F.interpolate(self.scale2(x), size=[height, width],
                        mode='bilinear', align_corners=algc)+x_)
        scale_list.append(F.interpolate(self.scale3(x), size=[height, width],
                        mode='bilinear', align_corners=algc)+x_)
        scale_list.append(F.interpolate(self.scale4(x), size=[height, width],
                        mode='bilinear', align_corners=algc)+x_)
        
        scale_out = self.scale_process(torch.cat(scale_list, 1))
       
        out = self.compression(torch.cat([x_,scale_out], 1)) + self.shortcut(x)
        return out

Bag:平衡细节和上下文

作者设计了一个边界注意力引导融合模块(Bag)来融合三个分支的特征,以边界信息指导细节分支§和上下文分支(I)的融合。上下文分支可以提供准确的语义信息,但丢失了很多空间和几何细节,尤其是边界区域和小物体,而细节分支更好的保留了空间细节信息,Bag模块使得模型沿着边界区域更加信任细节分支,在对象内部区域则更信任上下文特征。实时语义分割---PIDNet论文笔记

图6 Bag(a)和Light Bag(b)模块

当σ > 0.5时,模型更信任细节特征,小于0.5时更信任上下文特征。
代码实现:

##Bag:
class Bag(nn.Module):
    def __init__(self, in_channels, out_channels, BatchNorm=nn.BatchNorm2d):
        super(Bag, self).__init__()

        self.conv = nn.Sequential(
                                BatchNorm(in_channels),
                                nn.ReLU(inplace=True),
                                nn.Conv2d(in_channels, out_channels, 
                                          kernel_size=3, padding=1, bias=False)                  
                                )

        
    def forward(self, p, i, d):
        edge_att = torch.sigmoid(d)
        return self.conv(edge_att*p + (1-edge_att)*i)

##Light-Bag:
class Light_Bag(nn.Module):
    def __init__(self, in_channels, out_channels, BatchNorm=nn.BatchNorm2d):
        super(Light_Bag, self).__init__()
        self.conv_p = nn.Sequential(
                                nn.Conv2d(in_channels, out_channels, 
                                          kernel_size=1, bias=False),
                                BatchNorm(out_channels)
                                )
        self.conv_i = nn.Sequential(
                                nn.Conv2d(in_channels, out_channels, 
                                          kernel_size=1, bias=False),
                                BatchNorm(out_channels)
                                )
        
    def forward(self, p, i, d):
        edge_att = torch.sigmoid(d)
        
        p_add = self.conv_p((1-edge_att)*i + p)
        i_add = self.conv_i(i + edge_att*p)
        
        return p_add + i_add

分割头S/B-Head

分割头的结构比较简单,主要作用是计算辅助损失,文中损失函数的定义如下:
Loss = λ₀l₀ + λ₁l₁ + λ₂l₂ + λ₃l₃
其中l₀是额外的语义损失,l₁是加权二元交叉熵损失,l₂和l₃是边界感知CE loss,在文中设置的权重为λ₀ = 0.4,λ₁ = 20, λ₂ = λ₃ = 1
分割头的代码实现:

class segmenthead(nn.Module):

    def __init__(self, inplanes, interplanes, outplanes, scale_factor=None):
        super(segmenthead, self).__init__()
        self.bn1 = BatchNorm2d(inplanes, momentum=bn_mom)
        self.conv1 = nn.Conv2d(inplanes, interplanes, kernel_size=3, padding=1, bias=False)
        self.bn2 = BatchNorm2d(interplanes, momentum=bn_mom)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(interplanes, outplanes, kernel_size=1, padding=0, bias=True)
        self.scale_factor = scale_factor

    def forward(self, x):
        
        x = self.conv1(self.relu(self.bn1(x)))
        out = self.conv2(self.relu(self.bn2(x)))

        if self.scale_factor is not None:
            height = x.shape[-2] * self.scale_factor
            width = x.shape[-1] * self.scale_factor
            out = F.interpolate(out,
                        size=[height, width],
                        mode='bilinear', align_corners=algc)

        return out

训练和结果

由于论文中的训练的代码比较复杂,博主功力不够看着比较费劲,就没有用论文中的训练策略,而是参考一位大佬的语义分割系列25-BiSeNetV2(pytorch实现)和语义分割系列26-VIT+SETR——Transformer结构如何在语义分割中大放异彩来进行训练和推理可视化的。论文中采用的是yacs库以yaml格式的文件来配置模型的各种参数,包括训练和测试等等的参数,为模型复现、调整参数提供了很多便利,大伙可以一起学习学习,yacs传送门

下面是博主用PIDNet在camvid数据集上的分割结果,训练时没有使用辅助损失,所以分割精度也不是特别高。
实时语义分割---PIDNet论文笔记文章来源地址https://www.toymoban.com/news/detail-402774.html

图7 camvid数据集中的分割结果

到了这里,关于实时语义分割---PIDNet论文笔记的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 论文笔记:ViT Adapter——Transformer与CNN特征融合,屠榜语义分割!

    论文题目:《VISION TRANSFORMER ADAPTER FOR DENSE PREDICTIONS》 会议时间:ICLR 2023 论文地址:https://openreview.net/pdf?id=plKu2GByCNW 源码地址:https://github.com/czczup/ViT-Adapter   Transformer在计算机视觉领域取得了显著的成功,主要得益于transformer的 动态建模能力(dynamic modeling capability) 和 注

    2024年04月15日
    浏览(32)
  • 论文阅读—2023.7.13:遥感图像语义分割空间全局上下文信息网络(主要为unet网络以及改unet)附加个人理解与代码解析

    前期看的文章大部分都是深度学习原理含量多一点,一直在纠结怎么改模型,论文看的很吃力,看一篇忘一篇,总感觉摸不到方向。想到自己是遥感专业,所以还是回归遥感影像去谈深度学习,回归问题,再想着用什么方法解决问题。 1、易丢失空间信息 在 Decoder 阶段输出多

    2024年02月16日
    浏览(44)
  • 语义分割大模型RSPrompter论文阅读

    RSPrompter: Learning to Prompt for Remote Sensing Instance Segmentation based on Visual Foundation Model RSPrompter 摘要 Abstract—Leveraging vast training data (SA-1B), the foundation Segment Anything Model (SAM) proposed by Meta AI Research exhibits remarkable generalization and zero-shot capabilities. Nonetheless, as a category-agnostic instance segmen

    2024年02月12日
    浏览(51)
  • 关联分割点云中的实例和语义<论文>

    题目:Associatively Segmenting Instances and Semantics in Point Clouds 代码:https://github.com/WXinlong/ASIS 文章讨论: Instances Segmentation 和 Semantics Segmentation 实例Instances Segmentation:分辨出每个单独事物,但不知道是否是一类 语义Semantics Segmentation:分辨出不同类事物,但不知道每类事物具体

    2024年02月03日
    浏览(39)
  • 语义分割大模型SAM论文阅读(二)

    Segment Anything SAM 我们介绍了分割一切(SA)项目:一个新的图像分割任务,模型和数据集。在数据收集循环中使用我们的高效模型,我们建立了迄今为止(到目前为止)最大的分割数据集,在1100万张许可和尊重隐私的图像上拥有超过10亿个掩模。 该模型被设计和训练为提示 ,因此它

    2024年02月13日
    浏览(43)
  • 3D点云分割系列5:RandLA-Net:3D点云的实时语义分割,随机降采样的重生

    《RandLA-Net: Efficient Semantic Segmentation of Large-Scale Point Clouds》发布于CVPR 2020。 在自动驾驶等领域,高效的分割网络是目前最基本和最关键的研究方向。目前存在的一些点云处理方法包括PointNet、PointNet++、PointCNN、KPConv等方法,或多或少都存在效率不高或是特征采样不足的情况,

    2024年02月04日
    浏览(49)
  • 【语义分割】ST_Unet论文 逐步代码解读

    主要工程文件为这5个 分别作用为: 构造相应的deform 卷积 DCNN的残差网络 编写相应的配置文件,可以改变相应参数 模型的主函数和主框架 模型的连接部分 代码框架由3部分组成,encode,decode和decode中将图像还原成语义分割预测图 Transformer(config, img_size) 组成编码部分,包含主

    2024年02月07日
    浏览(43)
  • BiSeNet:用于实时语义分割的双边分割网络——BiSeNet:Bilateral Segmentation Network for Real-time Semantic Segmentation

            语义分割需要丰富的空间信息和较大的感受野。然而,现代的方法通常为了实现实时推断速度而牺牲空间分辨率,导致性能下降。本文提出了一种新的双边分割网络(BiSeNet)来解决这个问题。我们首先设计了一个具有小步长的空间路径来保留空间信息并生成高分

    2024年04月28日
    浏览(39)
  • 【论文阅读】Swin Transformer Embedding UNet用于遥感图像语义分割

    Swin Transformer Embedding UNet for Remote Sensing Image Semantic Segmentation 全局上下文信息是遥感图像语义分割的关键 具有强大全局建模能力的Swin transformer 提出了一种新的RS图像语义分割框架ST-UNet型网络(UNet) 解决方案:将Swin transformer嵌入到经典的基于cnn的UNet中 ST-UNet由Swin变压器和CNN并联

    2024年02月08日
    浏览(58)
  • 【论文阅读】MCTformer: 弱监督语义分割的多类令牌转换器

    Multi-class Token Transformer for Weakly Supervised Semantic Segmentation 本文提出了一种新的基于变换的框架来学习类特定对象定位映射作为弱监督语义分割(WSSS)的伪标签    可以利用标准视觉转换器中一个类令牌的参与区域来形成与类无关的定位映射,我们研究了转换器模型是否也可以通

    2024年04月17日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包