1 引入
DenseNet是2017年计算机视觉顶会的年度最佳论文。
论文链接:DenseNet论文
代码的github链接:DenseNet代码
DenseNet想法的由来在很大程度上是来自随机深度网络(Deep networks with stochastic depth),当时提出了一种类似于 Dropout 的方法来改进ResNet。在训练过程中的每一步都随机地「扔掉」(drop)一些层,可以显著的提高 ResNet 的泛化性能。这个方法的成功至少带给DenseNet两点启发:
- 首先,它说明了神经网络其实并不一定要是一个递进层级结构,也就是说网络中的某一层可以不仅仅依赖于紧邻的上一层的特征,而可以依赖于更前面层学习的特征。
- 其次,在训练的过程中随机扔掉很多层也不会破坏算法的收敛,说明了 ResNet 具有比较明显的冗余性,网络中的每一层都只提取了很少的特征(即所谓的残差)。实际上,我们将训练好的 ResNet 随机的去掉几层,对网络的预测结果也不会产生太大的影响。既然每一层学习的特征这么少,能不能降低它的计算量来减小冗余呢?
——>DenseNet 的设计正是基于以上两点观察。
- 我们让网络中的每一层都直接与其前面层相连,实现特征重用;
- 同时把网络的每一层设计得特别「窄」,即只学习非常少的特征图(最极端情况就是每一层只学习一个特征图),达到降低冗余性的目的。
2 DenseNet模型结构
DenseNet:以前馈的方式,将层与层都连接起来(so需要具有匹配的feature-map大小),它建立的是前面所有层与后面层的密集连接(dense connection)。如下图所示。
DenseNet脱离了加深网络层数(ResNet)和加宽网络结构(Inception)来提升网络性能的定式思维,从特征的角度考虑。
整个网络主要包含了三个核心结构:
- DenseLayer/bottleneck层:模型中的原子单元,利用卷积完成一次最基础的特征提取
- DenseBlock:整个模型密集连接的基础单元,由多个原子单元构成,整个网络最核心的部分
- Transition层:通常用于两个相邻的DenseBlock之间,主要的两个作用是减小特征图的大小和特征图的数量
实际上主要由DenseBlock+Transition组成。
- DenseBlock(定义了输入输出如何连接) 是包含很多层的模块,每个层的特征图大小相同,层与层之间采用密集连接方式。
- Transition模块(控制通道数) 是连接两个相邻的Dense Block,并且通过Pooling使特征图大小降低。
3 DenseBlock
DenseBlock模块其实就是堆叠一定数量的DenseLayer层,不同DenseLayer层之间会发生密集连接。
在一个DenseBlock内部(即特征图大小都相同),将所有的DenseLayer层都进行连接,即第一层的特征会直接传输给后面的所有的层,后面的层会接受前面所有层的输出特征。
3.1 公式
假设输入为一个图片 x 0 x_0 x0,经过一个L层的神经网络,第l层的输入特征图记作 x l − 1 x_{l-1} xl−1,第l层的特征输出记作 x l x_l xl。
-
对于传统的网络: x l = H l ( x l − 1 ) x_l = H_l(x_{l-1}) xl=Hl(xl−1),其中 H l H_l Hl是一个组合函数,通常包括BN、ReLU、Pooling、Conv操作。
-
对于ResNet: x l = H l ( x l − 1 ) + x l − 1 x_l = H_l(x_{l-1}) + x_{l-1} xl=Hl(xl−1)+xl−1
-
对于DenseNet: x l = H l ( [ x 0 , x 1 , ⋯ , x l − 1 ] ) x_l = H_l([x_0,x_1,\cdots,x_{l-1}]) xl=Hl([x0,x1,⋯,xl−1])
其中[]代表拼接操作,既将第0层 到 l-1层的所有输出特征图在通道维度上组合在一起。这里所用到的非线性变换 H l H_l Hl为BN +ReLU+3×3Conv的组合。
DenseNet的前向过程如下图所示,可以更直观地理解其密集连接方式,如
h
3
h_3
h3的输入不仅包括来自
h
2
h_2
h2的
x
2
x_2
x2,还包括前面两层的
x
0
x_0
x0和
x
1
x_1
x1,它们是在channel维度上连接在一起的。
因为是直接跨通道直接做concat,所以要求不同层concat之前他们的特征图大小应当是相同的,
所以DenseNet分为了好几个Dense Block,每个Dense Block内部的feature map的大小相同,
而每个Dense Block之间使用一个Transition模块来进行下采样过渡连接。
3.2 增长率
DenseBlock中各个层卷积之后均输出 k 个特征图,即得到的特征图的channel数为 k,或者说采用 k 个卷积核。 k 在DenseNet称为growth rate,这是一个超参数。论文表明一般情况下使用较小的 k,就可以得到较佳的性能。
假定输入层的特征图的channel数为
k
0
k_0
k0 ,Dense Block中各个层卷积之后均输出 k 个特征图,即每层得到的特征图的channel数为k。那么l层输入的channel数为
k
0
+
k
×
(
l
−
1
)
k_0 + k \times (l − 1)
k0+k×(l−1) 。
注:
每一层都可以访问其块中的前面所有的特征图,即可以访问网络的全局知识。如果将特征图视为网络的全局状态,每一层都将自己的k个特征图添加到全局状态中。
增长率控制了每一层向全局状态贡献的新信息的数量。
一旦写入全局状态,就可以从网络中的任何地方访问它,并且与传统的网络体系结构不同,无需将其逐层复制。
3.3 Bottleneck层
目的:随着层数增加,concat后的通道数会增加到上千。尽管 k 设定得较小,但DenseBlock的输入会非常多,DenseBlock内部可以采用bottleneck层来减少计算量。
方法:主要是在原有的结构(下图第一张)前面增加1x1 Conv,即BN+ReLU+1x1 Conv + BN+ReLU+3x3 Conv,称为DenseNet-B结构(下图第二张)。
其中
1
×
1
1 \times 1
1×1 Conv得到
4
×
k
4 \times k
4×k 个特征图,目的是减少输入的feature map数量,既能降维减少计算量,又能融合各个通道的特征。
如果不增加1×1的卷积来降维,后续3×3卷积所需的参数量会急剧增加。
举例:
以DenseNet-121(6,12,24,16)的Dense Block(1)为例,包含6个
1
×
1
1 \times 1
1×1和
3
×
3
3 \times 3
3×3的卷积操作,也就是6个Bottleneck层。输入通道数 是64,增长率k=32。
经过6个Bottleneck,通道数输出是64+6*32=256。
如果不使用
1
×
1
1 \times 1
1×1 Conv,参数量是:
3
×
3
×
256
×
32
=
73
,
728
3\times3\times256\times32=73,728
3×3×256×32=73,728
如果使用
1
×
1
1 \times 1
1×1 Conv,代码中的
1
×
1
1 \times 1
1×1 Conv的输出channel是
4
×
k
4 \times k
4×k,也就是128,然后再作为3*3卷积的输入。参数量是:
1
×
1
×
256
×
128
+
3
×
3
×
128
×
32
=
69
,
632
1\times1\times256\times128+3\times3\times128\times32=69,632
1×1×256×128+3×3×128×32=69,632。使用bottleneck就大大减少了计算量。
注:
DenseBlock中采用BN+ReLU+Conv的结构,平常我们常见的是Conv+BN+ReLU。
原因是:卷积层的输入包含了它前面所有层的输出特征,它们来自不同层的输出,因此数值分布差异比较大,所以它们在输入到下一个卷积层时,必须先经过BN层将其数值进行标准化,然后再进行卷积操作。
4 Transition
将DenseNet划分为多个Dense Blocks,而块之间的称为转换层,即Transition层,用于卷积和池化。
transition layer用于两个Dense Block中间。
4.1 Pooling layers——池化层
Transition层主要包括一个1x1的Conv和2x2的AvgPooling。其结构为BN+ReLU+1x1 Conv + 2x2 AvgPooling。
- 1 × 1 1 \times 1 1×1 Conv负责降低通道数。这是因为每个Dense Block结束后的输出channel个数很多,需要用1*1的卷积核来降维。
- 2 × 2 2 \times 2 2×2 AvgPool负责改变特征图尺寸,减半。作用: 通过池化操作从而减少了特征和参数数量;实际上就是过滤掉那些作用小、信息冗余的特征,保留关键信息。
4.2 Compression——压缩
目的:为了进一步提高模型的紧凑性,可以减少转换层的特征图数量。
方法:引入一个压缩因子
θ
\theta
θ (0 <
θ
\theta
θ ≤1),
- 当 θ \theta θ = 1 时,转换层的输入和输出特征数不变,也就是经过转换层后特征数不变;
- 当 θ \theta θ < 1 时,输入特征图数为m时,输出为⌊ θ m \theta_m θm⌋。将 θ \theta θ < 1 的DenseNet称为DenseNet-C (DenseNet论文中设置θ=0.5)。
将Dense Block+bottleneck+Translation的模型称为DenseNet-BC。
DenseNet-B已经压缩了,为什么还要提出DenseNet-C?
——>瓶颈层是指用于dense block内的
H
(
⋅
)
H(\cdot)
H(⋅)压缩,而压缩是指在转换层进行压缩。
5 总结
DenseNet:
- 密集块:DenseNet将网络分成多个密集块(Dense Block)。在每个密集块内,每一层都连接到前面所有的层。这种跳跃连接有助于解决梯度消失问题,因为每一层都可以直接访问之前层的梯度信息,使得训练更加稳定。
- 过渡层:在密集块之间,通常会使用过渡层来控制特征图的大小,从而减少计算量。
优点:
- 网络可以更好地利用浅层特征信息,加强了特征传播,提高了特征重用的能力,从而提高网络的性能。
- 网络具有更强的特征重用能力,可以减少参数数量,降低过拟合风险。
- 网络训练更加稳定,可以缓解梯度消失和梯度爆炸等问题。
- 改善了整个网络中的信息流和梯度,由于密集连接方式,DenseNet提升了梯度的反向传播,使得网络更容易训练。每一层都可以直接访问损失函数和初始输入。
缺点:
- 因为有稠密直连的过程,所以各个feature都要存下来,实际上很容易爆显存。
- 另外,这种密集连接也意味着反向传播计算梯度更加复杂,每一步训练并不一定会更快。