自学内容网 自学内容网

深度学习笔记30_DenseNet算法实战与解析

一、我的环境

1.语言环境:Python 3.9

2.编译器:Pycharm

3.深度学习环境:pytorch

二、GPU设置

       若使用的是cpu则可忽略

gpus = tf.config.list_physical_devices("GPU")
print(gpus)

、数据导入

import pathlib
import warnings

import matplotlib.pyplot as plt
import tensorflow as tf
from keras.layers import Activation
from keras.layers import Dense, Conv2D, MaxPooling2D, ZeroPadding2D, AveragePooling2D, BatchNormalization, \
    GlobalAveragePooling2D
from keras.models import Model

warnings.filterwarnings('ignore')  # 忽略一些warning内容,无需打印
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
data_dir = "data/bird_photos"
data_dir = pathlib.Path(data_dir)
image_count = len(list(data_dir.glob('*/*')))
data_paths = list(data_dir.glob("*"))
classnames = [str(path).split("\\")[2] for path in data_paths]
print(classnames)
num_classes = len(classnames)
print(num_classes)
#['Bananaquit', 'Black Skimmer', 'Black Throated Bushtiti', 'Cockatoo']

、加载数据

# 加载数据
batch_size = 32
img_height = 224
img_width = 224
"""
关于image_dataset_from_directory()的详细介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/117018789
"""
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="training",
    seed=123,
    image_size=(img_height, img_width),
    batch_size=batch_size)
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
    data_dir,
    validation_split=0.2,
    subset="validation",
    seed=123,
    image_size=(img_height, img_width),
    batch_size=batch_size)

五、数据处理

class_names = train_ds.class_names
plt.figure(figsize=(10, 5))  # 图形的宽为10高为5
plt.suptitle("DataBase")
for images, labels in train_ds.take(1):
    for i in range(8):
        ax = plt.subplot(2, 4, i + 1)
        plt.imshow(images[i].numpy().astype("uint8"))
        plt.title(class_names[labels[i]])

        plt.axis("off")

# 显示第一张
plt.imshow(images[1].numpy().astype("uint8"))
plt.show()

 

再次检查数据

#数据预处理-再次检查数据
# Image_batch是形状的张量(16, 336, 336, 3)。这是一批形状336x336x3的16张图片(最后一维指的是彩色通道RGB)。
# Label_batch是形状(16,)的张量,这些标签对应16张图片
for image_batch, labels_batch in train_ds:
    print(image_batch.shape)
    print(labels_batch.shape)
    break

数据预处理

AUTOTUNE = tf.data.AUTOTUNE
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

六、构建ResNet50V2模型

class DenseLayer(Model):
    def __init__(self, bottleneck_size, growth_rate):
        super().__init__()
        self.filters = growth_rate
        self.bottleneck_size = bottleneck_size
        self.b1 = BatchNormalization()
        self.a1 = Activation('relu')
        self.c1 = Conv2D(filters=self.bottleneck_size, kernel_size=(1, 1), strides=1)
        self.b2 = BatchNormalization()
        self.a2 = Activation('relu')
        self.c2 = Conv2D(filters=32, kernel_size=(3, 3), strides=1, padding='same')

    def call(self, *x):
        x = tf.concat(x, 2)
        x = self.b1(x)
        x = self.a1(x)
        x = self.c1(x)
        x = self.b2(x)
        x = self.a2(x)
        y = self.c2(x)
        return y


# denseblock

class DenseBlock(Model):
    def __init__(self, Dense_layers_num, growth_rate):  # Dense_layers_num每个denseblock中的denselayer数,growth
        super().__init__()
        self.Dense_layers_num = Dense_layers_num
        self.Dense_layers = []
        bottleneck_size = 4 * growth_rate
        for i in range(Dense_layers_num):
            layer = DenseLayer(bottleneck_size, growth_rate)
            self.Dense_layers.append(layer)

    def call(self, input):
        x = [input]
        for layer in self.Dense_layers:
            output = layer(*x)
            x.append(output)
        y = tf.concat(x, 2)
        return y


# transition part
class Transition(Model):
    def __init__(self, filters):
        super().__init__()
        self.b = BatchNormalization()
        self.a = Activation('relu')
        self.c = Conv2D(filters=filters, kernel_size=(1, 1), strides=1)
        self.p = AveragePooling2D(pool_size=(2, 2), strides=2)

    def call(self, x):
        x = self.b(x)
        x = self.a(x)
        x = self.c(x)
        y = self.p(x)
        return y


# DenseNet
class DenseNet(Model):
    def __init__(self, block_list=[6, 12, 24, 16], compression_rate=0.5, filters=64):
        super().__init__()
        growth_rate = 32
        self.padding = ZeroPadding2D(((1, 2), (1, 2)))
        self.c1 = Conv2D(filters=filters, kernel_size=(7, 7), strides=2, padding='valid')
        self.b1 = BatchNormalization()
        self.a1 = Activation('relu')
        self.p1 = MaxPooling2D(pool_size=(3, 3), strides=2, padding='same')
        self.blocks = tf.keras.models.Sequential()
        input_channel = filters
        for i, layers_in_block in enumerate(block_list):
            if i < 3:
                self.blocks.add(DenseBlock(layers_in_block, growth_rate))
                block_out_channels = input_channel + layers_in_block * growth_rate
                self.blocks.add(Transition(filters=block_out_channels * 0.5))
            if i == 3:
                self.blocks.add(DenseBlock(Dense_layers_num=layers_in_block, growth_rate=growth_rate))
        self.p2 = GlobalAveragePooling2D()
        self.d2 = Dense(1000, activation='softmax')

    def call(self, x):
        x = self.padding(x)
        x = self.c1(x)
        x = self.b1(x)
        x = self.a1(x)
        x = self.p1(x)
        x = self.blocks(x)
        x = self.p2(x)
        y = self.d2(x)
        return y


model = DenseNet()

七、总结

这周学习DenseNet算法实战与解析:

        DenseNet是CVPR2017年的Best Paper,它脱离了加深网络层数(ResNet)和加宽网络结构(Inception)来提升网络性能的定式思维,从特征的角度考虑,通过特征重用和旁路(Bypass)设置,既大幅度减少了网络的参数量,又在一定程度上缓解了gradient vanishing问题的产生.结合信息流和特征复用的假设,DenseNet当之无愧成为2017年计算机视觉顶会的年度最佳论文。

  众所周知,CNN已经成为了深度学习方向最主要的网络结构之一。从一开始的只有五层结构的LeNet, 到后来拥有19层结构的VGG,再到首次跨越100层网络的HighwayNetworks与ResNet, 网络层数的加深成为CNN发展的主要方向之一。

  但是随着CNN网络层数的不断增加开始出现梯度消失和模型退化(50层的网络不如20层的网络),批量归一化(BatchNormalization)的广泛使用在一定程度上缓解了梯度消失的问题,而ResNet和Highway Networks通过构造恒等映射设置旁路,进一步减少了梯度消失和模型退化的产生。Fractal Nets通过将不同深度的网络并行化,在获得了深度的同时保证了梯度的传播,随机深度网络通过对网络中一些层进行失活,既证明了ResNet深度的冗余性,又缓解了上述问题的产生。虽然这些不同的网络框架通过不同的实现加深的网络层数,但是他们都包含了相同的核心思想:将feature map进行跨网络层的连接。

DenseNet与ResNet 

ResNet

  ResNet(Deep Residual Network深度残差网络):通过建立前面层与后面层之间的“短路连接”,这有助于训练过程中梯度的反向传播,从而能训练出更深的CNN网络。

DenseNet

  DenseNet采用密集连接机制,即互相连接所有的层,每个层都会与前面所有层在channel维度上连接(concat)在一起,实现特征重用,作为下一层的输入。这样,不但减缓了梯度消失的现象,也使其可以在参数与计算量更少的情况下实现比ResNet更优的性能。

 

DenseNet网络架构
  DenseNet的密集连接方式需要特征图大小保持一致 。 所 以 DenseNet 网络中使用DenseBlock+Transition的结构。

  DenseBlock是包含很多层的模块,每个层的特征图大小相同,层与层之间采用密集连接方式。Transition模块是连接两个相邻的DenseBlock,并且通过Pooling使特征图大小降低。

DenseBlock
在DenseBlock中,各个层的特征图大小一致,可以在channel维度上连接。DenseBlock中的非线性组合函数采用的是BN+ReLU+3x3Conv的结构。

这里有一个参数 k kk, 称为增长率, 指的是每一层的额外通道数, , 假如输入层特征图的channel为K0+(L+1)K , 因为每一层都接受前面所有层的特征图,即特征传递方式是直接将前面所有层的特征concat后传到下一层,一般情况下使用较小的K(一般为12),要注意这个K的实际含义就是这层新提取出的特征。

Dense Block采用了激活函数在前、卷积层在后的顺序,即BN-ReLU-Conv的顺序,这种方式也被称为pre-activation。通常的模型relu等激活函数处于卷积conv、批归一化batchnorm之后,即Conv-BN-ReLU,也被称为post-activation。作者证明,如果采用post-activation设计,性能会变差。

BottleNeck层
  由于后面层的输入会非常大,DenseBlock内部可以采用Bottleneck层(瓶颈层)来减少计算量,主要是原有的结构中增加1x1 Conv,即BN+ReLU+1x1 Conv+BN+ReLU+3x3 Conv称为DenseNet-B结构。其中1x1 Conv得到4k个特征图它起到的作用是降低特征数量,从而提升计算效率。
  每一个Bottleneck输入输出的特征通道数是相同的。这里1×1卷积的作用是固定输出通道数,达到降维的作用,1×1卷积输出的通道数通常是GrowthRate的4倍。当几十个Bottleneck相连接时,concat后的通道数会增加到上千,如果不增加1×1的卷积来降维,后续3×3卷积所需的参数量会急剧增加。

Transition层

  Transition层它主要是连接两个相邻的DenseBlock,并且降低特征图大小。Transition层包括一个1x1的卷积和2x2的AvgPooling,结构为BN+ReLU+1x1 Conv+2x2 AvgPooling

DenseNet的不足在于由于需要进行多次Concatnate操作,数据需要被复制多次显存容易增加得很快,需要一定的显存优化技术。另外,DenseNet是一种更为特殊的网络,ResNet则相对一般化一些,因此ResNet的应用范围更广泛。 

参考:DenseNet


原文地址:https://blog.csdn.net/L1073203482/article/details/145168135

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!