实时语义分割---PIDNet论文笔记-程序员宅基地

技术标签: 论文阅读  计算机视觉  

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

论文开源代码在这里。

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

思路:提出一种三分支网络架构,分别解析细节、上下文和边界信息,并设计边界注意力引导融合模块(Bag)融合三个分支的特征。图1 Cityscapes测试集中实时分割模型推理速度与准确度之间的权衡

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

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

在语义分割任务中,上下文依赖性可以通过大的感受野来提取,而精确的边界和小范围物体识别则依赖于空间细节信息。在双分支网络如DDRNet中,细节分支的输出分辨率为上下文分支的8倍(BiSeNet中为4倍),它们的直接融合将不可避免地导致过冲现象(overshoot),即物体边界很容易被其周围的像素淹没,小规模物体可能被相邻的大物体淹没。文中用下图来解释过冲现象:
图2 动态系统(左)和图像分割(右)的过冲问题。左图为二阶系统的PI和PID控制器的阶跃响应,右图从第一行到最后一行分别从groundtruth、DDRNet-23和ADB-Bag-DDRNet23的输出中裁剪

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

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

PIDNet网络结构

图3 PIDNet网络结构

图3 PIDNet网络结构

PIDNet主要包含以下结构:

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

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

Pag:选择性学习高级语义

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

图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)。
图5 DDRNet中的DAPPM模块(左)与PIDNet中的PAPPM模块(右)

图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模块使得模型沿着边界区域更加信任细节分支,在对象内部区域则更信任上下文特征。图6 Bag(a)和Light Bag(b)模块

图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数据集上的分割结果,训练时没有使用辅助损失,所以分割精度也不是特别高。
在这里插入图片描述

图7 camvid数据集中的分割结果
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/hswei8808/article/details/127771153

智能推荐

5个超厉害的资源搜索网站,每一款都可以让你的资源满满!_最全资源搜索引擎-程序员宅基地

文章浏览阅读1.6w次,点赞8次,收藏41次。生活中我们无时不刻不都要在网站搜索资源,但就是缺少一个趁手的资源搜索网站,如果有一个比较好的资源搜索网站可以帮助我们节省一大半时间!今天小编在这里为大家分享5款超厉害的资源搜索网站,每一款都可以让你的资源丰富精彩!网盘传奇一款最有效的网盘资源搜索网站你还在为找网站里面的资源而烦恼找不到什么合适的工具而烦恼吗?这款网站传奇网站汇聚了4853w个资源,并且它每一天都会持续更新资源;..._最全资源搜索引擎

Book类的设计(Java)_6-1 book类的设计java-程序员宅基地

文章浏览阅读4.5k次,点赞5次,收藏18次。阅读测试程序,设计一个Book类。函数接口定义:class Book{}该类有 四个私有属性 分别是 书籍名称、 价格、 作者、 出版年份,以及相应的set 与get方法;该类有一个含有四个参数的构造方法,这四个参数依次是 书籍名称、 价格、 作者、 出版年份 。裁判测试程序样例:import java.util.*;public class Main { public static void main(String[] args) { List <Book>_6-1 book类的设计java

基于微信小程序的校园导航小程序设计与实现_校园导航微信小程序系统的设计与实现-程序员宅基地

文章浏览阅读613次,点赞28次,收藏27次。相比于以前的传统手工管理方式,智能化的管理方式可以大幅降低学校的运营人员成本,实现了校园导航的标准化、制度化、程序化的管理,有效地防止了校园导航的随意管理,提高了信息的处理速度和精确度,能够及时、准确地查询和修正建筑速看等信息。课题主要采用微信小程序、SpringBoot架构技术,前端以小程序页面呈现给学生,结合后台java语言使页面更加完善,后台使用MySQL数据库进行数据存储。微信小程序主要包括学生信息、校园简介、建筑速看、系统信息等功能,从而实现智能化的管理方式,提高工作效率。

有状态和无状态登录

传统上用户登陆状态会以 Session 的形式保存在服务器上,而 Session ID 则保存在前端的 Cookie 中;而使用 JWT 以后,用户的认证信息将会以 Token 的形式保存在前端,服务器不需要保存任何的用户状态,这也就是为什么 JWT 被称为无状态登陆的原因,无状态登陆最大的优势就是完美支持分布式部署,可以使用一个 Token 发送给不同的服务器,而所有的服务器都会返回同样的结果。有状态和无状态最大的区别就是服务端会不会保存客户端的信息。

九大角度全方位对比Android、iOS开发_ios 开发角度-程序员宅基地

文章浏览阅读784次。发表于10小时前| 2674次阅读| 来源TechCrunch| 19 条评论| 作者Jon EvansiOSAndroid应用开发产品编程语言JavaObjective-C摘要:即便Android市场份额已经超过80%,对于开发者来说,使用哪一个平台做开发仍然很难选择。本文从开发环境、配置、UX设计、语言、API、网络、分享、碎片化、发布等九个方面把Android和iOS_ios 开发角度

搜索引擎的发展历史

搜索引擎的发展历史可以追溯到20世纪90年代初,随着互联网的快速发展和信息量的急剧增加,人们开始感受到了获取和管理信息的挑战。这些阶段展示了搜索引擎在技术和商业模式上的不断演进,以满足用户对信息获取的不断增长的需求。

随便推点

控制对象的特性_控制对象特性-程序员宅基地

文章浏览阅读990次。对象特性是指控制对象的输出参数和输入参数之间的相互作用规律。放大系数K描述控制对象特性的静态特性参数。它的意义是:输出量的变化量和输入量的变化量之比。时间常数T当输入量发生变化后,所引起输出量变化的快慢。(动态参数) ..._控制对象特性

FRP搭建内网穿透(亲测有效)_locyanfrp-程序员宅基地

文章浏览阅读5.7w次,点赞50次,收藏276次。FRP搭建内网穿透1.概述:frp可以通过有公网IP的的服务器将内网的主机暴露给互联网,从而实现通过外网能直接访问到内网主机;frp有服务端和客户端,服务端需要装在有公网ip的服务器上,客户端装在内网主机上。2.简单的图解:3.准备工作:1.一个域名(www.test.xyz)2.一台有公网IP的服务器(阿里云、腾讯云等都行)3.一台内网主机4.下载frp,选择适合的版本下载解压如下:我这里服务器端和客户端都放在了/usr/local/frp/目录下4.执行命令# 服务器端给执_locyanfrp

UVA 12534 - Binary Matrix 2 (网络流‘最小费用最大流’ZKW)_uva12534-程序员宅基地

文章浏览阅读687次。题目:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=93745#problem/A题意:给出r*c的01矩阵,可以翻转格子使得0表成1,1变成0,求出最小的步数使得每一行中1的个数相等,每一列中1的个数相等。思路:网络流。容量可以保证每一行和每一列的1的个数相等,费用可以算出最小步数。行向列建边,如果该格子是_uva12534

免费SSL证书_csdn alphassl免费申请-程序员宅基地

文章浏览阅读504次。1、Let's Encrypt 90天,支持泛域名2、Buypass:https://www.buypass.com/ssl/resources/go-ssl-technical-specification6个月,单域名3、AlwaysOnSLL:https://alwaysonssl.com/ 1年,单域名 可参考蜗牛(wn789)4、TrustAsia5、Alpha..._csdn alphassl免费申请

测试算法的性能(以选择排序为例)_算法性能测试-程序员宅基地

文章浏览阅读1.6k次。测试算法的性能 很多时候我们需要对算法的性能进行测试,最简单的方式是看算法在特定的数据集上的执行时间,简单的测试算法性能的函数实现见testSort()。【思想】:用clock_t计算某排序算法所需的时间,(endTime - startTime)/ CLOCKS_PER_SEC来表示执行了多少秒。【关于宏CLOCKS_PER_SEC】:以下摘自百度百科,“CLOCKS_PE_算法性能测试

Lane Detection_lanedetectionlite-程序员宅基地

文章浏览阅读1.2k次。fromhttps://towardsdatascience.com/finding-lane-lines-simple-pipeline-for-lane-detection-d02b62e7572bIdentifying lanes of the road is very common task that human driver performs. This is important ..._lanedetectionlite

推荐文章

热门文章

相关标签