DenseNet密集卷积网络详解(附代码实现) |
您所在的位置:网站首页 › detnet网络结构 › DenseNet密集卷积网络详解(附代码实现) |
文章目录
前言Dense BlockGrowth rateBottleneckTransition layer压缩因子DenseNet网络结构DenseNet优缺点实验效果Pytorch实现DenseNet
前言
DenseNet是CVPR2017的最佳论文,由康奈尔大学黄高博士(Gao Huang)、清华大学本科生刘壮(Zhuang Liu)、Facebook 人工智能研究院研究科学家 Laurens van der Maaten 及康奈尔大学计算机系教授 Kilian Q. Weinberger 所作,有兴趣的同学可以结合原文阅读。 ResNet通过前层与后层的“短路连接”(Shortcuts),加强了前后层之间的信息流通,在一定程度上缓解了梯度消失现象,从而可以将神经网络搭建得很深,具体可以参考ResNet残差网络及变体详解。更进一步,这次的主角DenseNet最大化了这种前后层信息交流,通过建立前面所有层与后面层的密集连接,实现了特征在通道维度上的复用,不但减缓了梯度消失的现象,也使其可以在参数与计算量更少的情况下实现比ResNet更优的性能。连接方式可以看下面这张图:
Dense Block是DenseNet的一个基本模块,这里我们从一般的神经网络说起: 这里要注意,因为我们是直接跨通道直接做concat,所以这里要求不同层concat之前他们的特征图大小应当是相同的,所以DenseNet分为了好几个Dense Block,每个Dense Block内部的feature map的大小相同,而每个Dense Block之间使用一个Transition模块来进行下采样过渡连接,这个后文会介绍。 Growth rate假如输入特征图的channel为
K
0
K_0
K0,那么第
l
l
l 层的channel数就为
K
0
+
(
l
−
1
)
K
K_0+(l-1)K
K0+(l−1)K,我们将其称之为网络的增长率(growth rate)。因为每一层都接受前面所有层的特征图,即特征传递方式是直接将前面所有层的特征concat后传到下一层,所以这个
K
K
K不能很大,要注意这个K的实际含义就是这层新提取出的特征。 在刚才Dense Block中的非线性组合函数是指BN+ReLU+3x3 Conv的组合,尽管每前进一层,只产生K张新特征图,但还是嫌多,于是在进行3×3卷积之前先用一个 1×1卷积将输入的特征图个数降低到 4*k,我们发现这个设计对于DenseNet来说特别有效。所以我们的非线性组合函数就变成了BN+ReLU+1x1 Conv+BN+ReLU+3x3 Conv的结构,由此形成的网络结构我们称之为DenseNet-B。 增加了1x1的卷积的Dense Block也称为Bottleneck结构,实现细节如下: 两个相邻的Dense Block之间的部分被称为Transition层,具体包括BN、ReLU、1×1卷积、2×2平均池化操作。通过1×1卷积层来减小通道数,并使用步幅为2的平均池化层减半高和宽,从而进一步降低模型复杂度。 压缩因子为进一步提高网络的紧密度,我们可以在转换层(transition layers)减少feature-maps的数量。我们引入一个压缩因子 θ \theta θ,假定上一层得到的feature map的channel大小为 m m m,那经过Transition层就可以产生 θ m \theta m θm 个特征,其中 θ \theta θ在0和1之间。在DenseNet-C中,我们令 θ \theta θ=0.5。当模型结构即含瓶颈层,又含压缩层时,我们记模型为DenseNet-BC。 DenseNet网络结构DenseNet网络构成如下: 以DenseNet-121为例,看下其网络构成: DenseNet-121由121层权重层组成,其中4个Dense block,共计2×(6+12+24+16) = 116层权重,加上初始输入的1卷积层+3过渡层+最后输出的全连接层,共计121层;训练时采用了DenseNet-BC结构,压缩因子0.5,增长率k = 32;初始卷积层有2k个通道数,经过7×7卷积将224×224的输入图片缩减至112×112;Denseblock块由layer堆叠而成,layer的尺寸都相同:1×1+3×3的两层conv(每层conv = BN+ReLU+Conv);Denseblock间由过渡层构成,过渡层通过1×1卷积层来减小通道数,并使用步幅为2的平均池化层减半高和宽。最后经过全局平均池化 + 全连接层的1000路softmax得到输出。 DenseNet优缺点DenseNet的优点主要有3个: 更强的梯度流动![]() ![]() ![]() ![]() DenseNet的不足在于由于需要进行多次Concatnate操作,数据需要被复制多次,显存容易增加得很快,需要一定的显存优化技术。另外,DenseNet是一种更为特殊的网络,ResNet则相对一般化一些,因此ResNet的应用范围更广泛。 实验效果这里给出DenseNet在CIFAR-100和ImageNet数据集上与ResNet的对比结果,首先来看下DenseNet与ResNet在CIFAR-100数据集上实验结果,如下图所示,可以看出,只有0.8M大小的DenseNet-100性能已经超越ResNet-1001,并且后者参数大小为10.2M。 下面是DenseNet与ResNet在ImageNet数据集上的比较,可以看出,同等参数大小时,DenseNet也优于ResNet网络。其它实验结果见原论文。 首先实现DenseBlock中的内部结构,这里是BN+ReLU+1x1 Conv+BN+ReLU+3x3 Conv结构,最后也加入dropout层以用于训练过程。 class _DenseLayer(nn.Sequential): """Basic unit of DenseBlock (using bottleneck layer) """ def __init__(self, num_input_features, growth_rate, bn_size, drop_rate): super(_DenseLayer, self).__init__() 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)) self.drop_rate = drop_rate def forward(self, x): new_features = super(_DenseLayer, self).forward(x) if self.drop_rate > 0: new_features = F.dropout(new_features, p=self.drop_rate) # 在通道维上将输入和输出连结 return torch.cat([x, new_features], 1)据此,实现DenseBlock模块,内部是密集连接方式(输入特征数线性增长): class _DenseBlock(nn.Sequential): """DenseBlock""" def __init__(self, num_layers, num_input_features, bn_size, growth_rate, drop_rate): super(_DenseBlock, self).__init__() for i in range(num_layers): layer = _DenseLayer(num_input_features+i*growth_rate, growth_rate, bn_size, drop_rate) self.add_module("denselayer%d" % (i+1), layer)此外,实现Transition层,它主要是一个卷积层和一个池化层: class _Transition(nn.Sequential): """Transition layer between two adjacent DenseBlock""" def __init__(self, num_input_feature, num_output_features): super(_Transition, self).__init__() self.add_module("norm", nn.BatchNorm2d(num_input_feature)) self.add_module("relu", nn.ReLU(inplace=True)) self.add_module("conv", nn.Conv2d(num_input_feature, num_output_features, kernel_size=1, stride=1, bias=False)) self.add_module("pool", nn.AvgPool2d(2, stride=2))最后我们实现DenseNet网络: class DenseNet(nn.Module): "DenseNet-BC model" def __init__(self, growth_rate=32, block_config=(6, 12, 24, 16), num_init_features=64, bn_size=4, compression_rate=0.5, drop_rate=0, num_classes=1000): """ :param growth_rate: 增长率,即K=32 :param block_config: 每一个DenseBlock的layers数量,这里实现的是DenseNet-121 :param num_init_features: 第一个卷积的通道数一般为2*K=64 :param bn_size: bottleneck中1*1conv的factor=4,1*1conv输出的通道数一般为factor*K=128 :param compression_rate: 压缩因子 :param drop_rate: dropout层将神经元置0的概率,为0时表示不使用dropout层 :param num_classes: 分类数 """ super(DenseNet, self).__init__() # first Conv2d self.features = nn.Sequential(OrderedDict([ ("conv0", nn.Conv2d(3, num_init_features, kernel_size=7, stride=2, padding=3, bias=False)), ("norm0", nn.BatchNorm2d(num_init_features)), ("relu0", nn.ReLU(inplace=True)), ("pool0", nn.MaxPool2d(3, stride=2, padding=1)) ])) # DenseBlock num_features = num_init_features for i, num_layers in enumerate(block_config): block = _DenseBlock(num_layers, num_features, bn_size, growth_rate, drop_rate) self.features.add_module("denseblock%d" % (i + 1), block) num_features += num_layers*growth_rate if i != len(block_config) - 1: transition = _Transition(num_features, int(num_features*compression_rate)) self.features.add_module("transition%d" % (i + 1), transition) num_features = int(num_features * compression_rate) # final bn+ReLU self.features.add_module("norm5", nn.BatchNorm2d(num_features)) self.features.add_module("relu5", nn.ReLU(inplace=True)) # classification layer self.classifier = nn.Linear(num_features, num_classes) # params initialization for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight) elif isinstance(m, nn.BatchNorm2d): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1) elif isinstance(m, nn.Linear): nn.init.constant_(m.bias, 0) def forward(self, x): features = self.features(x) out = F.avg_pool2d(features, 7, stride=1).view(features.size(0), -1) out = self.classifier(out) return out【参考文档】 深入解析DenseNet(含大量可视化及计算) 来聊聊DenseNet及其变体PeleeNet、VoVNet 稠密连接网络(DenseNet) 深度学习网络篇——DenseNet 论文笔记DenseNet DenseNet:比ResNet更优的CNN模型 Densely Connected Convolutional Networks |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |