用于深度学习的图像数据增强

了解什么是图像数据增强以及如何使用 Keras 将其用于深度学习项目

照片由 克里斯·劳顿 on Unsplash

如果您曾经尝试使用深度学习进行图像识别,您就会知道良好的数据集对于训练的重要性。然而,找到足够的图像进行训练并不总是那么容易,并且模型的准确性直接取决于训练数据的质量。

幸运的是,您可以使用一些技术来补充用于训练的图像数据集。其中一种技术称为 图像数据增强。在本文中,我将讨论什么是图像数据增强、它是如何工作的、为什么它在深度学习中有用,以及最后如何使用 Keras 库执行图像数据增强。

图像数据增强 是一种技术 从现有图像创建新图像。为此,您可以对它们进行一些小的更改,例如调整图像的亮度,或旋转图像,或水平或垂直移动图像中的主体。

图像增强技术允许您人为地增加训练集的大小,从而为模型提供更多的数据进行训练。这使您可以通过增强模型识别训练数据新变体的能力来提高模型的准确性。

图像数据增强的类型

图像增强有多种形式,以下是一些常见的形式:垂直移位、水平移位、垂直翻转、水平翻转、旋转、亮度调整和放大/缩小。

我将首先使用 Python 和 Keras 演示各种图像增强技术。如果您想尝试一下,请确保您安装了以下软件和软件包:

安装 Anaconda 和 TensorFlow 后,创建一个新的 Jupyter Notebook。

垂直移位

我想展示的第一个图像增强技术是 垂直移位。 该 垂直移位 随机垂直向上或向下移动图像。对于这个例子,我将使用一个名为 747.jpg,位于与我的 Jupyter Notebook 相同的文件夹中。

图片来源: https://commons.wikimedia.org/wiki/File:Qantas_Boeing_747-438ER_VH-OEI_at_LAX.jpg. 此文件已获得许可 风景 署名-相同方式共享 2.0 通用 许可证。

以下代码片段使用 ImageDataGenerator Keras 中的类用于垂直移动图像。

ImageDataGenerator Keras 中的类通过实时数据增强生成批量图像数据。

#---导入模块---
将numpy导入为np
将matplotlib.pyplot导入为plt
从tensorflow.keras.preprocessing.image导入load_img
从tensorflow.keras.preprocessing.image导入img_to_array
从tensorflow.keras.preprocessing.image导入ImageDataGenerator
#---加载图像---
图片文件名 = '747.jpg'
img = load_img(图像文件名)
#---将图像转换为3D数组---
图像数据 = img_to_array(img)
#---转换为 4D 数组的 1 个元素的 3-D 数组表示
# 图片 - -
images_data = np.expand_dims(image_data, 轴=0)
#---创建图像数据增强生成器---
数据生成 = ImageDataGenerator(width_shift_range=0.2)
#---准备迭代器; flow() 接受 4D 数组并返回
# 包含一批图像的迭代器---
train_generator = datagen.flow(images_data,batch_size = 1)
行= 5
列 = 4
#---绘制5行4列---
图,轴= plt.subplots(行,列)
对于范围(行)中的 r:
对于范围(列)中的 c:
#---获取批次中的下一个图像(自批次以来的一个图像
# 大小为 1)---
image_batch = train_generator.next()

#---转换为无符号整数以便查看---
图像 = image_batch[0].astype('uint8')

#---显示图像---
轴[r,c].imshow(图像)
#---设置图形的大小---
图.set_size_inchs(15,10)

上面的代码片段产生以下输出:

从上面的输出中可以看出,每次调用 next() 的方法 train_generator 物体,你会得到一个稍微改变的图像。在上面的代码片段中,每次调用时都会返回一个基于原始图像高度移动 20% 的新图像 next() 方法:

数据生成 = 图像数据生成器(宽度偏移范围=0.2)

有趣的是,对于这个版本 ImageDataGenerator (tensorflow.keras.preprocessing.image) 类,指定 width_shift_range 参数垂直移动图像,而不是水平移动(这是旧版本的行为ImageDataGenerator来自 keras.preprocessing.image 模块)。同样,如果您希望图像水平移动,则需要使用 height_shift_range 参数(参见下一节)。

请注意 next() 方法将根据需要多次返回增强图像。在上面的代码片段中,我们调用了 20 次(5 行乘以 4 列)。

水平移动

您现在可以尝试使用水平移动图像 height_shift_range 参数:

数据生成 = 图像数据生成器(height_shift_range=0.2)
train_generator = datagen.flow(images_data,batch_size = 1)
行= 5
列 = 4
图,轴= plt.subplots(行,列)
对于范围(行)中的 r:
对于范围(列)中的 c:
image_batch = train_generator.next()
图像 = image_batch[0].astype('uint8')
轴[r,c].imshow(图像)
图.set_size_inchs(15,10)

上面的代码片段产生以下输出:

水平翻转

有时水平翻转图像是有意义的。对于飞机,飞机的前部可能面向左侧或右侧:

数据生成 = 图像数据生成器(水平翻转=真)
train_generator = datagen.flow(images_data,batch_size = 1)
行= 2
列 = 2
图,轴= plt.subplots(行,列)
对于范围(行)中的 r:
对于范围(列)中的 c:
image_batch = train_generator.next()
图像 = image_batch[0].astype('uint8')
轴[r,c].imshow(图像)
图.set_size_inchs(15,10)

对于上面的代码片段,生成四个图像就足够了,因为飞机的前部可能朝左或朝右:

请记住,翻转是随机的(有时您会获得所有四个原始图像,有时您会获得水平翻转的图像)。上面的四张图片很可能都是一样的。如果发生这种情况,只需再次运行该代码块即可。

垂直翻转

就像水平翻转一样,您也可以执行垂直翻转:

数据生成 = 图像数据生成器(垂直翻转=真)
train_generator = datagen.flow(images_data,batch_size = 1)
行= 2
列 = 2
图,轴= plt.subplots(行,列)
对于范围(行)中的 r:
对于范围(列)中的 c:
image_batch = train_generator.next()
图像 = image_batch[0].astype('uint8')
轴[r,c].imshow(图像)
图.set_size_inchs(15,10)

就飞机而言,将飞机颠倒过来可能没有多大意义!如果您尝试执行图像识别,您的飞机图像很可能是直立的,因此训练您的模型来识别颠倒的飞机可能不太常见。对于其他情况,垂直翻转很有意义。

回转

旋转,顾名思义,就是旋转图像。这对于我们的飞机图像非常有用。以下代码片段将图像随机旋转最多 50 度:

数据生成 = 图像数据生成器(旋转范围=50)
train_generator = datagen.flow(images_data)
行= 5
列 = 4
图,轴= plt.subplots(行,列)
对于范围(行)中的 r:
对于范围(列)中的 c:
image_batch = train_generator.next()
图像 = image_batch[0].astype('uint8')
轴[r,c].imshow(图像)
图.set_size_inchs(15,10)

通过旋转,输出显示飞机处于不同位置 - 模拟起飞和着陆位置:

亮度

另一种增强技术是调整图像的亮度。以下代码片段设置亮度偏移值的范围:

数据生成 = 图像数据生成器(亮度范围=[0.15,2.0])
train_generator = datagen.flow(images_data,batch_size = 1)
行= 5
列 = 4
图,轴= plt.subplots(行,列)
对于范围(行)中的 r:
对于范围(列)中的 c:
image_batch = train_generator.next()
图像 = image_batch[0].astype('uint8')
轴[r,c].imshow(图像)
图.set_size_inchs(15,10)

输出包含一系列不同亮度的图像:

缩放

您还可以放大或缩小图像:

数据生成 = 图像数据生成器(缩放范围=[5,0.5])
train_generator = datagen.flow(images_data,batch_size = 1)
行= 5
列 = 4
图,轴= plt.subplots(行,列)
对于范围(行)中的 r:
对于范围(列)中的 c:
image_batch = train_generator.next()
图像 = image_batch[0].astype('uint8')
轴[r,c].imshow(图像)
图.set_size_inchs(15,10)

输出显示不同缩放比例的图像:

请注意,缩放图像会改变图像的纵横比。

结合所有的增强

当然,我到目前为止讨论的所有各种增强技术都可以组合起来:

数据生成 = 图像数据生成器(宽度偏移范围=0.2,
height_shift_range=0.2,
水平翻转=真,
旋转范围=50,
亮度范围=[0.15,2.0],
缩放范围=[5,0.5]
)
train_generator = datagen.flow(images_data,batch_size = 1)行= 8
列 = 8
图,轴= plt.subplots(行,列)
对于范围(行)中的 r:
对于范围(列)中的 c:
image_batch = train_generator.next()
图像 = image_batch[0].astype('uint8')
轴[r,c].imshow(图像)
图.set_size_inchs(15,10)

请注意,我省略了垂直翻转,因为它对于我们的示例没有意义。

输出现在显示应用了各种增强的图像:

前面的部分展示了图像数据增强的基础知识以及如何将其应用于单个图像。在深度学习中,我们经常处理一组图像。现在让我们看看如何将图像增强应用于一组图像。为了便于说明,我假设在包含 Jupyter Notebook 的文件夹中,您有一个 水果 文件夹和以下子文件夹:

水果
|__香蕉
|__banana1.jpg
|__banana2.jpg
|__banana3.jpg
|__ ...
|__榴莲
|__durian1.jpg
|__durian2.jpg
|__durian3.jpg
|__ ...
|__橙子
|__orange1.jpg
|__orange2.jpg
|__orange3.jpg
|__ ...
|__草莓
|__草莓1.jpg
|__草莓2.jpg
|__草莓3.jpg
|__ ...

每个子文件夹包含一组图像。例如, 香蕉 文件夹包含许多名为 香蕉1.jpg, 香蕉2.jpg, 等等。子文件夹的名称将用作各种图像的标签。这意味着该目录下的所有文件 香蕉 文件夹包含香蕉等图像。

要从磁盘加载一系列图像,您现在调用 flow_from_directory() 的方法 ImageDataGenerator 实例而不是 flow() 方法(从内存加载图像):

train_datagen = 图像数据生成器(
水平翻转=真,
垂直翻转=真,
旋转范围=50,
)
批量大小 = 8火车发电机= train_datagen.flow_from_directory(
'。/水果',
目标大小=(224,224),
color_mode='rgb',
批量大小=批量大小,
class_mode='分类',
随机播放=真)

请注意,我现在设置了 batch_size 到 8。您很快就会看到批量大小的使用。

使用返回的迭代器,我可以找到各种水果(香蕉、榴莲、橙子和草莓)的标签:

class_dictionary = train_generator.class_indices#---创建标签字典---
class_dictionary = { value:key 为 key,value 为
class_dictionary.items()}
#---将字典转换为列表---
class_list = [_ 的值,class_dictionary.items() 中的值]
打印(类列表)

您将看到以下输出:

找到属于 54 个类别的 4 张图像。
[‘香蕉’、‘榴莲’、‘橙子’、‘草莓’]

总共 54 个文件夹中共有 4 张图像。另外, class_list 变量包含水果列表。

我现在将打印出由 ImageDataGenerator 班级。我将任意将行设置为 10,并且对于每一行,我想打印出返回的一批图像(在本例中为 8):

行= 10图,轴= plt.subplots(行,batch_size)对于范围(行)中的 r:    
#---获取批量增强图像---
image_batch = train_generator.next()
#---获取返回的图像数量---
images_count = image_batch[0].shape[0]

对于范围内的 c(images_count):
#---转换为无符号整数以便查看---
图像 = image_batch[0][c].astype('uint8')

#---显示图像---
轴[r,c].imshow(图像)

#---显示图像的标签---
轴[r,c].title.set_text(
class_list[np.argmax(image_batch[1][c])])
#---隐藏 x 和 y 刻度---
轴[r,c].set_xticks([])
轴[r,c].set_yticks([])
图.set_size_inchs(15,18)

由于 batch_size 现在设置为 8(不再是 1), train_generator.next() 方法将返回一个 批量 每次调用时都会显示八张增强图像。返回的图像数量基于 batch_size 您之前在 flow_from_directory() 方法:

train_generator = train_datagen.flow_from_directory(
'。/水果',
目标大小=(224,224),
color_mode='rgb',
批量大小=批量大小, # 批量大小 = 8
class_mode='分类',
随机播放=真)

的值 image_batch 变量(由返回 next() method) 是两个元素的元组:

  • 第一个元素(image_batch[0]) 是一个数组 批量大小 图像(4D 阵列)
  • 第二个元素(image_batch[1]) 包含图像的标签

上面的代码片段产生以下输出:

请注意,第七行有两个没有图像的空图表。回想一下,图像集中共有 54 个图像,由于每个批次返回 8 个图像(每行),因此前七行将总共显示 54 个图像 (8×6 + 6)。下图就很清楚了:

请注意,您可以设置 rows 到任何数字和 ImageDataGenerator 课程将继续为您生成新的增强图像。

使用迁移学习构建模型

您现在知道如何使用 ImageDataGenerator 从磁盘加载图像集进行增强。但如何使用它进行训练呢?以下代码片段展示了如何使用构建深度学习模型 转移学习.

迁移学习是一种机器学习方法,其中为一项任务开发的模型被重新用作第二项任务的模型的起点。迁移学习减少了您需要花费在培训上的时间。

从tensorflow.keras.models导入模型
从tensorflow.keras.applications导入VGG16
从tensorflow.keras.layers导入密集,GlobalAveragePooling2D
#---水果数量---
NO_CLASSES = max(train_generator.class_indices.values()) + 1
#---加载VGG16模型作为训练的基础模型---
基础模型 = VGG16(include_top=False, input_shape=(224, 224, 3))
#---添加我们自己的图层---
x = 基础模型.输出
x = GlobalAveragePooling2D()(x)
x = Dense(1024,activation='relu')(x) # 添加密集层
# 该模型可以
# 学习更复杂的
# 函数和
# 更好地分类
# 结果。
x = Dense(1024,activation='relu')(x) # 密集层 2
x = Dense(512,activation='relu')(x) # 密集层 3
preds = 密集(NO_CLASSES,
activate='softmax')(x) # 最后一层
#softmax激活
#---使用基础模型的原始模型创建一个新模型
# 输入和新模型的输出---
模型 = 模型(输入 = base_model.input,输出 = preds)
#---不要训练前 19 层 - 0..18---
对于 model.layers[:19] 中的图层:
层.trainable=False
#---训练其余层 - 19 层开始---
对于 model.layers[19:] 中的层:
层.trainable=True

#---编译模型---
model.compile(优化器='亚当',
损失='分类交叉熵',
指标= ['准确性'])

解释迁移学习的工作原理超出了本文的范围。我将把它留到另一篇文章中。

使用生成的图像进行训练

要使用增强图像进行训练,请传递 train_generatorfit() 模型方法:

#---训练模型---
step_size_train = train_generator.n // train_generator.batch_size
模型.拟合(火车发电机,
steps_per_epoch=step_size_train,
纪元=15)

steps_per_epoch 参数基本上意味着一个时期中有多少步——它取决于您拥有的图像数量和之前定义的批量大小。如果您将其设置为较高的数字,那么您正在进行重复训练。您应该根据以下公式进行设置:

图像数量/批量大小

在我们的示例中,总共有 54 张图像。所以在每个时代, ImageDataGenerator 类将转换所有 54 张图像进行训练。在每个时期,模型都会获得不同的图像变化。如果您有 15 个 epoch,那么总共将生成 15×54 个图像变体并将其输入到训练模型中。

ImageDataGenerator 类允许您的模型在每个时期接收图像的新变体。但请记住,它只返回转换后的图像,不会将其添加到您拥有的图像集中。

我希望本文能让您很好地了解图像数据增强的含义以及为什么在深度学习模型的训练中需要它们。特别是,我使用 TensorFlow 库中的 Keras 模块演示了它。

用于深度学习的图像数据增强从来源重新发布 https://towardsdatascience.com/image-data-augmentation-for-deep-learning-77a87fabd2bf?source=rss—-7f60cf5620c9—4 通过 https://towardsdatascience.com/feed

–>

时间戳记:

更多来自 区块链顾问