Pytorch实现DenseNet

论文链接:https://arxiv.org/pdf/1608.06993.pdf
顾名思义,DenseNet采用了高密度的跳连结构,对于每一层,使用先前所有层的输出作为输入,该层的输出将作为之后所有层的输入的一部分。因此对于一个dense模块,假设有 L L L层,那么存在 L ( L + 1 ) 2 \frac{L(L+1)}{2} 2L(L+1)直接的连接。
在这里插入图片描述
dense模块之后会连接一个transition层,由1x1卷积和平均池化构成。
作者认为有如下优点:
(1)由于存在很多跳连,减轻了空梯度问题,加强了梯度和信息流动,更容易训练。
(2)加强了特征的重用。
(3)DenseNet层的filter数量比较少,使得层比较狭窄。每层只生成了很小的特征图,成为共同知识中的一员。这样网络的参数更少,且更有效率。

DenseNet结构分析

在这里插入图片描述
定义混合函数 H l ( ⋅ ) H_{l}(\cdot) Hl(),由三个连接操作组成:批标准化(BN),ReLU和3x3卷积。
ResNet比较 :恒等函数和混合函数的加和为输出,这样的做法可能会阻止网络中的信息流动。
密度连接: 对于某层,前 l − 1 l-1 l1层的输出 x 0 , ⋯   , x l − 1 \bm{x}_{0},\cdots,\bm{x}_{l-1} x0,,xl1进行channel水平的连接,生成一个单一的特征图:
x l = H l ( [ x 0 , ⋯   , x l − 1 ] ) \bm{x}_{l}=H_{l}([\bm{x}_{0},\cdots,\bm{x}_{l-1}]) xl=Hl([x0,,xl1])
池化层:当特征图的尺寸改变时,就无法进行连接了,然后在设计网络时必须要求特征图的尺寸在往后时减少,所以将dense模块分为几个模块。在模块之间设计transition层进行如下:BN,1x1卷积和2x2平均池化。
增长率 k k k: 在一个dense模块中,初始输入的特征图模块是 k 0 k_{0} k0,若则每层的输出通道 k k k, H l H_{l} Hl的输入为 k 0 + k ( l − 1 ) k_{0}+k(l-1) k0+k(l1) k k k调整每层多少信息加入到全局信息。
瓶颈层: 虽然每层仅仅产生 k k k通道的输出特征图,但是具有很多的输入。使用预激活的瓶颈层,采用如下的处理顺序:BN-ReLU-Conv(1x1)-BN-ReLU-Conv(3x3),称为DenseNet-B。在实验中,每1x1卷积降维变成 4 k 4k 4k通道特征图。
压缩: 在transition层减少特征图数量,设置压缩因子 0 < θ ≤ 1 0<\theta \leq 1 0<θ1,通过transition层后,产生了 ⌊ θ m ⌋ \left \lfloor \theta m\right \rfloor θm个通道的输出特征图。当 θ < 1 \theta<1 θ<1,称为DenseNet-C。在实验中,作者设为 θ = 0.5 \theta=0.5 θ=0.5。在DenseNet-C上使用瓶颈层被称为DenseNet-BC。

特征重用分析

DenseNet中由于使用该密度的跳连结构使得能够复用前面的feature-map,使用heap-map来分析前面层不同feature-map对当前层的重要程度。
在这里插入图片描述
上图展示了卷积层filter权重绝对值的平均值。坐标(s,l)表示在一个dense块中,由第s层传到l层的feature-map,l层权重的平均L1-norm(即权重绝对值的平均值)。我们知道,权重绝对值越大,表示对应的特征越重要,因此也反映了l层对s层的依赖性。红色部分表示强大的使用了s层的feature-map。
从图中作者得到如下4点结论:
1.在相同的块中,所有层的对于输入都具有权重。特征提取在早期层,直接使用在深度层。
2.transition层对于输入都具有权重。DenseNet从第一层到最后一层的信息流动仅通过很少的间接连接。
3.第二层和第三层的dense块一致的对transition层的输出赋予了很小的权重(第一层除外),表明了transition层的输出对第一层之后的层产生了冗余的特征。
4.最终的分类层更加关注最终的feature-maps,表明有一些高水平的特征产生在后面。

Pytorch实现DenseNet-BC

在论文中,作者公开了ImageNet的DenseNet结构。对于CIFAR,denseNet采用3个dense模块,每个dense模块采用相同数量的bottleneck。例如在DenseNet-BC(k=12) 100中,每个dense模块有16个bottleneck层,3 * (16 *2)=96层,剩下的4层分别为第一层卷积,中间的2层transition层,和最后的全连接层。

import torch
import torch.nn as nn
from collections import OrderedDict


class _DenseLayer(nn.Sequential):
    def __init__(self, in_channels, growth_rate, bn_size):
        super(_DenseLayer, self).__init__()
        self.add_module('norm1', nn.BatchNorm2d(in_channels))
        self.add_module('relu1', nn.ReLU(inplace=True))
        self.add_module('conv1', nn.Conv2d(in_channels, 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))

    # 重载forward函数
    def forward(self, x):
        new_features = super(_DenseLayer, self).forward(x)
        return torch.cat([x, new_features], 1)


class _DenseBlock(nn.Sequential):
    def __init__(self, num_layers, in_channels, bn_size, growth_rate):
        super(_DenseBlock, self).__init__()
        for i in range(num_layers):
            self.add_module('denselayer%d' % (i+1),
                            _DenseLayer(in_channels+growth_rate*i,
                                        growth_rate, bn_size))


class _Transition(nn.Sequential):
    def __init__(self, in_channels, out_channels):
        super(_Transition, self).__init__()
        self.add_module('norm', nn.BatchNorm2d(in_channels))
        self.add_module('relu', nn.ReLU(inplace=True))
        self.add_module('conv', nn.Conv2d(in_channels, out_channels,
                                          kernel_size=1,
                                          stride=1, bias=False))
        self.add_module('pool', nn.AvgPool2d(kernel_size=2, stride=2))


class DenseNet_BC(nn.Module):
    def __init__(self, growth_rate=12, block_config=(6,12,24,16),
                 bn_size=4, theta=0.5, num_classes=10):
        super(DenseNet_BC, self).__init__()

        # 初始的卷积为filter:2倍的growth_rate
        num_init_feature = 2 * growth_rate

        # 表示cifar-10
        if num_classes == 10:
            self.features = nn.Sequential(OrderedDict([
                ('conv0', nn.Conv2d(3, num_init_feature,
                                    kernel_size=3, stride=1,
                                    padding=1, bias=False)),
            ]))
        else:
            self.features = nn.Sequential(OrderedDict([
                ('conv0', nn.Conv2d(3, num_init_feature,
                                    kernel_size=7, stride=2,
                                    padding=3, bias=False)),
                ('norm0', nn.BatchNorm2d(num_init_feature)),
                ('relu0', nn.ReLU(inplace=True)),
                ('pool0', nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
            ]))



        num_feature = num_init_feature
        for i, num_layers in enumerate(block_config):
            self.features.add_module('denseblock%d' % (i+1),
                                     _DenseBlock(num_layers, num_feature,
                                                 bn_size, growth_rate))
            num_feature = num_feature + growth_rate * num_layers
            if i != len(block_config)-1:
                self.features.add_module('transition%d' % (i + 1),
                                         _Transition(num_feature,
                                                     int(num_feature * theta)))
                num_feature = int(num_feature * theta)

        self.features.add_module('norm5', nn.BatchNorm2d(num_feature))
        self.features.add_module('relu5', nn.ReLU(inplace=True))
        self.features.add_module('avg_pool', nn.AdaptiveAvgPool2d((1, 1)))

        self.classifier = nn.Linear(num_feature, num_classes)

        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.weight, 1)
                nn.init.constant_(m.bias, 0)
            elif isinstance(m, nn.Linear):
                nn.init.constant_(m.bias, 0)

    def forward(self, x):
        features = self.features(x)
        out = features.view(features.size(0), -1)
        out = self.classifier(out)
        return out


# DenseNet_BC for ImageNet
def DenseNet121():
    return DenseNet_BC(growth_rate=32, block_config=(6, 12, 24, 16), num_classes=1000)

def DenseNet169():
    return DenseNet_BC(growth_rate=32, block_config=(6, 12, 32, 32), num_classes=1000)

def DenseNet201():
    return DenseNet_BC(growth_rate=32, block_config=(6, 12, 48, 32), num_classes=1000)

def DenseNet161():
    return DenseNet_BC(growth_rate=48, block_config=(6, 12, 36, 24), num_classes=1000,)

# DenseNet_BC for cifar
def densenet_BC_100():
    return DenseNet_BC(growth_rate=12, block_config=(16, 16, 16))


评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值