深度学习一个比较好的原则是使用专家学习得到的预训练网络模型,这里面包括几个概念,特征提取、微调模型、卷积基、卷积块等内容。
想要将深度学习应用于小型图像数据集,一种常用且非常高效的方法是使用预训练网络。 预训练网络(pretrained network)是一个保存好的网络,之前已在大型数据集(通常是大规模图 像分类任务)上训练好。如果这个原始数据集足够大且足够通用,那么预训练网络学到的特征 的空间层次结构可以有效地作为视觉世界的通用模型,因此这些特征可用于各种不同的计算机 视觉问题,即使这些新问题涉及的类别和原始任务完全不同。
VGG16架构,它是一种简单而又广泛使用的卷积神经网络架构。
使用预训练网络有两种方法:特征提取(feature extraction)和微调模型(fine-tuning)。
用于图像分类的卷积神经网络包含两部分:首先是一系列池化层和卷积层,最 后是一个密集连接分类器。第一部分叫作模型的卷积基(convolutional base)。对于卷积神经网 络而言,特征提取就是取出之前训练好的网络的卷积基,在上面运行新数据,然后在输出上面 训练一个新的分类器.
通过VGG16架构训练得到的验证精度达到了约90%,比上一节从头开始训练的小型模型效果要好得多。
代码语言:javascript复制from keras.applications import VGG16
from keras.utils.vis_utils import plot_model
#下载VGG16模型训练数据
conv_base = VGG16(weights='imagenet', include_top=False, input_shape=(150, 150, 3))
#可视化VGG16模型
plot_model(conv_base, to_file='conv_base.png', show_shapes=True)
#打印VGG16模型
print(conv_base.summary())
#1、在数据集上运行卷积基,将输出保存成硬盘中的Numpy 数组,然后用这个数据作为输入,输入到独立的密集连接分类器中
# (与本书第一部分介绍的分类器类似)。这种 方法速度快,计算代价低,因为对于每个输入图像只需运行一次卷积基,
# 而卷积基是目 前流程中计算代价最高的。但出于同样的原因,这种方法不允许你使用数据增强。
# 2、在顶部添加 Dense 层来扩展已有模型(即 conv_base),并在输入数据上端到端地运行 整个模型。
# 这样你可以使用数据增强,因为每个输入图像进入模型时都会经过卷积基。
# 但出于同样的原因,这种方法的计算代价比第一种要高很多。
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
#定义相关路径
base_dir = 'E:/Pycharm/datamine/dataset/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')
#运行 ImageDataGenerator 实例,将图像及其标签提取为Numpy 数组
datagen = ImageDataGenerator(rescale=1. / 255)
batch_size = 20
def extract_features(directory, sample_count):
#定义返回样本和目标
features = np.zeros(shape=(sample_count, 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')
# flow_from_directory(directory): 以文件夹路径为参数,生成经过数据提升/归一化后的数据,在一个无限循环中无限产生batch数据
# directory: 目标文件夹路径,对于每一个类,该文件夹都要包含一个子文件夹.子文件夹中任何JPG、PNG、BNP、PPM的图片都会被生成器使用.详情请查看此脚本
# target_size: 整数tuple,默认为(256, 256). 图像将被resize成该尺寸
# color_mode: 颜色模式,为"grayscale","rgb"之一,默认为"rgb".代表这些图片是否会被转换为单通道或三通道的图片.
# classes: 可选参数,为子文件夹的列表,如['dogs','cats']默认为None. 若未提供,则该类别列表将从directory下的子文件夹名称/结构自动推断。每一个子文件夹都会被认为是一个新的类。(类别的顺序将按照字母表顺序映射到标签值)。通过属性class_indices可获得文件夹名与类的序号的对应字典。
# class_mode: "categorical", "binary", "sparse"或None之一. 默认为"categorical. 该参数决定了返回的标签数组的形式, "categorical"会返回2D的one-hot编码标签,"binary"返回1D的二值标签."sparse"返回1D的整数标签,如果为None则不返回任何标签, 生成器将仅仅生成batch数据, 这种情况在使用model.predict_generator()和model.evaluate_generator()等函数时会用到.
# batch_size: batch数据的大小,默认32
# shuffle: 是否打乱数据,默认为True
# seed: 可选参数,打乱数据和进行变换时的随机数种子
# save_to_dir: None或字符串,该参数能让你将提升后的图片保存起来,用以可视化
# save_prefix:字符串,保存提升后图片时使用的前缀, 仅当设置了save_to_dir时生效
# save_format:"png"或"jpeg"之一,指定保存图片的数据格式,默认"jpeg"
# flollow_links: 是否访问子文件夹中的软链接
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
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)
#目前,提取的特征形状为 (samples, 4, 4, 512)。
# 我们要将其输入到密集连接分类器中, 所以首先必须将其形状展平为 (samples, 8192)。
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
#以定义你的密集连接分类器(注意要使用dropout 正则化)
# 并在刚刚保存的数据 和标签上训练这个分类器
model = models.Sequential()
model.add(layers.Dense(256, activation='relu', input_dim=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))
import matplotlib.pyplot as plt
#训练期间的损失曲线和精度曲线
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc) 1)
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()