Densely Connected Convolutional Networks
最后更新于
最后更新于
在残差网络的文章中,我们知道残差网格,能够应用在特别深的网络中的一个重要原因是,无论正向计算精度还是反向计算梯度,信息都能毫无损失的从一层传到另一层。如果我们的目的是保证信息毫无阻碍的传播,那么残差网络的stacking残差块的设计便不是信息流通最合适的结构。
基于信息流通的原理,一个最简单的思想便是在网络中的每个卷积操作中,将其低层的所有特征作为该网络的输入,也就是在一个层数为L的网络中加入个short-cut, 如图1。为了更好的保存低层网络的特征,DenseNet 使用的是将不同层的输出拼接在一起,而在残差网络中使用的是单位加操作。以上便是DenseNet算法的动机。
在DenseNet中,如果全部采用图1的结构的话,第L层的输入是之前所有的Feature Map拼接到一起。考虑到现今内存/显存空间的问题,该方法显然是无法应用到网络比较深的模型中的,故而DenseNet采用了图2所示的堆积Dense Block的形式,下面我们针对图2详细解析DenseNet算法。
下面Demo是在MNIST数据集上的DenseNet代码,完整代码见:https://github.com/senliuy/CNN-Structures/blob/master/DenseNet.ipynb
DenseNet具有如下优点:
信息流通更为顺畅;
支持特征重用;
网络更窄
由于DenseNet需要在内存中保存Dense Block的每个节点的输出,此时需要极大的显存才能支持较大规模的DenseNet,这也导致了现在工业界主流的算法依旧是残差网络。
图1便是一个Dense Block,在Dense Block中,第层的输入是这个块中前面所有层的输出:
其中,中括号表示拼接操作,即按照Feature Map将个输入拼接成一个Tensor。表示合成函数(Composite function)。在实现时,我使用了stored_features存储每个合成函数的输出。
合成函数位于Dense Block的每一个节点中,其输入是拼接在一起的Feature Map, 输出则是这些特征经过BN->ReLU->3*3
卷积的三步得到的结果,其中卷积的Feature Map的数量是成长率(Growth Rate)。在DenseNet中,成长率k一般是个比较小的整数,在论文中,。但是拼接在一起的Feature Map的数量一般比较大,为了提高网络的计算性能,DenseNet先使用了卷积将输入数据降维到,再使用卷积提取特征,作者将这一过程标准化为BN->ReLU->1*1卷积->BN->ReLU->3*3卷积
,这种结构定义为DenseNetB。
成长率是DenseNet的一个超参数,反应的是Dense Block中每个节点的输入数据的增长速度。在Dense Block中,每个节点的输出均是一个维的特征向量。假设整个Dense Block的输入数据是维的,那么第个节点的输入便是。作者通过实验验证,一般取一个比较小的值,作者通过实验将设置为12。
至此,DenseNet的Dense Block已经介绍完毕,在图2中,Dense Block之间的结构叫做压缩层(Compression Layer)。压缩层有降维和降采样两个作用。假设Dense Block的输出是维的特征向量,那么下一个Dense Block的输入是,其中是压缩因子(Compression Factor),用户自行设置的超参数。当等于1时,Dense Block的输入和输出的维度相同,当时,网络叫做DenseNet-C,在论文中,。包含瓶颈层和压缩层的DenseNet叫做DenseNet-BC。Pooling层使用的是的Average Pooling层。