VGGNet创新点
VGGNet是牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司的研究员一起研发的的深度卷积神经网络。VGGNet探索了卷积神经网络的深度与其性能之间的关系,通过反复堆叠33的小型卷积核和22的最大池化层,VGGNet成功地构筑了16~19层深的卷积神经网络。VGGNet相比之前state-of-the-art的网络结构,错误率大幅下降,并取得了ILSVRC 2014比赛分类项目的第2名和定位项目的第1名。
VGGNet论文中全部使用了33的卷积核和22的池化核,通过不断加深网络结构来提升性能。下图所示为VGGNet各级别的网络结构图,和每一级别的参数量,从11层的网络一直到19层的网络都有详尽的性能测试。
虽然从A到E每一级网络逐渐变深,但是网络的参数量并没有增长很多,这是因为参数量主要都消耗在最后3个全连接层。前面的卷积部分虽然很深,但是消耗的参数量不大,不过训练比较耗时的部分依然是卷积,因其计算量比较大。
VGGNet拥有5段卷积,每一段内有2~3个卷积层,同时每段尾部会连接一个最大池化层用来缩小图片尺寸。每段内的卷积核数量一样,越靠后的段的卷积核数量越多:64 – 128 – 256 – 512 – 512。其中经常出现多个完全一样的33的卷积层堆叠在一起的情况,这其实是非常有用的设计。
如上图所示,两个33的卷积层串联相当于1个55的卷积层,即一个像素会跟周围55的像素产生关联,可以说感受野大小为55。而3个33的卷积层串联的效果则相当于1个77的卷积层。除此之外,3个串联的33的卷积层,拥有比1个7*7的卷积层更少的参数量,只有后者的55%。
最重要的是,3个33的卷积层拥有比1个77的卷积层更多的非线性变换(前者可以使用三次ReLU激活函数,而后者只有一次),使得CNN对特征的学习能力更强。
作者在对比各级网络时总结出了以下几个观点。
LRN层作用不大。
越深的网络效果越好。
11的卷积也是很有效的,但是没有33的卷积好,大一些的卷积核可以学习更大的空间特征。
整体代码
import tensorflow as tf
import os
import numpy as np
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras import layers
import matplotlib.pyplot as plt
# 构建数据集
def getDatas():
image_path = "./monkey/" # monkey数据集路径
train_dir = image_path + "training/training/" # 训练集路径
validation_dir = image_path + "validation/validation/" # 验证集路径
# test_dir = image_path + "test" # 测试集路径
# 定义训练集图像生成器,并进行图像增强
train_image_generator = ImageDataGenerator(rotation_range=40,
width_shift_range=0.2,
height_shift_range=0.2,
rescale=1. / 255,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')
# 使用图像生成器从文件夹train_dir中读取样本,对标签进行one-hot编码
train_data_gen = train_image_generator.flow_from_directory(directory=train_dir,
batch_size=batch_size,
shuffle=True,
target_size=(im_height, im_width),
class_mode='categorical')
print(train_data_gen)
# 训练集样本数
# total_train = train_data_gen.n
# 定义验证集图像生成器,对图像进行预处理
validation_image_generator = ImageDataGenerator(rescale=1. / 255) # 归一化
# 使用图像生成器从验证集validation_dir中读取样本
val_data_gen = validation_image_generator.flow_from_directory(directory=validation_dir,
batch_size=batch_size,
shuffle=True,
target_size=(im_height, im_width),
class_mode='categorical')
print(val_data_gen)
# 验证集样本数
# total_val = val_data_gen.n
return train_data_gen, val_data_gen
def base(x, filters, flags):
x = layers.Conv2D(filters, kernel_size=(3, 3), padding='same')(x)
x = layers.BatchNormalization()(x)
x = layers.Activation('relu')(x)
if flags is True:
x = layers.MaxPool2D(pool_size=(2, 2), strides=(2, 2), padding='same')(x)
x = layers.Dropout(0.2)(x)
return x
# 定义模型
def VGG16():
image_input = tf.keras.layers.Input(shape=(224, 224, 3))
x = base(image_input, 64, flags=False)
x = base(x, 64, flags=True)
x = base(x, 128, flags=False)
x = base(x, 128, flags=True)
x = base(x, 256, flags=False)
x = base(x, 256, flags=False)
x = base(x, 256, flags=True)
x = base(x, 512, flags=False)
x = base(x, 512, flags=False)
x = base(x, 512, flags=True)
x = base(x, 512, flags=False)
x = base(x, 512, flags=False)
x = base(x, 512, flags=True)
x = layers.Flatten()(x)
x = layers.Dense(512, activation='relu')(x)
x = layers.Dropout(0.2)(x)
x = layers.Dense(512, activation='relu')(x)
x = layers.Dropout(0.2)(x)
x = layers.Dense(10, activation='softmax')(x)
net = tf.keras.Model(image_input, x)
return net
model = VGG16()
model.summary()
if __name__ == '__main__':
# 训练参数
im_height = 224
im_width = 224
batch_size = 8
epochs = 50
learning_rate = 0.001
# 获得数据集
train_data_gen_, val_data_gen_ = getDatas()
# 编译模型
model.compile(optimazer=tf.keras.optimizers.Adam(learning_rate=learning_rate),
loss=tf.keras.losses.CategoricalCrossentropy(from_logits=False), # 交叉熵损失函数
metrics=["categorical_accuracy"]) # 评价函数
# # 断点续训
checkpoint_save_path = "./checkpoint/mobilenetv2.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print('--------------load the model---------------')
model.load_weights(checkpoint_save_path)
# 保存模型
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)
# 开始训练
# history = model.fit(x=train_data_gen_, epochs=epochs, validation_data=val_data_gen_, callbacks=[cp_callback]) # 此方法把数据全部加载进来
history = model.fit_generator(train_data_gen_,
steps_per_epoch=1098 // batch_size, epochs=epochs, initial_epoch=0,
validation_data=val_data_gen_,
validation_steps=272 // batch_size,
callbacks=[cp_callback])
# 记录训练和验证集的准确率和损失值
history_dict = history.history
train_loss = history_dict["loss"]
train_accuracy = history_dict["accuracy"]
val_loss = history_dict["val_loss"]
val_accuracy = history_dict["val_accuracy"]
# 绘制损失值
plt.figure()
plt.plot(range(epochs), train_loss, label='train_loss')
plt.plot(range(epochs), val_loss, label='val_loss')
plt.legend()
plt.xlabel('epochs')
plt.ylabel('loss')
# 绘制准确率
plt.figure()
plt.plot(range(epochs), train_accuracy, label='train_accuracy')
plt.plot(range(epochs), val_accuracy, label='val_accuracy')
plt.legend()
plt.xlabel('epochs')
plt.ylabel('accuracy')
plt.show()