神经网络-DenseNet 18

摘要:
每一层的输入由所有先前卷积层的输出组成。神经网络通常需要使用池化和其他操作来减少特征图的大小以提取语义特征,而DenseBlock需要保持每个块中特征图的尺寸一致,以直接执行连接操作。瓶颈由BNReLU,1×1卷积,BN,ReLU,3×3卷积序列控制。Concatnate操作后的通道数增加32。

     上一节的ResNet通过前层与后层的短路连接Shortcuts) , 加强了前后层之间的信息流通, 在一定程度上缓解了梯度消失现象, 从而可以将神经网络搭建得很深。 更进一步, 本节的主角DenseNet最大化了这种前后层信息交流, 通过建立前面所有层与后面层的密集连接, 实现了特征在通道维度上的复用, 使其可以在参数与计算量更少的情况下实现比ResNet更优的性能, 提出DenseNet的《Densely ConnectedConvolutional Networks》 也一举拿下了2017CVPR的最佳论文。

     DenseNet的网络架构如图3.19所示, 网络由多个Dense Block与中间的卷积池化组成, 核心就在Dense Block中。 Dense Block中的黑点代表一个卷积层, 其中的多条黑线代表数据的流动, 每一层的输入由前面的所有卷积层的输出组成。 注意这里使用了通道拼接(Concatnate) 操作, 而非ResNet的逐元素相加操作。

神经网络-DenseNet 18第1张

 DenseNet的结构有如下两个特性:

·神经网络一般需要使用池化等操作缩小特征图尺寸来提取语义特征, 而Dense Block需要保持每一个Block内的特征图尺寸一致来直接进行Concatnate操作, 因此DenseNet被分成了多个BlockBlock的数量一般为4

·两个相邻的Dense Block之间的部分被称为Transition层, 具体包括BNReLU1×1卷积、 2×2平均池化操作。 1×1卷积的作用是降维, 起到压缩模型的作用, 而平均池化则是降低特征图的尺寸,

具体的Block实现细节如图3.20所示, 每一个Block由若干个Bottleneck的卷积层组成, 对应图3.19中的黑点。 BottleneckBNReLU1×1卷积、 BNReLU3×3卷积的顺序构成。

神经网络-DenseNet 18第2张

 关于Block, 有以下4个细节需要注意:
·每一个Bottleneck输出的特征通道数是相同的, 例如这里的32。 同时可以看到, 经过Concatnate操作后的通道数是按32的增长量增加的,因此这个32也被称为GrowthRate
·这里1×1卷积的作用是固定输出通道数, 达到降维的作用。 当几十个Bottleneck相连接时, Concatnate后的通道数会增加到上千, 如果不增加1×1的卷积来降维, 后续3×3卷积所需的参数量会急剧增加。 1×1卷积的通道数通常是GrowthRate4倍。
·3.20中的特征传递方式是直接将前面所有层的特征Concatnate后传到下一层, 这种方式与具体代码实现的方式是一致的, 而不像图3.19中, 前面层都要有一个箭头指向后面的所有层。

·Block采用了激活函数在前、 卷积层在后的顺序, 这与一般的网络上是不同的。

利用PyTorch来实现DenseNet的一个Block, 新建一个densenet_block.py文件, 代码如下:

神经网络-DenseNet 18第3张神经网络-DenseNet 18第4张
 1 import torch
 2 from torch import nn
 3 import torch.nn.functional as F
 4 
 5 # 实现一个Bottleneck的类, 初始化需要输入通道数与GrowthRate这两个参数
 6 class Bottleneck(nn.Module):
 7 
 8     def __init__(self, nChannels, growthRate):
 9 
10         super(Bottleneck, self).__init__()
11         # 通常1×1卷积的通道数为GrowthRate的4倍
12         interChannels = 4*growthRate
13         self.bn1 = nn.BatchNorm2d(nChannels)
14         self.conv1 = nn.Conv2d(nChannels, interChannels, kernel_size=1, bias=False)
15 
16         self.bn2 = nn.BatchNorm2d(interChannels)
17         self.conv2 = nn.Conv2d(interChannels, growthRate, kernel_size=3, 
18                                 padding=1, bias=1)
19     
20     def forward(self, x):
21         out = self.conv1(F.relu(self.bn1(x)))
22         out = self.conv2(F.relu(self.bn2(out)))
23         # 将输入x同计算的结果out进行通道拼接
24         out = torch.cat((x, out), 1)
25         return out
26 
27 class Denseblock(nn.Module):
28     
29     def __init__(self, nChannels, growthRate, nDenseBlocks):
30         super(Denseblock, self).__init__()
31         layers = []
32         # 将每一个Bottleneck利用nn.Sequential()整合起来, 输入通道数需要线性增长
33         for i in range(int(nDenseBlocks)):
34             layers.append(Bottleneck(nChannels, growthRate))
35             nChannels += growthRate
36         self.denseblock = nn.Sequential(*layers)
37     
38     def forward(self, x):
39         return self.denseblock(x)
View Code
神经网络-DenseNet 18第5张神经网络-DenseNet 18第6张
 1 import torch
 2 from densenet_block import Denseblock
 3 
 4 # 实例化DenseBlock, 包含了6个Bottleneck
 5 denseblock = Denseblock(64, 32, 6).cuda()
 6 
 7 # 查看denseblock的网络结构, 由6个Bottleneck组成
 8 print(denseblock)
 9 >>  Denseblock(
10   (denseblock): Sequential(
11       # 第1个Bottleneck的输入通道数为64, 输出固定为32
12     (0): Bottleneck(
13       (bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
14       (conv1): Conv2d(64, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
15       (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
16       (conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
17     )
18     #
19     # 第2个Bottleneck的输入通道数为96, 输出固定为32
20     (1): Bottleneck(
21       (bn1): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
22       (conv1): Conv2d(96, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
23       (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
24       (conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
25     )
26     # 第3个Bottleneck的输入通道数为128, 输出固定为32
27     (2): Bottleneck(
28       (bn1): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
29       (conv1): Conv2d(128, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
30       (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
31       (conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
32     )
33     #第4个Bottleneck的输入通道数为160, 输出固定为32
34     (3): Bottleneck(
35       (bn1): BatchNorm2d(160, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
36       (conv1): Conv2d(160, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
37       (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
38       (conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
39     )
40     # 第5个Bottleneck的输入通道数为192, 输出固定为32
41     (4): Bottleneck(
42       (bn1): BatchNorm2d(192, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
43       (conv1): Conv2d(192, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
44       (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
45       (conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
46     )
47     # 第6个Bottleneck的输入通道数为224, 输出固定为32
48     (5): Bottleneck(
49       (bn1): BatchNorm2d(224, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
50       (conv1): Conv2d(224, 128, kernel_size=(1, 1), stride=(1, 1), bias=False)
51       (bn2): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
52       (conv2): Conv2d(128, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
53     )
54   )
55 )
56 
57 input = torch.randn(1, 64, 256, 256).cuda()
58 output = denseblock(input) # 将输入传入denseblock结构中
59 print(input.shape)
60 >> torch.Size([1, 64, 256, 256])
61 
62 # 输出的通道数为: 224+32=64+32×6=256
63 print(output.shape)
64 >> torch.Size([1, 256, 256, 256])
View Code

 DenseNet网络的优势主要体现在以下两个方面:
·密集连接的特殊网络, 使得每一层都会接受其后所有层的梯度,而不是像普通卷积链式的反传, 因此一定程度上解决了梯度消失的问题。
·通过Concatnate操作使得大量的特征被复用, 每个层独有的特征图
的通道是较少的, 因此相比ResNetDenseNet参数更少且计算更高效。DenseNet的不足在于由于需要进行多次Concatnate操作, 数据需要被复制多次, 显存容易增加得很快, 需要一定的显存优化技术。 另外,DenseNet是一种更为特殊的网络, ResNet则相对一般化一些, 因此ResNet的应用范围更广泛。

免责声明:文章转载自《神经网络-DenseNet 18》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇FIR滤波器的实现方法(转)channel 介绍下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

sql server 2008安装图解

本篇文章介绍了安装SQL Server 2008企业版的软硬件配置要求,安装过程的具体步骤,以及须要注意的事项。 步骤/方法 1 在这里我们将用图解的方式。来介绍SQL Server 2008安装和配置过程。希望对大家有所帮助。 2 选择版本号,或者输入密钥自己主动识别版本号...

二层网络架构,接入交换机和核心交换机

  在交换机应用中,我们经常会听到接入交换机、汇聚交换机和核心交换机,它们究竟代表什么含义,什么样的交换机是接入交换机、汇聚交换机或者核心交换机呢? 简单来说,接入交换机是指运行在接入层的交换机,汇聚交换机是指运行在汇聚层的交换机,核心交换机是指运行在核心层的交换机。那么接下来我们就来介绍层次化网络结构。 多层网络架构采用层次化模型设计,即将复杂的网络设...

UNIX网络编程——TCP/IP简介

一、ISO/OSI参考模型        OSI(open system interconnection)开放系统互联模型是由ISO(International Organization for Standardization)国际标准化组织定义的网络分层模型,共七层,如下图:                                      物理...

Win10离线安装.NET Framework 3.5的方法技巧(附离线安装包下载)

原文链接: https://www.windows10.pro/win10-net-framework-3-5/ 在Windows10中,当我们安装某些软件的时候会提示“你的电脑上的应用需要使用以下Windows功能:.NET Framework 3.5(包括.NET 2.0和3.0)”。如图: 首先在Win10 ISO文件上点击右键,选择“装载”。如图:...

LoadRunner简介

  LoadRunner是什么 LoadRunner是一个性能测试工具,它最初是Mercury公司的产品,后背HP收购。 LoadRunner常用来做什么 l 验证某系统在某环境下是否满足性能需求。 l 通过测试,规划如何达到要求的性能指标。 l 调整系统环境,进行性能测试,最终使性能达到最优。 为什么要使用LoadRunner进行性能测试 考虑这样一种情...

网络爬虫基本原理

 网络爬虫是捜索引擎抓取系统的重要组成部分。爬虫的主要目的是将互联网上的网页下载到本地形成一个或联网内容的镜像备份。这篇博客主要对爬虫以及抓取系统进行一个简单的概述。 一、网络爬虫的基本结构及工作流程     一个通用的网络爬虫的框架如图所示:  网络爬虫的基本工作流程如下:     1.首先选取一部分精心挑选的种子URL;     2.将这些URL放入...