在训练或评估深度学习神经网络模型时,图像中的像素值必须在将图像作为输入提供给模型之前进行缩放。
传统上,图像必须在模型开发之前进行缩放,并以缩放格式存储在内存或磁盘上。
另一种方法是在训练或模型评估过程中,使用首选的缩放技术及时缩放图像。Keras 通过 ImageDataGenerator 类和 API 支持这种图像数据准备。
在本教程中,您将学习如何使用 ImageDataGenerator 类在拟合和评估深度学习神经网络模型时及时缩放像素数据。
完成本教程后,您将了解:
- 如何为图像的训练、验证和测试数据集配置和使用 ImageDataGenerator 类。
- 如何在拟合和评估卷积神经网络模型时,使用 ImageDataGenerator 对像素值进行归一化。
- 如何在拟合和评估卷积神经网络模型时,使用 ImageDataGenerator 对像素值进行居中和标准化。
通过我的新书《计算机视觉深度学习》启动您的项目,包括逐步教程和所有示例的 Python 源代码文件。
让我们开始吧。

如何使用 Keras 中的 ImageDataGenerator 对图像进行归一化、居中和标准化
照片作者:Sagar,保留部分权利。
教程概述
本教程分为五个部分;它们是:
- MNIST 手写图像分类数据集
- 用于像素缩放的 ImageDataGenerator 类
- 如何使用 ImageDataGenerator 对图像进行归一化
- 如何使用 ImageDataGenerator 对图像进行居中处理
- 如何使用 ImageDataGenerator 对图像进行标准化
MNIST 手写图像分类数据集
在我们深入了解 ImageDataGenerator 类用于准备图像数据的用法之前,我们必须选择一个图像数据集来测试生成器。
MNIST 问题是一个图像分类问题,由 70,000 张手写数字图像组成。
问题的目标是将给定的手写数字图像分类为 0 到 9 之间的整数。因此,它是一个多类图像分类问题。
此数据集作为 Keras 库的一部分提供,可以通过调用 keras.datasets.mnist.load_data() 函数自动下载(如果需要)并加载到内存中。
该函数返回两个元组:一个用于训练输入和输出,一个用于测试输入和输出。例如
1 2 3 |
# 加载 MNIST 数据集的示例 from keras.datasets import mnist (x_train, y_train), (x_test, y_test) = mnist.load_data() |
我们可以加载 MNIST 数据集并总结数据集。完整的示例在下面列出。
1 2 3 4 5 6 7 8 9 10 |
# 加载并总结 MNIST 数据集 from keras.datasets import mnist # 加载数据集 (train_images, train_labels), (test_images, test_labels) = mnist.load_data() # 总结数据集形状 print('训练', train_images.shape, train_labels.shape) print('测试', (test_images.shape, test_labels.shape)) # 总结像素值 print('训练', train_images.min(), train_images.max(), train_images.mean(), train_images.std()) print('测试', test_images.min(), test_images.max(), test_images.mean(), test_images.std()) |
运行该示例首先将数据集加载到内存中。然后报告训练和测试数据集的形状。
我们可以看到所有图像都是 28 x 28 像素,黑白图像有一个通道。训练数据集有 60,000 张图像,测试数据集有 10,000 张。
我们还可以看到像素值是 0 到 255 之间的整数值,并且像素值的平均值和标准差在两个数据集之间相似。
1 2 3 4 |
训练 (60000, 28, 28) (60000,) 测试 ((10000, 28, 28), (10000,)) 训练 0 255 33.318421449829934 78.56748998339798 测试 0 255 33.791224489795916 79.17246322228644 |
我们将使用此数据集来探索 Keras 中使用 ImageDataGenerator 类的不同像素缩放方法。
想通过深度学习实现计算机视觉成果吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
用于像素缩放的 ImageDataGenerator 类
Keras 中的 ImageDataGenerator 类提供了一套技术,用于在建模之前缩放图像数据集中的像素值。
该类将封装您的图像数据集,然后在请求时,它将在训练、验证或评估期间将批量图像返回给算法,并及时应用缩放操作。这提供了一种高效便捷的方法,用于在使用神经网络建模时缩放图像数据。
ImageDataGenerator 类 的用法如下。
- 1. 加载您的数据集。
- 2. 配置 ImageDataGenerator(例如,构造一个实例)。
- 3. 计算图像统计数据(例如,调用 fit() 函数)。
- 4. 使用生成器拟合模型(例如,将实例传递给 fit_generator() 函数)。
- 5. 使用生成器评估模型(例如,将实例传递给 evaluate_generator() 函数)。
ImageDataGenerator 类支持多种像素缩放方法,以及一系列数据增强技术。我们将重点介绍像素缩放技术,而将数据增强方法留待以后讨论。
ImageDataGenerator 类支持的三种主要像素缩放技术如下:
- 像素归一化:将像素值缩放到 0-1 范围。
- 像素居中:将像素值缩放到零均值。
- 像素标准化:将像素值缩放到零均值和单位方差。
像素标准化支持两个级别:每个图像(称为样本级)或每个数据集(称为特征级)。具体来说,标准化像素值所需的均值和/或均值和标准差统计数据只能从每个图像中的像素值(样本级)或整个训练数据集(特征级)中计算。
还支持其他像素缩放方法,例如 ZCA、增亮等,但我们将重点关注这三种最常见的方法。
像素缩放的选择是通过在构造 ImageDataGenerator 实例时指定参数来完成的;例如
1 2 |
# 创建并配置数据生成器 datagen = ImageDataGenerator(...) |
接下来,如果选择的缩放方法需要计算训练数据集的统计数据,那么可以通过调用 fit() 函数来计算并存储这些统计数据。
在评估和选择模型时,通常会在训练数据集上计算这些统计数据,然后将其应用于验证和测试数据集。
1 2 |
# 计算训练数据集上的缩放统计数据 datagen.fit(trainX) |
准备好后,数据生成器可以通过调用 flow() 函数来检索一个返回批次样本的迭代器,并将其传递给 fit_generator() 函数,从而用于拟合神经网络模型。
1 2 3 4 |
# 获取批次迭代器 train_iterator = datagen.flow(trainX, trainy) # 拟合模型 model.fit_generator(train_iterator, ...) |
如果需要验证数据集,可以从相同的数据生成器创建单独的批次迭代器,该迭代器将执行相同的像素缩放操作,并使用在训练数据集上计算的任何所需统计数据。
1 2 3 4 5 6 |
# 获取训练批次迭代器 train_iterator = datagen.flow(trainX, trainy) # 获取验证批次迭代器 val_iterator = datagen.flow(valX, valy) # 拟合模型 model.fit_generator(train_iterator, validation_data=val_iterator, ...) |
模型拟合后,可以通过为测试数据集创建批次迭代器并调用模型上的 evaluate_generator() 函数来评估模型。
同样,将执行相同的像素缩放操作,并根据需要使用在训练数据集上计算的任何统计数据。
1 2 3 4 |
# 获取测试批次迭代器 test_iterator = datagen.flow(testX, testy) # 评估测试数据集上的模型损失 loss = model.evaluate_generator(test_iterator, ...) |
现在我们已经熟悉了如何使用 ImageDataGenerator 类来缩放像素值,下面我们来看一些具体的示例。
如何使用 ImageDataGenerator 对图像进行归一化
ImageDataGenerator 类可用于将像素值从 0-255 范围重新缩放到神经网络模型首选的 0-1 范围。
将数据缩放到 0-1 范围传统上称为归一化。
这可以通过将 rescale 参数设置为一个比率来实现,每个像素乘以该比率即可达到所需的范围。
在这种情况下,比率为 1/255 或约 0.0039。例如
1 2 |
# 创建生成器 (1.0/255.0 = 0.003921568627451) datagen = ImageDataGenerator(rescale=1.0/255.0) |
在这种情况下,ImageDataGenerator 不需要拟合,因为没有需要计算的全局统计数据。
接下来,可以使用生成器为训练和测试数据集创建迭代器。我们将使用 64 的批量大小。这意味着训练和测试图像数据集中的每个数据集都被分成 64 张图像组,这些图像将在从迭代器返回时进行缩放。
我们可以通过打印每个迭代器的长度来查看一个 epoch 中有多少批次,例如,通过训练数据集的一次传递。
1 2 3 4 |
# 准备迭代器以缩放图像 train_iterator = datagen.flow(trainX, trainY, batch_size=64) test_iterator = datagen.flow(testX, testY, batch_size=64) print('训练批次=%d, 测试批次=%d' % (len(train_iterator), len(test_iterator))) |
然后,我们可以通过检索第一批缩放图像并检查最小和最大像素值来确认像素归一化已按预期执行。
1 2 3 |
# 确认缩放是否有效 batchX, batchy = train_iterator.next() print('批次形状=%s, 最小=%.3f, 最大=%.3f' % (batchX.shape, batchX.min(), batchX.max())) |
接下来,我们可以使用数据生成器来拟合和评估模型。我们将定义一个简单的卷积神经网络模型,并在 train_iterator 上拟合五个 epoch,每个 epoch 包含 60,000 个样本,每个批次 64 个样本,大约每个 epoch 938 个批次。
1 2 |
# 使用生成器拟合模型 model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5) |
一旦拟合完成,我们将在测试数据集上评估模型,大约有 10,000 张图像,每批 64 个样本,大约一个 epoch 有 157 个步骤。
1 2 |
_, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0) print('测试准确率: %.3f' % (acc * 100)) |
我们可以将所有这些结合起来;完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# 使用 ImageDataGenerator 归一化图像的示例 from keras.datasets import mnist from keras.utils import to_categorical from keras.models import Sequential 从 keras.layers 导入 Conv2D 从 keras.layers 导入 MaxPooling2D from keras.layers import Dense from keras.layers import Flatten from keras.preprocessing.image import ImageDataGenerator # 加载数据集 (trainX, trainY), (testX, testY) = mnist.load_data() # 将数据集重塑为单通道 width, height, channels = trainX.shape[1], trainX.shape[2], 1 trainX = trainX.reshape((trainX.shape[0], width, height, channels)) testX = testX.reshape((testX.shape[0], width, height, channels)) # 独热编码目标值 trainY = to_categorical(trainY) testY = to_categorical(testY) # 确认像素的比例 print('训练最小=%.3f, 最大=%.3f' % (trainX.min(), trainX.max())) print('测试最小=%.3f, 最大=%.3f' % (testX.min(), testX.max())) # 创建生成器 (1.0/255.0 = 0.003921568627451) datagen = ImageDataGenerator(rescale=1.0/255.0) # 准备迭代器以缩放图像 train_iterator = datagen.flow(trainX, trainY, batch_size=64) test_iterator = datagen.flow(testX, testY, batch_size=64) print('训练批次=%d, 测试批次=%d' % (len(train_iterator), len(test_iterator))) # 确认缩放是否有效 batchX, batchy = train_iterator.next() print('批次形状=%s, 最小=%.3f, 最大=%.3f' % (batchX.shape, batchX.min(), batchX.max())) # 定义模型 model = Sequential() model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(width, height, channels))) model.add(MaxPooling2D((2, 2))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(MaxPooling2D((2, 2))) model.add(Flatten()) model.add(Dense(64, activation='relu')) model.add(Dense(10, activation='softmax')) # 编译模型 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 使用生成器拟合模型 model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5) # 评估模型 _, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0) print('测试准确率: %.3f' % (acc * 100)) |
运行该示例首先报告训练集和测试集上的最小和最大像素值。这证实原始数据确实具有 0-255 范围内的像素值。
接下来,创建数据生成器并准备迭代器。我们可以看到,训练数据集每个 epoch 有 938 个批次,测试数据集每个 epoch 有 157 个批次。
我们从数据集中检索第一个批次,并确认它包含 64 张图像,高度和宽度(行和列)为 28 像素,1 个通道,并且新的最小和最大像素值分别为 0 和 1。这证实了归一化达到了预期的效果。
1 2 3 4 |
训练最小=0.000,最大=255.000 测试最小=0.000,最大=255.000 批次训练=938,测试=157 批次形状=(64, 28, 28, 1), 最小=0.000, 最大=1.000 |
然后,模型在归一化图像数据上进行拟合。训练在 CPU 上花费的时间不长。最后,模型在测试数据集上进行评估,应用相同的归一化。
1 2 3 4 5 6 7 8 9 10 11 |
第 1/5 周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.1841 - 准确率:0.9448 第2/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.0573 - 准确率:0.9826 第3/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.0407 - 准确率:0.9870 第4/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.0299 - 准确率:0.9904 第5/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.0238 - 准确率:0.9928 测试准确率:99.050 |
现在我们已经熟悉了 ImageDataGenerator 的一般用法以及专门用于图像归一化的用法,下面我们来看一下像素居中和标准化的示例。
如何使用 ImageDataGenerator 对图像进行居中处理
另一种流行的像素缩放方法是计算整个训练数据集的平均像素值,然后将其从每张图像中减去。
这称为居中,其作用是将像素值分布居中在零点:也就是说,居中图像的平均像素值将为零。
ImageDataGenerator 类将使用在训练数据集上计算的均值进行的居中称为特征级居中。它要求在缩放之前在训练数据集上计算统计量。
1 2 3 4 |
# 创建用于居中像素值的生成器 datagen = ImageDataGenerator(featurewise_center=True) # 计算训练数据集上的均值 datagen.fit(trainX) |
它与计算每个图像的平均像素值不同,Keras 将其称为样本级居中,并且不需要在训练数据集上计算任何统计数据。
1 2 |
# 创建用于居中像素值的生成器 datagen = ImageDataGenerator(samplewise_center=True) |
本节将演示特征级居中。一旦在训练数据集上计算出统计数据,我们就可以通过访问和打印它来确认其值;例如
1 2 |
# 打印在训练数据集上计算的均值。 print(datagen.mean) |
我们还可以通过计算从批次迭代器返回的图像批次的均值来确认缩放过程是否达到了预期的效果。我们预计均值将是一个接近零的小值,但不是零,因为批次中的图像数量较少。
1 2 3 4 |
# 获取一个批次 batchX, batchy = iterator.next() # 批次中的平均像素值 print(batchX.shape, batchX.mean()) |
更好的检查方法是将批处理大小设置为训练数据集的大小(例如 60,000 个样本),检索一个批处理,然后计算平均值。它应该是一个非常接近零的小值。
1 2 3 4 5 6 |
# 尝试流式传输整个训练数据集 iterator = datagen.flow(trainX, trainy, batch_size=len(trainX), shuffle=False) # 获取一个批次 batchX, batchy = iterator.next() # 批次中的平均像素值 print(batchX.shape, batchX.mean()) |
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# 居中图像数据集的示例 from keras.datasets import mnist from keras.preprocessing.image import ImageDataGenerator # 加载数据集 (trainX, trainy), (testX, testy) = mnist.load_data() # 将数据集重塑为单通道 width, height, channels = trainX.shape[1], trainX.shape[2], 1 trainX = trainX.reshape((trainX.shape[0], width, height, channels)) testX = testX.reshape((testX.shape[0], width, height, channels)) # 报告每个图像的均值 print('均值训练=%.3f, 测试=%.3f' % (trainX.mean(), testX.mean())) # 创建用于居中像素值的生成器 datagen = ImageDataGenerator(featurewise_center=True) # 计算训练数据集上的均值 datagen.fit(trainX) print('数据生成器均值: %.3f' % datagen.mean) # 演示对单个批次样本的影响 iterator = datagen.flow(trainX, trainy, batch_size=64) # 获取一个批次 batchX, batchy = iterator.next() # 批次中的平均像素值 print(batchX.shape, batchX.mean()) # 演示对整个训练数据集的影响 iterator = datagen.flow(trainX, trainy, batch_size=len(trainX), shuffle=False) # 获取一个批次 batchX, batchy = iterator.next() # 批次中的平均像素值 print(batchX.shape, batchX.mean()) |
运行该示例首先报告训练和测试数据集的平均像素值。
MNIST 数据集只有一个通道,因为图像是黑白的(灰度),但如果图像是彩色的,则平均像素值将在训练数据集中的所有图像的所有通道中计算,即每个通道不会有单独的平均值。
ImageDataGenerator 在训练数据集上进行拟合,我们可以确认平均像素值与我们手动计算的值匹配。
检索到一批量居中图像,我们可以确认平均像素值是一个接近零的小值。使用整个训练数据集作为批处理大小重复测试,在这种情况下,缩放数据集的平均像素值是一个非常接近零的数字,证实居中正在产生预期的效果。
1 2 3 4 |
训练均值=33.318,测试均值=33.791 数据生成器均值:33.318 (64, 28, 28, 1) 0.09971977 (60000, 28, 28, 1) -1.9512918e-05 |
我们可以用上一节中开发的卷积神经网络来演示居中。
下面列出了带有特征居中的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# 使用 ImageDataGenerator 居中图像的示例 from keras.datasets import mnist from keras.utils import to_categorical from keras.models import Sequential 从 keras.layers 导入 Conv2D 从 keras.layers 导入 MaxPooling2D from keras.layers import Dense from keras.layers import Flatten from keras.preprocessing.image import ImageDataGenerator # 加载数据集 (trainX, trainY), (testX, testY) = mnist.load_data() # 将数据集重塑为单通道 width, height, channels = trainX.shape[1], trainX.shape[2], 1 trainX = trainX.reshape((trainX.shape[0], width, height, channels)) testX = testX.reshape((testX.shape[0], width, height, channels)) # 独热编码目标值 trainY = to_categorical(trainY) testY = to_categorical(testY) # 创建生成器以居中图像 datagen = ImageDataGenerator(featurewise_center=True) # 计算训练数据集上的均值 datagen.fit(trainX) # 准备迭代器以缩放图像 train_iterator = datagen.flow(trainX, trainY, batch_size=64) test_iterator = datagen.flow(testX, testY, batch_size=64) print('训练批次=%d, 测试批次=%d' % (len(train_iterator), len(test_iterator))) # 定义模型 model = Sequential() model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(width, height, channels))) model.add(MaxPooling2D((2, 2))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(MaxPooling2D((2, 2))) model.add(Flatten()) model.add(Dense(64, activation='relu')) model.add(Dense(10, activation='softmax')) # 编译模型 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 使用生成器拟合模型 model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5) # 评估模型 _, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0) print('测试准确率: %.3f' % (acc * 100)) |
运行该示例会准备 ImageDataGenerator,使用在训练数据集上计算的统计数据来居中图像。
我们可以看到,性能一开始很差,但确实有所改善。居中后的像素值范围大约在 -227 到 227 之间,神经网络通常在输入较小的情况下训练更有效。在实践中,先进行归一化再进行居中会是更好的方法。
重要的是,模型在测试数据集上进行评估,其中测试数据集中的图像是使用在训练数据集上计算的平均值进行居中的。这是为了避免任何数据泄露。
1 2 3 4 5 6 7 8 9 10 11 12 |
批次训练=938,测试=157 第 1/5 周期 938/938 [==============================] - 12s 13ms/步 - 损失:12.8824 - 准确率:0.2001 第2/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:6.1425 - 准确率:0.5958 第3/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.0678 - 准确率:0.9796 第4/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.0464 - 准确率:0.9857 第5/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.0373 - 准确率:0.9880 测试准确率:98.540 |
如何使用 ImageDataGenerator 对图像进行标准化
标准化是一种数据缩放技术,它假设数据分布是高斯分布,并将数据分布转移为均值为零和标准差为一。
具有这种分布的数据被称为标准高斯分布。在训练神经网络时,它可能很有益,因为数据集总和为零,并且输入是大致在 -3.0 到 3.0 范围内的小值(例如,99.7% 的值将落在均值三个标准差之内)。
图像的标准化是通过减去平均像素值并将结果除以像素值的标准差来实现的。
均值和标准差统计量可以在训练数据集上计算,如上一节所述,Keras 将此称为特征级。
1 2 3 4 |
# 特征级生成器 datagen = ImageDataGenerator(featurewise_center=True, featurewise_std_normalization=True) # 计算训练数据集上的均值和标准差 datagen.fit(trainX) |
也可以计算统计数据,然后用于单独标准化每个图像,Keras 将此称为样本级标准化。
1 2 |
# 样本级标准化 datagen = ImageDataGenerator(samplewise_center=True, samplewise_std_normalization=True) |
本节将演示图像标准化的前者或特征级方法。效果将是平均值接近零、标准差接近一的图像批次。
与上一节一样,我们可以通过一些简单的实验来证实这一点。完整的示例在下面列出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# 标准化图像数据集的示例 from keras.datasets import mnist from keras.preprocessing.image import ImageDataGenerator # 加载数据集 (trainX, trainy), (testX, testy) = mnist.load_data() # 将数据集重塑为单通道 width, height, channels = trainX.shape[1], trainX.shape[2], 1 trainX = trainX.reshape((trainX.shape[0], width, height, channels)) testX = testX.reshape((testX.shape[0], width, height, channels)) # 报告像素均值和标准差 print('统计数据训练=%.3f (%.3f), 测试=%.3f (%.3f)' % (trainX.mean(), trainX.std(), testX.mean(), testX.std())) # 创建用于居中像素值的生成器 datagen = ImageDataGenerator(featurewise_center=True, featurewise_std_normalization=True) # 计算训练数据集上的均值 datagen.fit(trainX) print('数据生成器均值=%.3f, 标准差=%.3f' % (datagen.mean, datagen.std)) # 演示对单个批次样本的影响 iterator = datagen.flow(trainX, trainy, batch_size=64) # 获取一个批次 batchX, batchy = iterator.next() # 批次中的像素统计 print(batchX.shape, batchX.mean(), batchX.std()) # 演示对整个训练数据集的影响 iterator = datagen.flow(trainX, trainy, batch_size=len(trainX), shuffle=False) # 获取一个批次 batchX, batchy = iterator.next() # 批次中的像素统计 print(batchX.shape, batchX.mean(), batchX.std()) |
运行该示例首先报告训练集和测试集中像素值的均值和标准差。
然后将数据生成器配置为特征级标准化,并在训练数据集上计算统计数据,这与我们手动计算统计数据时所期望的相符。
然后检索一个包含 64 张标准化图像的批次,我们可以确认这个小样本的均值和标准差接近预期的标准高斯分布。
然后对整个训练数据集重复测试,我们可以确认均值确实是一个非常接近 0.0 的小值,标准差是一个非常接近 1.0 的值。
1 2 3 4 |
统计数据训练=33.318 (78.567),测试=33.791 (79.172) 数据生成器均值=33.318,标准差=78.567 (64, 28, 28, 1) 0.010656365 1.0107679 (60000, 28, 28, 1) -3.4560264e-07 0.9999998 |
现在我们已经确认像素值的标准化正如我们所预期的那样执行,我们可以将像素缩放应用于拟合和评估卷积神经网络模型。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# 使用 ImageDataGenerator 标准化图像的示例 from keras.datasets import mnist from keras.utils import to_categorical from keras.models import Sequential 从 keras.layers 导入 Conv2D 从 keras.layers 导入 MaxPooling2D from keras.layers import Dense from keras.layers import Flatten from keras.preprocessing.image import ImageDataGenerator # 加载数据集 (trainX, trainY), (testX, testY) = mnist.load_data() # 将数据集重塑为单通道 width, height, channels = trainX.shape[1], trainX.shape[2], 1 trainX = trainX.reshape((trainX.shape[0], width, height, channels)) testX = testX.reshape((testX.shape[0], width, height, channels)) # 独热编码目标值 trainY = to_categorical(trainY) testY = to_categorical(testY) # 创建生成器以标准化图像 datagen = ImageDataGenerator(featurewise_center=True, featurewise_std_normalization=True) # 计算训练数据集上的均值 datagen.fit(trainX) # 准备迭代器以缩放图像 train_iterator = datagen.flow(trainX, trainY, batch_size=64) test_iterator = datagen.flow(testX, testY, batch_size=64) print('训练批次=%d, 测试批次=%d' % (len(train_iterator), len(test_iterator))) # 定义模型 model = Sequential() model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(width, height, channels))) model.add(MaxPooling2D((2, 2))) model.add(Conv2D(64, (3, 3), activation='relu')) model.add(MaxPooling2D((2, 2))) model.add(Flatten()) model.add(Dense(64, activation='relu')) model.add(Dense(10, activation='softmax')) # 编译模型 model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy']) # 使用生成器拟合模型 model.fit_generator(train_iterator, steps_per_epoch=len(train_iterator), epochs=5) # 评估模型 _, acc = model.evaluate_generator(test_iterator, steps=len(test_iterator), verbose=0) print('测试准确率: %.3f' % (acc * 100)) |
运行示例配置 ImageDataGenerator 类以标准化图像,仅在训练集上计算所需的统计数据,然后准备训练和测试迭代器以分别拟合和评估模型。
1 2 3 4 5 6 7 8 9 10 11 |
第 1/5 周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.1342 - 准确率:0.9592 第2/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.0451 - 准确率:0.9859 第3/5个周期 938/938 [==============================] - 12s 13ms/步 - 损失:0.0309 - 准确率:0.9906 第4/5个周期 938/938 [==============================] - 13s 13ms/步 - 损失:0.0230 - 准确率:0.9924 第5/5个周期 938/938 [==============================] - 13s 14ms/步 - 损失:0.0182 - 准确率:0.9941 测试准确率:99.120 |
扩展
本节列出了一些您可能希望探索的扩展本教程的想法。
- 颜色。更新示例以使用彩色图像数据集,并确认缩放是在整个图像上执行,而不是按通道执行。
- 样本级。演示图像像素的样本级居中或标准化的示例。
- ZCA 白化。演示使用 ZCA 方法准备图像数据的示例。
如果您探索了这些扩展中的任何一个,我很想知道。
请在下面的评论中发布您的发现。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
API
文章
总结
在本教程中,您学习了如何在拟合和评估深度学习神经网络模型时,使用 ImageDataGenerator 类及时缩放像素数据。
具体来说,你学到了:
- 如何为图像的训练、验证和测试数据集配置和使用 ImageDataGenerator 类。
- 如何在拟合和评估卷积神经网络模型时,使用 ImageDataGenerator 对像素值进行归一化。
- 如何在拟合和评估卷积神经网络模型时,使用 ImageDataGenerator 对像素值进行居中和标准化。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
感谢您的文章。第一个问题,使用样本级或特征级对模型有什么影响?第二个问题,如果我打算将模型用于生产,我是否需要保存使用特征级方法训练模型时的均值和标准差?
从帖子中
“每幅图像(称为样本级)或每个数据集(称为特征级)”
杰森大师级的帖子!谢谢。
无论如何,阅读你的帖子,你说“……像素标准化支持两个级别:每个图像(称为样本级)或每个数据集(称为特征级)……”这与你之前的回答相反!
我的问题是,我以为 ImageDataGenerator 用于数据增强,也就是说,为了模拟我们有更多数据(图像)来训练,并且应用归一化、居中和标准化(重新缩放)是这种更一般的“增强”方法的一部分。无论如何,我困惑的是,验证或测试数据是否必须与训练数据以相同的方式进行预处理。即,显然需要拟合训练数据(带数据增强),但我认为验证和测试数据不需要……我说的对吗?或者我们如何保持验证和测试数据分开)。谢谢
已修正,谢谢。
它既可以用于数据准备,也可以用于数据增强,也可以两者同时进行。在这篇文章中,我试图展示前者的情况。
如果统计数据是跨样本(图像)计算的,则统计数据必须在训练集上计算并用于验证/测试集。
这可以通过几种方式完成,但也许最简单的方法是为每个数据集设置一个单独的实例,但在获取要传递给相关 flow() 函数的迭代器之前,使用相同的训练数据集拟合实例(计算统计数据)。
精彩的文章!
很明显,flow_from_directory 处理来自“目录”的数据,因此在这种情况下,RAM 不会过载。这在利用大型数据集时很有用。
现在,使用图像生成器进行标准化需要先拟合数据,这意味着数据必须加载到内存中,所以
如何处理无法容纳在 RAM 中的大型图像数据集?
在这种情况下,如何在大型训练数据集上计算均值和标准差?
有很多解决方案
– 从较小的样本中估计统计数据
– 使用渐进式加载估计统计数据
– 使用不需要全局统计数据的缩放
谢谢你的回答!🙂 你介意指出在哪里可以找到一些关于所提及解决方案的“如何在 Keras 中……”吗?
您可以将本教程作为起点,并添加额外的配置和测试。
您好,感谢分享宝贵的见解。我对使用 ImageGenerator 进行数据增强有一些疑问。请问如何将旋转、倾斜等增强应用于上述的归一化、居中和标准化?
一旦对训练集应用了 .fit(),请问如何对验证集应用归一化、居中和标准化?
谢谢你
是的,您可以直接指定增强,这里有一个示例
https://machinelearning.org.cn/how-to-configure-image-data-augmentation-when-training-deep-learning-neural-networks/
要创建 3x3 图像网格,我们使用“subplot(行, 列, 索引)”
但在你的例子中你使用了“subplot(330 +1 +i)”
你这段代码是什么意思?
那是旧版 API。两者做相同的事情。
好文章,谢谢!
一个问题——如果我在训练时居中数据,我假设在预测时也必须对图像做同样的事情。如果是这样,最好的方法是什么?
我在其他帖子中看到,从预测图像中减去一个平均值,该平均值是在训练数据集上计算的。如果是这种情况,我如何获得训练数据集的平均值(可能还有标准差)?
是的,请参阅此处的最佳实践
https://machinelearning.org.cn/best-practices-for-preparing-and-augmenting-image-data-for-convolutional-neural-networks/
太棒了!感谢您!
嗨,Jason,
感谢您的精彩文章。我正在尝试了解使用一种技术而不是另一种技术是否有任何原因。您能给我一些指导吗?我正在处理灰度数据。谢谢!
这很难证明。通常我们会效仿他人并取得良好成果,或者我们会测试每种方法并使用效果最好的方法。
看这里
https://machinelearning.org.cn/best-practices-for-preparing-and-augmenting-image-data-for-convolutional-neural-networks/
我们能否对图像同时进行重新缩放,然后进行(样本级居中=True,样本级标准差归一化=True)?哪些技术是互斥的?如果我对图像进行归一化,然后也进行这两种样本级技术,这意味着什么?
是的,但这可能很奇怪。
这是一个很好的问题。也许从一个假设/想法开始,然后测试它是否能改进建模,而不是列举所有缩放方法,会更容易。
感谢您的撰写,非常有教育意义。
不过有一个问题——归一化后,您的卷积层仍然使用 relu 激活函数。正如您在文章中说的,大多数值现在位于 [-3,3] 范围内,这意味着 relu 函数丢失了大约一半的输入。归一化后 tanh 函数不是更适合吗?
此致,
萨尔。
不客气。
通常在实践中效果不佳。你可以自己尝试一下。记住,relu 作用于加权和,而不是原始输入。
感谢您提供的精彩教程!
我一直在思考如何判断是否应该使用标准化。我正在处理一个用于情绪识别的数据集,其中包含少量人脸但样本量很大,使用的是 VGG-Face。
我最好的建议是评估有和没有缩放操作的模型,并比较结果。
如果它能使模型获得更好的技能,就使用它。
如何为验证数据设置流,假设我们可以将所有图像加载到 CPU 内存中
也许这会有帮助。
https://machinelearning.org.cn/how-to-load-large-datasets-from-directories-for-deep-learning-with-keras/
但是图片不在目录中,我是从 cifar10 加载的?
如果您正在使用 cifar10,则可以将所有图像加载到内存中并分割数据集或使用一定比例进行验证。
看这里
https://machinelearning.org.cn/how-to-develop-a-cnn-from-scratch-for-cifar-10-photo-classification/
如何确定中间舞层和最后一层的节点数,
我试图理解输入神经元和输出神经元之间的关系。
使用试错法
https://machinelearning.org.cn/faq/single-faq/how-many-layers-and-nodes-do-i-need-in-my-neural-network
非常感谢您的回复,真的很有帮助 🙂
嗨,Jason先生,
在使用 flow_from_directory 时出现此错误
“ImageDataGenerator 指定了
featurewise_std_normalization
,但尚未在任何训练数据上进行拟合。”有一个 train_datagen.fit(x_train) 步骤,它需要数组中的数据,但我的图像在目录中。我如何实现 ImageDataGenerator 的 featurewise_std_normalization 增强功能?
很抱歉听到这个消息,这可能会有所帮助。
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
当图像数据生成器具有年龄、性别等多个特征时会发生什么?换句话说,当每个图像都与年龄和性别相关联时。
ImageDataGenerator 仅对图像进行操作。
如果您有其他数据,您可能需要设计一个自定义数据生成器,该生成器使用增强图像并保持其他静态数据不变。
数据增强和数据准备方面的精彩见解。它消除了我对 ImageDataGenerator 的一些疑虑。谢谢。我有一个关于 featurewise_std_normalization 和 samplewise_std_normalisation 的问题。如果我错了请纠正我,对于 featurewise_std_normalization,整个图像数据集被假定为具有正态分布,因此在整个数据集上计算均值和标准差并应用于每个像素值。但在 samplewise_std_normalisation 中,它是针对每个图像完成的。我的问题是,为什么要有不同的归一化方法,一个与整个图像数据集相关,另一个与单个图像相关。它们对模型训练有什么具体影响吗?
好问题,您可以在这里找到解释和示例
https://machinelearning.org.cn/how-to-configure-image-data-augmentation-when-training-deep-learning-neural-networks/
/opt/conda/lib/python3.7/site-packages/keras_preprocessing/image/image_data_generator.py:720: UserWarning: 此 ImageDataGenerator 指定了
featurewise_center
,但尚未在任何训练数据上进行拟合。请先通过调用.fit(numpy_data)
进行拟合。warnings.warn('此 ImageDataGenerator 指定了 '
当我提到 feature_wise_center 并进行标准化后,它抛出了这个警告。有什么想法吗?
在使用该方法时,您需要在数据生成器使用之前对其进行拟合。
感谢您的帖子。我想知道是否有任何用于 X 射线图像的预处理方法?
我相信有,这不是我的专业领域,抱歉。我建议查阅相关文献。
嗨,Jason,
不幸的是,tf.keras.preprocessing 现在已被弃用,不推荐用于新代码。您是否考虑更新帖子或创建一篇关于使用 tf.keras.utils 模块和 tf.keras.utils.text_dataset_from_directory 的新帖子?
祝好!
嗨,道格拉斯……您可能会对以下讨论感兴趣
https://lightrun.com/answers/dennybritz-cnn-text-classification-tf-warning-tensorflowcontriblearnpythonlearnpreprocessingtext-is-deprecated-and-will-be-removed