DenseNet解析以及和Resnet对比

前言

前面笔者写了一篇关于resnet的文章
https://blog.csdn.net/weixin_44106928/article/details/101627948
当时看完resnet的论文,理解到设计思想,真的感觉到作者何凯明的牛逼之处

巧妙了运用短连接的方式将输入输出add到一起,减轻了深度神经网络的梯度消失,弥散的问题

而后几年,DenseNet参考了resnet的网络结构,将相加变成了concatenate连结,以DenseBlock为基础块,减少了参数量,并成功引起学术界注意

Densenet介绍

DenseNet解析以及和Resnet对比_第1张图片
这里引用论文中的Densenet结构图片
每一个模块进行相关操作后,与输入(input)进行连接,再作为新的输入,进入到下一个DenseBlock

这个结构是不是很熟悉?

它与Resnet的resBlock结构很相似

下面我来介绍一下DenseBlock的设计
首先 简单介绍下预处理
DenseNet解析以及和Resnet对比_第2张图片
也就是常规的卷积,激活,批量归一化

然后输入进一个个denseBlock里面

denseblock首先采用的是Resnet改良版的 批量归一化,激活,卷积的 新结构

第一个卷积先进行的是1x1 的卷积,第二次则进行的是3x3的卷积,然后与输入相连接
DenseNet解析以及和Resnet对比_第3张图片
注意这里concatenate区别于Resnet的add

连结的意思是将两个相同长度,宽度的特征图合并在一起,生成一个新的图,通道数是两个特征图通道数之和

而ResBlock里的Add,是将特征图的值相加,这就需要保证两个特征图的各个维度相同
以下是ADD的图
DenseNet解析以及和Resnet对比_第4张图片
可以看到最后两个相加的张量都是(256, 256, 16)

回来我们继续说Densenet的结构
论文里面定义了一个growth生长率这个参数

也就是经过了growth个DenseBlock后
我们使用步幅为2,1x1卷积块进行通道数上的降维,并且用 平均池化层对张量的长,宽进行降维
DenseNet解析以及和Resnet对比_第5张图片
我们通过1x1的卷积将通道数从600降到了300
然后再用平均池化将256, 256 降维到128, 128

经过这样一个大结构后
最后我们使用全局平均池化层,然后接上你需要分类的全连接层
DenseNet解析以及和Resnet对比_第6张图片
整体的结构就完成
接下来贴上总结构图

亮点介绍

  1. 采用与Resnet相似的结构,使用短连接的拓扑结构,减缓梯度弥散情况

  2. 通过1x1卷积核减少维数,避免传统池化层对特征的忽略

  3. 强调特征复用,将前一级的输入相连接,构成新的特征图

4.参数减少,我们在Resnet下,如果做大量的卷积核卷积,如从256个卷积核逐步减少到32个卷积核
所耗费的参数也是很大的,笔者最近搭的一个Resnet网络参数训练量高达一千万
DenseNet解析以及和Resnet对比_第7张图片
而表面上Densenet的维数也很高

但其实它每一个模块卷积的数量并不大
DenseNet解析以及和Resnet对比_第8张图片
可以看到虽然后面输入维数很高,但它始终是保持一个48的卷积核在进行卷积,所以实质上,在每个DenseBlock里面,相加的通道数都是48 48 48

DenseBlock就不是很强调卷积的深度,它更强调通过每一次卷积得到的特征图复用

DenseNet的网络更,这也使得参数相较于Resnet有明显的减少

搭了三四十层的网络参数也仅仅只有200多万
DenseNet解析以及和Resnet对比_第9张图片

代码实现

我写的代码参考了一些博主的
用的keras版本较老
里面有个方法是plot_ model,是将模型可视化,如果没有安装也可以注释掉
首先是conv_bl;ock.py文件

import keras
from keras import backend as K
from keras.utils.vis_utils import plot_model
from keras.models import *
from keras.layers import *
def DenseLayer(x, nb_filters, bn_size=4, alpha=0.0, drop_rate=0.2):
    x = BatchNormalization()(x)
    x = LeakyReLU(alpha=alpha)(x)
    x = Conv2D(bn_size*nb_filters, (1, 1), strides=(1, 1), padding='SAME')(x)

    x = BatchNormalization()(x)
    x = LeakyReLU(alpha=alpha)(x)
    x = Conv2D(bn_size * nb_filters, (3, 3), strides=(1, 1), padding='SAME')(x)
    if drop_rate:
        x = Dropout(drop_rate)(x)

    return x

def DenseBlock(x, nb_layers, growth_rate, drop_rate=0.2):
    for ii in range(nb_layers):
        conv = DenseLayer(x, nb_filters=growth_rate, drop_rate=drop_rate)
        x = Concatenate()([x, conv])

    return x

def TransitonLayer(x, compression=0.5, alpha=0.0, is_max=0):
    # 这是is_max只是为了定义使用最大池化层还是平均池化层
    nb_filters = int(x.shape.as_list()[-1]*compression)
    # 这里是将通道维数 乘一个压缩率,这里是0.5
    # 比如我原本通道数有600,那么经过后面的1x1 卷积输出后通道压缩为一半也就是300
    x = BatchNormalization()(x)
    x = LeakyReLU(alpha=alpha)(x)
    x = Conv2D(nb_filters, (1, 1), strides=(1, 1), padding='SAME')(x)
    if is_max!=0: x = MaxPool2D(pool_size=(2, 2), strides=2)(x)
    else: x = AveragePooling2D(pool_size=(2, 2), strides=2)(x)

    return x

然后是我们主程序
NetStruct.py

from conv_block import *

BATCH_SIZE = 5 # 批处理图片个数 默认为50 前期测试
EPOCHS = 10 # DEFAULT 10
IMAGE_SIZE = 1024 # 输入的是256 x 256
NUM_CHANNEL = 1 # 通道为3,默认是灰度图
NUM_LABELS = 2 

MODEL_DIR = './model/train_demo/'
MODEL_FORMAT = '.h5'
HISTORY_DIR = './history/train_demo/'
HISTORY_FORMAT = '.history'

# 模型网络结构文件
MODEL_VIS_FILE = 'My_net_classfication' + '.png'
# 定义增长率
growth_rate = 12

input = Input(shape=(256, 256, 3))
x = BatchNormalization()(input)
x = LeakyReLU(alpha=0.1)(x)
x = Conv2D(growth_rate*2, (3, 3), strides=1, padding='SAME')(x)

x = DenseBlock(x, 12, growth_rate, drop_rate=0.2)

x = TransitonLayer(x)

x = DenseBlock(x, 12 , growth_rate, drop_rate=0.2)

x = TransitonLayer(x)

x = DenseBlock(x, 12 , growth_rate, drop_rate=0.2)

x = BatchNormalization(axis=3)(x)
x = GlobalAveragePooling2D()(x)

x = Dense(10, activation='softmax')(x)

model = Model(input, x)
model.summary()
plot_model(model, to_file=MODEL_VIS_FILE, show_shapes=True)

这里我没有添加实际的数据,只是简单的以一个shape来进行模型的搭建

你可能感兴趣的:(Python,MXNET,机器学习)