欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

DenseNet

程序员文章站 2024-03-15 09:12:47
...

DenseNet

还是想主要解决梯度消失的问题,虽然这个问题在之前的模型中,例如resnet给出了解决办法。之前的模型都有一个共同的特点:都在新层和旧层之间创造了捷径。Densenet沿用了这种方法,选择将所有的层都链接在一起,保证最大信息在层与层之间进行流动。

DenseNet
与ResNet不同的是,densenet 并没有将特征进行相加得到新的特征,而是将每一层传进来的特征进行拼接操作。

所以第L层将会有L个inputs,包含着前面所有卷积块的特征图。

  • 需要更少的参数,不用去学习冗余的特征图 feature-map
  • 每一层都和最初的input有通道连接,又和最终的loss函数直接连接,这会有助于模型的训练。
  • 还有一定的规范化的作用,能够克制在小型数据集上的过拟合。
  • 说起拼接这一点,和inception,goodlenet有点像,但是比他们更加的高效。

DenseNet

Composite function:复合函数

xl=Hl([x0,x1,..,xl1])x_l=H_l([x_0,x_1,..,x_{l-1}]) , 第l层的输出就是将前l-1层的输出拼接起来再经过一个符合函数H得到的,这个函数包括:

  1. BN:batch normalization
  2. ReLU
  3. 3×3的Conv

Dense Block-transition layers

这个就是Densenet的关键核心所在,之前所说的东西基本上都是说的这个,每两个Dense Block之间夹着一个trastion layers

主要用来做卷积和池化操作,在他们的实验中使用的是1×1的Conv 接上一个2×2的pooling。

Growth rate

这个参数感觉就是来限定每一层,使用多少个filter差不多。如果每一层都产生一个通道数为K的输出,那么到了第L层输出的总通道数就是 k0+k×(l1)k_0+k×(l-1)个通道数。k0k_0是开始时输入的图像的通道数。k=12的时候的结果就很好了。k其实决定了每一层贡献出多少的信息给全局的状态。

Architecture for ImageNet

DenseNet

bottle neck layer

再dense block 内部的3×3卷积之前,加入一个1×1的卷积bottle neck 从而去减少featuremap的数量,能够有效的提高计算的效率。所以,block内部的的情况就是 BN-RELU-CONV1×1-BN-RELU-CONV3×3.

Compression

上面已经讲过了,在 Dense Block 之间加入的是Transition Layer,为了改善网络的运行效率,从一个Dense Block 出来之后,通过Transition Layer 对feature-map进行压缩,如果前一个block出来的有m个featuremap,经过transition后,它只会有θm\theta m取整个featuremap 流出。当θ=1\theta=1时,是保持不变的。在本文的实验中,他们设置为0.5

Implementation Details

Except ImageNet,在其他数据集上都是用的三个block,在进入第一个block之前,图像会被16个通道数卷积所处理。

对于3×3的kernel,都使用一个0填充去保证大小的一致

训练集50000,测试集10000,验证集50000.

data augmentation scheme:shif,mirror

preprocess: channel mean and standard deviation , normalize

框架简单代码-pytorch

class DenseBlock(nn.Module):
    def __init__(self, num_convs, in_channels, out_channels):
        super(DenseBlock, self).__init__()
        net = []
        for i in range(num_convs):
            in_c = in_channels + i * out_channels
            net.append(conv_block(in_c, out_channels))
        self.net = nn.ModuleList(net)
        self.out_channels = in_channels + num_convs * out_channels # 计算输出通道数

    def forward(self, X):
        for blk in self.net:
            Y = blk(X)
            X = torch.cat((X, Y), dim=1)  # 在通道维上将输入和输出连结
        return X
def transition_block(in_channels, out_channels):
    blk = nn.Sequential(
            nn.BatchNorm2d(in_channels), 
            nn.ReLU(),
            nn.Conv2d(in_channels, out_channels, kernel_size=1),
            nn.AvgPool2d(kernel_size=2, stride=2))
    return blk
    
num_channels, growth_rate = 64, 32  # num_channels为当前的通道数
num_convs_in_dense_blocks = [4, 4, 4, 4]

for i, num_convs in enumerate(num_convs_in_dense_blocks):
    DB = DenseBlock(num_convs, num_channels, growth_rate)
    net.add_module("DenseBlosk_%d" % i, DB)
    # 上一个稠密块的输出通道数
    num_channels = DB.out_channels
    # 在稠密块之间加入通道数减半的过渡层
    if i != len(num_convs_in_dense_blocks) - 1:
        net.add_module("transition_block_%d" % i, transition_block(num_channels, num_channels // 2))
        num_channels = num_channels // 2
        
        
net.add_module("BN", nn.BatchNorm2d(num_channels))
net.add_module("relu", nn.ReLU())
net.add_module("global_avg_pool", d2l.GlobalAvgPool2d()) # GlobalAvgPool2d的输出: (Batch, num_channels, 1, 1)
net.add_module("fc", nn.Sequential(d2l.FlattenLayer(), nn.Linear(num_channels, 10)))

m_channels, 1, 1)
net.add_module(“fc”, nn.Sequential(d2l.FlattenLayer(), nn.Linear(num_channels, 10)))