ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

DenseNet网络结构的讲解与代码实现

2021-10-22 21:30:30  阅读:381  来源: 互联网

标签:features nn self module rate num 讲解 DenseNet 网络结构


目录

1.概念

2.公式

3.网络结构

3.1DenseBlock

3.2 Transition层

 4 DenseNet的主要优点与缺点

5 代码实现

5.1 Denseblock代码实现

5.2 Transition代码实现

5.3 DenseNet-121完整代码实现



1.概念

DenseNet:采用密集连接机制,即互相连接所有的层,每个层都会与前面的层在channel(通道)维度上连接道一起,实现特征重用,作为下一层的输入。

这样操作的好处在于 不仅减缓了梯度消失的问题,也可使其在参数与计算量更少的情况下实现比Resnet更优的性能。

2.公式

在DenseNet中,会连接前面所有层作为输入,它的公式为

其中H(.) 代表的是非线性转化函数,它是一个组合操作,其可能包括一系列的BN,ReLU,Pooling及Conv的操作。

特征传递方式 是 直接将前面所有层的特征concat后传到下一个层,而不是前面层都要有一个箭头指向后面的所有层。

3.网络结构

DenseNet网络中模块主要由 **DenseBlock+Transition** 组成

3.1DenseBlock

  1. 其中DenseBlock其中的每一个“圆点”代表的使用 BN+ReLU+Conv(3*3)层的组成。

  2. 假定输入层的特征层的channel为k0。DenseBlock中各个卷积之后均输出k个特征图。即得到得特征图的channel数为k。那么L层输入的channel为k0 + (L - 1)k.

  3. 采用的是BN+ReLU+Conv 的pre-position的结构。作者验证过这种反方向的组合效果更好

  4. bottleneck

    由于后面的层输入channel会非常大,DenseBlock有其扩展版,即DensBlock_B。其和普通版的区别在于 DenseBlock_B在前面使用了bottleneck

    (BN+ReLU+(1*1)Conv ) 的 1 * 1的卷积层用于降维。(即DensBlock_B的结构为( BN+ReLU+Conv (1* 1)+BN+ReLU+Conv(3*3)

    1 * 1卷积输出的通道数通常是GrowthRate的4倍。(GrowthRate即为增长率,一般为16、32.....)

    附:

    若不适用1*1 的卷积层进行降维,则到后面输出的通道数可能会很大。

    例如:

    输入通道数为64,增长率为32,经过15个bottleneck,则通道数输出为 64+32 X 15 = 544

    若不适用 1 X 1 的卷积进行降维,则第16个bottleneck层的参数量是 3 X 3 X 544 X 32 = 156672

    若使用1 X 1 的卷积进行降维,则第16个bottleneck层的参数为 1 X 1 X 544 X 128 + 3 X 3 X 128 X 32 = 106496

    可以看到参数量减少了1/3

3.2 Transition层

  1. 主要作用在于连接两个相邻的Denseblock,并且降低特征图的大小

    Transition层包括一个 1 X 1 的卷积与 2 X 2 的AvgPooling的结构。

  2. Trasition的主要起到压缩模型的作用

    Transition层可以输出[ xm]个特征,其中x是压缩系数。当x=1时,表示无压缩,特征个数经过Transition层没有变化。

    当x小于1时,这种结构称为DenseNet-C,一般使用 x=0.5

对于使用Bottleneck层的DenseBlock结构压缩系数小于1的Transition组合结构成为Densenet-BC。

 4 DenseNet的主要优点与缺点

优点

1.更强的梯度流动,减轻了梯度消失在网络深度越深的时候越容易出现。

2.减少了参数量

3.保存了低维度的特征

缺点

运行时容易占用显存的大小

5 代码实现

DenseNet的网络结构图,代码主要是实现**DenseNet-121层网络结构**

5.1 Denseblock代码实现

#构建DenseBlock中的内部结构
#通过语法结构,把这个当成一个layer即可.
#bottleneck + DenseBlock == > DenseNet-B

class _DenseLayer(nn.Sequential):
    # num_input_features作为输入特征层的通道数, growth_rate增长率, bn_size输出的倍数一般都是4, drop_rate判断是都进行dropout层进行处理
    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)

#定义Denseblock模块
class _DenseBlock(nn.Sequential):
    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)

5.2 Transition代码实现

#定义Transition层
#负责将Denseblock连接起来,一般都有0.5的维道(通道数)的压缩
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(2,stride=2))

5.3 DenseNet-121完整代码实现

import torch
import torch.nn as nn
import torch.nn.functional as F
from torchsummary import summary
from torchstat import stat
from collections import OrderedDict

#构建DenseBlock中的内部结构
#通过语法结构,把这个当成一个layer即可.
#bottleneck + DenseBlock == > DenseNet-B

class _DenseLayer(nn.Sequential):
    # num_input_features作为输入特征层的通道数, growth_rate增长率, bn_size输出的倍数一般都是4, drop_rate判断是都进行dropout层进行处理
    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)

#定义Denseblock模块
class _DenseBlock(nn.Sequential):
    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层
#负责将Denseblock连接起来,一般都有0.5的维道(通道数)的压缩
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(2,stride=2))


#实现DenseNet网络
class DenseNet(nn.Module):
    def __init__(self, growth_rate=32,block_config=(6,12,24,26), num_init_features=64,bn_size=4, comparession_rate=0.5, drop_rate=0,num_classes=1000):
        super(DenseNet,self).__init__()
        #前面 卷积层+最大池化
        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   #确定一个DenseBlock输出的通道数

            if i!=len(block_config)-1:    #判断是不是最后一个Denseblock
                transition = _Transition(num_features,int(num_features*comparession_rate))
                self.features.add_module("transition%d" %(i+1), transition)
                num_features = int(num_features*comparession_rate)  #为下一个DenseBlock的输出做准备

        #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 initalization
        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



def densenet121(pretrained=False, **kwargs):
    """DenseNet121"""
    model = DenseNet(num_init_features=64, growth_rate=32, block_config=(6, 12, 24, 16),
                     **kwargs)
    return model


if __name__ == '__main__':
    #输出模型的结构
    dense = densenet121()

    #输出模型每层的输出
    device =torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    dense= dense.to(device)
    # summary(dense,input_size=(3,416,416), batch_size = 3)

    # 每层模型的输入输出(这个能同时显示 输入输出)
    # stat(dense, (3,416,416))

标签:features,nn,self,module,rate,num,讲解,DenseNet,网络结构
来源: https://blog.csdn.net/weixin_41655296/article/details/120913554

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有