• 深度学习Day-19:DenseNet算法实战与解析


     🍨 本文为:[🔗365天深度学习训练营] 中的学习记录博客
     🍖 原作者:[K同学啊 | 接辅导、项目定制]

    要求:

    • 根据 Pytorch 代码,编写出 TensorFlow 代码
    • 研究 DenseNet 与 ResNetV 的区别
    • 改进思路是否可以迁移到其他地方

    一、前言

            在计算机视觉领域,卷积神经网络(CNN)已经成为最主流的方法,比如GoogLenet,VGG-16,Incepetion等模型。CNN史上的一个里程碑事件是ResNet模型的出现,ResNet可以训练出更深的CNN模型,从而实现更高的准确度。ResNet模型的核心是通过建立前面层与后面层之间的“短路连接”进而训练出更深的CNN网络。

            DenseNet,它的基本思路与ResNet一致,但是它建立的是前面所有层与后面层的密集连接(dense connection),它的名称也是由此而来。DenseNet的另一大特色是通过特征在channel上的连接来实现特征重用(feature reuse)。这些特点让DenseNet在参数和计算成本更少的情形下实现比ResNet更优的性能。

    二、设计理念

            相比ResNet,DenseNet提出了一个更激进的密集连接机制:即互相连接所有的层,具体来说就是每个层都会接受其前面所有层作为其额外的输入。

            图1为ResNet网络的残差连接机制,作为对比,图2为DenseNet的密集连接机制。可以看到,ResNet是每个层与前面的某层(一般是2~4层)短路连接在一起,连接方式是通过元素相加。而在DenseNet中,每个层都会与前面所有层在channel维度上连接(concat)在一起(即元素叠加),并作为下一层的输入。

            对于一个 L 层的网络,DenseNet共包含 \frac{L(L+1))}{2} 个连接,相比ResNet,这是一种密集连接。而且DenseNet是直接concat来自不同层的特征图,这可以实现特征重用,提升效率,这一特点是DenseNet与ResNet最主要的区别。

     图1:ResNet网络的短路连接机制(其中+代表的是元素级相加操作)

     图2:DenseNet网络的密集连接机制(其中c代表的是channel级连接操作)

            而对于DesNet,则是采用跨通道concat的形式来连接,会连接前面所有层作为输入,输入和输出的公式是Xl​=Hl​(X0​,X1​,...,Xl−1​)。这里要注意所有的层的输入都来源于前面所有层在channel维度的concat,用一张动图体会一下:

    图3 DenseNet的前向过程 

     三、网络结构

    图4 DenseNet的网络结构 

            CNN网络一般要经过Pooling或者stride>1的Conv来降低特征图的大小,而DenseNet的密集连接方式需要特征图大小保持一致。为了解决这个问题,DenseNet网络采用DenseBlock+Transition的结构,其中DenseBlock是包含很多层的模块,每个层的特征图大小相同,层与层之间采用密集连接方式。而Transition层是连接两个相邻的DenseBlock,并且通过Pooling使特征图大小降低。图5给出了DenseNet的网路结构,它共包含4个DenseBlock,各个DenseBlock之间通过Transition层连接在一起。

    图5 采用DenseBlock+Transition的DenseNet网络 

             在DenseBlock中,各个层的特征图大小一致,可以在channel维度上连接。DenseBlock中的非线性组合函数 H ( ⋅ )的是BN + ReLU + 3x3 Conv的结构,如图所示。另外值得注意的一点是,与ResNet不同,所有DenseBlock中各个层卷积之后均输入k个特征图,即得到的特征图的channel数为k,或者说采用k个卷积核。k在DenseNet称为growth rate,这是一个超参数。一般情况下使用较小的k,就可以得到较佳的性能。

    图6 DenseBlock中的非线性转换结构 

            由于后面层的输入会非常大,DenseBlock内部可以采用bottleneck层来减少计算量,主要是原有的结构中增加1x1 Conv,如图7所示,即 BN + ReLU + 1x1 Conv + BN + ReLU + 3x3 Conv,称为DenseNet-B结构。其中1x1 Conv得到4k个特征图,它起到的作用是降低特征数量,从而提升计算的效率。

    图7 使用bottleneck层的DenseBlock结构 

            对于Transition层,它主要是连接两个相邻的DenseBlock,并且降低特征图大小。Transition层包括一个1x1的卷积和2x2的AvgPooling,结构为BN+ReLU+1x1Conv+2x2AvgPooling。另外,Transition层可以起到压缩模型的作用。

    四、Tensorflow实现

    1.代码

    1. import tensorflow as tf
    2. from tensorflow.keras import layers, models
    3. from tensorflow import keras
    4. data_dir = "./data/bird_photos"
    5. train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    6. data_dir,
    7. validation_split=0.2, # 分割数据集
    8. subset="training", # 数据集类型
    9. seed=123,
    10. image_size=(224, 224),
    11. batch_size=32)
    12. val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    13. data_dir,
    14. validation_split=0.2, # 分割数据集
    15. subset="validation", # 数据集类型
    16. seed=123,
    17. image_size=(224, 224),
    18. batch_size=32)
    19. class BottleNeck(keras.Model):
    20. def __init__(self, growth_rate, bn_size=4, dropout=0.3):
    21. super().__init__()
    22. self.bn1 = layers.BatchNormalization()
    23. self.relu = layers.Activation("relu"),
    24. self.conv1 = layers.Conv2D(filters=bn_size * growth_rate, kernel_size=(1, 1),
    25. strides=1, padding='same')
    26. self.bn2 = layers.BatchNormalization()
    27. self.conv2 = layers.Conv2D(filters=growth_rate, kernel_size=(3, 3),
    28. strides=1, padding='same')
    29. self.dropout = layers.Dropout(rate=dropout)
    30. self.listLayers = [
    31. self.bn1,
    32. self.relu,
    33. self.conv1,
    34. self.bn2,
    35. self.relu,
    36. self.conv2,
    37. self.dropout
    38. ]
    39. def call(self, x):
    40. tem = x
    41. for layer in self.listLayers.layers:
    42. x = layer(x)
    43. return layers.concatenate([tem, x], axis=-1)
    44. class Transition(tf.keras.Model):
    45. def __init__(self, growth_rate):
    46. super().__init__()
    47. self.bn1 = layers.BatchNormalization()
    48. self.relu = layers.Activation('relu')
    49. self.conv1 = layers.Conv2D(filters=growth_rate, kernel_size=(1, 1),
    50. strides=1, activation='relu', padding='same')
    51. self.pooling = layers.AveragePooling2D(pool_size=(2, 2), strides=2, padding='same')
    52. self.listLayers = [
    53. self.bn1,
    54. self.relu,
    55. self.conv1,
    56. self.pooling
    57. ]
    58. def call(self, x):
    59. for layer in self.listLayers.layers:
    60. x = layer(x)
    61. return x
    62. class DenseBlock(tf.keras.Model):
    63. def __init__(self, num_layer, growth_rate, bn_size=4, dropout=0.3, efficient=False):
    64. super().__init__()
    65. self.efficient = efficient
    66. self.listLayers = []
    67. if self.efficient:
    68. _x = tf.recompute_grad(BottleNeck(growth_rate, bn_size=bn_size, dropout=dropout))
    69. else:
    70. _x = BottleNeck(growth_rate, bn_size=bn_size, dropout=dropout)
    71. for _ in range(num_layer):
    72. self.listLayers.append(BottleNeck(growth_rate, bn_size=bn_size, dropout=dropout))
    73. def call(self, x):
    74. for layer in self.listLayers.layers:
    75. x = layer(x)
    76. return x
    77. class DenseNet(tf.keras.Model):
    78. def __init__(self, num_init_feature, growth_rate, block_config, num_classes,
    79. bn_size=4, dropout=0.3, compression_rate=0.5, efficient=False):
    80. super().__init__()
    81. self.num_channels = num_init_feature
    82. self.conv = layers.Conv2D(filters=num_init_feature, kernel_size=7,
    83. strides=2, padding='same')
    84. self.bn = layers.BatchNormalization()
    85. self.relu = layers.Activation('relu')
    86. self.max_pool = layers.MaxPool2D(pool_size=3, strides=2, padding='same')
    87. self.dense_block_layers = []
    88. for i in block_config[:-1]:
    89. self.dense_block_layers.append(DenseBlock(num_layer=i, growth_rate=growth_rate,
    90. bn_size=bn_size, dropout=dropout, efficient=efficient))
    91. self.num_channels = compression_rate * (self.num_channels + growth_rate * i)
    92. self.dense_block_layers.append(Transition(self.num_channels))
    93. self.dense_block_layers.append(DenseBlock(num_layer=block_config[-1], growth_rate=growth_rate,
    94. bn_size=bn_size, dropout=dropout, efficient=efficient))
    95. self.avgpool = layers.GlobalAveragePooling2D()
    96. self.fc = tf.keras.layers.Dense(units=num_classes, activation=tf.keras.activations.softmax)
    97. def call(self, x):
    98. x = self.conv(x)
    99. x = self.bn(x)
    100. x = self.relu(x)
    101. x = self.max_pool(x)
    102. for layer in self.dense_block_layers.layers:
    103. x = layer(x)
    104. x = self.avgpool(x)
    105. return self.fc(x)
    106. model = DenseNet(num_init_feature=64,
    107. growth_rate=32,
    108. block_config=[6, 12, 24, 16],
    109. compression_rate=0.5,
    110. num_classes=4,
    111. dropout=0.0,
    112. efficient=True)
    113. x = tf.random.normal((1, 224, 224, 3))
    114. for layer in model.layers:
    115. x = layer(x)
    116. print(layer.name, 'output shape:\t', x.shape)
    117. AUTOTUNE = tf.data.AUTOTUNE
    118. train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
    119. val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
    120. opt = tf.keras.optimizers.Adam(learning_rate=0.002, decay=0.01)
    121. model.compile(optimizer=opt,
    122. loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
    123. metrics=['accuracy'])
    124. epochs = 10
    125. history = model.fit(train_ds, validation_data=val_ds, epochs=epochs)

    2.输出

    1. Found 565 files belonging to 4 classes.
    2. Using 113 files for validation.
    3. conv2d output shape: (1, 112, 112, 64)
    4. batch_normalization output shape: (1, 112, 112, 64)
    5. activation output shape: (1, 112, 112, 64)
    6. max_pooling2d output shape: (1, 56, 56, 64)
    7. dense_block output shape: (1, 56, 56, 256)
    8. transition output shape: (1, 28, 28, 128)
    9. dense_block_1 output shape: (1, 28, 28, 512)
    10. transition_1 output shape: (1, 14, 14, 256)
    11. dense_block_2 output shape: (1, 14, 14, 1024)
    12. transition_2 output shape: (1, 7, 7, 512)
    13. dense_block_3 output shape: (1, 7, 7, 1024)
    14. global_average_pooling2d output shape: (1, 1024)
    15. dense output shape: (1, 4)
    16. Epoch 1/10
    17. 15/15 [==============================] - 83s 5s/step - loss: 2.5051 - accuracy: 0.4508 - val_loss: 37955.5703 - val_accuracy: 0.3186
    18. Epoch 2/10
    19. 15/15 [==============================] - 75s 5s/step - loss: 1.1275 - accuracy: 0.6922 - val_loss: 3854.2537 - val_accuracy: 0.2301
    20. Epoch 3/10
    21. 15/15 [==============================] - 78s 5s/step - loss: 0.6559 - accuracy: 0.7780 - val_loss: 794.8064 - val_accuracy: 0.3186
    22. Epoch 4/10
    23. 15/15 [==============================] - 84s 6s/step - loss: 0.5599 - accuracy: 0.7926 - val_loss: 94.6405 - val_accuracy: 0.2655
    24. Epoch 5/10
    25. 15/15 [==============================] - 74s 5s/step - loss: 0.7278 - accuracy: 0.7682 - val_loss: 45.8066 - val_accuracy: 0.3186
    26. Epoch 6/10
    27. 15/15 [==============================] - 74s 5s/step - loss: 0.3567 - accuracy: 0.8904 - val_loss: 16.1634 - val_accuracy: 0.3186
    28. Epoch 7/10
    29. 15/15 [==============================] - 74s 5s/step - loss: 0.2239 - accuracy: 0.9287 - val_loss: 10.3661 - val_accuracy: 0.3186
    30. Epoch 8/10
    31. 15/15 [==============================] - 75s 5s/step - loss: 0.1488 - accuracy: 0.9406 - val_loss: 1.8957 - val_accuracy: 0.5044
    32. Epoch 9/10
    33. 15/15 [==============================] - 75s 5s/step - loss: 0.1024 - accuracy: 0.9630 - val_loss: 1.1245 - val_accuracy: 0.6018
    34. Epoch 10/10
    35. 15/15 [==============================] - 76s 5s/step - loss: 0.0563 - accuracy: 0.9895 - val_loss: 1.2219 - val_accuracy: 0.5133

            具体代码细节在之前的文章中已有涉及,故不再做具体解释。

  • 相关阅读:
    C++ Decltype 关键字
    (经验贴)怎么对HTML文件进行分析
    腾讯云EKS 上部署 eshopondapr
    eCognition 分类
    k8s安全机制
    将二维整型数组以二进制输出到文件与二进制文件读入到数组
    c# String.Format
    【英语:基础高阶_全场景覆盖表达】K13.口语主题陈述——历史类
    MyBatisPlus-多记录操作及逻辑删除
    工程管理系统简介 工程管理系统源码 java工程管理系统 工程管理系统功能设计
  • 原文地址:https://blog.csdn.net/m0_51359915/article/details/136834939