摘要
在这篇博客中,您将学习如何使用Keras的ImageDataGenerator类执行数据扩充/增强。另外将介绍什么是数据增强,数据增强的类型,为什么使用数据增强以及它能做什么/不能做什么。
有三种数据增强类型,默认情况下,Keras的ImageDataGenerator该类执行就地/即时数据扩充。
检测到过度拟合的俩种解决方案是(1)减少模型容量或(2)执行正则化。
数据增强是正则化的一种形式,使我们的网络可以更好地将其推广到我们的测试/验证集。
在训练中不应用数据增强会导致过度拟合。应用数据增强,可以进行平滑的训练,避免过度拟合以及拥有更高的准确性/更低的损失。
强烈建议在所有的训练中都使用数据增强。
1. Keras ImageDataGenerator是什么
Keras的ImageDataGenerator在训练卷积神经网络中很常见,是对待训练的数据集执行一系列随机变换后进行训练模型,提高模型的通用性,使得模型具有更好的泛化能力。
在修改后的扩充数据上训练的模型更有可能概括为训练集中未包含的示例数据点。
也可以通过一些简单的几何变换得到增强后的图像,如平移,旋转,放大/缩小,剪切,水平/垂直翻转等;
对输入图像应用少量的转换将稍微改变其外观,但不会更改类标签,从而使数据增强成为适用于计算机视觉任务的非常自然,简便的方法。
2. Keras ImageDataGenerator工作原理
ImageDataGenerator接受原始数据,对其进行随机转换,并仅返回转换后的新数据。
- 接受一批用于训练的图像;
- 进行此批处理并对批处理中的每个图像应用一系列随机变换(包括随机旋转,调整大小,剪切等);
- 用新的,随机转换的批次替换原始批次;
- 在此随机转换的批次上训练CNN(即原始数据本身不用于训练)。
3. Keras ImageDataGenerator的三种类型
- (1)通过数据增强生成数据集和数据扩展(较少见)
这种方法存在一个问题——尚未完全提高模型的泛化能力。
想象一下通过一张图生成100张图然后进行训练;由于所有这些数据均基于超小型数据集。
我们不能期望在少量数据上训练NN,然后期望将其推广到从未训练过且从未见过的数据。
- (2)就地/即时数据增强(最常见)
这种加强方式是使用最普遍的,有俩个地方需要注意:
- ImageDataGenerator 是不是原始数据和变换后的数据都返回——只返回随机变换的数据。
- 因为这种扩充是在训练时完成的,因此称其为“就地”和“即时”数据扩充(即不会在训练之前生成这些示例);
由于训练的时候用的是经过随机平移、旋转、剪切等变换后的数据进行的,因此模型具有了比较好的泛化能力,其在测试集上表现良好,而在训练集上将差一些,由于我们并没有拿原始的训练数据训练,因此具有一定的偏差。
- (3)将数据集生成和就地扩充相结合
在训练数据很少,并且真实的场景数据比较难以收集的情况下,可以用将类型2数据扩充(即就地/即时数据扩充)应用于通过模拟收集的数据。
类似于行为克隆,在自动驾驶应用中有运用。
4. 项目结构
5. 实现generate_images.py, train.py并训练CNN
- generate_images.py 生成数据增强后的数据集
- train.py 并进行不同的数据增强后,进行模型训练
(1)通用1张图像生成100张训练数据,并训练CNN; 50%的准确率
(2)使用 Kaggle狗与猫的数据 集的一个子集,并在不进行数据扩充的情况下训练CNN; 64%的准确率
(3)使用 Kaggle狗与猫的数据 集的一个子集,并在进行数据扩充的情况下训练CNN; 69%的准确率
运用(1)生成的训练精确度/损失图
运用(2)生成的训练精确度/损失图
运用(3)生成的训练精确度/损失图【收敛的比较好,不会有精确度身高,损失也跟着升高的情况,可以完美的避开过度拟合,并且具有比较好的泛化能力】
得出结论:
- 数据增强可以减少过度拟合,并提高模型进行泛化的能力;
- 数据增强是一种正则化形式,保证验证和训练损失如何在几乎没有分歧的情况下下降。同样,训练和验证拆分的分类准确性也一起提高;
- 通过使用数据增强,可以克服过度拟合!
# 测试三种数据增强类型后训练的模型情况
# 第一种试验:通用1张图像生成100张训练数据,进行训练 50%的准确率
# python train.py --dataset generated_dataset --plot plot_generated_dataset.png
# 探讨数据扩充如何通过两次实验来减少过度拟合并提高模型进行泛化的能力,获取到了 64%的准确率,检测到过度拟合的俩种解决方案是(1)减少模型容量或(2)执行正则化。
# 第二种试验:不使用数据扩充
# python train.py --dataset dogs_vs_cats_small --plot plot_dogs_vs_cats_no_aug.png
# 第三种试验:运用数据扩充 研究数据增强如何充当正则化形式 69%的准确率 【注意验证和训练损失如何在几乎没有分歧的情况下下降。同样,训练和验证拆分的分类准确性也一起提高。】
# 通过使用数据增强,我们可以克服过度拟合!
# 强烈建议在任何情况下训练神经网络时都使用 数据增强;
# python train.py --dataset dogs_vs_cats_small --augment 1 --plot plot_dogs_vs_cats_with_aug.png
# 导入必要的包
# 设置matplot为Agg以保存模型训练的plot图到磁盘
import matplotlib
matplotlib.use("Agg")
from pyimagesearch.resnet import ResNet
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
# 导入ImageDataGenerator
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.utils import to_categorical
from imutils import paths
import matplotlib.pyplot as plt
import numpy as np
import argparse
import cv2
import os
# 构建命令行参数
# --dataset 数据集的路径
# --augment 是否使用数据增强方式2(1.通过数据增强生成数据集和数据扩展(较少见) 2.就地/即时数据增强(最常见) 3.将数据集生成和就地扩充相结合 )
# --plot 保存 loss/accuracy 图的路径
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", required=True,
help="path to input dataset")
ap.add_argument("-a", "--augment", type=int, default=-1,
help="whether or not 'on the fly' data augmentation should be used")
ap.add_argument("-p", "--plot", type=str, default="plot.png",
help="path to output loss/accuracy plot")
args = vars(ap.parse_args())
# 初始化初始学习率,批处理大小batchsize,训练的期数epochs
INIT_LR = 1e-1
BS = 8
EPOCHS = 50
# 获取数据集,并把数据,标签按顺序存储在list中
print("[INFO] loading images...")
imagePaths = list(paths.list_images(args["dataset"]))
data = []
labels = []
# 循环遍历图片路径
for imagePath in imagePaths:
# 从文件名中提取分类标签名称,加载图片,忽略宽高比缩放为 64*64
label = imagePath.split(os.path.sep)[-2]
image = cv2.imread(imagePath)
image = cv2.resize(image, (64, 64))
# 更新数据、标签list
data.append(image)
labels.append(label)
# 转换数据、标签list为Numpy array,并将数据的像素强度转换为[0,255]
data = np.array(data, dtype="float") / 255.0
# 编码类标签,由字符串转为integer转为 一键热编码数组(echc:[1,0]代表cats,[0,1]代表dogs)
le = LabelEncoder()
labels = le.fit_transform(labels)
labels = to_categorical(labels, 2)
#分组数据为75%的训练数据,25%的测试数据
(trainX, testX, trainY, testY) = train_test_split(data, labels,
test_size=0.25, random_state=42)
# 初始化数据扩充对象(初始化一个空对象)
aug = ImageDataGenerator()
# 检查是否需要进行数据扩充 --augment参数的值
if args["augment"] > 0:
print("[INFO] performing 'on the fly' data augmentation")
# 随机旋转,缩放,移动,剪切和翻转。(random rotations, zooms, shifts, shears, and flips)
aug = ImageDataGenerator(
rotation_range=20,
zoom_range=0.15,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.15,
horizontal_flip=True,
fill_mode="nearest")
# 初始化优化器和模型
# 构建我们的ResNet,使用随机梯度下降优化和学习率衰减的模型。我们使用“ binary_crossentropy” 2类问题的损失。如果您有两个以上的类标签,请确保使用“ categorial_crossentropy”
print("[INFO] compiling model...")
opt = SGD(lr=INIT_LR, momentum=0.9, decay=INIT_LR / EPOCHS)
model = ResNet.build(64, 64, 3, 2, (2, 3, 4),
(32, 64, 128, 256), reg=0.0001)
model.compile(loss="binary_crossentropy", optimizer=opt,
metrics=["accuracy"])
# 训练模型
# 对象分批处理数据扩充(仅当--augment命令行参数已设置,对象才会执行数据扩充)
print("[INFO] training network for {} epochs...".format(EPOCHS))
H = model.fit(
x=aug.flow(trainX, trainY, batch_size=BS),
validation_data=(testX, testY),
steps_per_epoch=len(trainX) // BS,
epochs=EPOCHS)
# 评估模型
print("[INFO] evaluating network...")
predictions = model.predict(x=testX.astype("float32"), batch_size=BS)
print(classification_report(testY.argmax(axis=1),
predictions.argmax(axis=1), target_names=le.classes_))
# 绘制训练损失/精确度图
N = np.arange(0, EPOCHS)
plt.style.use("ggplot")
plt.figure()
plt.plot(N, H.history["loss"], label="train_loss")
plt.plot(N, H.history["val_loss"], label="val_loss")
plt.plot(N, H.history["accuracy"], label="train_acc")
plt.plot(N, H.history["val_accuracy"], label="val_acc")
plt.title("Training Loss and Accuracy on Dataset")
plt.xlabel("Epoch #")
plt.ylabel("Loss/Accuracy")
plt.legend(loc="lower left")
plt.savefig(args["plot"])
参考: