参考资料:《python深度学习》第五章。keras官方中文文档。
使用数据集:数据集来自kaggle https://www.kaggle.com/tongpython/cat-and-dog。
VGG16模型下载自GitHub:https://github.com/fchollet/deep-learning-models/releases。里面包括各种模型,如果你的keras模型导入过慢,你可以将模型下载下来再进行导入。
当我们的数据集较小,只有几百几千张图片的时候,我们很难在一个新的网络结构上训练出具有很高准确率的模型,为此我们需要借助预训练网络模型(即已经训练好的网络模型,如VGG16)。我们利用自己的数据集来重新训练这些模型的分类层,就可以获得比较高的准确率。
目前大部分的卷积神经网络都分为两部分,第一部分由卷积层、池化层组成的卷积基部分,主要用于特征提取;第二部分是由全连接神经网络组成的分类器,主要用于图像分类。当然,目前大部分网络逐渐使用GlobalAveragePooling代替全连接层进行分类,但我们本文对此不进行讨论。
微调主要包括五个部分:
代码如下,我已经在代码里进行了详尽的解释。初学者建议先学习一下Keras再来参考。模型的最终准确率为82%左右,还是比较低的。我使用GlobalAveragePooling代替全连接层,并使用BatchNormalization代替Dropout,反而获得了更低的准确率,所以最后仍然使用了全连接层进行分类试验。
from keras.applications import VGG16 #导入VGG16,首次导入会进行下载,下载速度太慢就去我提供的连接进行下载
from keras import layers
from keras import models
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
from keras import losses
import matplotlib.pyplot as plt
train_dir = 'E:\\DeepLearning\\DataSet\\dogs_vs_cats\\training_set\\training_set' #训练数据集
test_dir = 'E:\\DeepLearning\\DataSet\\dogs_vs_cats\\test_set\\test_set' #验证数据集
#加载VGG16模型
conv_base = VGG16(weights='imagenet', #使用预训练数据
include_top=False, #不使用原来的分类器
input_shape=(150, 150, 3))#将输入尺寸调整为(150, 150, 3)
#添加自己的网络模型
model = models.Sequential()
model.add(conv_base) #首先添加我们加载的VGG16模型
model.add(layers.Flatten()) #添加全连接神经网络,即分类器
model.add(layers.Dense(units=512))
model.add(layers.Dropout(0.5)) #使用Dropout,50%失活
model.add(layers.Dense(units=2, activation='softmax')) #最终分类为2,即猫与狗
#以文本显示模型超参数
model.summary()
conv_base.trainable = False #第一步,设置网络模型不可训练
#对训练数据使用数据增强
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,
horizontal_flip=True,
fill_mode='nearest')
#验证数据不能使用模型增强
tesst_datagen = ImageDataGenerator(rescale=1./255)
#训练数据由硬盘导入
train_generator = train_datagen.flow_from_directory(directory=train_dir,
target_size=(150, 150),
batch_size=20,
class_mode='categorical')
#验证数据由硬盘导入
validation_generator = tesst_datagen.flow_from_directory(directory=test_dir,
target_size=(150, 150),
batch_size=20,
class_mode='categorical')
#对网络模型进行配置
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
loss=losses.binary_crossentropy,
metrics=['acc'])
#开始训练网络模型
history = model.fit_generator(generator=train_generator,
steps_per_epoch=100,
epochs=20,
validation_data=validation_generator,
validation_steps=50)
#将训练好的模型进行保存
model.save('VGG16_Base.h5')
#显示训练与验证过程的曲线
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
print(history.history['acc'][-1])
epochs = range(1, len(acc)+1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training acc')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.legend()
plt.show()
后两步跟前三步是差不多的步骤,只不过加载的模型不再是VGG16而是前面我们刚刚训练好的模型。同时,不再对所有网络模型进行冻结,而是解冻靠近分类器的几层网络模型。然后使用同样的数据对解冻的部分与之前的分类器部分重新进行训练。这次我们获得了95%左右的准确率,大幅高于上面没有进行微调的网络模型。
from keras.models import load_model
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
from keras import losses
import matplotlib.pyplot as plt
#数据集的地址
train_dir = 'E:\\DeepLearning\\DataSet\\dogs_vs_cats\\training_set\\training_set'
test_dir = 'E:\\DeepLearning\\DataSet\\dogs_vs_cats\\test_set\\test_set'
#加载上面训练好的模型
conv_base = load_model('F:\\Code\\Exercise\\keras\\VGG16_Base.h5')
#解冻所有网络
conv_base.trainable = True
#对block_conv1之前的网络进行再次冻结,而之后的网络依然保存解冻状态
set_trainiable = False
for layer in conv_base.layers:
if layer.name == 'block_conv1':
set_trainiable = True
if set_trainiable:
layer.trainiable = True
else:
layer.trainiable = False
model = conv_base
#训练使用数据增强
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,
horizontal_flip=True,
fill_mode='nearest')
#验证数据不可能使用数据增强
tesst_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(directory=train_dir,
target_size=(150, 150),
batch_size=20,
class_mode='categorical')
validation_generator = tesst_datagen.flow_from_directory(directory=test_dir,
target_size=(150, 150),
batch_size=20,
class_mode='categorical')
#配置网络
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
loss=losses.binary_crossentropy,
metrics=['acc'])
#进行训练
history = model.fit_generator(generator=train_generator,
steps_per_epoch=100,
epochs=20,
validation_data=validation_generator,
validation_steps=50)
#微调之后的网络模型进行保存
model.save('VGG16_Base_FT.h5')
#显示训练过程
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
print(history.history['acc'][-1])
epochs = range(1, len(acc)+1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training acc')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.legend()
plt.show()