DenseNet算法详解

        这篇文章转载自:https://blog.csdn.net/u014380165/article/details/75142664和https://blog.csdn.net/zchang81/article/details/76155291,文章下面的评论也非常好,建议大家去看作者原来的博客,我这里只是做一个转录。


论文: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的对比中优势还是非常明显的。


后记:笔者在读完原作者的scdn文章以后,发现了一些不明白的地方,于是又查了一些资料,参考博客(https://blog.csdn.net/zchang81/article/details/76155291)并加上自己的一些理解整理如下:

densenet主要拥有以下两个特性:1)一定程度上减轻在训练过程中梯度消散的问题。因为从上左图我们可以看出,在反传时每一层都会接受其后所有层的梯度信号,所以不会随着网络深度的增加,靠近输入层的梯度会变得越来越小。2)由于大量的特征被复用,使得使用少量的卷积核就可以生成大量的特征,最终模型的尺寸也比较小。

上右图所示的是构成 DenseNet 的单元模块,看上去和 ResNet 的单元模块非常相似,但实际上差异较大。我对结构设计上的细节进行了以下总结:

1)为了进行特征复用,在跨层连接时使用的是在特征维度上的 Concatenate 操作,而不是 Element-wise Addition 操作。

2)由于不需要进行 Elewise-wise 操作,所以在每个单元模块的最后不需要一个 1X1 的卷积来将特征层数升维到和输入的特征维度一致。

3)采用 Pre-activation 的策略来设计单元,将 BN 操作从主支上移到分支之前。(BN->ReLU->1x1Conv->BN->ReLU->3x3Conv)

4)由于网络中每层都接受前面所有层的特征作为输入,为了避免随着网络层数的增加,后面层的特征维度增长过快,在每个阶段之后进行下采样的时候,首先通过一个卷积层将特征维度压缩至当前输入的一半,然后再进行 Pooling 的操作。如下图所示:


5)增长率的设置。增长率指的是每个单元模块最后那个 3x3 的卷积核的数量,记为 k。由于每个单元模块最后是以 Concatenate 的方式来进行连接的,所以每经过一个单元模块,下一层的特征维度就会增长 k。它的值越大意味着在网络中流通的信息也越大,相应地网络的能力也越强,但是整个模型的尺寸和计算量也会变大。作者在本文中使用了 k=32 和 k=48 两种设置。

作者基于以上原则针对于 ImageNet 物体识别任务分别设计了 DesNet-121(k=32)、DesNet-169(k=32)、DesNet-201(k=32) 和 DesNet-161(k=48) 四种网络结构。其网络的组织形式和 ResNet 类似,也是分为 4 个阶段,将原先的 ResNet 的单元模块进行了替换,下采样过程略有不同。整体结构设计如下所示:


对于上面这个表我想解释两点:

1、DesNet-121(k=32)、DesNet-169(k=32)、DesNet-201(k=32) 和 DesNet-161(k=48) 四种网络结构里面的数字,代表的是这四种densenet结构的层数,具体来说是卷积的层数,比如说densenet-169,169=(6+12+32+32)*2+1(7*7的卷积)+3(transition layer)+1(classification layer),之所以(6+12+32+32)*2是因为每个dense block里面都有1*1和3*3两个尺度的卷积,当然分别进行了6次、12次、32次和32次

2、DenseNet 中非常关键的连续的跨层 Concatenate 操作仅存在于每个 Dense Block 之内,不同 Dense Block 之间则没有这种操作,这一点可以从下面这张图里看出:

事实上,每个 Dense Block 最后的特征图已经将当前 Block 内所有的卷积模块的输出拼接在一起,整体经过降采样之后送入了下一个 Dense Block,其中已经包含了这个 Dense Block 的全部信息,这样做也是一种权衡。

后面的也是转载的上面的https://blog.csdn.net/zchang81/article/details/76155291

总的来说,这是一篇非常有创新性的工作,提出了共享特征、任意层间互连的概念很大程度上减轻了深层网络在训练过程中梯度消散而难以优化的问题,同时也减小了模型的尺寸和计算量,在标准数据集上获得了非常好的性能。唯一可能的不足是网络不能设计地特别「深」,因为随着 DenseNet 网络层数的增加,模型的特征维度会线性增长,使得在训练过程中的计算量和显存开销也会爆发地增长。

Q&A:

1.DenseNet 是否可以在物体检测任务中使用?效果如何?

A:当然,DenseNet 可以通过和 ResNet 一样的方法被应用到物体检测任务中。但是作者并没有在物体检测任务上进行实验,如果关注 DenseNet 在物体检测任务上的效果,可以参考第三方的将 DenseNet 用在物体检测任务上的实验结果。

2.通过图表可以看到,DenseNet 在相对较小计算量和相对较小的模型大小的情况下,相比同等规模的 ResNet 的准确率提升会更明显。是否说明 DenseNet 结构更加适合小模型的设计?

A:确实,在小模型的场景下 DenseNet 有更大的优势。同时,作者也和近期发表的 MobileNet 这一针对移动端和小模型设计的工作进行了对比,结果显示 DenseNet(~400MFlops)可以在更小的计算量的情况下取得比 MobileNet(~500MFlops)更高的 ImageNet 分类准确率。

3.DenseNet 中非常关键的连续的跨层 Concatenate 操作仅存在于每个 Dense Block 之内,不同 Dense Block 之间则没有这种操作,是怎样一种考虑?

A:事实上,每个 Dense Block 最后的特征图已经将当前 Block 内所有的卷积模块的输出拼接在一起,整体经过降采样之后送入了下一个 Dense Block,其中已经包含了这个 Dense Block 的全部信息,这样做也是一种权衡。

4.DenseNet 这样的模型结构在训练过程中是否有一些技巧?

A:训练过程采用了和 ResNet 的文章完全相同的设定。但仍然存在一些技巧,例如因为多次 Concatenate 操作,同样的数据在网络中会存在多个复制,这里需要采用一些显存优化技术,使得训练时的显存占用可以随着层数线性增加,而非增加的更快,相关代码会在近期公布。 



猜你喜欢

转载自blog.csdn.net/dulingtingzi/article/details/79927441