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

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的DenseNet网络实现代码示例: ```python import torch import torch.nn as nn class DenseBlock(nn.Module): def __init__(self, in_channels, growth_rate, num_layers): super(DenseBlock, self).__init__() self.layers = nn.ModuleList() for i in range(num_layers): self.layers.append(nn.Sequential( nn.BatchNorm2d(in_channels + i * growth_rate), nn.ReLU(inplace=True), nn.Conv2d(in_channels + i * growth_rate, growth_rate, kernel_size=3, stride=1, padding=1, bias=False) )) def forward(self, x): features = [x] for layer in self.layers: new_features = layer(torch.cat(features, dim=1)) features.append(new_features) return torch.cat(features, dim=1) class TransitionLayer(nn.Module): def __init__(self, in_channels, out_channels): super(TransitionLayer, self).__init__() self.downsample = nn.Sequential( nn.BatchNorm2d(in_channels), nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=1, bias=False), nn.AvgPool2d(kernel_size=2, stride=2) ) def forward(self, x): return self.downsample(x) class DenseNet(nn.Module): def __init__(self, growth_rate=32, block_config=(6, 12, 24, 16), num_classes=1000): super(DenseNet, self).__init__() self.features = nn.Sequential( nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False), nn.BatchNorm2d(64), nn.ReLU(inplace=True), nn.MaxPool2d(kernel_size=3, stride=2, padding=1) ) num_features = 64 for i, num_layers in enumerate(block_config): block = DenseBlock(num_features, growth_rate, num_layers) self.features.add_module('denseblock%d' % (i + 1), block) num_features = num_features + num_layers * growth_rate if i != len(block_config) - 1: trans = TransitionLayer(num_features, num_features // 2) self.features.add_module('transition%d' % (i + 1), trans) num_features = num_features // 2 self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) self.classifier = nn.Linear(num_features, num_classes) def forward(self, x): features = self.features(x) out = self.avgpool(features) out = out.view(features.size(0), -1) out = self.classifier(out) return out ``` 在这个实现中,我们定义了三个类:`DenseBlock`,`TransitionLayer`和`DenseNet`。`DenseBlock`实现DenseNet中的密集块,`TransitionLayer`实现DenseNet中的过渡,而`DenseNet`则定义了整个网络的结构。 在`DenseNet`中,我们首先定义了一个序列模块,用于实现DenseNet的前几个卷积和池化。然后,我们循环遍历`block_config`中的元素,并在每个元素中定义一个密集块。如果这不是最后一个元素,则定义一个过渡,将特征图的通道数减半,以便在下一个密集块中使用。最后,我们使用自适应平均池化和线性分类器对特征图进行全局池化和分类。 这个实现代码可以自由修改以适应不同的数据集和任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值