使用预训练的卷积神经网络学习猫狗分类
在Python 深度学习–学习笔记(七)中,我们从零构建模型并进行学习。增加了增强学习,训练了100轮后,准确率还是没到80%。这很大程度上因为学习的数据不够多。所以,今天我们使用预训练的神经网络。预训练网络(pretrained network)是一个保存好的网络,之前已在大型数据集(通常是大规模图 像分类任务)上训练好。 本次采用的训练神经网络已在ImageNet上训练了140万标记图片,1000个不同的类别。
我们将使用VGG16架构。
- 首先,我们先导入架构。
这里注意,我们只需要VGG16中的卷积层
from keras.applications import VGG16
conv_base = VGG16(weights='imagenet',
include_top=False,
#指定模型最后是否包含密集连接分类器,默认情况下,这个密集连接分类器对应于 ImageNet 的 1000 个类别
input_shape=(150,150,3))
#规定网络处理的输入大小
conv_base.summary()
密集层分类器输出的是1000个类别,而我们只需要猫狗分类,所以,我们不要VGG16 的密集连接分类器,而需要自己定义。
VGG16的结构是:
输入层 -> 两次卷积一次池化 -> 两次卷积一次池化 -> 三次卷积一次池化 -> 三次卷积一次池化 -> 三次卷积一次池化
VGG结构基于 3x3的小型卷积核的卷积层运算,卷积层重叠2次到4次,再通过池化层将大小减半,最后经由全连接层输出结果.
不使用数据增强的快速特征提取
思路:分两步。
第一步,将数据经过VGG16的卷积层后提取输出。
第二步,再在自己定义的模型下学习,分类。
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
base_dir = './cats_and_dogs_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')
#文件夹定位
datagen = ImageDataGenerator(rescale=1./255)
batch_size = 20
def extract_features(directory,sample_count):
features = np.zeros(shape=(sample_count,4,4,512))
#卷积最后一层输出大小为(none,4,4,512)
labels = np.zeros(shape=(sample_count))
# 一张图一个标签
generator = datagen.flow_from_directory(
directory,
target_size=(150,150),
batch_size=batch_size,
class_mode='binary'
)
i = 0
for inputs_batch,labels_batch in generator:
features_batch = conv_base.predict(inputs_batch)
features[i * batch_size : (i + 1) * batch_size] = features_batch
labels[i * batch_size : (i + 1) * batch_size] = labels_batch
i += 1
if i * batch_size >= sample_count:
break
return features, labels
#获得从VGG16最后的卷积层输出的数据
- 准备好提取器后,开始提取处理数据
train_features, train_labels = extract_features(train_dir,2000)
validation_features, validation_labels = extract_features(validation_dir,1000)
test_features, test_labels = extract_features(test_dir, 1000)
train_features = np.reshape(train_features,(2000,4*4*512))
validation_features = np.reshape(validation_features,(1000,4*4*512))
test_features = np.reshape(test_features,(1000,4*4*512))
- 在自己定义的模型下学习
from keras import models
from keras import layers
from keras import optimizers
model = models.Sequential()
model.add(layers.Dense(256,activation='relu',input_dim=4*4*512))
#或input_shape=(4*4*512,)
model.add(layers.Dropout(0.5))
#抑制过拟合
model.add(layers.Dense(1,activation='sigmoid'))
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
loss='binary_crossentropy',
metrics=['acc'])
history = model.fit(train_features,train_labels,
epochs=30,
batch_size=20,
validation_data=(validation_features,validation_labels))
可以看出,在训练模型下,准确度达到0.9,得到质的飞跃!
- 数据可视化(省略代码)
使用数据增强的特诊提取
思路,直接嵌入自己定义的模型底层,将数据输入,从端到端的学习。
缺点:学习速度慢,计算代价高。
- 导入模型
from keras.applications import VGG16
conv_base = VGG16(weights='imagenet',
include_top=False,
#指定模型最后是否包含密集连接分类器,默认情况下,这个密集连接分类器对应于 ImageNet 的 1000 个类别
input_shape=(150,150,3))
#规定网络处理的输入大小
conv_base.trainable = False
特别注意,在编译和训练模型之前,一定要“冻结”卷积基(conv_base.trainable = False)。
解释:冻结一个或多个层是指在训练过程中保持其权重不变。
原因:如果不这么做,那么卷积基之前学到的表示将会在训练过程中被修改。因为其上添加的 Dense 层是随机初始化的,所以非常大的权重更新将会在网络中传播,对之前学到的表示造成很大破坏。
- 定义模型
from keras import models
from keras import layers
model = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256,activation='relu'))
model.add(layers.Dense(1,activation='sigmoid'))
model.summary()
- 创建数据增强器
from keras.preprocessing.image import ImageDataGenerator
from keras import optimizers
import os
base_dir = './cats_and_dogs_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')
# Note that the validation data should not be augmented!
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
# This is the target directory
train_dir,
# All images will be resized to 150x150
target_size=(150, 150),
batch_size=20,
# Since we use binary_crossentropy loss, we need binary labels
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
- 编译模型
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=2e-5),
metrics=['acc'])
- 训练模型
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=30,
validation_data=validation_generator,
validation_steps=50)
model.save('cats_and_dogs_small_3.h5')
这种方法的准确率与第一种方法的变化不大,但第二种方法的增强学习能有效抑制过拟合。
训练的模型相当久!博主用CPU训练了整整10小时!!!
本方法计算代价很高,只在有 GPU 的情况下才能尝试运行。它在 CPU 上是绝对难以运行的。如果你无法在 GPU 上运行代码,那么就采用第一种方法。
- 最后数据可视化(省略代码)