LRNNet :上海交大最新提出0.68M超轻量实时语义分割模型,简化Non-local计算量-程序员宅基地

点击上方“AI算法修炼营”,选择“星标”公众号

精选作品,第一时间送达

论文地址:https://arxiv.org/pdf/2006.02706.pdf

本文是上海交通大学团队提出的轻量级实时语义分割算法。本文主要从视觉注意力机制中的non-local 模块出发,通过对non-local模块的简化,使得整体模型计算量更少、参数量更小、占用内存更少。在Cityscapes测试集上,没有预训练步骤和额外的后处理过程,最终LRNNET模型在GTX 1080Ti显卡上的速度为71FPS,获得了72.2% mIoU,整体模型的参数量仅有0.68M。

简介

语义分割可以看作是逐像素分类的任务,它可以为图像中的每个像素分配特定的预定义类别。该任务广泛应用于在自动驾驶领域。开发轻量,高效和实时的语义分割方法对于语义分割算法实际应用至关重要。在这些属性中,轻量级可能是最重要的属性,因为使用较小规模的网络可以导致更快的速度和更高的计算效率,或者更容易获得内存成本。

随着视觉注意力机制在计算机视觉领域的广泛应用。语义分割中也采用non-local网络来建模远程依赖(具体的Non-local网络的讲解可以参考公众号视觉注意力机制系列文章,在文末有推荐)。但是,针对每个像素建模位置关系计算和存储成本都非常大。本文利用spatial regional dominant singular vectors 得到更简洁但更具表示能力的non-local 模块,更加有效地建模远程依赖关系并进行全局特征选择。

本文的主要贡献:

1、本文提出了分解因子卷积块(FCB),通过更适当的方式处理远程依赖关系和短距离的特征来构建轻量级且高效的特征提取网络。

2、本文提出的高效简化Non-local模块,其利用了区域性奇异向量可产生更多的简化特征和代表性特征,以对远程依赖关系和全局特征选择进行建模。

3、实验显示了LRNNet在Cityscapes数据集和Camvid数据集上兼顾了计算速度和分割精度,都具有不错的表现。

本文方法

首先,来介绍一下Cascade RCNN。2018年CVPR上的Cascade RCNN算法通过级联多个检测器来不断优化结果,每个检测器都基于不同的IoU阈值来界定正负样本,前一个检测器的输出作为后一个检测器的输入,并且检测器越靠后,IoU的阈值越高。

LRNNet整体结构:(a)LRNNet的编码器,由分解因子卷积块(FCB)和下采样模块组成 (b)LRNNet带SVN模块的解码器。上分支以不同比例的区域优势奇异向量来执行非局部运算。红色箭头表示1×1卷积,可调整通道大小以形成瓶颈结构。分类器(classifier)包括一个3×3卷积,然后是一个1×1卷积。

编码器环节

LRNNet编码器大致来看是由三个阶段的ResNet形式组成。在每个阶段的开始都使用下采样单元用于对各个阶段提取的特征图进行过渡。编码器环节的核心组件是分解因子卷积FCB(Factorized Convolution Block)单元,可提供轻量级且高效的特征提取。同时,在最后一个下采样单元之后,采用了空洞卷积上输出特征图的分辨率保持在1/8。

FCB(Factorized Convolution Block)

具有较大空洞率的空洞卷积核在空间中接收复杂的远程空间信息特征,并且在空间中需要更多参数。同时,具有较小空洞率的空洞卷积核在空间中接收简单的或较少信息的短距离特征,而只需要较少参数就足够了。因此FCB(上图(c))首先将通道拆分成两组,然后在两组通道中分别用两个一维卷积处理短距离和空间较少的信息特征,这样会大大降低参数和计算量。将两个通道合并后,FCB利用2维卷积来扩大感受野捕获远距离特征,并使用深度可分离卷积来减少参数和计算量。最后设置了通道混洗操作

LEDNet中的SS-nbt模块(具体代码可以用来参考,复现本文的模型)

def Split(x):
    c = int(x.size()[1])
    c1 = round(c * 0.5)
    x1 = x[:, :c1, :, :].contiguous()
    x2 = x[:, c1:, :, :].contiguous()
    return x1, x2 


def Merge(x1,x2):
    return torch.cat((x1,x2),1) 
    
def Channel_shuffle(x,groups):
    batchsize, num_channels, height, width = x.data.size()
    
    channels_per_group = num_channels // groups
    
    #reshape
    x = x.view(batchsize,groups,
        channels_per_group,height,width)
    
    x = torch.transpose(x,1,2).contiguous()
    
    #flatten
    x = x.view(batchsize,-1,height,width)
    
    return x


class SS_nbt_module_paper(nn.Module):
    def __init__(self, chann, dropprob, dilated):        
        super().__init__()
        oup_inc = chann//2
        
        #dw
        self.conv3x1_1_l = nn.Conv2d(oup_inc, oup_inc, (3,1), stride=1, padding=(1,0), bias=True)
        self.conv1x3_1_l = nn.Conv2d(oup_inc, oup_inc, (1,3), stride=1, padding=(0,1), bias=True)
        self.bn1_l = nn.BatchNorm2d(oup_inc, eps=1e-03)
        self.conv3x1_2_l = nn.Conv2d(oup_inc, oup_inc, (3,1), stride=1, padding=(1*dilated,0), bias=True, dilation = (dilated,1))
        self.conv1x3_2_l = nn.Conv2d(oup_inc, oup_inc, (1,3), stride=1, padding=(0,1*dilated), bias=True, dilation = (1,dilated))
        self.bn2_l = nn.BatchNorm2d(oup_inc, eps=1e-03)
        
        #dw
        self.conv3x1_1_r = nn.Conv2d(oup_inc, oup_inc, (3,1), stride=1, padding=(1,0), bias=True)
        self.conv1x3_1_r = nn.Conv2d(oup_inc, oup_inc, (1,3), stride=1, padding=(0,1), bias=True)
        self.bn1_r = nn.BatchNorm2d(oup_inc, eps=1e-03)
        self.conv3x1_2_r = nn.Conv2d(oup_inc, oup_inc, (3,1), stride=1, padding=(1*dilated,0), bias=True, dilation = (dilated,1))
        self.conv1x3_2_r = nn.Conv2d(oup_inc, oup_inc, (1,3), stride=1, padding=(0,1*dilated), bias=True, dilation = (1,dilated))
        self.bn2_r = nn.BatchNorm2d(oup_inc, eps=1e-03)       
        
        self.relu = nn.ReLU(inplace=True)
        self.dropout = nn.Dropout2d(dropprob)
        # self.channel_shuffle = PermutationBlock(2)
       
    
    def forward(self, x):
    
        residual = x
    
        x1, x2 = Split(x)
    
        output1 = self.conv3x1_1_l(x1)
        output1 = self.relu(output1)
        output1 = self.conv1x3_1_l(output1)
        output1 = self.bn1_l(output1)
        output1_mid = self.relu(output1)
        output2 = self.conv1x3_1_r(x2)
        output2 = self.relu(output2)
        output2 = self.conv3x1_1_r(output2)
        output2 = self.bn1_r(output2)
        output2_mid = self.relu(output2)
        output1 = self.conv3x1_2_l(output1_mid)
        output1 = self.relu(output1)
        output1 = self.conv1x3_2_l(output1)
        output1 = self.bn2_l(output1)
      
        output2 = self.conv1x3_2_r(output2_mid)
        output2 = self.relu(output2)
        output2 = self.conv3x1_2_r(output2)
        output2 = self.bn2_r(output2)
        if (self.dropout.p != 0):
            output1 = self.dropout(output1)
            output2 = self.dropout(output2)
        out = Merge(output1, output2)
        
        out = F.relu(residual + out)
        # out = self.channel_shuffle(out)   ### channel shuffle
        out = Channel_shuffle(out,2)   ### channel shuffle
        return out
        # return    ### channel shuffle


解码器环节

LRNNet的解码器环节主要有上下两个分支,上分支是对Non-local模块进行简化来捕获长距离依赖关系的信息,下分支是一个分类器(classifier),其结构包括一个3×3卷积,然后是一个1×1卷积。解码器环节中最重要的SVN模块,这也是简化Non-local模块的根本。

SVN模块

本文通过两种方式降低了Non-local的计算成本:1、通过Conv1和Conv2两个1x1卷积以减少non-local计算操作的通道数;2、用区域主导的奇异向量(spatial regional dominant singular vectors)替换key和value。

SVN由两个分支组成,下分支是输入的残差连接,上分支是简化的non-local操作。在上分支中,将C′×H×W特征图划分为S = H×W/H′×W′(S远小于N = WH)个空间子区域,其尺度为C′×H′×W′。对于每个子区域,将其展平为大小为C'×(H'W')的矩阵,然后使用幂迭代算法( Power Iteration Algorithm)有效地计算其主导的奇异左向量(C'×1)。主导的奇异左向量(C'×1)与展平方式无关,并且此属性类似于合并。然后将区域主导奇异向量用作Non-local运算S集合下的Keys和Value,而来自特征的Queries位置向量(C′×1)提前映射。为了增强简化的non-local模块,还执行了多尺度区域提取,并收集了不同尺度的优势奇异向量作为key和value。

幂迭代算法如下:

实验与结果

实验配置:在Cityscapes数据集上使用480×360图像进行训练和测试。采用单个GTX 1080Ti进行训练和测试。

消融实验:

在消融实验中,将不带SVN的LRNNet表示为model A,带单尺度的SVN(有64(8×8)个子区域)表示为model B和带多尺度的SVN(有8×8 + 4×4个子区域)表示model C。

FCB的消融

ERFNet 和LEDNet 仅使用一维因式分解内核来处理短距离和长距离特征。FCB能够利用一维分解因子小卷积核捕获短程特征的同时利用二维大卷积核捕获远程特征。

SVN的消融

与其他模型对比实验

从表格可以看出,在Cityscapes测试集上,没有预训练步骤和额外的后处理过程,最终LRNNET模型在GTX 1080Ti显卡上的速度为71FPS,获得了72.2% mIoU,整体模型的参数量仅有0.68M

更多实验细节可以参考原文。


视觉注意力机制系列


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sinat_17456165/article/details/106581254

智能推荐

攻防世界_难度8_happy_puzzle_攻防世界困难模式攻略图文-程序员宅基地

文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文

达梦数据库的导出(备份)、导入_达梦数据库导入导出-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作  导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释:   cwy_init/init_123..._达梦数据库导入导出

js引入kindeditor富文本编辑器的使用_kindeditor.js-程序员宅基地

文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js

STM32学习过程记录11——基于STM32G431CBU6硬件SPI+DMA的高效WS2812B控制方法-程序员宅基地

文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6

计算机网络-数据链路层_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输-程序员宅基地

文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输

软件测试工程师移民加拿大_无证移民,未受过软件工程师的教育(第1部分)-程序员宅基地

文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...

随便推点

Thinkpad X250 secure boot failed 启动失败问题解决_安装完系统提示secureboot failure-程序员宅基地

文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure

C++如何做字符串分割(5种方法)_c++ 字符串分割-程序员宅基地

文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割

2013第四届蓝桥杯 C/C++本科A组 真题答案解析_2013年第四届c a组蓝桥杯省赛真题解答-程序员宅基地

文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答

基于供需算法优化的核极限学习机(KELM)分类算法-程序员宅基地

文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。

metasploitable2渗透测试_metasploitable2怎么进入-程序员宅基地

文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入

Python学习之路:从入门到精通的指南_python人工智能开发从入门到精通pdf-程序员宅基地

文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf