DenseNet是继ResNet之后提出的一种局部直连的网络。
image.png
DenseNet的优点可以总结为
1)有效减轻梯度弥散问题
2)增强特征传播
3)鼓励特征复用
4)更少的参数量
个人觉得DenseNet最妙的地方在于前面层的feature map使用了累积的方式,而不是resnet中的累加的方式,这样就直观的保留了前面层的信息,而且加宽的网络的宽度。
个人将在阅读中产生的一些疑惑内容放在最后一个模块---小问答中
一、背景介绍
ResNet和HighwayNet通过identity connection使得signal进行流动。Stochastic depth则通过类似于dropout的方法对ResNet进行随机失活。FractalNets重复结合多个平行层序列组合以获得较大的标称深度,并同时在网络中保留着不少的short paths。其实他们都有一个关键的特性就是创造short cut来连接layer与layer之间。
总结来说,一两年卷积神经网络提高效果的方向有三种,
- 深层网络,deeper会带来特征在正向传播中的消失和梯度在反向传播中的弥散。Resnet的出现,是的深度问题的梯度消失得到了解决,网络可以设计的更加深。
- 增加feature map的宽度(比如GoogleNet的Inception)或者说是通道数,
- Densenet则从feature入手,通过对feature的极致利用达到更好的效果和更少的参数。
DenseNet受到ResNet的启发, 通过shortcut连接可以有效使得网络更加deeper,且不会出现像plainNet那样的degradation现象出现。而且一些ResNet变体则发现随机drop掉一些resblock并不会降低多少accuracy,这意味着一些Resblock并没有学习到多少有用的东西,而主要通过shortCut将上一个state进行传递而已。延续这个思路,Densenet在保证网络中层与层之间最大程度的信息传输的前提下,直接将所有层连接起来!
在作者的DenseNet中,作者并不准备通过summation来连接layers,而是通过concatenating将这些features进行连接起来。
这个就很刺激了。
在传统的卷积神经网络中,如果你有L层,那么就会有L个连接,但是在DenseNet中,会有L(L+1)/2个连接。简单讲,就是每一层的输入来自前面所有层的输出。如下图:x0是input,H1的输入是x0(input),H2的输入是x0和x1(x1是H1的输出)……
直觉上来说,输出层好像越来越多了。但是实际上这种设计其实有个反直觉的好处,那就是参数量可以减少。
原因是因为每个layer其实都相当于一个state,signal经过这个layer则改变这个state并将其传播到下一个state。ResNet显式的保存了上一个layer的state并传给了下一个layer可以有效改善网络的accuracy。这意味着一些feature可以被重复利用。dense block中每个卷积层的输出feature map的数量都很小,而不是像其他网络一样动不动就几百上千的宽度。同时这种连接方式使得特征和梯度的传递更加有效,网络也就更加容易训练。
Each layer has direct access to the gradients from the loss function and the original input signal, leading to an implicit deep supervision.
还一个好处就是信息可以多方向的往下传,而梯度则可以多方向的反向传,这会使得整个网络更加容易训练。每一层都直接连接input和loss,因此就可以减轻梯度消失现象,这样更深网络不是问题。
观察到这种dense connection有正则化的效果,因此对于过拟合有一定的抑制作用,博主认为是因为参数减少了(后面会介绍为什么参数会减少),所以过拟合现象减轻。
二、DenseNet结构
denseNetdenseNet是由dense block穿插卷积层和池化层构成的。每个denseBlock的模块就是首张图上的样子。
DenseBlock
下面这个公式很清晰,也就是在函数H(.)之前将所有feature map进行投入,可以跟之前的ResNet的输出函数进行对比。即可发现summation和concatenating之间的区别。
image.png
根据dense block的设计,后面几层可以得到前面所有层的输入,因此concat后的输入channel还是比较大的。
公式中的指的是复合函数,包含BN, relu, conv三个函数。
需要注意到一个growth rate的问题,在DenseNEt中有一个参数是growth rate,这个参数表示H(.)函数输出的feature map的channel数量。假设每个H(.)函数产生k层 feature map,那么对于第l层来说,其输入为 ,其中k0为输入denseblock的输入层大小。densenet的每层输出channel数量一般比较小,比如,论文中吧这个参数理解为growth rate,实验证明,这个小数值已经可以充分包含足够的信息量了。
Pooling划分dense block
由于在deep Network中有个重要的环节就是pooling,pooling会导致特征map尺寸的变化,导致不能进行concatenating。ResNet当时给出了几种处理方法,1)补零,2)projection connect。在DenseNet中,按照pooling划分,在每个区域中进行dense connect
denseBlock的优化---Bottleneck layers
虽然每个layer只产生k个feature maps,但是对每个layer的输入还是太多了。在之前的grow rate中,我们就用一个公式进行表示过输入feature maps的数量:。
在每个Dense Block中都包含很多个子结构,以DenseNet-169的Dense Block(3)为例,包含32个和的卷积操作,也就是第32个子结构的输入是前面31层的输出结果,每层输出的channel是32(growth rate),那么如果不做bottleneck操作,第32层的卷积操作的输入就是,近1000了。而加上1*1的卷积,代码中的卷积的channel是,也就是128,然后再作为卷积的输入。这就大大减少了计算量,这就是bottleneck。
那么第二种DenseNet的Hl(.)模型便表示如下:
BN-ReLu-Conv() -BN-ReLu-Conv().
并将该模型重新命名为DenseNet-B
Note:在本实验中,我们将卷积的固定输出在4k feature-maps。其中k则为上面的grow rate。
transition layer操作
放在两个Dense Block中间,是因为每个Dense Block结束后的输出channel个数很多,需要用的卷积核来降维。还是以DenseNet-169的Dense Block(3)为例,虽然第32层的卷积输出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操作来随机减少分支,避免过拟合,毕竟这篇文章的连接确实多。
训练
DneseNet在训练时十分消耗内存,非常吃内存,一般需要整块的GPU运行,这是由于算法实现不优带来的。当前的深度学习框架对 DenseNet 的密集连接没有很好的支持,所以只能借助于反复的拼接(Concatenation)操作,将之前层的输出与当前层的输出拼接在一起,然后传给下一层。对于大多数框架(如Torch和TensorFlow),每次拼接操作都会开辟新的内存来保存拼接后的特征。这样就导致一个 L 层的网络,要消耗相当于 L(L+1)/2 层网络的内存(第 l 层的输出在内存里被存了 (L-l+1) 份)。为此作者又写了一个技术报告(Memory-Efficient Implementation of DenseNets)https://arxiv.org/pdf/1707.06990.pdf专门针对这一问题,介绍如何提升对内存的使用率,同时提供的Torch, PyTorch, MxNet 以及 Caffe 的实现。
小问答
1. densenet什么时候效果比resnet好?
答案:小数据集的时候,因为小数据集的时候容易产生过拟合,但是densenet能够很好的解决过拟合的问题,所以对于小数据集的情况下densenet的效果好于resnet。原因:DenseNet 具有非常好的抗过拟合性能,尤其适合于训练数据相对匮乏的应用。这一点从论文中 DenseNet 在不做数据增强(data augmentation)的 CIFAR 数据集上的表现就能看出来。例如不对 CIFAR100 做数据增强,之前最好的结果是 28.20% 的错误率,而 DenseNet 可以将这一结果提升至 19.64%。对于 DenseNet 抗过拟合的原因有一个比较直观的解释:神经网络每一层提取的特征都相当于对输入数据的一个非线性变换,而随着深度的增加,变换的复杂度也逐渐增加(更多非线性函数的复合)。相比于一般神经网络的分类器直接依赖于网络最后一层(复杂度最高)的特征,DenseNet 可以综合利用浅层复杂度低的特征,因而更容易得到一个光滑的具有更好泛化性能的决策函数。[1]
2. DenseNet和resnet为什么可以防止梯度弥散?
回答:传统的“提拉米苏”式卷积神经网络模型,都以层叠卷积层的方式提高网络深度,从而提高识别精度。但层叠过多的卷积层会出现一个问题,就是梯度弥散(Vanishing),backprop无法把有效地把梯度更新到前面的网络层,导致前面的层参数无法更新。
而BatchNormalization(BN)、ResNet的skip connection就是为了解决这个问题,BN通过规范化输入数据改变数据分布,在前传过程中消除梯度弥散。而skip connection则能在后传过程中更好地把梯度传到更浅的层次中。那么为什么加了一个捷径就能把梯度传到浅层网络?
image.png
当进行后向传播时,右边来自深层网络传回来的梯度为1,经过一个加法门,橙色方向的梯度为dh(x)/dx=1,蓝色方向的梯度也为1。这样,经过梯度传播后,现在传到前一层的梯度就变成了[1, 0.0001, 0.01],多了一个“1”!正是由于多了这条捷径,来自深层的梯度能直接畅通无阻地通过,去到上一层,使得浅层的网络层参数等到有效的训练!
确实bn能很好地解决梯度弥散问题。bn对解决梯度弥散更大的作用在于前传过程中对数据的分布处理,而skip connection更多的贡献在后传的信号传播。
BN就不是必需品,通过精准的调参就可以解决梯度弥散问题,我平时也不用BN,因为速度会慢,BN是给懒人用的。
但要注意resnet解决的并不是梯度弥散问题,原文里也说了,特别深层网络性能下降不是因为梯度弥散,具体原因resnet系列并没有给出回答,shattered gradient problem这篇文章里给出了一个解释。
参考文献
[1]https://blog.csdn.net/gbyy42299/article/details/80434388
[2]https://blog.csdn.net/shwan_ma/article/details/78165966
[3] https://zhuanlan.zhihu.com/p/28124810?group_id=883267168542789632
网友评论