Densely Connected Convolutional Networks
密集连接的卷积网络;
发表时间:[Submitted on 25 Aug 2016 (v1), last revised 28 Jan 2018 (this version, v5)];
发表会议/期刊:Computer Vision and Pattern Recognition;
论文地址:https://arxiv.org/abs/1608.06993;
代码地址:https://github.com/liuzhuang13/DenseNet;
0 摘要
短路连接/残差连接可以让网络变得更深,收敛更快,精度更高;
本文结合这种思想,提出密集卷积网络(DenseNet),以前馈方式实现密集连接;
- 传统的L层网络有L个连接,就是每层和后续层之间的连接;
- 而DenseNet有 L ( L + 1 ) 2 \frac{L(L+1)}{2} 2L(L+1)个直接连接;对每一层来说,将之前所有层的结果作为输入;(图1所示)
DenseNet的优点:
- 缓解了梯度消失问题;
- 加强了特征传播;
- 鼓励特征重用;
- 大幅度减少参数数量;
在四个分类任务中(CIFAR-10、CIFAR-100、SVHN和ImageNet)评估了DenseNet的性能;
DenseNet性能更高,参数量更少;
1 简介
随着CNN变得越来越深,梯度消失问题严重;
- ResNet和HIghway Networks通过 identity connections来解决这个问题;
- Stochastic depth通过在训练时随机dropping layers,来获取更好的信息和梯度流;
- FractalNets组合几个具有不同数量卷积核的并行层;
尽管以上这些方法的网络拓扑结构和训练过程各不相同,但它们都有一个关键的特征:在浅层和深层创建了短路连接(short paths);
本文提出DenseNet,将这种想法浓缩成一种简单的模式:为了确保网络中各层之间的信息流最大化,将所有层(具有匹配的特征映射大小)直接彼此连接(极限操作,把所有层都连起来了);
为了保持前馈特性,每一层从所有前面的层获得额外的输入,并将自己的特征映射传递给所有后续层;如图1所示:
直觉上觉得DenseNet可能会有更多的参数,事实上,DenseNet比传统的卷积网络需要更少的参数,因为它不需要重新学习冗余的特征图;
DenseNet是非常窄的网络(例如,每层12个滤波器),只向网络的“集体知识 (collective knowledge)”中添加一小部分特征映射,并保持其余特征映射不变,最终分类器根据网络中的所有特征映射做出决定;
除了效率外,DenseNet的另一优势是它们在整个网络中改进的信息流和梯度,这使得它们易于训练;每一层都可以直接访问来自损失函数和原始输入信号的梯度,从而实现隐式深度监督。这有助于训练更深层次的网络架构。此外,我们还观察到密集连接具有正则化效应,这减少了训练集规模较小的任务上的过拟合。
2 相关工作
3 DenseNets
一些变量…
-
输入图像: x 0 x_0 x0;
-
网络包含 L L L层,每一层包含一个非线性变换 H l ( ⋅ ) H_l(·) Hl(⋅)
-
H l ( ⋅ ) H_l(·) Hl(⋅)可以是:BN/ReLU/Pooling/Conv;
-
输出结果: x l x_l xl;
3.1 ResNets
传统卷积网络: 将 l t h l^{th} lth层的输出作为 ( l + 1 ) t h (l+1)^{th} (l+1)th的输入;
ResNets: 加入skip-connection=>identity mapping;ResNets的一个优点是梯度可以直接通过identity mapping从后面的层流向前面的层
3.2 Dense connectivity
为了进一步改善,本文提出一种不同的连接模式,将之前所有的层作为本层的输入,如图1所示;
l t h l^{th} lth层的输入是: x 0 , x 1 , . . . , x l − 1 x_0, x_1, ..., x_{l-1} x0,x1,...,xl−1,其中 [ x 0 , x 1 , . . . , x l − 1 ] [x_0, x_1, ..., x_{l-1}] [x0,x1,...,xl−1]是之前所有层feature map的concatenation;
3.3 复合函数
H l ( ⋅ ) H_l(·) Hl(⋅)定义为三个连续操作的符合函数:
- BN;
- ReLU;
- 3×3 Conv;
3.4 池化
特征图大小发生变化就不可以直接concatenation,需要先用池化层进行下采样;
为了方便进行下采样,将网络划分成多个dense block,如图2所示;
将dense block之间的操作称为过渡层(transition layers),过渡层进行卷积和池化;
本文实验中使用的过渡层包含一个BN(ReLU)、一个1×1卷积和一个2×2池化;
# 过渡层
class _Transition(nn.Sequential):
def __init__(self, num_input_features, num_output_features):
super(_Transition, self).__init__()
self.add_module('norm', nn.BatchNorm2d(num_input_features))
self.add_module('relu', nn.ReLU(inplace=True))
self.add_module('conv', nn.Conv2d(num_input_features, num_output_features,
kernel_size=1, stride=1, bias=False))
self.add_module('pool', nn.AvgPool2d(kernel_size=2, stride=2))
# dense block
class _DenseBlock(nn.Module):
def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate, memory_efficient=False):
super(_DenseBlock, self).__init__()#num_layers层重复次数
for i in range(num_layers):
layer = _DenseLayer(
num_input_features + i * growth_rate, #for一次层数增加32
growth_rate=growth_rate,
bn_size=bn_size,
drop_rate=drop_rate,
memory_efficient=memory_efficient,
)
self.add_module('denselayer%d' % (i + 1), layer) # 追加denselayer层到字典里面
def forward(self, init_features):
features = [init_features] #原来的特征,64
for name, layer in self.named_children(): # 依次遍历添加的6个layer层,
new_features = layer(*features) #计算特征
features.append(new_features) # 追加特征
return torch.cat(features, 1) # 注意这里 按通道数合并特征 64 + 6*32=256
3.5 grow rate增长速率
如果每一个 H l ( ⋅ ) H_l(·) Hl(⋅)会产生 k k k个feature maps,则第 l l l层会有 k 0 + k × ( l − 1 ) k_0 + k × (l - 1) k0+k×(l−1)个输入的feature map,其中 k 0 k_0 k0是输入层的通道数;
DenseNet和现有网络架构之间的一个重要区别是DenseNet可以具有非常窄的层,例如k = 12,本文将 k k k称为网络的增长率;(在第4节有相应的实验)
3.6 瓶颈层
虽然每一层只产生k个输出特征映射,但它通常有更多的输入。Inception指出,在每次3×3卷积之前引入一个1×1卷积作为瓶颈层,以减少输入特征映射的数量,从而提高计算效率。我们发现这种设计对DenseNet特别有效;
本文使用:
H
l
H_l
Hl:BN-ReLu-Conv(1×1)-BN-ReLU-Conv(3×3)——DenseNet-B;
在本文的实验中,每个1×1卷积产生4k特征图。
class _DenseLayer(nn.Sequential):
def __init__(self, num_input_features, growth_rate, bn_size, drop_rate, memory_efficient=False):#num_input_features特征层数
super(_DenseLayer, self).__init__()#growth_rate=32增长率 bn_size=4
#(56 * 56 * 64)
self.add_module('norm1', nn.BatchNorm2d(num_input_features)),
self.add_module('relu1', nn.ReLU(inplace=True)),
self.add_module('conv1', nn.Conv2d(num_input_features, bn_size *
growth_rate, kernel_size=1, stride=1,
bias=False)),
self.add_module('norm2', nn.BatchNorm2d(bn_size * growth_rate)),
self.add_module('relu2', nn.ReLU(inplace=True)),
self.add_module('conv2', nn.Conv2d(bn_size * growth_rate, growth_rate,
kernel_size=3, stride=1, padding=1,
bias=False)),
#(56 * 56 * 32)
self.drop_rate = drop_rate
self.memory_efficient = memory_efficient
def forward(self, *prev_features):
bn_function = _bn_function_factory(self.norm1, self.relu1, self.conv1)#(56 * 56 * 64*3)
if self.memory_efficient and any(prev_feature.requires_grad for prev_feature in prev_features):
bottleneck_output = cp.checkpoint(bn_function, *prev_features)
else:
bottleneck_output = bn_function(*prev_features)
# bn1 + relu1 + conv1
new_features = self.conv2(self.relu2(self.norm2(bottleneck_output)))
if self.drop_rate > 0:
new_features = F.dropout(new_features, p=self.drop_rate,
training=self.training)
return new_features
3.7 Compression
为了进一步提高模型的紧凑性,我们可以减少过渡层的feature map数量;
如果一个dense block包含m个feature map,则让下一个过渡层生成 f l o o r ( θ m ) floor(θm) floor(θm)个feature map。 θ θ θ被称为压缩因子(compression factor);
- θ = 1 θ = 1 θ=1:feature map数量保持不变;
- θ < 1 θ < 1 θ<1:——DenseNet-C;
- θ = 0.5 θ = 0.5 θ=0.5:实验中使用;
当使用θ < 1的瓶颈层和过渡层时,我们将我们的模型称为DenseNet-BC。
trans = _Transition(num_input_features=num_features,
num_output_features=num_features // 2) # 输出通道数减半 θ=0.5
self.features.add_module('transition%d' % (i + 1), trans)
num_features = num_features // 2 # 更新num_features= num_features//2 取整数部分
3.8 实现细节
在除ImageNet之外的所有数据集上,实验中使用的DenseNet都有3个dense block,每个dense block都具有相同数量的层;三个dense block里的feature map大小分别为32× 32、16×16和8×8;
在图像进入第一个dense block之前,对输入图像进行16个输出通道(或者DenseNet-BC增长率的两倍)的卷积;
对于3×3的卷积层,使用0 padding来保持feature map的大小相同;
使用1×1卷积和2×2平均池化作为两个相邻密集块之间的过渡层;在最后一个密集块的末尾,执行全局平均池化,然后附加一个softmax分类器;
基础DenseNet——{L = 40, k = 12}, {L = 100, k = 12} and {L = 100, k = 24};
DenseNet-BC——{L = 100, k = 12}, {L= 250, k = 24} and {L= 190, k = 40};
在ImageNet上的实验中,我们在224×224输入图像上使用了带有4个密集块的DenseNet-BC结构。
初始卷积层包括2k个大小为7×7的卷积,stride为2;所有其他层的特征映射的数量也遵循设置k。我们在ImageNet上使用的确切网络配置如表1所示。
4 实验
4.1 数据集
CIFAR:
- 32×32;
- CIFAR-10:10类;
- CIFAR-100:100类;
- train:50,000 + test:10,000;
- 额外5,000 validation;
- 数据增强:镜像/平移=>用"+"来标注;
- 预处理:均值、标准化;
SVHN:
- 全称:Street View House Numbers;
- 图像大小:32×32×3;
- train:73,257+test:26,032;
- 没有任何数据增强;
- 从训练集中分离出一个包含6,000张图像的验证集;
- 预处理;归一化;
ImageNet:
- ILSVRC 2012 classification dataset;
- 1.2 million images for training, and 50,000 for validation;
- 1,000类;
- 数据增强:10crop,224×224;
4.2 训练
- SGD;
- CIFAR:batch size = 64. epoch = 300;
- SVHN:batch size = 64. epoch = 40;
- init lr = 0.1, 在epoch的50%和75%除以10(上面两个数据集);
- ImageNet上,我们训练模型90个epoch,批处理大小为256, init lr = 0.1,在epoch 30 和epoch 60的时候除以10;
- dropout = 0.2;
4.3 Classification Results on CIFAR and SVHN
精度: L = 190和k = 40的DenseNet-BC在所有CIFAR数据集上始终优于现有的方法,明显低于宽ResNet;
容量: 如果没有压缩或瓶颈层,densenet的总体趋势是随着L和k的增加表现得更好。我们将这主要归因于模型容量的相应增长;
C10+和C100+这一列最好地说明了这一点。在C10+上,随着参数数从1.0M增加,超过7.0M增加到27.2M,误差从5.24%下降到4.10%,最后下降到3.74%;在C100+上,我们观察到类似的趋势;
这表明DenseNets可以利用更大、更深入的模型所增加的表征能力。也表明它们不存在过拟合或残差网络[11]的优化困难。
参数效率: 我们的250层模型只有15.3M参数,但它始终优于其他具有超过30M参数的模型;
L = 100和k = 12的DenseNet-BC与使用少90%参数的101层预激活ResNet具有相当的性能;
过拟合: DenseNet不太容易过拟合,在没有数据增强的数据集上,DenseNet架构的改进比以前的工作特别明显。在C10上,改进表明误差相对减少了29%,从7.33%降至5.19%。在C100上,从28.20%下降到19.64%,降幅约为30%。在我们的实验中,我们在单一设置中观察到潜在过拟合:在C10上,通过增加k = 12到k = 24所产生的参数增长4倍,导致误差从5.77%适度增加到5.83%。DenseNet-BC瓶颈和压缩层似乎是对抗这一趋势的有效方法。
4.4 Classification Results on ImageNet
结果如表3所示:
图3显示了DenseNets和ResNets的单次收获top-1验证误差随参数数量(左)和flop数量(右)的函数关系。
5 讨论
从表面上看,DenseNet与ResNets非常相似:一个是concat,一个是求和,然而,这个看似很小的修改导致两个网络架构的行为有了本质上的不同;
5.1 Model compactness模型密实度
任何DenseNet层学习的特征映射都可以被所有后续层访问。这鼓励在整个网络中重用特性,并导致更紧凑的模型。
图4表示了DenseNet和ResNet的参数效率;
DenseNet- BC始终是DenseNet的参数效率最高的变体,为了达到相同的精度水平,DenseNet-BC只需要ResNets的1/3左右的参数;
5.2 隐性深度监督
密集卷积网络精度提高的一种解释可能是,各个层通过较短的连接从损失函数获得额外的监督。人们可以将DenseNet解释为执行一种“深度监督”。
DenseNets以一种隐式的方式执行类似的深度监督:网络顶部的单个分类器通过最多两到三个过渡层对所有层提供直接监督。然而,DenseNet的损失函数和梯度基本上不那么复杂,因为所有层之间共享相同的损失函数。
5.3 随机与确定性的联系
密集卷积网络(DenseNet)和残差网络(ResNet)的随机深度正则化[13]之间有一个有趣的联系:
在随机深度中,残差网络中的层是随机drop的,这在周围层之间建立了直接连接。由于池化层永远不会被drop,因此网络的结果与DenseNet的连接模式相似:
如果所有中间层都被随机丢弃,那么同一池化层之间的任何两层都有很小的概率直接连接。虽然这些方法最终是完全不同的,但DenseNet对随机深度的解释可能会为这个正则化器的成功提供见解。
5.4 特征重用
按照设计,DenseNets允许各层从其之前的所有层访问feature map(尽管有时通过过渡层)。我们进行了一个实验来调查一个训练有素的网络是否利用了这个机会。
首先在C10+上训练一个L = 40, k = 12的DenseNet。对于一个块内的每个卷积层,我们计算分配给层s连接的平均(绝对)权重。图5显示了所有三个dense block的热图。
平均绝对权值可以代替卷积层对其前一层的依赖关系。位置 ( l , s ) (l,s) (l,s)上的红点表示该层平均而言大量使用了之前生成的s层的特征映射。从图中可以观察到以下几点:
-
所有层都将它们的权重分布在同一块中的许多输入上。这表明,由非常早期的层提取的特征确实直接被同一密集块中的深层层使用。
-
过渡层的权重也将它们的权重分散到前面密集块中的所有层,表明信息流从DenseNet的第一层到最后一层通过很少的间接性。
-
第二个和第三个密集块内的层一致地将最小的权重分配给过渡层的输出(三角形的顶部行),表明过渡层输出了许多冗余的特征(平均权重较低)。这与DenseNet-BC的强大结果是一致的,这些输出都被压缩了。
-
尽管最后的分类层(显示在最右边)也在整个密集的块中使用权重,但似乎对最终的特征图有一个集中,这表明在网络的后期可能会产生一些更高级别的特征。