开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第1天
参考论文:Densely Connected Convolutional Networks
作者:Gao Huang, Zhuang Liu, Laurens van der Maaten, Kilian Q. Weinberger
==CVPR2017最佳论文奖==
1、DenseNet简介
DenseNet:以前馈方式将每一层连接到其他每一层。对于具有L层的传统卷积网络有L个连接(每一层与其后续层之间有一个连接),而DenseNet有个连接。
对于每一层,所有前面层的特征图都被用作输入,它自己的特征图被用作所有后续层的输入。
DenseNet有几个引人注目的优势:
- 缓解了梯度消失问题,加强了特征传播,鼓励特征重用,并大大减少了参数量。
官方代码和预训练模型:github.com/liuzhuang13…
图 1:增长率为 k = 4 的 5 层密集块。每一层都将所有前面的特征图作为输入。
在本文中,我们提出了一种架构,将这种见解提炼成一个简单的连接模式:为了确保网络中各层之间的最大信息流,我们将所有层(具有匹配的特征图大小)直接相互连接。为了保持前馈特性,每一层都从所有前面的层获得额外的输入,并将其自己的特征图传递给所有后续层。图 1 示意性地说明了这种布局。至关重要的是,与 ResNets 相比,我们在将特征传递到层之前从不通过求和来组合特征。相反,我们通过连接(Concatenate操作)它们来组合特征。
因此,第 ℓ 层有 ℓ 输入,由所有先前卷积块的特征图组成。它自己的特征图被传递到所有 L−ℓ 后续层。这在 L 层网络中引入了 L(L+1)2 个连接,而不是传统架构中的仅 L 个连接。由于其密集的连接模式,我们将我们的方法称为密集卷积网络(DenseNet)。
除了更好的参数效率外,DenseNets 的一大优势是它们改进了整个网络的信息流和梯度,这使得它们易于训练。每一层都可以直接访问损失函数和原始输入信号的梯度,从而实现隐式深度监督 [20]。这有助于训练更深层次的网络架构。此外,我们还观察到密集连接具有正则化效果,可以减少对较小训练集大小的任务的过度拟合。
当然,他自己的论文介绍中肯定要吹一波自己的模型很牛逼,可以理解。
2、DenseNet与ResNet的主要区别
DenseNets 不是从极深或极宽的架构中汲取表征能力,而是通过特征重用利用网络的潜力,产生易于训练且参数效率高的浓缩模型。连接不同层学习的特征图增加了后续层输入的变化并提高了效率。这构成了 DenseNets 和 ResNets 之间的主要区别。与同样连接来自不同层的特征的 Inception 网络 相比,DenseNets 更简单、更高效。
3、DenseNets
==这部分是重点,因为论文后面所有的参数都是通过这部分得到的,仔细看下面加粗的部分,这就是后面代码中的超参数和网络结构搭建方法,重点中的重点,要不你都看不懂代码中参数是为什么那样写的。==
考虑通过卷积网络传递的单个图像 x0。该网络由 L 层组成,每一层都实现了一个非线性变换 Hℓ(·),其中 ℓ 对层进行索引。 Hℓ(·) 可以是批量归一化 (BN) [14]、整流线性单元 (ReLU) [6]、池化 [19] 或卷积 (Conv) 等操作的复合函数。我们将第 ℓ 层的输出表示为 xℓ。
传统的卷积前馈网络将第 ℓ 层的输出作为输入连接到第 (ℓ + 1) 层 [16],从而产生以下层转换:xℓ = Hℓ(xℓ−1)。
ResNets [11] 添加了一个Skip Connection,可以绕过具有恒等函数的非线性变换:
3.1 ResNets
ResNets 的一个优点是梯度可以直接通过恒等函数从后面的层流到前面的层。但是,恒等函数和 Hℓ 的输出是通过求和组合的,这可能会阻碍网络中的信息流。
3.2 Dense connectivity(密集连接)
Dense connectivity,为了进一步改善层之间的信息流,我们提出了一种不同的连接模式:我们引入了从任何层到所有后续层的直接连接。图 1 示意性地说明了生成的 DenseNet 的布局。因此,第ℓ层接收所有前面层的特征图,x0,...。 . . , xℓ−1,作为输入:
其中 [x0, x1, . . . , xℓ−1] 是指在第 0 . . . , ℓ − 1层中生成的特征图的串联,。由于其密集的连接性,我们将此网络架构称为密集卷积网络(DenseNet)
3.3 Composite function(复合函数)
我们将 Hℓ(·) 定义为三个连续操作的复合函数:批量归一化 (BN) ,然后是整流线性单元 (ReLU) 和 3 × 3 卷积 (Conv )。
3.4 Pooling layers(池化层)
方程式(2)中使用的连接操作。 当特征图的大小发生变化时是不可行的。然而,卷积网络的一个重要部分是下采样层,它会改变特征图的大小。为了便于在我们的架构中进行下采样,我们将网络划分为多个密集连接的密集块;参见图 2。我们将块之间的层称为过渡层,它们进行卷积和池化。我们实验中使用的过渡层由一个批量归一化层和一个 1×1 卷积层和一个 2×2 平均池化层组成。
3.5 Growth rate(增长率)
如果每个函数 Hℓ 产生 k 个特征图,则第 ℓ 层有 k0 + k × (ℓ − 1) 个输入特征图,其中 k0 是输入层中的通道数。 DenseNet 与现有网络架构的一个重要区别是 DenseNet 可以有非常窄的层,例如 k = 12。我们将超参数 k 称为网络的增长率。
3.6 Bottleneck layers(瓶颈层)
尽管每一层只产生 k 个输出特征图,但它通常有更多的输入。在 [36, 11] 中已经注意到,可以在每个 3×3 卷积之前引入一个 1×1 卷积作为瓶颈层,以减少输入特征图的数量,从而提高计算效率。我们发现这种设计对 DenseNet 特别有效,我们将我们的网络称为具有这样一个瓶颈层的网络,即 Hℓ 的 BN-ReLU-Conv(1×1)-BN-ReLU-Conv(3×3) 版本,如DenseNet-B。在我们的实验中,我们让每个 1×1 卷积产生 4k 个特征图。
3.7 Compression(压缩)
为了进一步提高模型的紧凑性,我们可以减少过渡层的特征图数量。如果一个密集块包含 m 个特征图,我们让下面的过渡层生成 ⌊θm⌋ 输出特征图,其中 0 < θ ≤ 1 称为压缩因子。当 θ = 1 时,跨过渡层的特征图数量保持不变。我们将 θ < 1 的 DenseNet 称为 DenseNet-C,我们在实验中设置 θ = 0.5。当同时使用 θ < 1 的瓶颈层和过渡层时,我们将我们的模型称为 DenseNet-BC。
3.8 Implementation Details(实施细节)
在除 ImageNet 之外的所有数据集上,我们实验中使用的 DenseNet 具有三个密集块,每个块具有相同的层数。在进入第一个密集块之前,对输入图像执行具有 16 个(或 DenseNet-BC 增长率的两倍)输出通道的卷积。对于内核大小为 3×3 的卷积层,输入的每一边都用一个像素补零,以保持特征图大小固定。我们使用 1×1 卷积和 2×2 平均池化作为两个连续密集块之间的过渡层。在最后一个密集块结束时,执行全局平均池化,然后附加一个 softmax 分类器。三个密集块中的特征图大小分别为 32×32、16×16 和 8×8。我们用配置 {L = 40, k = 12}, {L = 100, k = 12} 和 {L = 100, k = 24} 来试验基本的 DenseNet 结构。对于 DenseNetBC,对配置为 {L = 100, k = 12},{L = 250, k = 24} 和 {L = 190, k = 40} 的网络进行评估。
L为网络层数,k为网络增长率。
在 ImageNet 上的实验中,我们在 224×224 输入图像上使用具有 4 个密集块的 DenseNet-BC 结构。==初始卷积层包含 2k 个大小为 7×7 的卷积,步长为 2==;所有其他层中的特征图的数量也来自设置 k。
3.9 training
表 2:CIFAR 和 SVHN 数据集的错误率 (%)。 k 表示网络的增长率。超过所有竞争方法的结果为粗体,整体最佳结果为蓝色。 “+”表示标准数据增强。 ∗ 表示我们自己运行的结果。没有数据增强的 DenseNets 的所有结果(C10、C100、SVHN)都是使用 Dropout 获得的。 DenseNets 在使用比 ResNet 更少的参数的同时实现了更低的错误率。如果没有数据增强,DenseNet 的性能会大大提高。
表 3:ImageNet 验证集上的前 1 和前 5 错误率,采用单裁剪(10 裁剪)测试。
图 3:ImageNet 验证数据集上 DenseNets 和 ResNets top-1 错误率(单裁剪测试)的比较,作为学习参数(左)和测试期间 FLOP(右)的函数。
4、网络结构
表 1:ImageNet 的 DenseNet 架构。前 3 个网络的增长率为 k = 32,对于 DenseNet-161,k = 48。请注意,==表中显示的每个“conv”层都对应于序列 BN-ReLU-Conv==。
5、DenseNet121代码复现
5.1 Dense Block
Dense Block的每一个密集层:
每一层产生k个特征图,原论文中让每个1*1个卷积产生4k个特征图
growth_rate:网络的增长率,原论文中超参数名称为k
论文中对内核大小为3*3的卷积层,输入的每一边都用一个像素补零,以保持特征图大小固定。(代码中设置padding='same'即可)
import tensorflow as tf
from tensorflow.keras.layers import Conv2D,BatchNormalization,Activation,ReLU
from tensorflow.keras.layers import Concatenate,AveragePooling2D,Input,ZeroPadding2D
from tensorflow.keras.layers import MaxPooling2D,GlobalAveragePooling2D,Dense
from tensorflow.keras.models import Model
from plot_model import plot_model
# Bottleneck layers
# BN+ReLU+1*1Conv+BN+ReLU+3*3Conv
# 每一层产生k个特征图,原论文中让每个1*1个卷积产生4k个特征图
# growth_rate:网络的增长率,原论文中超参数名称为k
def conv_block(x,growth_rate,name):
x1=BatchNormalization(name=name+'_0_bn')(x)
x1=ReLU(name=name+'_0_relu')(x1)
x1=Conv2D(filters=4*growth_rate,
kernel_size=(1,1),
use_bias=False,
name=name+'_1_conv')(x1)
x1=BatchNormalization(name=name+'_1_bn')(x1)
x1=ReLU(name=name+'_1_relu')(x1)
# 论文原话:对内核大小为3*3的卷积层,输入的每一边都用一个像素补零,以保持特征图大小固定。
x1=Conv2D(growth_rate,
kernel_size=(3,3),
padding='same',
use_bias=False,
name=name+'_2_conv')(x1)
# 将前面所有层的特征堆叠后传到下一层
out=Concatenate(axis=-1,name=name+'_concat')([x,x1])
return out
'''
x: input tensor.
blocks: integer, the number of building blocks.
name: string, block label.
'''
# DenseNet
def dense_block(x,blocks,name):
for i in range(blocks):
x=conv_block(x,32,name=name+'_block'+str(i+1))
return x
5.2 Transition Layer(过渡层)
过渡层:
reduction:压缩因子,原论文中名称为,设置为0.5。
# 论文原话:使用1*1卷积和2*2平均池化作为两个连续密集块之间的过渡层
# 过渡层:BN+1*1Conv+2*2AvgPool
# reduction:压缩因子,原论文中设置为0.5
def transition_block(x,reduction,name):
x=BatchNormalization(name=name+'_bn')(x)
x=ReLU(name=name+'_relu')(x)
x=Conv2D(filters=int(x.shape[-1]*reduction),
kernel_size=(1,1),
use_bias=False,
name=name+'_conv')(x)
x=AveragePooling2D(pool_size=(2,2),strides=2,name=name+'_pool')(x)
return x
5.3 DensNet121网络骨干
几种网络结构的参数设置如下:
DenseNet121(k=32):blocks=[6,12,24,16] DenseNet169(k=32):blocks=[6,12,32,32] DenseNet201(k=32):blocks=[6,12,48,32] DenseNet161(k=48):blocks=[6,12,36,24]
def DenseNet(blocks,include_top=True,weights='imagenet',intput_shape=None,classes=1000):
img_input=Input(shape=intput_shape)
x=ZeroPadding2D(padding=((3,3),(3,3)))(img_input)
# 论文原话:初始卷积层包含2k个大小为7*7的卷积,步长为2(k=32)
# 其实这里完全可以去掉这两个ZeroPadding2D,直接给这里的卷积和池化设置padding='same'即可,
# 但是我看谷歌的源码中没用same padding,所以这里我也没用。
x=Conv2D(filters=64,kernel_size=(7,7),strides=2,use_bias=False,name='conv1/conv')(x)
x=BatchNormalization(name='conv1/bn')(x)
x=ReLU(name='conv1/relu')(x)
x=ZeroPadding2D(padding=((1,1),(1,1)))(x)
x=MaxPooling2D(pool_size=(3,3),strides=2,name='pool1')(x)
x=dense_block(x,blocks[0],name='conv2')
x=transition_block(x,0.5,name='pool2')
x=dense_block(x,blocks[1],name='conv3')
x=transition_block(x,0.5,name='pool3')
x=dense_block(x,blocks[2],name='conv4')
x=transition_block(x,0.5,name='pool4')
x=dense_block(x,blocks[3],name='conv5')
x=BatchNormalization(name='bn')(x)
x=ReLU(name='relu')(x)
x=GlobalAveragePooling2D(name='global_avg_pool')(x)
x=Dense(classes,activation='softmax')(x)
model=Model(img_input,x)
return model
5.4 构建网络并查看结构
if __name__ == '__main__':
densenet121=DenseNet(blocks=[6,12,24,16],intput_shape=(224,224,3))
densenet121.summary()
plot_model(densenet121,to_file='img/DenseNet121.png')
References
Huang G , Liu Z , Laurens V , et al. Densely Connected Convolutional Networks[C]// IEEE Computer Society. IEEE Computer Society, 2016.
Ioffe S , Szegedy C . Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift[C]// JMLR.org. JMLR.org, 2015.
X. Glorot, A. Bordes, and Y. Bengio. Deep sparse rectifier neural networks. In AISTATS, 2011. 3
www.tensorflow.org/versions/r2…
DenseNet 天气图片四分类(权重迁移学习),附Tensorflow完整代码
引用打的不全,翻译可能有点蹩脚,不过今天这个模型是对照着源码搭建的,以后还是尽量要看源码。
还有一点就是搭建模型的时候最好把那个模型的论文看一下,要不很多给定的超参数你不知道为什么那样写,这些超参数论文中都是通过实验给出来的经验值,看过论文你就知道我在说什么了。