图像分割必备知识点 | Unet++超详解+注解
文章来自周纵苇大佬的知乎,是Unet++模型的一作大佬,其在2019年底详细剖析了Unet++模型,讲解的非常好。所以在此做一个搬运+个人的理解。 文中加粗部分为个人做的注解。需要讨论交流的朋友可以加我的微信:cyx645016617,也可以加入我建立的一个氛围超好的AI算法交流群。我只是一个在智能算法路上缓慢前行的应届混子。
参考目录:
1 铺垫
在计算机视觉领域,全卷积网络(FCN)是比较有名的图像分割网络,医学图像处理方向,U-Net可以说是一个更加炙手可热的网络,基本上所有的分割问题,我们都会拿U-Net先看一下基本的结果,然后进行“魔改”。
U-Net和FCN非常的相似,U-Net比FCN稍晚提出来,但都发表在2015年,和FCN相比,U-Net的第一个特点是完全对称,也就是左边和右边是很类似的,而FCN的decoder相对简单,只用了一个deconvolution的操作,之后并没有跟上卷积结构。第二个区别就是skip connection,FCN用的是加操作(summation),U-Net用的是叠操作(concatenation)。这些都是细节,重点是它们的结构用了一个比较经典的思路,也就是编码和解码(encoder-decoder),早在2006年就被Hinton大神提出来发表在了nature上.
当时这个结构提出的主要作用并不是分割,而是压缩图像和去噪声。输入是一幅图,经过下采样的编码,得到一串比原先图像更小的特征,相当于压缩,然后再经过一个解码,理想状况就是能还原到原来的图像。这样的话我们存一幅图的时候就只需要存一个特征和一个解码器即可。这个想法我个人认为是很漂亮了。同理,这个思路也可以用在原图像去噪,做法就是在训练的阶段在原图人为的加上噪声,然后放到这个编码解码器中,目标是可以还原得到原图。
后来把这个思路被用在了图像分割的问题上,也就是现在我们看到的U-Net结构,在它被提出的三年中,有很多很多的论文去讲如何改进U-Net或者FCN,不过这个分割网络的本质的拓扑结构是没有改动的。举例来说,去年ICCV上凯明大神提出的Mask RCNN. 相当于一个检测,分类,分割的集大成者,我们仔细去看它的分割部分,其实使用的也就是这个简单的FCN结构。说明了这种“U形”的编码解码结构确实非常的简洁,并且最关键的一点是好用。
简单的过一下这个网红结构,我们先提取出它的拓扑结构,这样会比较容易分析它的实质,排除很多细节的干扰。
输入是一幅图,输出是目标的分割结果。继续简化就是,一幅图,编码,或者说降采样,然后解码,也就是升采样,然后输出一个分割结果。根据结果和真实分割的差异,反向传播来训练这个分割网络。我们可以说,U-Net里面最精彩的部分就是这三部分:
- 下采样;
- 上采样;
- skip connection (这个就是下采样和上采样得到的相同尺度的特征度在通道维度上的拼接,concatenate)
这个图中的续建表示一个直观的模型处理过程,分割任务的输出和输入的维度相同;至于向下的实线,表示需要对图片降采样(encode过程),向上的实线表示对图片进行上采样(decode过程),上一篇讲解的Unet很好的体现了这个过程
超链接
2 展开
基本的铺垫都已经完成了,看着这个拓扑结构,一个非常广义的问题就是:
这个三年不动的拓扑结构真的一点儿毛病都没有吗?
在这三年中,U-Net得到的超过2500次的引用,FCN接近6000次的引用,大家都在做什么样的改进呢?如果让你在这个经典的结构基础上改进,你会去关注哪些点呢?
首先一个问题是:要多深合适?
这里我想强调的一点是,很多论文给出了他们建议的网络结构,其中包括非常多的细节,比如用什么卷积,用几层,怎么降采样,学习率多少,优化器用什么,这些都是比较直观的参数,其实这些在论文中给出参数并不见得是最好的,所以关注这些的意义不大,一个网络结构,我们真正值得关注的是它的设计传达了什么信息。就拿U-Net来说,原论文给出的结构是原图经过四次降采样,四次上采样,得到分割结果,实际呢,为什么四次?就是作者喜欢呗,或者说当时作者使用的数据集,四次降采样的效果好,我们也可以更再专业一点,四次降采样的接受域或者感受野大小正合适处理图像。或者四次降采样比较适合输入图像的尺寸等等,理由一堆,但是你们真的相信吗?不见得吧。
我先给一个2017年在CVPR上发表的一个名叫PSPNet的分割网络,你会发现,好像整体的架构和U-Net还是像的,只是降采样的数目减小了,当然,他们也针对性的增强了中间的特征抓取环节的复杂性。
要是你觉得这个工作还不够说明4次降采样不是必须的话,我们再来看看Yoshua Bengio组最近的关于图像分割的论文,这是他们提出的结构,名叫提拉米苏。
也是U形结构,但你会发现,他们就只用了三次降采样。所以到底要多深,是不是越深越好?还是一个open quesion。
我想分享的第一个信息就是:关注论文所传递的大方向,不要被论文中的细节局限了创造力。像这种细节参数的调整是属于比较朴素的深度学习方法论,很容易花费你很多时间,而最终并没有自身科研水平的提升。
好,我们回来继续讨论到底需要多深的问题。其实这个是非常灵活的,涉及到的一个点就是特征提取器,U-Net和FCN为什么成功,因为它相当于给了一个网络的框架,具体用什么特征提取器,随便。这个时候,高引就出现了,各种在encoder上的微创新络绎不绝,最直接的就是用ImageNet里面的明星结构来套嘛,前几年的BottleNeck,Residual,还有去年的DenseNet,就比谁出文章快。这一类的论文就相当于从1到10的递进,而U-Net这个低层结构的提出却是从0到1。说句题外话,像这种从1到10的论文,引用往往不会比从0到1的论文高,因为它不自觉的局限了自己的扩展空间,比如我说,我写一篇论文,说特征提取器就必须是dense block,或者必须是residual block效果好,然后名字也就是DenseUNet或者ResUNet,就这样结束了。所以关于backbone到底用什么的问题,并不是我这次要讲的重点。
关于到底要多深这个问题,还有一个引申的问题就是,降采样对于分割网络到底是不是必须的?问这个问题的原因就是,既然输入和输出都是相同大小的图,为什么要折腾去降采样一下再升采样呢?
比较直接的回答当然是降采样的理论意义,我简单朗读一下,它可以增加对输入图像的一些小扰动的鲁棒性,比如图像平移,旋转等,减少过拟合的风险,降低运算量,和增加感受野的大小。升采样的最大的作用其实就是把抽象的特征再还原解码到原图的尺寸,最终得到分割结果。
这些理论的解释都是有道理的,在我的理解中,对于特征提取阶段,浅层结构可以抓取图像的一些简单的特征,比如边界,颜色,而深层结构因为感受野大了,而且经过的卷积操作多了,能抓取到图像的一些说不清道不明的抽象特征,讲的越来越玄学了,总之,浅有浅的侧重,深有深的优势。那我就要问一个比较犀利的问题了,既然浅层特征和深层特征都很重要,U-Net为什么只在4层以后才返回去,也就是只去抓深层特征。我不知道有没有说明白这个问题本身。问题实际是这样的,看这个图,既然 \(X^{1.0}\),\(X^{2.0}\),\(X^{3.0}\),\(X^{4.0}\)所抓取的特征都很重要,为什么我非要降到 \(X^{4.0}\) 层了才开始上采样回去呢?
3 主体
沿着这个逻辑往下捋,不知道大家能否猜到我的下一页会放什么?此处可以摁个暂停。有没有呼之欲出了?如果排除一切其他干扰,既然我们不知道需要多深,是不是会衍生出一系列像这样子的U-Net,它们的深度不一。这个不难理解吧。为了搞清楚是不是越深越好,我们应该对它们做一下实验,看看它们各自的分割表现:
先不要看后两个UNet++,就看这个不同深度的U-Net的表现,我们可以看出,不是越深越好吧,它背后的传达的信息就是,不同层次特征的重要性对于不同的数据集是不一样的,并不是说我设计一个4层的U-Net,就像原论文给出的那个结构,就一定对所有数据集的分割问题都最优。
那么接下来是关键,我们心中的目标很明确了,就是使用浅层和深层的特征!但是总不能训练这些个U-Net吧,未免也太多了。好,这里可以暂停一下,想一想,要你来,你怎么来利用这些不同深度的,各自能抓取不同层次的特征的U-Net。
我把图打出来就很简单了。
我们来看一看,这样是不是把1~4层的U-Net全给连一起了。我们来看它们的子集,包含1层U-Net,2层U-Net,以此类推。这个结构的好处就是我不管你哪个深度的特征有效,我干脆都给你用上,让网络自己去学习不同深度的特征的重要性。第二个好处是它共享了一个特征提取器,也就是你不需要训练一堆U-Net,而是只训练一个encoder,它的不同层次的特征由不同的decoder路径来还原。这个encoder依旧可以灵活的用各种不同的backbone来代替。
可惜的是,这个网络结构是不能被训练的,原因在于,不会由任何梯度会经过这个红色区域,因为它和算loss function的地方是在反向传播时是断开的。我停顿一下,大家想一想是不是这么回事。
这个不能梯度下降训练的问题,仔细看一下图中的虚线就明白了,三角形中的元素与最终的输出是不连接的
请问,如果是你,如何去解决这个问题?
其实既然已经把结构盘成这样了,还是很自然能想到的吧,我这里提供有两个候选的解决方案。
第一个是用deep supervision,强行加梯度是吧,关于这个,我待会儿展开来说。
第二个解决方案是把结构改成这样子:
如果没有我上面说的那一堆话来盘逻辑,这个结构可能不那么容易想到,但是如果顺着思路一步一步的走,这个结构虽然不能说是显而易见,也算是顺理成章的。我的故事还没有讲完,但是我提一句,这个结构由UC Berkeley的团队提出,发表在今年(2019年)的CVPR上,是一个oral的论文,题目是"Deep Layer Aggregation"。
可能就有小伙伴惊呆了,就这样吗,就发了CVPR!?是,也不是。。。这只是他们在论文中给出的关于分割网络结构的改进,他们还做了其他的工作,包括分类,和边缘检测。但是主要的思路就是刚刚盘的那些,目标就是取整合各个不同层次的特征。
这只是一个题外话哦,我们继续来看这个结构,请问,你觉得有什么问题?
为了回答这个问题,现在我们和上面那个结构对比一下,不难发现这个结构强行去掉了U-Net本身自带的长连接。取而代之的是一系列的短连接。那么我们来看看U-Net引以为傲的长连接(skip-connection)到底有什么优点。
我们认为,U-Net中的长连接是有必要的,它联系了输入图像的很多信息,有助于还原降采样所带来的信息损失,在一定程度上,我觉得它和残差的操作非常类似,也就是residual操作,x+f(x)。我不知道大家是否同意这一个观点。因此,我的建议是最好给出一个综合长连接和短连接的方案。请问,这个方案在你的脑海中是什么样的呢?此处可暂停。
其实我感觉,就是疯狂的特征拼接拼接拼接。。。
基本上就是这样一个结构没跑了是么。我们来对比一下一开始的U-Net,它其实是这个结构的一个子集。这个结构,就是我们在MICCAI中发表的UNet++。热心的网友可能会问哦,你的UNet++和刚刚说的那个CVPR的论文结构也太像了吧,我说一下这个工作和UC Berkeley的研究是完全两个独立的工作,也算是一个美丽的巧合。UNet++在今年年初时思路就已经成型了,CVPR那篇是我们今年七月份开会的时候看到的,当时UNet++已经被录用了,所以相当于同时提出。另外,和CVPR的那篇论文相比,我还有一个更精彩的点埋在后面说,刚刚也留了一个伏笔。
好,当你现在盯着UNet++的时候,不知道会不会冷不丁会冒出来一个问题:
这个网络比U-Net效果好,但是这个网络增加了多少的参数,加粗的参数可都是比U-Net多出来的啊?
这个问题非常的尖锐,实际上是需要设计实验来回答的,如何设计这个实验呢?我们的做法是强行增加U-Net里面的参数量,让它变宽,也就是增加它每个层的卷积核个数。由此,我们设计了一个叫wide U-Net的参考结构,先来看看UNet++的参数数量是9.04M,而U-Net是7.76M,多了差不多16%的参数,所以wide U-Net我们在设计时就让它的参数比UNet++差不多,并且还稍微多一点点,来证明并不是无脑增加参数量,模型效果就会好。
我本人是认为这个回击力度并不大,因为这样增加参数说心里话有点敷衍,应该能找到更好的对比方法。尽管有不足,我们先来看看结果。
根据现有的结果,我的总结是,单纯的把网络变宽,把参数提上去对效果的提升并不大,如何能把参数用在刀刃上是很重要的。那么UNet++这种设计就是将参数用在刀刃上的一个方案。
我们在回来看这个UNet++,对于这个主体结构,我们在论文中给出了一些点评,说白了就是把原来空心的U-Net填满了,优势是可以抓取不同层次的特征,将它们通过特征叠加的方式整合,不同层次的特征,或者说不同大小的感受野,对于大小不一的目标对象的敏感度是不同的,比如,感受野大的特征,可以很容易的识别出大物体的,但是在实际分割中,大物体边缘信息和小物体本身是很容易被深层网络一次次的降采样和一次次升采样给弄丢的,这个时候就可能需要感受野小的特征来帮助。另一个解读就是如果你横着看其中一层的特征叠加过程,就像一个去年很火的DenseNet的结构,非常的巧合,原先的U-Net,横着看就很像是Residual的结构,这个就很有意思了,UNet++对于U-Net分割效果提升可能和DenseNet对于ResNet分类效果的提升,原因如出一辙,因此,在解读中我们也参考了Dense Connection的一些优势,比方说特征的再利用等等。
这些解读都是很直观的认识,其实在深度学习里面,某某结构效果优于某某结构的理由,或者你加这个操作比不加操作要好,很多时候是有玄学的味道在里头,也有大量的工作也在探索深度网络的可解释性。关于UNet++的主体结构,我不想花时间赘述了。
4 高潮
接下来我要说的这部分,非常的有意思,如果这次分享就给我三分钟,我可能就会花两分半钟在这里。刚刚在讲这里的时候留了一个伏笔,说这个结构在反向传播的时候中间部分会收不到过来的梯度,如果只用最右边的一个loss来做的话。
刚才说了,一个非常直接的解决方案就是深监督,也就是deep supervision。这个概念不是新的,有很多对U-Net对改进论文中也有用到,具体的实现操作就是在图中 [公式] , [公式] , [公式] , [公式] 后面加一个1x1的卷积核,相当于去监督每个level,或者每个分支的U-Net的输出。
我给大家提供三个用Deep Supervision的结构来比较,一个就是这个,第二个是加在UC Berkeley提出的结构上,最后一个是加在UNet++上,请问是你认为哪个会更棒?这是一个开放问题,我这里不做展开讨论,我们论文中使用的是最后一个。
因为deep supervision具体应该套在哪个结构上面不是我想说的重点,deep supervision的优点也在很多论文中有讲解,我这里想着重讨论的是当它配合上这样一个填满的U-Net结构时,带来其中一个非常棒的优势。此处强烈建议暂停,想一想,如果我在训练过程中在各个level的子网络中加了这种深监督,可以带来怎样的好处呢?
两个字:剪枝。
同时引出三个问题:
- 为什么UNet++可以被剪枝
- 如何剪枝
- 好处在哪里
我们来看看为什么可以剪枝,这张图特别的精彩。关注被剪掉的这部分,你会发现,在测试的阶段,由于输入的图像只会前向传播,扔掉这部分对前面的输出完全没有影响的,而在训练阶段,因为既有前向,又有反向传播,被剪掉的部分是会帮助其他部分做权重更新的。这两句话同样重要,我再重复一遍,测试时,剪掉部分对剩余结构不做影响,训练时,剪掉部分对剩余部分有影响。这意味什么?
因为在深监督的过程中,每个子网络的输出都其实已经是图像的分割结果了,所以如果小的子网络的输出结果已经足够好了,我们可以随意的剪掉那些多余的部分了。
来看一下这个动图,为了定义的方便起见,我们把每个剪完剩下的子网络根据它们的深度命名为UNet++ L1,L2,L3,L4,后面会简称为L1~L4。最理想的状态是什么?当然是L1喽,如果L1的输出结果足够好,剪完以后的分割网络会变得非常的小。
这个剪枝其实很简单, 跟决策树的剪枝的规则相比简单许多。因为Unet++会给出四个输出,如果三层的那个输出准确度够高了,那么就没有必要在推理阶段还要第四层的了。我记得从GoogleNet开始就有这种辅助输出的概念了,GoogleNet是分类网络所以其中叫做辅助分类器(好像是),专业的概念应该是深度监督deep supervision,感兴趣的可以查一下
在训练阶段我们需要四层的特征指导来训练,所以训练中还是要用完整的模型来训练。推理阶段,作者考虑也许可以牺牲掉很小的精度来实现模型参数的大幅度减小(总之这个剪枝的技巧我觉得没什么用,仁者见仁哈)。好我们继续看后面作者的解释
这里我想问两个问题:
- 为什么要在测试的时候剪枝,而不是直接拿剪完的L1,L2,L3训练?
- 怎么去决定剪多少?
对于为什么要在测试的时候剪枝,而不是直接拿剪完的L1,L2,L3训练,我们的解释其实上一页ppt上面写了,剪掉的部分在训练时的反向传播中是有贡献的,如果直接拿L1,L2,L3训练,就相当于只训练了不同深度的U-Net,最后的结果会很差。
第二个问题,如何去决定剪多少,还是比较好回答的。因为在训练模型的时候会把数据分为训练集,验证集和测试集,训练集上是一定拟合的很好的,测试集是我们不能碰的,所以我们会根据子网络在验证集的结果来决定剪多少。所谓的验证集就是一开始从训练集中分出来的数据,用来监测训练过程用的。
好,讲完了思路,我们来看结果。
先看看L1~L4的网络参数量,差了好多,L1只有0.1M,而L4有9M,也就是理论上如果L1的结果我是满意的,那么模型可以被剪掉的参数达到98.8%。不过根据我们的四个数据集,L1的效果并不会那么好,因为太浅了嘛。但是其中有三个数据集显示L2的结果和L4已经非常接近了,也就是说对于这三个数据集,在测试阶段,我们不需要用9M的网络,用半M的网络足够了。
回想一下一开始我提出的问题,网络需要多深合适,这幅图是不是就一目了然。网络的深度和数据集的难度是有关系的,这四个数据集当中,第二个,也就是息肉分割是最难的,大家可以看到纵坐标,它代表分割的评价指标,越大越好,其他都能达到挺高的,但是唯独息肉分割只有在30左右,对于比较难的数据集,可以看到网络越深,它的分割结果是在不断上升的。对于大多数比较简单的分割问题,其实并不需要非常深,非常大的网络就可以达到很不错的精度了。
横坐标代表的是在测试阶段,单显卡12G的TITAN X (Pascal)下,分割一万张图需要的时间。我们可以看到不同的模型大小,测试的时间差好多。如果比较L2和L4的话,就差了三倍之多。
对于测试的速度,用这一幅图会更清晰。我们统计了用不同的模型,1秒钟可以分割多少的图。如果用L2来代替L4的话,速度确实能提升三倍。
剪枝应用最多的就是在移动手机端了,根据模型的参数量,如果L2得到的效果和L4相近,模型的内存可以省18倍。还是非常可观的数目。
关于剪枝的这部分我认为是对原先的U-Net的一个很大的改观,原来的结构过于刻板,并且没有很好的利用不用层级的特征。
简单的总结一下,UNet++的第一个优势就是精度的提升,这个应该它整合了不同层次的特征所带来的,第二个是灵活的网络结构配合深监督,让参数量巨大的深度网络在可接受的精度范围内大幅度的缩减参数量。
5 最后一提
再次放上我们的UNet++结构
图像分割必备知识点 | Unet++超详解+注解的更多相关文章
- 图像分割必备知识点 | Unet详解 理论+ 代码
文章转自:微信公众号[机器学习炼丹术].文章转载或者交流联系作者微信:cyx645016617 喜欢的话可以参与文中的讨论.在文章末尾点赞.在看点一下呗. 0 概述 语义分割(Semantic Seg ...
- html5的float属性超详解(display,position, float)(文本流)
html5的float属性超详解(display,position, float)(文本流) 一.总结 1.文本流: 2.float和绝对定位都不占文本流的位置 3.普通流是默认定位方式,就是依次按照 ...
- HTML中DOM核心知识有哪些(带实例超详解)
HTML中DOM核心知识有哪些(带实例超详解) 一.总结: 1.先取html元素,然后再对他进行操作,取的话可以getElementById等 2.操作的话,可以是innerHtml,value等等 ...
- Mysql超详解
Mysql超详解 一.命令框基本操作及连接Mysql 找到Mysql安装路径,查看版本 同时按快捷键win+R会弹出一个框,在框中输入cmd 点击确定后会出现一个黑框,这是命令框,我们的操作要在这命令 ...
- Mybatis案例超详解(上)
Mybatis案例超详解(上) 前言: 本来是想像之前一样继续跟新Mybatis,但由于种种原因,迟迟没有更新,快开学了,学了一个暑假,博客也更新了不少,我觉得我得缓缓,先整合一些案例练练,等我再成熟 ...
- Python3调用C程序(超详解)
Python3调用C程序(超详解) Python为什么要调用C? 1.要提高代码的运算速度,C比Python快50倍以上 2.对于C语言里很多传统类库,不想用Python重写,想对从内存到文件接口这样 ...
- JUC中的AQS底层详细超详解
摘要:当你使用java实现一个线程同步的对象时,一定会包含一个问题:你该如何保证多个线程访问该对象时,正确地进行阻塞等待,正确地被唤醒? 本文分享自华为云社区<JUC中的AQS底层详细超详解,剖 ...
- Android vector标签 PathData 画图超详解
SVG是一种矢量图格式,是Scalable Vector Graphics三个单词的首字母缩写.在xml文件中的标签是<vector>,画出的图形可以像一般的图片资源使用,例子如下: &l ...
- 高斯消元法(Gauss Elimination)【超详解&模板】
高斯消元法,是线性代数中的一个算法,可用来求解线性方程组,并可以求出矩阵的秩,以及求出可逆方阵的逆矩阵.高斯消元法的原理是:若用初等行变换将增广矩阵 化为 ,则AX = B与CX = D是同解方程组. ...
随机推荐
- Mybatis的dao层实现 接口代理方式实现规范+plugins-PageHelper
Mybatis的dao层实现 接口代理方式实现规范 Mapper接口实现时的相关规范: Mapper接口开发只需要程序员编写Mapper接口而不用具体实现其代码(相当于我们写的Imp实现类) Mapp ...
- [Luogu P2831] 愤怒的小鸟 (状压DP)
题面: 传送门:https://www.luogu.org/problemnew/show/P2831 Solution 首先,我们可以先康一康题目的数据范围:n<=18,应该是状压或者是搜索. ...
- c#导入文件
string[] lines = System.IO.File.ReadAllLines(@"C:\Users\Administrator\Desktop\2.txt",Encod ...
- 查看并配置JAVA_HOME
转载自https://blog.csdn.net/weixin_41713592/article/details/79941418which java [root@host2 hadoop-1.1.2 ...
- Mybatis的二级缓存、使用Redis做二级缓存
目录 什么是二级缓存? 1. 开启二级缓存 如何使用二级缓存: userCache和flushCache 2. 使用Redis实现二级缓存 如何使用 3. Redis二级缓存源码分析 什么是二级缓存? ...
- EMP-面向未来微前端方案正式开源了!
原文团队掘金平台:https://juejin.im/post/6891532248269783054 EMP项目github链接: https://github.com/efoxTeam/emp E ...
- bWAPP----PHP Code Injection
PHP Code Injection 主要代码 1 <div id="main"> 2 3 <h1>PHP Code Injection</h1> ...
- 分布式监控系统之Zabbix主动、被动及web监控
前文我们了解了zabbix的网络发现功能,以及结合action实现自动发现主机并将主机添加到zabbix hosts中,链接指定模板进行监控:回顾请参考https://www.cnblogs.com/ ...
- 关于Redis的一些思考
1.从Java语言考虑,已经有ConcurrentHashMap等并发集合类了,与Redis相比,区别于差异在哪? 一直有这么个疑问,今天有搜了很久,很巧,搜到个有同样想法的问答,如下: When p ...
- Druid配置和初始化参数 转发地址图片有
配置数据源 1.添加上 Druid 数据源依赖. <!-- https://mvnrepository.com/artifact/com.alibaba/druid --> <dep ...