深度学习经典网络解析图像分类篇(四):DenseNet

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: DenseNet是CVPR2017年的Best Paper,它脱离了加深网络层数(ResNet)和加宽网络结构(Inception)来提升网络性能的定式思维,从特征的角度考虑,通过特征重用和旁路(Bypass)设置,既大幅度减少了网络的参数量,又在一定程度上缓解了gradient vanishing问题的产生.结合信息流和特征复用的假设,DenseNet当之无愧成为2017年计算机视觉顶会的年度最佳论文。

1.背景介绍


 DenseNet是CVPR2017年的Best Paper,它脱离了加深网络层数(ResNet)和加宽网络结构(Inception)来提升网络性能的定式思维,从特征的角度考虑,通过特征重用和旁路(Bypass)设置,既大幅度减少了网络的参数量,又在一定程度上缓解了gradient vanishing问题的产生.结合信息流和特征复用的假设,DenseNet当之无愧成为2017年计算机视觉顶会的年度最佳论文。


 众所周知,CNN已经成为了深度学习方向最主要的网络结构之一。从一开始的只有五层结构的LeNet, 到后来拥有19层结构的VGG,再到首次跨越100层网络的HighwayNetworks与ResNet, 网络层数的加深成为CNN发展的主要方向之一。


 但是随着CNN网络层数的不断增加开始出现梯度消失和模型退化(50层的网络不如20层的网络),批量归一化(BatchNormalization)的广泛使用在一定程度上缓解了梯度消失的问题,而ResNet和Highway Networks通过构造恒等映射设置旁路,进一步减少了梯度消失和模型退化的产生。Fractal Nets通过将不同深度的网络并行化,在获得了深度的同时保证了梯度的传播,随机深度网络通过对网络中一些层进行失活,既证明了ResNet深度的冗余性,又缓解了上述问题的产生。虽然这些不同的网络框架通过不同的实现加深的网络层数,但是他们都包含了相同的核心思想:将feature map进行跨网络层的连接。


 用通俗的话说——CNN网络层数越大,输出到输入的路径就会变得更长,就会出现一个问题:梯度经过这么长的路径反向传播回输入的时候很可能就会消失。


那有没有一种方法可以让网络又深梯度又不会消失?


 DenseNet提出了一种很简单的方法:将前面所有层与后面的层建立密集连接来对特征进行重用来解决这个问题。


e747276cb49d4a5a9c295549c60d6811.png


2.DenseNet与ResNet


2.1ResNet


 ResNet(Deep Residual Network深度残差网络):通过建立前面层与后面层之间的“短路连接”,这有助于训练过程中梯度的反向传播,从而能训练出更深的CNN网络。


d3fbe1c4089c4908ab349c4117938388.png


2.2DenseNet


 DenseNet采用密集连接机制,即互相连接所有的层,每个层都会与前面所有层在channel维度上连接(concat)在一起,实现特征重用,作为下一层的输入。这样,不但减缓了梯度消失的现象,也使其可以在参数与计算量更少的情况下实现比ResNet更优的性能。


d11c68200d844d399cb6e70e0852741c.png


3.DenseNet的数学表示


传统的网络在L层的输出为:


image.png

68b5ae6d0e9e4f26aa223f708219c6a7.png


ResNet,增加了来自上一层输入:


image.png

68d30f51736b4f0f9d5f8fb3602c0952.png


DenseNet中,会连接前面所有层作为输入:


image.png

36ba5150f8b545778a2bd8f3dc51c25d.png


H l ( ⋅ ) 代表是非线性转化函数,它是一个组合操作,其可能包括一系列的BN(Batch Normalization), ReLU, Pooling及Conv操作。特征传递方式是直接将前面所有层的特征concat后传 到下一层,而不是前面层都要有一个箭头指向后面的所有层.


aHR0cHM6Ly9pbWdrci5jbi1iai51ZmlsZW9zLmNvbS8yZTkwMTk4ZC1hOWM4LTQ1M2EtYWIwYi1jMzFjY2VkZWU4YWEuZ2lm.gif


4.DenseNet网络架构


 DenseNet的密集连接方式需要特征图大小保持一致 。 所 以 DenseNet 网络中使用DenseBlock+Transition的结构。


 DenseBlock是包含很多层的模块,每个层的特征图大小相同,层与层之间采用密集连接方式。


 Transition模块是连接两个相邻的DenseBlock,并且通过Pooling使特征图大小降低。


f8c130cfaa3248d998462e508624ec51.png


4.1DenseBlock


  • 在DenseBlock中,各个层的特征图大小一致,可以在channel维度上连接。DenseBlock中的非线性组合函数采用的是BN+ReLU+3x3Conv的结构。


  • 这里有一个参数 k, 称为增长率, 指的是每一层的额外通道数, , 假如输入层特征图的channel为K 0  , 那么第 L层的channel数就为 K 0 + ( L − 1 ) K。因为每一层都接受前面所有层的特征图,即特征传递方式是直接将前面所有层的特征concat后传到下一层,一般情况下使用较小的K(一般为12),要注意这个K的实际含义就是这层新提取出的特征。


f6e4a5f916c741e6841ffd9c4e18127d.png


  • Dense Block采用了激活函数在前、卷积层在后的顺序,即BN-ReLU-Conv的顺序,这种方式也被称为pre-activation。通常的模型relu等激活函数处于卷积conv、批归一化batchnorm之后,即Conv-BN-ReLU,也被称为post-activation。作者证明,如果采用post-activation设计,性能会变差。


aHR0cHM6Ly9pbWdrci5jbi1iai51ZmlsZW9zLmNvbS80YTk3M2MzNy0xMjFhLTQ5MzQtOTJlZS04ZmEwM2M2ZThjNzAuZ2lm.gif


BottleNeck层


 由于后面层的输入会非常大,DenseBlock内部可以采用Bottleneck层(瓶颈层)来减少计算量,主要是原有的结构中增加1x1 Conv,即BN+ReLU+1x1 Conv+BN+ReLU+3x3 Conv称为DenseNet-B结构。其中1x1 Conv得到4k个特征图它起到的作用是降低特征数量,从而提升计算效率。


 每一个Bottleneck输入输出的特征通道数是相同的。这里1×1卷积的作用是固定输出通道数,达到降维的作用,1×1卷积输出的通道数通常是GrowthRate的4倍。当几十个Bottleneck相连接时,concat后的通道数会增加到上千,如果不增加1×1的卷积来降维,后续3×3卷积所需的参数量会急剧增加。


fc176e9477154ebc97cb057a96c92da2.png


 假设输入通道数为64,增长率K=32,经过15个Bottleneck,通道数输出为:


64+15x32=544


 如果不使用1×1卷积,第16个Bottleneck层参数量是:


3x3x544x32=156672


 如果使用1×1卷积,第16个Bottleneck层参数量是:


1x1x544x128+3x3x128x32=106496


 可以看到参数量大大降低。


a25c1087995c4e1c8c82e4a3139b23f7.png


4.2Transition层


 Transition层它主要是连接两个相邻的DenseBlock,并且降低特征图大小。


Transition层包括一个1x1的卷积和2x2的AvgPooling,结构为BN+ReLU+1x1 Conv+2x2 AvgPooling。


1f0f55fef02f4ddc80a76bb36e4388ee.png


 Transition层可以起到压缩模型的作用。假定Transition的上接DenseBlock得到的特征图 channels数为 m  ,Transition层可以产生 ⌊ θ m ⌋个特征(通过卷积层),其中 θ ∈ ( 0 , 1 ] 是压缩系 数 (compression rate)。当Θ = 1 时,特征个数经过Transition层没有变化,即无压缩,而当压缩系数小于1时,这种结构称为DenseNet-C,一般使用 Θ = 0.5 。对于使用bottleneck层的 DenseBlock结构和压缩系数小于1的Transition组合结构称为DenseNet-BC。DenseBlock结构和压缩系数小于1的Transition组合结构称为DenseNet-BC。


 可以通过一张参数计算的图来回顾一下(其中 D x  是Dense Block, T x 是Transition模块), 我们可以看到每经过一个Dense Block深度增加量=增长率✖密集块数量, 其中+号代表concat。


0010d3f5cad94e54af28b05568f5c98a.png


5.DenseNet评价


5.1DenseNet优点


5.1.1更强的梯度流动


 由于密集连接方式,DenseNet提升了梯度的反向传播,使得网络更容易训练。由于每层可以直达最后的误差信号,实现了隐式的“deep supervision”。误差信号可以很容易地传播到较早的层,所以较早的层可以从最终分类层获得直接监管(监督)。


 减轻了vanishing-gradient(梯度消失) 梯度消失问题在网络深度越深的时候越容易出现,原因就是输入信息和梯度信息在很多层之间传递导致的,而现在这种dense -connection相当于每一层都直接连接input和loss,因此就可以减轻梯度消失现象,这样更深网络不是问题。


725fd91855764c35a34d03dd0175985d.png


5.1.2减少了参数数量


 如下图所示,在ResNet中, 参数量与 C x C  成正比,而在DenseNet中参数量与 l × k × k 成正比, 因为 k  远小于 C , 所以DenseNet的参数量小很多。


f664780868d14381833968c3c5fb89b0.png

503182df26954ea1a70ecf507cf4445e.png


5.1.3保证低维度特征


 在标准的卷积网络中,最终输出只会利用提取最高层次的特征。


 而在DenseNet中,它使用了不同层次的特征,倾向于给出更平滑的决策边界。这也解释了为什么训练数据不足时DenseNet表现依旧良好。


eb4345e6dc3c4dbbb6effa16789e1205.png

c7a30632293047a0850905ff921787a6.png


5.2DenseNet不足


 DenseNet的不足在于由于需要进行多次Concatnate操作,数据需要被复制多次,显存容易增加得很快,需要一定的显存优化技术。另外,DenseNet是一种更为特殊的网络,ResNet则相对一般化一些,因此ResNet的应用范围更广泛。


6.使用Pytorch实现DenseNet


6.1实现DenseBlock中的内部结构


 这里是BN+ReLU+1x1 Conv+BN+ReLU+3x3Conv结构,最后加入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, training=self.training)
        return torch.cat([x, new_features], 1)


6.2实现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)


6.3 实现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))


6.4 实现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: (int) number of filters used in DenseLayer, `k` in the paper        :param block_config: (list of 4 ints) number of layers in each DenseBlock        :param num_init_features: (int) number of filters in the first Conv2d        :param bn_size: (int) the factor using in the bottleneck layer        :param compression_rate: (float) the compression rate used in Transition Layer        :param drop_rate: (float) the drop rate after each DenseLayer        :param num_classes: (int) number of classes for classification        """
        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


目录
相关文章
|
17天前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的眼疾识别系统实现~人工智能+卷积网络算法
眼疾识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了4种常见的眼疾图像数据集(白内障、糖尿病性视网膜病变、青光眼和正常眼睛) 再使用通过搭建的算法模型对数据集进行训练得到一个识别精度较高的模型,然后保存为为本地h5格式文件。最后使用Django框架搭建了一个Web网页平台可视化操作界面,实现用户上传一张眼疾图片识别其名称。
76 4
基于Python深度学习的眼疾识别系统实现~人工智能+卷积网络算法
|
8天前
|
监控 安全 网络安全
深入解析PDCERF:网络安全应急响应的六阶段方法
PDCERF是网络安全应急响应的六阶段方法,涵盖准备、检测、抑制、根除、恢复和跟进。本文详细解析各阶段目标与操作步骤,并附图例,助读者理解与应用,提升组织应对安全事件的能力。
158 89
|
8天前
|
机器学习/深度学习 传感器 人工智能
穹彻智能-上交大最新Nature子刊速递:解析深度学习驱动的视触觉动态重建方案
上海交大研究团队在Nature子刊发表论文,提出基于深度学习的视触觉动态重建方案,结合高密度可拉伸触觉手套与视觉-触觉联合学习框架,实现手部与物体间力量型交互的实时捕捉和重建。该方案包含1152个触觉感知单元,通过应变干扰抑制方法提高测量准确性,平均重建误差仅1.8厘米。实验结果显示,其在物体重建的准确性和鲁棒性方面优于现有方法,为虚拟现实、远程医疗等领域带来新突破。
44 32
|
8天前
|
机器学习/深度学习 监控 算法
基于yolov4深度学习网络的排队人数统计系统matlab仿真,带GUI界面
本项目基于YOLOv4深度学习网络,利用MATLAB 2022a实现排队人数统计的算法仿真。通过先进的计算机视觉技术,系统能自动、准确地检测和统计监控画面中的人数,适用于银行、车站等场景,优化资源分配和服务管理。核心程序包含多个回调函数,用于处理用户输入及界面交互,确保系统的高效运行。仿真结果无水印,操作步骤详见配套视频。
41 18
|
1月前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于yolov4深度学习网络的公共场所人流密度检测系统matlab仿真,带GUI界面
本项目使用 MATLAB 2022a 进行 YOLOv4 算法仿真,实现公共场所人流密度检测。通过卷积神经网络提取图像特征,将图像划分为多个网格进行目标检测和识别,最终计算人流密度。核心程序包括图像和视频读取、处理和显示功能。仿真结果展示了算法的有效性和准确性。
68 31
|
1月前
|
网络协议
TCP报文格式全解析:网络小白变高手的必读指南
本文深入解析TCP报文格式,涵盖源端口、目的端口、序号、确认序号、首部长度、标志字段、窗口大小、检验和、紧急指针及选项字段。每个字段的作用和意义详尽说明,帮助理解TCP协议如何确保可靠的数据传输,是互联网通信的基石。通过学习这些内容,读者可以更好地掌握TCP的工作原理及其在网络中的应用。
|
1月前
|
网络协议 安全 网络安全
探索网络模型与协议:从OSI到HTTPs的原理解析
OSI七层网络模型和TCP/IP四层模型是理解和设计计算机网络的框架。OSI模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,而TCP/IP模型则简化为链路层、网络层、传输层和 HTTPS协议基于HTTP并通过TLS/SSL加密数据,确保安全传输。其连接过程涉及TCP三次握手、SSL证书验证、对称密钥交换等步骤,以保障通信的安全性和完整性。数字信封技术使用非对称加密和数字证书确保数据的机密性和身份认证。 浏览器通过Https访问网站的过程包括输入网址、DNS解析、建立TCP连接、发送HTTPS请求、接收响应、验证证书和解析网页内容等步骤,确保用户与服务器之间的安全通信。
112 3
|
1月前
|
存储 监控 网络协议
一次读懂网络分层:应用层到物理层全解析
网络模型分为五层结构,从应用层到物理层逐层解析。应用层提供HTTP、SMTP、DNS等常见协议;传输层通过TCP和UDP确保数据可靠或高效传输;网络层利用IP和路由器实现跨网数据包路由;数据链路层通过MAC地址管理局域网设备;物理层负责比特流的物理传输。各层协同工作,使网络通信得以实现。
|
1月前
|
SQL 安全 网络安全
网络安全与信息安全:知识分享####
【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
75 17
|
1月前
|
存储 SQL 安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将介绍网络安全的重要性,分析常见的网络安全漏洞及其危害,探讨加密技术在保障网络安全中的作用,并强调提高安全意识的必要性。通过本文的学习,读者将了解网络安全的基本概念和应对策略,提升个人和组织的网络安全防护能力。

热门文章

最新文章

推荐镜像

更多