【超详细】DenseNet论文及tensorflow实现详解

前言:
       今天拜读了Gao Huang的Densely Connected Convolutional Networks,不禁感叹天才真的是百分之九九的努力加上百分之一的灵感。在看过ResNet的架构后,DenseNet的框架便不难想到,谁灵感突现,谁就成了摘果子的人。闲言少叙,我们直接进入正题。

1.什么是DenseNet:

       要讲DenseNet就不得不讲一下传统的CNN和ResNet,大家先来看下图-1:

compare

                                                                                         (图-1) 

       简单的说,传统的CNN是的layers是一种将输入 映射为 F(x),再让F(x)去拟合目标分布的过程。而ResNet的layer则是通过在输入和输出之间的一条旁路,将输入直接连到layer的输出,这样layer所要拟合的对象就从F(x)变成了G(x),抽象地说,这样的连接使得layer只需要拟合0便好(而不是去拟合赋值更大的x),这大大降低了深层网络在学习过程中的梯度消失的问题,因为在0附近,任何变动都是相对来说较大的(针对这一问题的解决方法还有batch_norm,输入归一化等等,但都没有这个简单直接而又精巧)。而DenseNet(见图-2)则一不做二不休,既然输入与输出之间要有连接,我们就连多一点:在一个DenseBlock(图中黄、红、蓝、深蓝色代表不同的Denseblock)模块内,每一层的输入都来自这一层之前所有层的输入。而对DenseBlock而言,其输入则同样来自于在其之前所有的Denseblock和原始输入x

Dense_connectivity

                                                                                      (图2) 

 

       DenseNet的这种结构使得每一层都直接影响到最终的loss函数的梯度,每一层都受原始输入信号的影响,这就带来了不言自喻的深监督。这是DenseNet网络可以加深到上千层的基础,同时,我们可以在实验中观察到,这种密集连接可以产生正则化的效果,减少网络的过拟合。

       下面我们来分析DenseNet总体上的布局。首先,我们看一下论文中不同层数的DenseNet的结构(图3)

                                                                                          (图3)

         从图中可以看到,DenseNet会先对输入的tensor做一个卷积核大小为[7×7],步长为2卷积,然后再进行核大小为[3×3],步长为2的最大池化。 之后,便是DenseNet和transition的交替连接,最后跟一个含有[7×7]全局平局池化、1000的全连接和softmax的分类层。其代码如下(代码里出现的DenseNet和transition_layer我们下面会谈论):

    def Dense_net(self, input_x):
        x = conv_layer(input_x, filter=2 * self.filters, kernel=[7,7], stride=2, layer_name='conv0')
        # x = Max_Pooling(x, pool_size=[3,3], stride=2)


        """
        for i in range(self.nb_blocks) :
            # 6 -> 12 -> 48
            x = self.dense_block(input_x=x, nb_layers=4, layer_name='dense_'+str(i))
            x = self.transition_layer(x, scope='trans_'+str(i))
        """




        x = self.dense_block(input_x=x, nb_layers=6, layer_name='dense_1')
        x = self.transition_layer(x, scope='trans_1')

        x = self.dense_block(input_x=x, nb_layers=12, layer_name='dense_2')
        x = self.transition_layer(x, scope='trans_2')

        x = self.dense_block(input_x=x, nb_layers=48, layer_name='dense_3')
        x = self.transition_layer(x, scope='trans_3')

        x = self.dense_block(input_x=x, nb_layers=32, layer_name='dense_final')



 
        x = Batch_Normalization(x, training=self.training, scope='linear_batch')
        x = Relu(x)
        x = Global_Average_Pooling(x)
        x = flatten(x)
        x = Linear(x)

        return x

2.什么是Bottleneck_layer

         Bottleneck由两个部分组成:[1×1]的卷积组和[3×3]的卷积组,其意义在于[1×1]的卷积层能减少输入的特征图,之后再用[3×3]的卷积核进行处理。

Dense_block

        其实现代码如下:

    def bottleneck_layer(self, x, scope):
        # print(x)
        with tf.name_scope(scope):
            x = Batch_Normalization(x, training=self.training, scope=scope+'_batch1')
            x = Relu(x)
            x = conv_layer(x, filter=4 * self.filters, kernel=[1,1], layer_name=scope+'_conv1')
            x = Drop_out(x, rate=dropout_rate, training=self.training)

            x = Batch_Normalization(x, training=self.training, scope=scope+'_batch2')
            x = Relu(x)
            x = conv_layer(x, filter=self.filters, kernel=[3,3], layer_name=scope+'_conv2')
            x = Drop_out(x, rate=dropout_rate, training=self.training)

            # print(x)

            return x

 

 3.什么是Denseblock

        Denseblock是一个密连接的模块,在这个模块内,每一层的输入都来自这个模块内这一层之前所有层的输入,是DenseNet的灵魂所在。其与外部的联系见图4:

                                                                                     (图-4)   

        其实现代码见下:

    def dense_block(self, input_x, nb_layers, layer_name):
        with tf.name_scope(layer_name):
            layers_concat = list()
            layers_concat.append(input_x)

            x = self.bottleneck_layer(input_x, scope=layer_name + '_bottleN_' + str(0))

            layers_concat.append(x)

            for i in range(nb_layers - 1):
                x = Concatenation(layers_concat)
                x = self.bottleneck_layer(x, scope=layer_name + '_bottleN_' + str(i + 1))
                layers_concat.append(x)

            x = Concatenation(layers_concat)

            return x

4.什么是Transition_layer:

      Transition_layer是介于两个Denseblock之间的转换模块,每一个Denseblock输出的feature maps都比较多,如果统统都输入到下一层,将会极大的增加神经网络的参数,所以transition_layer的主要工作就是降维。

      其代码如下:

    def transition_layer(self, x, scope):
        with tf.name_scope(scope):
            x = Batch_Normalization(x, training=self.training, scope=scope+'_batch1')
            x = Relu(x)
            x = conv_layer(x, filter=self.filters, kernel=[1,1], layer_name=scope+'_conv1')
            x = Drop_out(x, rate=dropout_rate, training=self.training)
            x = Average_pooling(x, pool_size=[2,2], stride=2)

            return x

      具体总体代码可以参考:https://github.com/taki0112/Densenet-Tensorflow

  • 12
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 31
    评论
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值