DenseNet算法详解——思路就是highway

 

论文:Densely Connected Convolutional Networks
论文链接:https://arxiv.org/pdf/1608.06993.pdf
代码的github链接:https://github.com/liuzhuang13/DenseNet
MXNet版本代码(有ImageNet预训练模型,如果觉得有帮助,记得给个星): https://github.com/miraclewkf/DenseNet

文章详解:
这篇文章是CVPR2017的oral,非常厉害。文章提出的DenseNet(Dense Convolutional Network)主要还是和ResNet及Inception网络做对比,思想上有借鉴,但却是全新的结构,网络结构并不复杂,却非常有效!众所周知,最近一两年卷积神经网络提高效果的方向,要么深(比如ResNet,解决了网络深时候的梯度消失问题)要么宽(比如GoogleNet的Inception),而作者则是从feature入手,通过对feature的极致利用达到更好的效果和更少的参数。博主虽然看过的文章不算很多,但是看完这篇感觉心潮澎湃,就像当年看完ResNet那篇文章一样!

先列下DenseNet的几个优点,感受下它的强大:
1、减轻了vanishing-gradient(梯度消失)
2、加强了feature的传递
3、更有效地利用了feature
4、一定程度上较少了参数数量

在深度学习网络中,随着网络深度的加深,梯度消失问题会愈加明显,目前很多论文都针对这个问题提出了解决方案,比如ResNet,Highway Networks,Stochastic depth,FractalNets等,尽管这些算法的网络结构有差别,但是核心都在于:create short paths from early layers to later layers。那么作者是怎么做呢?延续这个思路,那就是在保证网络中层与层之间最大程度的信息传输的前提下,直接将所有层连接起来!

先放一个dense block的结构图。在传统的卷积神经网络中,如果你有L层,那么就会有L个连接,但是在DenseNet中,会有L(L+1)/2个连接。简单讲,就是每一层的输入来自前面所有层的输出。如下图:x0是input,H1的输入是x0(input),H2的输入是x0和x1(x1是H1的输出)……

这里写图片描述

DenseNet的一个优点是网络更窄,参数更少,很大一部分原因得益于这种dense block的设计,后面有提到在dense block中每个卷积层的输出feature map的数量都很小(小于100),而不是像其他网络一样动不动就几百上千的宽度。同时这种连接方式使得特征和梯度的传递更加有效,网络也就更加容易训练。原文的一句话非常喜欢:Each layer has direct access to the gradients from the loss function and the original input signal, leading to an implicit deep supervision.直接解释了为什么这个网络的效果会很好。前面提到过梯度消失问题在网络深度越深的时候越容易出现,原因就是输入信息和梯度信息在很多层之间传递导致的,而现在这种dense connection相当于每一层都直接连接input和loss,因此就可以减轻梯度消失现象,这样更深网络不是问题。另外作者还观察到这种dense connection有正则化的效果,因此对于过拟合有一定的抑制作用,博主认为是因为参数减少了(后面会介绍为什么参数会减少),所以过拟合现象减轻。

这篇文章的一个优点就是基本上没有公式,不像灌水文章一样堆复杂公式把人看得一愣一愣的。文章中只有两个公式,是用来阐述DenseNet和ResNet的关系,对于从原理上理解这两个网络还是非常重要的。

第一个公式是ResNet的。这里的l表示层,xl表示l层的输出,Hl表示一个非线性变换。所以对于ResNet而言,l层的输出是l-1层的输出加上对l-1层输出的非线性变换。

这里写图片描述

第二个公式是DenseNet的。[x0,x1,…,xl-1]表示将0到l-1层的输出feature map做concatenation。concatenation是做通道的合并,就像Inception那样。而前面resnet是做值的相加,通道数是不变的。Hl包括BN,ReLU和3*3的卷积。

这里写图片描述

所以从这两个公式就能看出DenseNet和ResNet在本质上的区别,太精辟。

前面的Figure 1表示的是dense block,而下面的Figure 2表示的则是一个DenseNet的结构图,在这个结构图中包含了3个dense block。作者将DenseNet分成多个dense block,原因是希望各个dense block内的feature map的size统一,这样在做concatenation就不会有size的问题。

这里写图片描述

这个Table1就是整个网络的结构图。这个表中的k=32,k=48中的k是growth rate,表示每个dense block中每层输出的feature map个数。为了避免网络变得很宽,作者都是采用较小的k,比如32这样,作者的实验也表明小的k可以有更好的效果。根据dense block的设计,后面几层可以得到前面所有层的输入,因此concat后的输入channel还是比较大的。另外这里每个dense block的3*3卷积前面都包含了一个1*1的卷积操作,就是所谓的bottleneck layer,目的是减少输入的feature map数量,既能降维减少计算量,又能融合各个通道的特征,何乐而不为。另外作者为了进一步压缩参数,在每两个dense block之间又增加了1*1的卷积操作。因此在后面的实验对比中,如果你看到DenseNet-C这个网络,表示增加了这个Translation layer,该层的1*1卷积的输出channel默认是输入channel到一半。如果你看到DenseNet-BC这个网络,表示既有bottleneck layer,又有Translation layer。

这里写图片描述

再详细说下bottleneck和transition layer操作。在每个Dense Block中都包含很多个子结构,以DenseNet-169的Dense Block(3)为例,包含32个1*1和3*3的卷积操作,也就是第32个子结构的输入是前面31层的输出结果,每层输出的channel是32(growth rate),那么如果不做bottleneck操作,第32层的3*3卷积操作的输入就是31*32+(上一个Dense Block的输出channel),近1000了。而加上1*1的卷积,代码中的1*1卷积的channel是growth rate*4,也就是128,然后再作为3*3卷积的输入。这就大大减少了计算量,这就是bottleneck。至于transition layer,放在两个Dense Block中间,是因为每个Dense Block结束后的输出channel个数很多,需要用1*1的卷积核来降维。还是以DenseNet-169的Dense Block(3)为例,虽然第32层的3*3卷积输出channel只有32个(growth rate),但是紧接着还会像前面几层一样有通道的concat操作,即将第32层的输出和第32层的输入做concat,前面说过第32层的输入是1000左右的channel,所以最后每个Dense Block的输出也是1000多的channel。因此这个transition layer有个参数reduction(范围是0到1),表示将这些输出缩小到原来的多少倍,默认是0.5,这样传给下一个Dense Block的时候channel数量就会减少一半,这就是transition layer的作用。文中还用到dropout操作来随机减少分支,避免过拟合,毕竟这篇文章的连接确实多。

实验结果:
作者在不同数据集上采用的DenseNet网络会有一点不一样,比如在Imagenet数据集上,DenseNet-BC有4个dense block,但是在别的数据集上只用3个dense block。其他更多细节可以看论文3部分的Implementation Details。训练的细节和超参数的设置可以看论文4.2部分,在ImageNet数据集上测试的时候有做224*224的center crop。

Table2是在三个数据集(C10,C100,SVHN)上和其他算法的对比结果。ResNet[11]就是kaiming He的论文,对比结果一目了然。DenseNet-BC的网络参数和相同深度的DenseNet相比确实减少了很多!参数减少除了可以节省内存,还能减少过拟合。这里对于SVHN数据集,DenseNet-BC的结果并没有DenseNet(k=24)的效果好,作者认为原因主要是SVHN这个数据集相对简单,更深的模型容易过拟合。在表格的倒数第二个区域的三个不同深度L和k的DenseNet的对比可以看出随着L和k的增加,模型的效果是更好的。

这里写图片描述

Figure3是DenseNet-BC和ResNet在Imagenet数据集上的对比,左边那个图是参数复杂度和错误率的对比,你可以在相同错误率下看参数复杂度,也可以在相同参数复杂度下看错误率,提升还是很明显的!右边是flops(可以理解为计算复杂度)和错误率的对比,同样有效果。

这里写图片描述

Figure4也很重要。左边的图表示不同类型DenseNet的参数和error对比。中间的图表示DenseNet-BC和ResNet在参数和error的对比,相同error下,DenseNet-BC的参数复杂度要小很多。右边的图也是表达DenseNet-BC-100只需要很少的参数就能达到和ResNet-1001相同的结果。

这里写图片描述

另外提一下DenseNet和stochastic depth的关系,在stochastic depth中,residual中的layers在训练过程中会被随机drop掉,其实这就会使得相邻层之间直接连接,这和DenseNet是很像的。

总结:
博主读完这篇文章真的有点相见恨晚的感觉,半年前就在arxiv上挂出来了,听说当时就引起了轰动,后来又被选为CVPR2017的oral,感觉要撼动ResNet的地位了,再加上现在很多分类检测的网络都是在ResNet上做的,这岂不是大地震了。惊讶之余来总结下这篇文章,该文章提出的DenseNet核心思想在于建立了不同层之间的连接关系,充分利用了feature,进一步减轻了梯度消失问题,加深网络不是问题,而且训练效果非常好。另外,利用bottleneck layer,Translation layer以及较小的growth rate使得网络变窄,参数减少,有效抑制了过拟合,同时计算量也减少了。DenseNet优点很多,而且在和ResNet的对比中优势还是非常明显的。

from:https://www.cnblogs.com/zhonghuasong/p/7712370.html

DenseNet 的基本结构

DenseNet 是一种具有密集连接的卷积神经网络。在该网络中,任何两层之间都有直接的连接,也就是说,网络每一层的输入都是前面所有层输出的并集,而该层所学习的特征图也会被直接传给其后面所有层作为输入。下图是 DenseNet 的一个示意图。

如果记第 l 层的变换函数为 H_l(通常对应于一组或两组 Batch-Normalization,ReLU 和 Convolution 的操作),输出为 x_l,那么我们可以用一个非常简单的式子描述 DenseNet 每一层的变换:

x_l = H_l([x_0, x_1, …, x_{l-1}])

可以看到,DenseNet 的思想非常简单,从理解到实现都不难(代码已经开源,并且 GitHub 上有用各种框架写的第三方实现)。可能很多人更关心的问题是为什么要提出 DenseNet,它有什么用,为什么会有用以及怎么把它用好。

DenseNet 是受什么启发提出来的?

DenseNet 的想法很大程度上源于我们去年发表在 ECCV 上的一个叫做随机深度网络(Deep networks with stochastic depth)工作。当时我们提出了一种类似于 Dropout 的方法来改进ResNet。我们发现在训练过程中的每一步都随机地「扔掉」(drop)一些层,可以显著的提高 ResNet 的泛化性能。这个方法的成功至少带给我们两点启发:

  • 首先,它说明了神经网络其实并不一定要是一个递进层级结构,也就是说网络中的某一层可以不仅仅依赖于紧邻的上一层的特征,而可以依赖于更前面层学习的特征。想像一下在随机深度网络中,当第 l 层被扔掉之后,第 l+1 层就被直接连到了第 l-1 层;当第 2 到了第 l 层都被扔掉之后,第 l+1 层就直接用到了第 1 层的特征。因此,随机深度网络其实可以看成一个具有随机密集连接的 DenseNet。

  • 其次,我们在训练的过程中随机扔掉很多层也不会破坏算法的收敛,说明了 ResNet 具有比较明显的冗余性,网络中的每一层都只提取了很少的特征(即所谓的残差)。实际上,我们将训练好的 ResNet 随机的去掉几层,对网络的预测结果也不会产生太大的影响。既然每一层学习的特征这么少,能不能降低它的计算量来减小冗余呢?

DenseNet 的设计正是基于以上两点观察。我们让网络中的每一层都直接与其前面层相连,实现特征的重复利用;同时把网络的每一层设计得特别「窄」,即只学习非常少的特征图(最极端情况就是每一层只学习一个特征图),达到降低冗余性的目的。这两点也是 DenseNet 与其他网络最主要的不同。需要强调的是,第一点是第二点的前提,没有密集连接,我们是不可能把网络设计得太窄的,否则训练会出现欠拟合(under-fitting)现象,即使 ResNet 也是如此。

DenseNet 有什么优点?

省参数。在 ImageNet 分类数据集上达到同样的准确率,DenseNet 所需的参数量不到 ResNet 的一半。对于工业界而言,小模型可以显著地节省带宽,降低存储开销。

省计算。达到与 ResNet 相当的精度,DenseNet 所需的计算量也只有 ResNet 的一半左右。计算效率在深度学习实际应用中的需求非常强烈,从本次 CVPR 会上大家对模型压缩以及 MobileNet 和 ShuffleNet 这些工作的关注就可以看得出来。最近我们也在搭建更高效的 DenseNet,初步结果表明 DenseNet 对于这类应用具有非常大的潜力,即使不用 Depth Separable Convolution 也能达到比现有方法更好的结果,预计在近期我们会公开相应的方法和模型。

另外,我们还提出了一个可实现自适应推理的多尺度 DenseNet,用于提高深度学习模型的推理效率。这个方法的主要思想是用浅层的特征来预测相对「简单」的图片,而只用深层的特征来预测比较「难」的图片。由于很多实际应用中,简单的图片占有较大的比例,而它们并不需要非常深的模型也能被正确预测,因此这种自适应推理方法可以有效的降低深度模型推理时的平均计算开销,而不降低精度。感兴趣的读者请关注我们的 arXiv 论文 《Multi-Scale Dense Convolutional Networks for Efficient Prediction》(),代码参见。

抗过拟合。DenseNet 具有非常好的抗过拟合性能,尤其适合于训练数据相对匮乏的应用。这一点从论文中 DenseNet 在不做数据增强(data augmentation)的 CIFAR 数据集上的表现就能看出来。例如不对 CIFAR100 做数据增强,之前最好的结果是 28.20% 的错误率,而 DenseNet 可以将这一结果提升至 19.64%。对于 DenseNet 抗过拟合的原因有一个比较直观的解释:神经网络每一层提取的特征都相当于对输入数据的一个非线性变换,而随着深度的增加,变换的复杂度也逐渐增加(更多非线性函数的复合)。相比于一般神经网络的分类器直接依赖于网络最后一层(复杂度最高)的特征,DenseNet 可以综合利用浅层复杂度低的特征,因而更容易得到一个光滑的具有更好泛化性能的决策函数。实际上,DenseNet 的泛化性能优于其他网络是可以从理论上证明的:去年的一篇几乎与 DenseNet 同期发布在 arXiv 上的论文(AdaNet: Adaptive Structural Learning of Artificial Neural Networks)所证明的结论(见文中 Theorem 1)表明类似于 DenseNet 的网络结构具有更小的泛化误差界。

密集连接不会带来冗余吗?

这是一个很多人都在问的问题,因为「密集连接」这个词给人的第一感觉就是极大的增加了网络的参数量和计算量。但实际上 DenseNet 比其他网络效率更高,其关键就在于网络每层计算量的减少以及特征的重复利用。DenseNet 的每一层只需学习很少的特征,使得参数量和计算量显著减少。比如对于 ImageNet 上的模型,ResNet 在特征图尺寸为 7x7 的阶段,每个基本单元(包含三个卷积层)的参数量为 2048x512x1x1+512x512x3x3+512x2048x1x1=4.5M,而 DenseNet 每个基本单元(包含两个卷积层,其输入特征图的数量一般小于 2000)的参数量约为 2000x4x32x1x1 + 4x32x32x3x3 = 0.26M,大幅低于 ResNet 每层的参数量。这就解释了为什么一个 201 层的 DenseNet 参数量和计算量都只有一个 101 层 ResNet 的一半左右。

还有一个自然而然的问题就是,这么多的密集连接,是不是全部都是必要的,有没有可能去掉一些也不会影响网络的性能?论文里面有一个热力图(heatmap),直观上刻画了各个连接的强度。从图中可以观察到网络中比较靠后的层确实也会用到非常浅层的特征。

via

我们还做过一些简单的实验,比如每一层都只连接到前面最近的 m 层(例如 m=4),或者奇(偶)数层只与前面的偶(奇)数层相连,但这样简化后的模型并没有比一个相应大小的正常 DenseNet 好。当然这些都只是一些非常初步的尝试,如果采用一些好的剪枝(prune)的方法,我觉得 DenseNet 中一部分连接是可以被去掉而不影响性能的。

DenseNet 特别耗费显存?

不少人跟我们反映过 DenseNet 在训练时对内存消耗非常厉害。这个问题其实是算法实现不优带来的。当前的深度学习框架对 DenseNet 的密集连接没有很好的支持,我们只能借助于反复的拼接(Concatenation)操作,将之前层的输出与当前层的输出拼接在一起,然后传给下一层。对于大多数框架(如 Torch 和 TensorFlow),每次拼接操作都会开辟新的内存来保存拼接后的特征。这样就导致一个 L 层的网络,要消耗相当于 L(L+1)/2 层网络的内存(第 l 层的输出在内存里被存了 (L-l+1) 份)。

解决这个问题的思路其实并不难,我们只需要预先分配一块缓存,供网络中所有的拼接层(Concatenation Layer)共享使用,这样 DenseNet 对内存的消耗便从平方级别降到了线性级别。在梯度反传过程中,我们再把相应卷积层的输出复制到该缓存,就可以重构每一层的输入特征,进而计算梯度。当然网络中由于 Batch Normalization 层的存在,实现起来还有一些需要注意的细节。为此我们专门写了一个技术报告(Memory-Efficient Implementation of DenseNets, )介绍如何提升 DenseNet 对内存的使用效率,同时还提供了 Torch, PyTorch, MxNet 以及 Caffe 的实现,代码参见:

Torch implementation:

PyTorch implementation:

MxNet implementation:

Caffe implementation:

新的实现极大地减小了 DenseNet 在训练时对显存的消耗,比如论文中 190 层的 DenseNet 原来几乎占满了 4 块 12G 内存的 GPU,而优化过后的代码仅需要 9G 的显存,在单卡上就能训练。

另外就是网络在推理(或测试)的时候对内存的消耗,这个是我们在实际产品中(尤其是在移动设备上)部署深度学习模型时最关心的问题。不同于训练,一般神经网络的推理过程不需要一直保留每一层的输出,因此可以在每计算好一层的特征后便将前面层特征占用的内存释放掉。而 DenseNet 则需要始终保存所有前面层的输出。但考虑到 DenseNet 每一层产生的特征图很少,所以在推理的时候占用内存不会多于其他网络。

使用 DenseNet 有什么需要注意的细节?

总的来说,训练 DenseNet 跟训练其他网络没有什么特殊的地方,对于训练 ResNet 的代码,只需要把模型替换成 DenseNet 就可以了。如果想对 DenseNet 的模型做一些改进,我们有一些建议供参考:

  1. 每层开始的瓶颈层(1x1 卷积)对于减少参数量和计算量非常有用。

  2. 像 VGG 和 ResNet 那样每做一次下采样(down-sampling)之后都把层宽度(growth rate) 增加一倍,可以提高 DenseNet 的计算效率(FLOPS efficiency)。

  3. 与其他网络一样,DenseNet 的深度和宽度应该均衡的变化,当然 DenseNet 每层的宽度要远小于其他模型。

  4. 每一层设计得较窄会降低 DenseNet 在 GPU 上的运算效率,但可能会提高在 CPU 上的运算效率。

DenseNet 用于图像语义分割和物体检测等视觉任务效果如何?

由于 DenseNet 不容易过拟合,在数据集不是很大的时候表现尤其突出。在一些图像分割和物体检测的任务上,基于 DenseNet 的模型往往可以省略在 ImageNet 上的预训练,直接从随机初始化的模型开始训练,最终达到相同甚至更好的效果。由于在很多应用中实际数据跟预训练的 ImageNet 自然图像存在明显的差别,这种不需要预训练的方法在医学图像,卫星图像等任务上都具有非常广阔的应用前景。

在图像语义分割任务上,CVPR 2017 的一篇 workshop 文章 《The One Hundred Layers Tiramisu: Fully Convolutional DenseNets for Semantic Segmentation》 () 表明,基于 DenseNet 的全卷积网络(FCN)模型在不需要预训练的情况下甚至可以达到比其他预训练方法更高的精度,并且比达到相同效果的其他方法的模型要小 10 倍。

同样,在物体检测任务上,我们即将发表在 ICCV 2017 上的工作也表明,基于 DenseNet 的检测方法可以在不需要 ImageNet 预训练的情况下达到 state-of-the-art 的效果,并且模型参数相比较其他模型要少很多。这是目前为止第一个不用 ImageNet 预训练的基于深度学习的物体检测系统。文章会在 8 月初放到 arxiv,敬请关注。

《Densely Connected Convolutional Networks》阅读笔记

代码地址:https://github.com/liuzhuang13/DenseNet

首先看一张图:
这里写图片描述
稠密连接:每层以之前层的输出为输入,对于有L层的传统网络,一共有L

个连接,对于DenseNet,则有L(L+1)2

这篇论文主要参考了Highway Networks,Residual Networks (ResNets)以及GoogLeNet,通过加深网络结构,提升分类结果。加深网络结构首先需要解决的是梯度消失问题,解决方案是:尽量缩短前层和后层之间的连接。比如上图中,H4

层可以直接用到原始输入信息X0,同时还用到了之前层对X0处理后的信息,这样能够最大化信息的流动。反向传播过程中,X0的梯度信息包含了损失函数直接对X0

的导数,有利于梯度传播。
DenseNet有如下优点:
1.有效解决梯度消失问题
2.强化特征传播
3.支持特征重用
4.大幅度减少参数数量

接着说下论文中一直提到的Identity function:
很简单 就是输出等于输入f(x)=x


这里写图片描述

传统的前馈网络结构可以看成处理网络状态(特征图?)的算法,状态从层之间传递,每个层从之前层读入状态,然后写入之后层,可能会改变状态,也会保持传递不变的信息。ResNet是通过Identity transformations来明确传递这种不变信息。

网络结构:
这里写图片描述
每层实现了一组非线性变换Hl(.)

,可以是Batch Normalization (BN) ,rectified linear units (ReLU) , Pooling , or Convolution (Conv). 第l层的输出为xl


对于ResNet:

xl=Hl(xl1)+xl1


这样做的好处是the gradient flows directly through the identity function from later layers to the earlier layers.
同时呢,由于identity function 和 H的输出通过相加的方式结合,会妨碍信息在整个网络的传播。

受GooLeNet的启发,DenseNet通过串联的方式结合:

xl=Hl([x0,x1,...,xl1])

这里Hl(.)

是一个Composite function,是三个操作的组合:BN>ReLU>Conv(3×3)

由于串联操作要求特征图x0,x1,...,xl1

大小一致,而Pooling操作会改变特征图的大小,又不可或缺,于是就有了上图中的分块想法,其实这个想法类似于VGG模型中的“卷积栈”的做法。论文中称每个块为DenseBlock。每个DenseBlock的之间层称为transition layers,由BN>Conv(1×1)>averagePooling(2×2)

组成。

Growth rate:由于每个层的输入是所有之前层输出的连接,因此每个层的输出不需要像传统网络一样多。这里Hl(.)

的输出的特征图的数量都为kk即为Growth Rate,用来控制网络的“宽度”(特征图的通道数).比如说第l层有k(l1)+k0的输入特征图,k0

是输入图片的通道数。

虽然说每个层只产生k

个输出,但是后面层的输入依然会很多,因此引入了Bottleneck layers 。本质上是引入1x1的卷积层来减少输入的数量,Hl

的具体表示如下

BN>ReLU>Conv(1×1)>BN>ReLU>Conv(3×3)


文中将带有Bottleneck layers的网络结构称为DenseNet-B。

除了在DenseBlock内部减少特征图的数量,还可以在transition layers中来进一步Compression。如果一个DenseNet有m个特征图的输出,则transition layer产生 θm

个输出,其中0<θ1

。对于含有该操作的网络结构称为DenseNet-C。

同时包含Bottleneck layer和Compression的网络结构为DenseNet-BC。
具体的网络结构:

这里写图片描述

实验以及一些结论
在CIFAR和SVHN上的分类结果(错误率):
这里写图片描述
L

表示网络深度,k为增长率。蓝色字体表示最优结果,+表示对原数据库进行data augmentation。可以发现DenseNet相比ResNet可以取得更低的错误率,并且使用了更少的参数。
接着看一组对比图:
这里写图片描述
前两组描述分类错误率与参数量的对比,从第二幅可以看出,在取得相同分类精度的情况下,DenseNet-BC比ResNet少了23

的参数。第三幅图描述含有10M参数的1001层的ResNet与只有0.8M的100层的DenseNet的训练曲线图。可以发现ResNet可以收敛到更小的loss值,但是最终的test error与DenseNet相差无几。再次说明了DenseNet参数效率(Parameter Efficiency)很高!

同样的在ImageNet上的分类结果:
这里写图片描述
右图使用FLOPS来说明计算量。通过比较ResNet-50,DenseNet-201,ResNet-101,说明计算量方面,DenseNet结果更好。

猜你喜欢

转载自www.cnblogs.com/bonelee/p/9029138.html