利用预训练VGG16实现分类

      现实场景中,我们可能获得的带标注的样本会比较小,因此可以利用一些机构提供的预训练网络进行图片分类,这种做法可以利用同行的研究成果,并极大缩短训练时间。以下是利用VGG16进行猫狗分类的一个例子。

 

导入keras的一些需要的库:

from keras.applications import VGG16

import os

import numpy as np

from keras.preprocessing import image

from keras.preprocessing.image import ImageDataGenerator

from keras import models

from keras import layers,Input

from keras import optimizers

import matplotlib.pyplot as plt

from keras.models import load_model,Model

import keras

 

设定工作目录,当然该目录下要放好继续训练的样本集、验证集和测试集图片:

 

base_dir='.\\cat_dog_small'

train_dir=os.path.join(base_dir,'train')

validation_dir=os.path.join(base_dir,'validation')

test_dir=os.path.join(base_dir,'test')

 

 

由于图片比较大,不太可能把所有图片一次装载入内存,因此设计一个迭代器,可以根据目录下的图片随机生成批量数据集,并且通过图片增强的方式增加样本数量,提升泛化能力。

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')

validation_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(train_dir,target_size=(150,150),batch_size=20,class_mode='binary')

validation_generator = validation_datagen.flow_from_directory(validation_dir,target_size=(150,150),batch_size=20,class_mode='binary')

test_generator = validation_datagen.flow_from_directory(test_dir,target_size=(150,150),batch_size=20,class_mode='binary')

 

利用VGG16的卷积层以及预训练好的参数,注意卷积层的参数先设定为不可训练。然后增加一个分类器,最后一级根据自己需要的分类选择dense的节点数量。

conv_base=VGG16(weights='imagenet',include_top=False)

conv_base.trainable=False

x_input = Input(shape=(150,150,3))

x = conv_base(x_input)

x = layers.Flatten()(x)

x = layers.Dense(512,activation='relu')(x)

x = layers.Dropout(0.5)(x)

feature_output = layers.Dense(1,activation='sigmoid')(x)

model = Model(x_input,feature_output)

model.summary()

 

设定回调函数,当满足条件时提前结束训练、改变学习率、保存最好的参数:

callbacks_list = [

#训练精度没下降,终止

    keras.callbacks.EarlyStopping(

        monitor='acc',

        patience=10

    ),

#保存最好的模型

    keras.callbacks.ModelCheckpoint(

        filepath='cats_and_dogs_small_vgg_loss.h5',

        monitor='val_loss',

        save_weights_only=True,

        save_best_only='True'),

   

    keras.callbacks.ModelCheckpoint(

        filepath='cats_and_dogs_small_vgg_acc.h5',

        monitor='val_acc',

        save_weights_only=True,

        save_best_only='True'),

#减小学习率

    keras.callbacks.ReduceLROnPlateau(

        monitor='val_loss',

        factor=0.3,

        patience = 5

    )

]

 

编译模型,并开始训练分类器:

model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=1e-4),metrics=['acc'])

history=model.fit_generator(train_generator,

                            steps_per_epoch=200,

                            epochs=200,

                            callbacks=callbacks_list,

                            validation_data=validation_generator,

                            validation_steps=50,

                            verbose=1)

 

Epoch 31/200

200/200 [==============================] - 97s 485ms/step - loss: 0.3003 - acc: 0.8742 - val_loss: 0.2566 - val_acc: 0.9040

Epoch 32/200

200/200 [==============================] - 97s 485ms/step - loss: 0.2858 - acc: 0.8817 - val_loss: 0.2565 - val_acc: 0.9040

Epoch 33/200

200/200 [==============================] - 97s 485ms/step - loss: 0.2903 - acc: 0.8742 - val_loss: 0.2568 - val_acc: 0.9030

Epoch 34/200

200/200 [==============================] - 97s 485ms/step - loss: 0.2799 - acc: 0.8812 - val_loss: 0.2567 - val_acc: 0.9030

Epoch 35/200

200/200 [==============================] - 97s 485ms/step - loss: 0.2804 - acc: 0.8782 - val_loss: 0.2569 - val_acc: 0.9030

 

训练结束时发现训练精度无法提升,直觉感觉应该是仅训练分类器是不够的,因为卷积层可以发现很对局部特征,而卷积层参数被固定,无法进行训练,因此下一步准备把最后几层的卷积层设定成可训练,重新编译模型开始训练。

 

#微调网络

#model.load_weights('cats_and_dogs_small_vgg.h5')

conv_base.trainable=True

set_trainable=False

for layer in conv_base.layers:

    if layer.name=='block4_conv1':

        set_trainable=True

   

    if set_trainable==True:

        layer.trainable=True

    else:

        layer.trainable=False

 

model.compile(loss='binary_crossentropy',optimizer=optimizers.RMSprop(lr=3e-5),metrics=['acc'])

history=model.fit_generator(train_generator,

                            steps_per_epoch=200,

                            epochs=200,

                            callbacks=callbacks_list,

                            validation_data=validation_generator,

                            validation_steps=50,

                            verbose=1)

 

将训练过程的验证精度和损失可视化

history_dict = history.history

loss_values = history_dict['loss']

val_loss_values = history_dict['val_loss']

epochs = range(1,len(loss_values)+1)

plt.plot(epochs,loss_values,'bo',label='Train_loss')

plt.plot(epochs,val_loss_values,'b',label='Validation_loss')

plt.title('Training and Validition loss')

plt.legend()

 

plt.figure()

 

acc_values = history_dict['acc']

val_acc_values = history_dict['val_acc']

plt.plot(epochs,acc_values,'bo',label='Train_acc')

plt.plot(epochs,val_acc_values,'b',label='Validation_acc')

plt.legend()

 

plt.show()

 

最后导入最好的模型参数,在测试集上看看精度如何。

model.load_weights('cats_and_dogs_small_vgg_loss.h5')

test_loss1,test_acc1 = model.evaluate_generator(test_generator, steps=50)

print('test acc1: ',test_acc1)

model.load_weights('cats_and_dogs_small_vgg_acc.h5')

test_loss2,test_acc2 = model.evaluate_generator(test_generator, steps=50)

print('test acc2: ',test_acc2)

 

test acc1:  0.9439999902248383
test acc2:  0.9589999938011169

 

 

对比自己搭建的简单的卷积网络82%左右的测试精度,还是提升很大的。

model = models.Sequential()

model.add(layers.Conv2D(32,(3,3),activation='relu',input_shape=(150,150,3)))

model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Conv2D(64,(3,3),activation='relu'))

model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Conv2D(128,(3,3),activation='relu'))

model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Conv2D(128,(3,3),activation='relu'))

model.add(layers.MaxPooling2D((2,2)))

model.add(layers.Flatten())

model.add(layers.Dense(512,activation='relu'))

model.add(layers.Dropout(0.4))

model.add(layers.Dense(1,activation='sigmoid'))

model.summary()

猜你喜欢

转载自blog.csdn.net/weixin_38712697/article/details/85018847