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更优的性能。

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

2.公式

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

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

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

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

3.网络结构

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

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层网络结构**

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

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))

上一篇:PHP审计之in_array函数缺陷绕过


下一篇:视频编码名词参数解释