本文会通过两部分来介绍DenseNet网络:DenseNet网络论文的介绍和代码的实现。
- DenseNet网络介绍
下图是DenseNet的论文中的基本组成部分,如图一所示:

图一:DenseNet网络结构
DenseNet网络结构主要是由Dense Block部分和Convolution、Pooling组成的Transition Layer 这两部分组成。图一中使用了三个Dense Block模块组成了DenseNet网络,本文中代码是采用了四个Dense Block模块,有一点点区别。也就是在图一Prediction前面再加上一组模块 Dense Block和Transition Layer组成四个个DenseNet网络。
1.1 Dense Block 模块介绍
下图是Dense Block模块,如图二所示:

图二:Dense Block模块
图二中Dense Block模块结构是论文中所提到的5层结构,Dense Block中结构的层数是可以自己自定义的,并不是说只能用图二中这样的。具体效果还是要看自己训练后的模型,对应效果。先来说说每一层的结构,第一层Dense Block 输入input层和其他几层有些不同,这里到后面后介绍一下。Dense Block模块中每一层的结构都是有1*1卷积+3*3卷积构成,如下图三所示。

图三:Dense Block结构
图中每个在进行卷积前都要通过BN+RELU处理,也就构成了BN+RELU+CONV的结构,每个卷积对应的filters是固定的(1*1卷积是4K 3*3的是K),每个Dense Block中的都是一样的。在这里有系数k确定,论文中作者给出的是K=32,在论文开始部分三层结构中k=4,也有k=12。也是可以自定义的,看网络效果。我这里程序里使用的k是32。
1.2 Transition Layer 模块介绍
Transition Layer模块由两部分组成1*1卷积和average pool层组成,这里1*1卷积的filter为k,不是上文所提出的4*k,如下图四所示(1*1卷积前面也要bn+relu)。

图四: Transition Layer 结构
接下来是论文中用于imagenet数据集上的结构图,如图五所示。

图五: DenseNet 4种结构
关于图中例如DenseNet-121中 这边的数字 121 169 201 264是网络结构的层数,Dense Block后面1*1卷积和6*6卷积后面×6 说明是用了 6组Dense Block结构。后面x12就代表了用了12组Dense Block结构。
在刚开始,输入图片进行处理的通过7*7卷积还有3*3的max pooling,其中7*7卷积的filter是2k个也就是64,在k=32的时候。使用前也有进行 BN+RELU处理
- DenseNet 代码
第一部分需要的配置文件
from tensorflow.keras import layers, models, Input
from keras.layers import Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, Dense
from tensorflow.keras.preprocessing.image import ImageDataGenerator
第二部分代码是关于Dense Block的代码
def DenseConv(featuremap,h): #h为k也就是filter的
x = BatchNormalization()(featuremap)
x = Activation('relu')(x)
x = Conv2D(filters=4*h, kernel_size=(1, 1), strides=1, padding='same')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(filters=h, kernel_size=(3, 3), strides=1, padding='same')(x)
return x
# 这里部分是1*1卷积和 3*3卷积
def DenseNet1(input_x, f_filters, number1):
hyj = []
hyj.append(input_x)
for i in range(number1):
x = DenseConv(input_x, f_filters)
hyj.append(x)
input_x = layers.concatenate(hyj)
return input_x
第三部分是关于Transition Layer的代码
def Transition(input_x):
x = BatchNormalization()(input_x)
x = Activation('relu')(x)
x = Conv2D(filters=32, kernel_size=(1, 1), strides=1)(x)
x = layers.AveragePooling2D(pool_size=(2, 2), strides=2)(x)
return x
第四部分是网络结构的代码DenseNet-121
def DenseNet_net(input_y):
x = BatchNormalization()(input_y)
x = Activation('relu')(x)
x = Conv2D(64, (7, 7), strides=2, padding='same')(x)
x = MaxPooling2D(pool_size=[3, 3], strides=2, padding='same')(x)
x = DenseNet1(x, 32, 6)
x = Transition(x)
x = DenseNet1(x, 32, 12)
x = Transition(x)
x = DenseNet1(x, 32, 24)
x = Transition(x)
x = DenseNet1(x, 32, 16)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(5, activation='softmax')(x) #这里数字5对应的是你要分类物品的种类 比如我这是5种列别的花就是5
return x
总代码
from tensorflow.keras import layers, models, Input
from keras.layers import Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, Dense
from tensorflow.keras.preprocessing.image import ImageDataGenerator
def DenseConv(featuremap,h):
x = BatchNormalization()(featuremap)
x = Activation('relu')(x)
x = Conv2D(filters=4*h, kernel_size=(1, 1), strides=1, padding='same')(x)
x = BatchNormalization()(x)
x = Activation('relu')(x)
x = Conv2D(filters=h, kernel_size=(3, 3), strides=1, padding='same')(x)
return x
def DenseNet1(input_x, f_filters, number1):
hyj = []
hyj.append(input_x)
for i in range(number1):
x = DenseConv(input_x, f_filters)
hyj.append(x)
input_x = layers.concatenate(hyj)
return input_x
def Transition(input_x):
x = BatchNormalization()(input_x)
x = Activation('relu')(x)
x = Conv2D(filters=32, kernel_size=(1, 1), strides=1)(x)
x = layers.AveragePooling2D(pool_size=(2, 2), strides=2)(x)
return x
def DenseNet_net(input_y):
x = BatchNormalization()(input_y)
x = Activation('relu')(x)
x = Conv2D(64, (7, 7), strides=2, padding='same')(x)
x = MaxPooling2D(pool_size=[3, 3], strides=2, padding='same')(x)
x = DenseNet1(x, 32, 6)
x = Transition(x)
x = DenseNet1(x, 32, 12)
x = Transition(x)
x = DenseNet1(x, 32, 24)
x = Transition(x)
x = DenseNet1(x, 32, 16)
x = layers.GlobalAveragePooling2D()(x)
x = layers.Dense(5, activation='softmax')(x) #训练自己数据集的时候5要修改改成对应的物品种类
return x
#下面的代码是用来训练的(分类任务) 如果用自己的数据的话只需要改一下三个地方就行
train_datagen = ImageDataGenerator(rescale=1./255,
rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
brightness_range=(0.6, 1.2),
horizontal_flip=True)
valid_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
directory='flowe2-set/train', # 你的数据训练集的位置
target_size=(224, 224),
batch_size=10 #这个大小根据你数据集和显卡显存大小来修改 比如我训练集3000个 那么batch_size*steps_per_epoch最好等于3000 steps_per_epoch这个在下面history里面修改
)
valid_generator = valid_datagen.flow_from_directory(
directory='flowe2-set/valid', #验证集的位置
target_size=(224, 224),
batch_size=10 #同上validation_steps*batch_size
)
input_shape = Input([224, 224, 3])
#input_tensor = Input(shape=(224,224,3))
k = DenseNet_net(input_shape)
model = models.Model(inputs=input_shape, outputs=k)
model.compile(
optimizer='adam',
loss='sparse_categorical_crossentropy', #这个loss有问题 所以才会报错 一般报错是[[{{node PyFunc}}]] 改成categorical_crossentropy 就好 如果报错的话
metrics=['accuracy']
)
history = model.fit_generator(
train_generator,
steps_per_epoch=300,
epochs=50,
validation_data=valid_generator,
validation_steps=74,
verbose=1
)