深度学习论文精读[6]:UNet++

article/2025/8/24 8:02:36

0db5b2c77fa0a2e90235848d384b607b.jpeg

UNet的编解码结构一经提出以来,大有统一深度学习图像分割之势,后续基于UNet的改进方案也经久不衰,一些研究者也在从网络结构本身来思考UNet的有效性。比如说编解码网络应该取几层,跳跃连接是否能够有更多的变化以及什么样的结构训练起来更加有效等问题。UNet本身是针对医学图像分割任务而提出来的网络结构,该任务不像自然图像分割,对分割精度要求并不是十分严格。但对于医学图像而言,器官和病灶的分割则要求极高的精确性,因为很多时候分割效果的好坏直接关系到对应的临床诊断决策。出于上述两个方面的动机,即设计更好的UNet结构和提升医学图像分割的精度,相关研究者提出了一种嵌套的UNet结构(Nested UNet),也叫UNet++,提出UNet++的论文为UNet++: A Nested U-Net Architecture for Medical Image Segmentation,发表于2018年的医学图像计算和计算机辅助干预(Medical Image Computing and Computer Assisted Intervention,MICCAI)会议上。

UNet++取名为嵌套的UNet,就在于其整体编解码网络结构中还嵌套了编解码的子网络(sub-networks),在此基础上重新设计UNet中间的跳跃连接,并补充了深监督机制加速网络训练收敛。完整的UNet++结构如下图所示。

3d8ea073864e63002bc3bbc95c502e0c.png

图中黑色部分为原始的UNet结构,包括编码器下采样、解码器上采样和黑色虚线的跳跃连接三个部分;绿色部分即嵌套的UNet子网络,包括卷积和上采样两部分,而蓝色虚线部分就是UNet++重新设计后的跳跃连接,这部分跟DenseNet的密集连接类似,这里是为子网络提供跳跃连接;最上面红黑连线则是UNet++补充的深监督机制,目的是为了网络能够顺利得到训练。

下面我们从结构设计的角度来对UNet++进行解读。关于UNet结构,最首要的问题就是网络应该有几层,原始的UNet结构用了4层下采样和4层上采样,那么是不是4层就足以满足所有的分割任务需要?答案是否定的。通过本节之前的网络结构分析,我们已经知道,浅层网络能够提取图像粗粒度特征,获取图像基本形态;深层网络能够提取图像的抽象特征,获取图像语义信息,总之浅有浅的侧重,深有深的好处。同之前RefineNet的观点一样,UNet++的作者认为,不管是浅层、深层还是中层,所有层次的特征对于最后的分割都是重要的。有的数据分割任务简单,图像信息单一,可能浅层网络就足以达到很好的效果,而有的数据任务复杂,图像信息丰富,可能需要更深层的网络结构才能达到不错的效果,之前的UNet结构设计很难同时照顾到这种普适性。而UNet++通过设计不同深度的嵌套UNet子网络来实现这种普适性,所以UNet的深度到这里就解决了。

第二个问题则是加入不同深度的嵌套网络后,跳跃连接部分该如何调整。在UNet中,跳跃连接由同层编码器直连到编码器上采样对应层。但加入嵌套子网络后,UNet中原先的长连接就不复存在了,取而代之的是各子网络中的短连接。UNet++的作者们认为,长连接在UNet中是有必要的,能够将图像中前后信息联系起来,对于下采样造成的信息损失有很好的补充作用。所以,UNet++又参考DenseNet的密集连接设计,给嵌套网络补充了长连接,如下图5所示。

88d0015a4fd980c2976db0dacbe9b805.png

但是这样又带来了第三个问题:反向传播的时候中间部分可能会收不到由损失函数反传回来的梯度。所以见招拆招,UNet++又通过深监督的方法来强行加梯度,帮助网络正常进行训练。但深监督对于UNet++的好处绝不仅仅限于此,通过不同深监督损失函数,UNet++可以通过网络剪枝来实现可伸缩性。所以,总结来说UNet++相较于原始的UNet,有如下两个优势:

(1)通过嵌套子网络和长短连接来整合不同层次的图像特征,使得网络分割精度更高;

(2)灵活的网络结构配合深监督机制,让参数量巨大的深度网络在可接受的精度范围内能够大幅度的缩减参数量。

UNet++与UNet等网络分割效果对比如下图所示。

d282f25e9f66497b5273fdde1a4e058d.png

UNet++也进一步壮大了UNet家族网络,后续基于其的改进版本也有很多,比如Attention UNet++、UNet 3+等。下述代码给出了UNet++的一个实现参考。完整代码可参考:

https://github.com/4uiiurz1/pytorch-nested-unet/blob/master/archs.py

class NestedUNet(nn.Module):def __init__(self, num_classes, input_channels=3, deep_supervision=False, **kwargs):super().__init__()nb_filter = [32, 64, 128, 256, 512]self.deep_supervision = deep_supervisionself.pool = nn.MaxPool2d(2, 2)self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)self.conv0_0 = VGGBlock(input_channels, nb_filter[0], nb_filter[0])self.conv1_0 = VGGBlock(nb_filter[0], nb_filter[1], nb_filter[1])self.conv2_0 = VGGBlock(nb_filter[1], nb_filter[2], nb_filter[2])self.conv3_0 = VGGBlock(nb_filter[2], nb_filter[3], nb_filter[3])self.conv4_0 = VGGBlock(nb_filter[3], nb_filter[4], nb_filter[4])self.conv0_1 = VGGBlock(nb_filter[0]+nb_filter[1], nb_filter[0], nb_filter[0])self.conv1_1 = VGGBlock(nb_filter[1]+nb_filter[2], nb_filter[1], nb_filter[1])self.conv2_1 = VGGBlock(nb_filter[2]+nb_filter[3], nb_filter[2], nb_filter[2])self.conv3_1 = VGGBlock(nb_filter[3]+nb_filter[4], nb_filter[3], nb_filter[3])self.conv0_2 = VGGBlock(nb_filter[0]*2+nb_filter[1], nb_filter[0], nb_filter[0])self.conv1_2 = VGGBlock(nb_filter[1]*2+nb_filter[2], nb_filter[1], nb_filter[1])self.conv2_2 = VGGBlock(nb_filter[2]*2+nb_filter[3], nb_filter[2], nb_filter[2])self.conv0_3 = VGGBlock(nb_filter[0]*3+nb_filter[1], nb_filter[0], nb_filter[0])self.conv1_3 = VGGBlock(nb_filter[1]*3+nb_filter[2], nb_filter[1], nb_filter[1])self.conv0_4 = VGGBlock(nb_filter[0]*4+nb_filter[1], nb_filter[0], nb_filter[0])if self.deep_supervision:self.final1 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)self.final2 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)self.final3 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)self.final4 = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)else:self.final = nn.Conv2d(nb_filter[0], num_classes, kernel_size=1)def forward(self, input):x0_0 = self.conv0_0(input)x1_0 = self.conv1_0(self.pool(x0_0))x0_1 = self.conv0_1(torch.cat([x0_0, self.up(x1_0)], 1))x2_0 = self.conv2_0(self.pool(x1_0))x1_1 = self.conv1_1(torch.cat([x1_0, self.up(x2_0)], 1))x0_2 = self.conv0_2(torch.cat([x0_0, x0_1, self.up(x1_1)], 1))x3_0 = self.conv3_0(self.pool(x2_0))x2_1 = self.conv2_1(torch.cat([x2_0, self.up(x3_0)], 1))x1_2 = self.conv1_2(torch.cat([x1_0, x1_1, self.up(x2_1)], 1))x0_3 = self.conv0_3(torch.cat([x0_0, x0_1, x0_2, self.up(x1_2)], 1))x4_0 = self.conv4_0(self.pool(x3_0))x3_1 = self.conv3_1(torch.cat([x3_0, self.up(x4_0)], 1))x2_2 = self.conv2_2(torch.cat([x2_0, x2_1, self.up(x3_1)], 1))x1_3 = self.conv1_3(torch.cat([x1_0, x1_1, x1_2, self.up(x2_2)], 1))x0_4 = self.conv0_4(torch.cat([x0_0, x0_1, x0_2, x0_3, self.up(x1_3)], 1))if self.deep_supervision:output1 = self.final1(x0_1)output2 = self.final2(x0_2)output3 = self.final3(x0_3)output4 = self.final4(x0_4)return [output1, output2, output3, output4]else:output = self.final(x0_4)return output

根据读者对于本系列的反馈,后续相关内容会逐步更新到深度学习语义分割与实战指南GitHub地址:

https://github.com/luwill/Semantic-Segmentation-Guide

往期精彩:

 深度学习论文精读[1]:FCN全卷积网络

 深度学习论文精读[2]:UNet网络

 深度学习论文精读[3]:SegNet

 深度学习论文精读[4]:RefineNet

 深度学习论文精读[5]:Attention UNet

 讲解视频来了!机器学习 公式推导与代码实现开录!

 完结!《机器学习 公式推导与代码实现》全书1-26章PPT下载


http://chatgpt.dhexx.cn/article/5ElueEe7.shtml

相关文章

【Unet系列】(三)Unet++网络

一、UNet整体网络结构 Unet主要是探索encoder和decoder需要多大的问题,以此为基础,提出了融合不同尺寸Unet结构的网络。创新点就是把不同尺寸的Uent结构融入到了一个网络里。 二、结构的好处 (1)不管哪个深度的特征有效&#…

深度学习零基础学习之路——第四章 UNet-Family中Unet、Unet++和Unet3+的简介

Python深度学习入门 第一章 Python深度学习入门之环境软件配置 第二章 Python深度学习入门之数据处理Dataset的使用 第三章 数据可视化TensorBoard和TochVision的使用 第四章 UNet-Family中Unet、Unet和Unet3的简介 第五章 个人数据集的制作 Unet-Family的学习 Python深度学习…

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

憨批的语义分割重制版6——Pytorch 搭建自己的Unet语义分割平台 注意事项学习前言什么是Unet模型代码下载Unet实现思路一、预测部分1、主干网络介绍2、加强特征提取结构3、利用特征获得预测结果 二、训练部分1、训练文件详解2、LOSS解析 训练自己的Unet模型一、数据集的准备二、…

UNet详解(附图文和代码实现)

卷积神经网络被大规模的应用在分类任务中,输出的结果是整个图像的类标签。但是UNet是像素级分类,输出的则是每个像素点的类别,且不同类别的像素会显示不同颜色,UNet常常用在生物医学图像上,而该任务中图片数据往往较少…

条纹进度条

最开始学习qml的时候,想实现一个条纹进度条,当时还不熟悉动画,做不出来,只做了个静止的。qml学习和使用了快1年之际,把这个遗憾弥补下。 先上效果图,颜色取自Bootflat。 以下是源码,先上Stripe…

数据批量插入与逐条插入分析

简述 今天抽空做了一下使用Jdbc对数据操作的实际性能。在平时开发过程中我们经常会使用Hibernate来操作数据库,所以我们很少会去使用批量插入数据。一般都是通过hibernate的insert、addSave等方法来一条条地插入数据。所以很少去考虑这个问题。下面是针对Jdbc进行的…

css滚动条

此部分针对webkit内核的浏览器,使用伪类来改变滚动条的默认样式,详情如下: 滚动条组成部分 1. ::-webkit-scrollbar 滚动条整体部分2. ::-webkit-scrollbar-thumb 滚动条里面的小方块,能向上向下移动(或向左向右移动…

计算机组成原理(五)-一条指令是怎么被执行的

什么是指令: 程序代码的本质就是一条一条的指令,我们需要通过编码的方式让CPU知道我们需要它干什么,最后由译码器翻译成一条条的机器指令。机器指令主要有两部分组成:操作码、地址码。地址码直接给出操作数和操作数的地址&#x…

CSS 斜条纹进度条动画

这是第一版进度条 ,用css写的.但是后续因为数据不同,要显示不同的颜色和数据,所以又改了一版,直接用的el-progress.自定义的样式.对于新手小白来说比较友好.先上这一版代码. <div class"state"><span>开机时间</span><!-- 进度条 --><div…

Acrobat DC 更改背景颜色会有一条条白色横纹

解决方法如下&#xff1a; 编辑->首选项->页面显示->取消 使用2D图形加速

turtle模块还能这样玩?(一条条金龙鱼、雨景)

文章目录 一条条金龙鱼雨景 Python的turtle模块不仅可以用来绘制一些基本的图形&#xff0c;还有与图片结合&#xff0c;做出一些特殊的效果&#xff0c;还可以用来做二维小游戏。本篇是介绍用turtle模块做出的一幅动态的鱼儿游过的画面和动态的雨景图 一条条金龙鱼 1、先看一…

2.Python # 代码注释

2. # 代码注释 文章目录 2. # 代码注释1. 什么是代码注释2. 注释语法3. 注释位置1. 注释在代码的上一行2. 注释在代码的末端 4. 课堂练习 1. 什么是代码注释 代码注释即对代码进行批注说明。 相当于给一个英文单词批注中文释义。 【温馨提示】注释是给程序员自己看的&#xf…

python:导入第三方库greenlet,gevent方法

greenlet&#xff0c;gevent greenlet&#xff0c;gevent是python支持的第三方库&#xff0c;它们可以帮助我们完成协程的使用&#xff0c;其中greenlet是手动调换方式&#xff08;switch方法&#xff09;&#xff0c;gevent是自动调换方式&#xff08;遇到IO操作&#xff09;…

python gevent使用

对大部分语言来说&#xff0c;经常用到并发来处理一些情况。比如必须要多次查询数据库&#xff0c;多次请求API&#xff0c;python内置的gevent就很简单好用。传参&#xff0c;获取返回值&#xff0c;捕获协程的错误都很方便。 直接上例子&#xff1a; import gevent as gevent…

指定Geany使用的Python版本

本文介绍&#xff1a; 在win7下配置Geany,使其使用 Python 3 因为电脑上安装了不同版本的Python&#xff0c;需要根据实际情况来进行版本切换。 第一步&#xff1a;首先点击"生成"按钮的三角箭头&#xff0c;再点击"设置生成命令" 第二步&#xff1a;在弹出…

ModuleNotFountError:No module named ‘gensim‘(在python代码中导入gensim模块)

运行窗口&#xff1a; conda install 模块 pip install 模块 第一个命令应该由于网速太慢没有下载完全&#xff0c;第二个命令我手动输入n退出了。 网速慢&#xff0c;可以使用如下命令&#xff1a; pip install -i https://pypi.douban.com/simple gensim pip install -i…

Python学习笔记--图例 legend

Python学习笔记--图例 legend 参靠视频:《Python数据可视化分析 matplotlib教程》链接&#xff1a;https://www.bilibili.com/video/av6989413/?p6 所用的库及环境: IDE:Pycharm Python环境&#xff1a;python3.7 Matplotlib: Matplotlib 1.11 Numpy&#xff1a; Numpy1.1…

【pybind11笔记】eigen与numpy数据交互

系列文章 【pybind11笔记】eigen与numpy数据交互 【pybind11笔记】python调用c函数 【pybind11笔记】python调用c结构体 【pybind11笔记】python调用c类 文件结构 为了方便演示&#xff0c;我们使用cmake构建该样例&#xff0c;文件结构如下&#xff1a; pybind11与eigen…

python学习笔记:问题一,Geany编辑器无法使用中文注释

python学习笔记&#xff1a; 问题一&#xff1a; Geany编辑器无法使用中文注释 Geany编译python时运行弹出SyntaxError: (unicode error) ‘utf-8’ codec can’t提升&#xff0c;文本编辑器Geany无法使用中文注释&#xff0c;可以设置一下文本编码格式就好了设置方法为&…

Python--注释

Python--注释 <font size4, colorblue> 一、Python中注释的形式<font size4, colorblue> 1、单行注释&#xff1a;使用“#”符号注释<font size4, colorblue> 2、多行注释&#xff1a;使用一对三个英文单引号注释<font size4, colorblue> 3、多行注释&…