当前位置: 首页 > news >正文

ENet——实时语义分割的深度神经网络架构与代码实现

概述

在移动设备上执行实时像素级分割任务具有重要意义。现有的基于分割的深度神经网络需要大量的浮点运算,并且通常需要较长时间才能投入使用。本文提出的ENet架构旨在减少潜在的计算负担。ENet在保持或提高分割精度的同时,相比现有的分割网络,速度提升了18倍,参数量减少了79倍。
在这里插入图片描述
论文地址:https://arxiv.org/abs/1606.02147

介绍

随着增强现实设备、智能家居设备和自动驾驶技术的兴起,将语义分割算法移植到性能较低的移动设备上变得尤为迫切。语义分割算法对图像中的每个像素进行分类标记。近期,大规模数据集的可用性以及强大的计算资源(如GPU和TPU)的出现,推动了卷积神经网络在超越传统计算机视觉算法方面取得了显著进展。尽管卷积网络在分类和识别任务上取得了良好的效果,但在进行像素级分割时,往往产生粗糙的空间结果。因此,通常需要将其他算法(如基于颜色的分割、条件随机场等)与之结合,以增强分割结果。

为了实现图像的空间分类和精细分割,已经出现了如SegNet、FCN等网络结构,这些结构通常基于VGG-16等大型多分类网络。然而,这些网络由于参数量大和推理时间长,不适用于要求图像处理速度超过10fps的移动设备或电池供电的应用设备。

本文提出的网络结构旨在实现快速推理和高准确率的分割。

相关工作

语义分割在图像理解和目标检测中扮演着关键角色,尤其在增强现实和自动驾驶中,其实时性要求极高。当前的计算机视觉应用普遍采用深度神经网络,其中场景分析较好的卷积网络采用编码-解码结构,受自编码器启发。SegNet中的编码-解码结构得到了改进,编码部分类似于VGG的卷积网络用于分类,解码部分主要用于上采样。但这些网络的参数量大,导致推理时间长。SegNet通过移除VGG16的全连接层来减少浮点运算和内存占用,但其轻量级网络仍无法实现实时分割。

其他结构采用简单的分类器后接CRF作为后处理步骤,但这些复杂的后处理操作常常无法准确标记图像中的小物体。CNN结合RNN可以提高准确率,但无法解决速度问题。RNN可作为后处理手段与其他技术结合使用。

网络结构

在这里插入图片描述

ENet网络结构参考了ResNet,描述为一个主分支和一个带有卷积核的附加分支,最后通过像素级相加进行融合。每个block包含三个卷积层:一个1x1的映射用于维度减少,一个主卷积层,一个1x1的扩张。在这些层之间穿插批量归一化(BN)层和PReLU层,定义为bottleneck模型。如果bottleneck进行下采样,则在主分支上添加最大池化层,并将第一个1x1的映射替换为2x2、步长为2的卷积,对激活值进行padding以匹配feature map尺寸。卷积核大小为3x3,类型包括普通卷积、空洞卷积和转置卷积,有时用1x5或5x1的非对称卷积替换。为进行正则化,在bottleneck2.0之前使用p=0.01,其他条件下使用p=0.1。

ENet的初始部分包含一个单独的block,第一阶段包含五个bottleneck部分。第二、三阶段结构相同,但第三阶段开始时没有下采样过程。前三个阶段为编码阶段,第四、五阶段为解码阶段。为减少内核调用和内存占用,网络未使用偏差项。在每个卷积层和pReLU层之间添加BN层。在解码网络部分,最大池化层被最大上采样层代替,padding被无偏差项的空间卷积替换。ENet在最后一个上采样过程中未使用最大池化索引,因为输入图像的通道数为3,而输出通道数为类别数。最终,使用全卷积模型作为网络的最后部分,占用部分解码网络的处理时间。
在这里插入图片描述

设计选择

Feature map尺寸:在语义分割中,对图像进行下采样存在两个缺点:一是分辨率减少导致空间位置信息损失,如边界信息;二是全像素级分割要求输出与输入尺寸相同,意味着下采样后必须进行同等程度的上采样,增加了模型大小和计算资源。为解决第一个问题,ENet采用SegNet的方式,保留最大池化过程中最大值的索引,以在解码网络中生成稀疏的上采样maps。考虑到内存需求,避免过度下采样,以免影响分割准确率。

下采样的优点:下采样后的图像进行卷积操作具有较大的感受野,有助于获取更多上下文信息,有利于不同类别的区分。研究发现使用空洞卷积效果更佳。

Early downsampling:为实现良好的分割效果和实时操作,需处理大尺寸输入图像,这非常消耗资源。ENet的前两个block大幅减小输入尺寸,同时只使用部分feature map。由于可视化信息具有高度空间冗余,可以压缩为更有效的表达形式。网络初始部分作为特征提取和预处理输入。

解码尺寸大小:SegNet具有高度对称的网络结构,而ENet是非对称的,包含较大的编码层和较小的解码网络。编码层类似于原始分类网络,处理较小数据并进行信息处理和滤波,解码网络对编码网络输出进行上采样,微调细节。

非线性操作:研究发现,去除网络初始层中的大部分ReLU层可提升分割效果,认为网络深度不足。随后,将所有ReLU替换为PReLUs,为每张feature map增加额外参数。

信息保留的维度变化:下采样是必要的,但剧烈维度衰减不利于信息流动。为解决这一问题,ENet在池化层后接卷积层以增加维度,进而增加计算资源。将池化和卷积操作并行执行,然后进行拼接。在ResNet结构中,下采样时第一个1x1映射使用步长为2的卷积,丢弃了约75%的输入信息。将卷积核增至2x2有助于信息保留。

分解卷积核:二维卷积可分解为两个一维卷积核(nxn -> nx1, 1xn)。ENet使用5x1, 1x5的非对称卷积,减小过拟合风险,增加感受野。卷积核分解还减少了参数量,加上非线性处理,使计算功能更丰富。

空洞卷积:ENet将bottleneck中的卷积层替换为空洞卷积并进行串联,增大感受野,提高分割的IOU。

正则化:现有分割数据集有限,网络训练易过拟合。ENet采用空间Dropout进行处理。

代码PyTorch实现

initial block

class InitialBlock(nn.Module):def __init__(self,in_channels,out_channels):super(InitialBlock, self).__init__()self.conv = nn.Conv2d(in_channels, out_channels-in_channels, kernel_size=3, stride=2,padding=1, bias=False)self.pool = nn.MaxPool2d(kernel_size=3,stride=2,padding=1)self.bn = nn.BatchNorm2d(out_channels)self.relu = nn.PReLU()def forward(self, x):return self.relu(self.bn(torch.cat([self.conv(x),self.pool(x)],dim=1)))

bottleneck module

class RegularBottleneck(nn.Module):def __init__(self,in_places,places, stride=1, expansion = 4,dilation=1,is_relu=False,asymmetric=False,p=0.01):super(RegularBottleneck, self).__init__()mid_channels = in_places // expansion        self.bottleneck = nn.Sequential(Conv1x1BNReLU(in_places, mid_channels, False),AsymmetricConv(mid_channels, 1, is_relu) if asymmetric else Conv3x3BNReLU(mid_channels, mid_channels, 1,dilation, is_relu),Conv1x1BNReLU(mid_channels, places,is_relu),nn.Dropout2d(p=p))self.relu = nn.ReLU(inplace=True) if is_relu else nn.PReLU()def forward(self, x):residual = x        out = self.bottleneck(x)out += residual        out = self.relu(out)return outclass DownBottleneck(nn.Module):def __init__(self,in_places,places, stride=2, expansion = 4,is_relu=False,p=0.01):super(DownBottleneck, self).__init__()mid_channels = in_places // expansion        self.bottleneck = nn.Sequential(Conv2x2BNReLU(in_places, mid_channels, is_relu),Conv3x3BNReLU(mid_channels, mid_channels, 1, 1, is_relu),Conv1x1BNReLU(mid_channels, places,is_relu),nn.Dropout2d(p=p))self.downsample = nn.MaxPool2d(3,stride=stride,padding=1,return_indices=True)self.relu = nn.ReLU(inplace=True) if is_relu else nn.PReLU()def forward(self, x):out = self.bottleneck(x)residual,indices = self.downsample(x)n, ch, h, w = out.size()ch_res = residual.size()[1]padding = torch.zeros(n, ch - ch_res, h, w)residual = torch.cat((residual, padding), 1)out += residual        out = self.relu(out)return out, indicesclass UpBottleneck(nn.Module):def __init__(self,in_places,places, stride=2, expansion = 4,is_relu=True,p=0.01):super(UpBottleneck, self).__init__()mid_channels = in_places // expansion        self.bottleneck = nn.Sequential(Conv1x1BNReLU(in_places,mid_channels,is_relu),TransposeConv3x3BNReLU(mid_channels,mid_channels,stride,is_relu),Conv1x1BNReLU(mid_channels,places,is_relu),nn.Dropout2d(p=p))self.upsample_conv = Conv1x1BN(in_places, places)self.upsample_unpool = nn.MaxUnpool2d(kernel_size=2)self.relu = nn.ReLU(inplace=True) if is_relu else nn.PReLU()def forward(self, x, indices):out = self.bottleneck(x)residual = self.upsample_conv(x)residual = self.upsample_unpool(residual,indices)out += residual        out = self.relu(out)return

architecture

class ENet(nn.Module):def __init__(self, num_classes):super(ENet, self).__init__()self.initialBlock = InitialBlock(3,16)self.stage1_1 = DownBottleneck(16, 64, 2)self.stage1_2 = nn.Sequential(RegularBottleneck(64, 64, 1),RegularBottleneck(64, 64, 1),RegularBottleneck(64, 64, 1),RegularBottleneck(64, 64, 1),)self.stage2_1 = DownBottleneck(64, 128, 2)self.stage2_2 = nn.Sequential(RegularBottleneck(128, 128, 1),RegularBottleneck(128, 128, 1, dilation=2),RegularBottleneck(128, 128, 1, asymmetric=True),RegularBottleneck(128, 128, 1, dilation=4),RegularBottleneck(128, 128, 1),RegularBottleneck(128, 128, 1, dilation=8),RegularBottleneck(128, 128, 1, asymmetric=True),RegularBottleneck(128, 128, 1, dilation=16),)self.stage3 = nn.Sequential(RegularBottleneck(128, 128, 1),RegularBottleneck(128, 128, 1, dilation=2),RegularBottleneck(128, 128, 1, asymmetric=True),RegularBottleneck(128, 128, 1, dilation=4),RegularBottleneck(128, 128, 1),RegularBottleneck(128, 128, 1, dilation=8),RegularBottleneck(128, 128, 1, asymmetric=True),RegularBottleneck(128, 128, 1, dilation=16),)self.stage4_1 = UpBottleneck(128, 64, 2, is_relu=True)self.stage4_2 = nn.Sequential(RegularBottleneck(64, 64, 1, is_relu=True),RegularBottleneck(64, 64, 1, is_relu=True),)self.stage5_1 = UpBottleneck(64, 16, 2, is_relu=True)self.stage5_2 = RegularBottleneck(16, 16, 1, is_relu=True)self.final_conv = nn.ConvTranspose2d(in_channels=16, out_channels=num_classes, kernel_size=3, stride=2, padding=1,output_padding=1, bias=False)def forward(self, x):x = self.initialBlock(x)x,indices1 = self.stage1_1(x)x = self.stage1_2(x)x, indices2 = self.stage2_1(x)x = self.stage2_2(x)x = self.stage3(x)x = self.stage4_1(x, indices2)x = self.stage4_2(x)x = self.stage5_1(x, indices1)x = self.stage5_2(x)out = self.final_conv(x)return

相关文章:

ENet——实时语义分割的深度神经网络架构与代码实现

概述 在移动设备上执行实时像素级分割任务具有重要意义。现有的基于分割的深度神经网络需要大量的浮点运算,并且通常需要较长时间才能投入使用。本文提出的ENet架构旨在减少潜在的计算负担。ENet在保持或提高分割精度的同时,相比现有的分割网络&#xf…...

游戏领域AI智能视频剪辑解决方案

游戏行业作为文化创意产业的重要组成部分,其发展和创新速度令人瞩目。然而,随着游戏内容的日益丰富和直播文化的兴起,传统的视频剪辑方式已难以满足玩家和观众日益增长的需求。美摄科技,凭借其在AI智能视频剪辑领域的深厚积累和创…...

腾讯云轻量2核2G3M云服务器优惠价格61元一年,限制200GB月流量

腾讯云轻量2核2G3M云服务器优惠价格61元一年,配置为轻量2核2G、3M带宽、200GB月流量、40GB SSD盘,腾讯云优惠活动 yunfuwuqiba.com/go/txy 活动链接打开如下图: 腾讯云轻量2核2G云服务器优惠价格 腾讯云:轻量应用服务器100%CPU性能…...

leecode 331 |验证二叉树的前序序列化 | gdb 调试找bug

计算的本质是数据的计算 数据的计算需要采用格式化的存储, 规则的数据结果,可以快速的按照指定要求存储数据 这里就不得不说二叉树了,二叉树应用场景真的很多 本题讲的是,验证二叉树的前序序列化 换言之,不采用建立树的…...

服务器安全事件应急响应排查方法

针对服务器操作系统的安全事件也非常多的。攻击方式主要是弱口令攻击、远程溢出攻击及其他应用漏洞攻击等。分析安全事件,找到入侵源,修复漏洞,总结经验,避免再次出现安全事件,以下是参考网络上文章,总结的…...

数码视讯Q7盒子刷armbian或emuelec的一些坑

首先,我手头的盒子是nand存储的,如果是emmc的,会省事很多…… 以下很多结论是我的推测,不一定准确。 1,原装安卓系统不支持SD卡或U盘启动,所以只能进uboot修改启动参数 2,原装安卓系统应该是…...

2_2.Linux中的远程登录服务

# 一.Openssh的功能 # 1.sshd服务的用途# #作用:可以实现通过网络在远程主机中开启安全shell的操作 Secure SHell >ssh ##客户端 Secure SHell daemon >sshd ##服务端 2.安装包# openssh-server 3.主配置文件# /etc/ssh/sshd_conf 4.…...

Spring Boot集成JPA快速入门demo

1.JPA介绍 JPA (Java Persistence API) 是 Sun 官方提出的 Java 持久化规范。它为 Java 开发人员提供了一种对象/关联映射工具来管理 Java 应用中的关系数据。他的出现主要是为了简化现有的持久化开发工作和整合 ORM 技术,结束现在 Hibernate,TopLink&am…...

深度学习理解及学习推荐(持续更新)

主推YouTuBe和Bilibili 深度学习博主推荐: Umar Jamil - YouTubehttps://www.youtube.com/umarjamilai StatQuest with Josh Starmer - YouTubehttps://www.youtube.com/statquest RNN Illustrated Guide to Recurrent Neural Networks: Understanding the Int…...

【C语言】贪吃蛇【附源码】

欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 一、游戏说明: 一个基于C语言链表开发的贪吃蛇游戏: 1. 按方向键上下左右,可以实现蛇移动方向的改变。 2. 短时间长按方向键上下左右其中之一,可实现蛇向该方向的短时间…...

【技巧】压缩文件如何设置“自动加密”?

很多人会在压缩文件的时候,同时设置密码,以此保护私密文件。如果经常需要压缩文件并设置密码,不妨使用解压缩软件的“自动加密”功能,更省时省力。 下面介绍WinRAR解压缩软件的两种“自动加密”的方法,一起来看看吧&a…...

内网穿透时报错【Bad Request This combination of host and port requires TLS.】的原因

目录 前言:介绍一下内网穿透 1.内网直接https访问(可以正常访问) 程序配置的证书 2.内网穿透后,通过外网访问 3.原因 4.内网非https的Web应用,使用https后,也变成了https访问 5.题外话 感觉自己的web应用配置了…...

计算机网络:物理层 - 信道复用

计算机网络:物理层 - 信道复用 频分复用时分复用统计时分复用波分复用码分复用 计算机网络中,用户之间通过信道进行通信,但是信道是有限的,想要提高网络的效率,就需要提高信道的利用效率。因此计算机网络中普遍采用信道…...

【算法集训】基础算法:滑动窗口

定义一个快慢指针,用于截取数组中某一段信息。同时可以改变快慢指针的值来获取结果,这个过程比较像滑动。 1493. 删掉一个元素以后全为 1 的最长子数组 定义快慢指针快指针先走,如果到了第二个0上的时候。前面1的个数就是fast - slow - 1&a…...

QT 二维坐标系显示坐标点及点与点的连线-通过定时器自动添加随机数据点

QT 二维坐标系显示坐标点及点与点的连线-通过定时器自动添加随机数据点 功能介绍头文件C文件运行过程 功能介绍 上面的代码实现了一个简单的 Qt 应用程序,其功能包括: 创建一个 MainWindow 类,继承自 QMainWindow,作为应用程序的…...

C语言TCP服务器模型 : select + 多线程与双循环单线程阻塞服务器的比较

观察到的实验现象: 启动三个客户端: 使用双循环阻塞服务器:只能accept后等待收发,同时只能与一个客户端建立连接,必须等已连接的客户端多次收发 明确断开后才能与下个客户端连接 使用IO多路复用select:可以同时接收所有的连接请求,并且连接状态一直是存活的,直到客户端关闭连…...

【数字IC/FPGA】手撕代码:模3检测器(判断输入序列能否被3整除)

今天我们来手撕一个常见的笔试题,使用的方法是三段式Moore状态机。 题目描述: 输入端口是串行的1bit数据,每个时钟周期进来一位新数据后,实时检查当前序列是否能整除3,若能则输出1,否则输出0。 例如&#…...

最小可行产品需要最小可行架构——可持续架构(三)

前言 最小可行产品(MVP)的概念可以帮助团队专注于尽快交付他们认为对客户最有价值的东西,以便在投入大量时间和资源之前迅速、廉价地评估产品的市场规模。MVP不仅需要考虑产品的市场可行性,还需要考虑其技术可行性,以…...

笔记: 数据结构与算法--时间复杂度二分查找数组

算法复杂度 不依赖于环境因素事前分析法 计算最坏情况的时间复杂度每一条语句的执行时间都按照t来计算 时间复杂度 大O表示法 n 数据量 ; f(n) 实际的执行条数当存在一个n0 , 使得 n > n0,并且 c * g(n) 恒> f(n) : 渐进上界(算法最坏的情况)那么f(n)的时间复杂度 …...

AI绘画教程:Midjourney使用方法与技巧从入门到精通

文章目录 一、《AI绘画教程:Midjourney使用方法与技巧从入门到精通》二、内容介绍三、作者介绍🌤️粉丝福利 一、《AI绘画教程:Midjourney使用方法与技巧从入门到精通》 一本书读懂Midjourney绘画,让创意更简单,让设计…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

线程同步:确保多线程程序的安全与高效!

全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

零基础设计模式——行为型模式 - 责任链模式

第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...

在Ubuntu24上采用Wine打开SourceInsight

1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...