本文是对图像的深度学习数据增强技术的全面综述
前言
我们都遇到过这种情况:您有一个可以使用机器学习模型实现的机会,但当您打开网络浏览器并搜索相关数据,很有可能,您会发现一个只有几百张图像的数据集。
您记得最受欢迎的数据集的图像以数万(或更多)为单位。您还记得有人提到拥有大数据集对良好性能至关重要。感到失望,你想知道;我的“最先进”神经网络能否通过我拥有的微弱数据来表现良好?
答案是,是的!但是,在我们了解实现这一目标的魔力之前,我们需要反思一些基本问题。
为什么需要大量数据?
参数的数量(million),用于流行的神经网络
当您训练机器学习模型时,您真正在做的是调整其参数,以便它可以将特定输入(例如图像)映射到某些输出(标签)。我们的优化目标是使得我们模型损失达到较低的位置,因此需要您的参数以正确的方式调整。
一般来说,如果您有很多参数,则需要向机器学习模型显示一个比例的示例,以获得良好的性能。此外,您需要的参数数量与模型与执行的任务的复杂性成正比。
如果我没有“更多数据”,如何获得更多数据?
您无需寻找可以添加到数据集中的新型新图像。为什么?因为,一开始,神经网络并不“明智”。例如,训练有素的神经网络会认为下面显示的这三个网球是不同的三张图像。
同一个网球,但平移了
因此,为了获取更多数据,我们只需要对现有数据集进行微小更改。较小的变化,例如翻转,平移或旋转。无论如何,我们的神经网络都会认为这些都是不同的图像。
数据增强
卷积神经网络即使对象放置在不同方向上,也可以对物体进行正确的分类,我们称其属性为不变性。更具体地说,CNN可能是平移、旋转、比例或光照不变性。
从本质上讲,这是数据增强的前提。在现实世界的情况下,我们可能会在有限的条件下拍摄图像的数据集。但是,我们的目标应用可能存在于多种条件下,例如不同的方向、位置、尺寸和亮度等。我们通过训练神经网络的其他合成增强的数据来解决这些情况。
即使我有很多数据,增强也可以提供帮助吗?
是的。它可以帮助增加数据集中的相关数据量。这与神经网络学习的方式有关。
我们假设数据集中有两个类。
左边的一个代表品牌A(福特),右边的一个代表品牌B(雪佛兰)
想象一下,您有一个数据集,该数据集由两个汽车品牌组成,如上所示。假设所有品牌A的汽车都与左侧的图片完全对齐(即所有汽车都面向左侧)。同样,B品牌的所有汽车都与右侧的图片(即面向右图)完全对齐。现在,您将此数据集馈送到“最先进的”神经网络中,并希望一旦受过训练,就能获得良好的分类效果。
福特汽车(品牌A),但面向右侧
假设模型已经完成了训练,您可以输入上面的图像。但是您的神经网络输出了这是B型汽车!
为什么会发生这种情况?是因为大多数机器学习算法的工作方式使然。它找到了将一个类别与另一个类别区分开的最明显差异。在这里,差异是所有Brand A的汽车都面向左侧,Brand B的所有汽车都面向右侧。
您的神经网络仅与您输入的数据一样好!
我们如何防止这种情况发生?我们必须减少数据集中无关的特征 。对于上面的汽车模型分类器,一个简单的解决方案是添加两类汽车的图片,面向我们原始数据集的另一个方向。更好的是,您可以水平地将现有数据集中的图像翻转,以便它们面对另一侧。现在,在训练此新数据集中的神经网络时,您将获得您想要的性能。
通过执行图像增强,可以防止您的神经网络学习“无关紧要”的特征,从而实质上可以提高整体性能。
在我们深入研究各种增强技术之前,我们必须事先考虑一个问题。
我们在哪里执行数据增强?
答案似乎很明显。在将数据输入到模型之前。但是您在这里有两个选择。一种选择是事先执行所有必要的转换,从本质上增加了数据集的大小。另一个选择是在将其输入机器学习模型之前,在 mini-batch 上执行这些变换。
第一个选择称为离线增强。对于相对较小的数据集而言,此方法是优选的,因为您最终将数据集的大小增加了N倍(N等于您执行增强方式的总次数)。
第二个选项称为在线扩展或即时增强。对于较大的数据集而言,此方法是优选的,因为负担不起数据集的爆炸性增加。取而代之的是,在数据输入模型时在 mini-batch 上进行转换。一些机器学习框架对在线扩展有支持,可以在GPU上加速。
常见的增强技术
在本节中,我们介绍了一些普遍使用的基本但功能强大的增强技术。在探索这些技术之前,为简单起见,让我们做一个假设。对于每种技术,我们还指定数据集大小会增加的因素(又称数据增强因子)。
1. 翻转
您可以水平和垂直翻转图像。某些框架不为垂直翻转提供功能。但是,垂直翻转等同于将图像旋转180度,然后进行水平翻转。以下是翻转图像的示例。
1:原始图像 2:水平翻转的图像 3:垂直翻转的图像
您可以使用以下任何命令执行翻转。数据增强因子 = 2~4
# NumPy.'img' = A single image.
flip_1 = np.fliplr(img)
# TensorFlow. 'x' = A placeholder for an image.
shape = [height, width, channels]
x = tf.placeholder(dtype = tf.float32, shape = shape)
flip_2 = tf.image.flip_up_down(x)
flip_3 = tf.image.flip_left_right(x)
flip_4 = tf.image.random_flip_up_down(x)
flip_5 = tf.image.random_flip_left_right(x)
2. 旋转
关于此操作要注意的一个关键点是:旋转后可能无法保留图像尺寸。如果您的图像是正方形,则将其直角旋转将保留图像尺寸。如果是矩形,则将其旋转180度可以保留原始大小,但是其他角度旋转图像可能并不能保持原始图像的所有像素。我们将在下一节中了解如何处理此问题。以下是正方形图像以直角旋转的示例。
1:原始图像 2:旋转90° 3:旋转180° 4:旋转270°
您可以使用以下任何命令执行旋转。数据增强因子 = 2~4
# Placeholders: 'x' = A single image, 'y' = A batch of images
# 'k' denotes the number of 90 degree anticlockwise rotations
shape = [height, width, channels]
x = tf.placeholder(dtype = tf.float32, shape = shape)
rot_90 = tf.image.rot90(img, k=1)
rot_180 = tf.image.rot90(img, k=2)
# To rotate in any angle. In the example below, 'angles' is in radians
shape = [batch, height, width, 3]
y = tf.placeholder(dtype = tf.float32, shape = shape)
rot_tf_180 = tf.contrib.image.rotate(y, angles=3.1415)
# Scikit-Image. 'angle' = Degrees. 'img' = Input Image
# For details about 'mode', checkout the interpolation section below.
rot = skimage.transform.rotate(img, angle=45, mode='reflect')
3. 缩放
图像可以向外缩放或向内缩放。在向外扩展时,最终图像大小将大于原始图像大小。大多数图像框架从新图像中切出一个部分,其大小等于原始图像。我们将在下一部分中处理向内缩放,因为它会降低图像大小,从而迫使我们对边界以外的内容做出假设。以下是向外缩放示例。
1:原始图像 2:图像向外缩放10% 3:图像向外缩放20%
您可以使用scikit-image以下命令来执行缩放。数据增强因子 = 任意
# Scikit Image. 'img' = Input Image, 'scale' = Scale factor
# For details about 'mode', checkout the interpolation section below.
scale_out = skimage.transform.rescale(img, scale=2.0, mode='constant')
scale_in = skimage.transform.rescale(img, scale=0.5, mode='constant')
# Don't forget to crop the images back to the original size (for
# scale_out)
4. 裁剪
与缩放不同,我们只是从原始图像中随机裁剪部分图像。然后,我们将此部分大小调整到原始图像大小。该方法通常称为随机裁剪。以下是随机裁剪的示例。如果仔细观察,您可以注意到此方法和缩放之间的区别。
1:原始图像 2:从左上角裁剪出一个正方形部分 3:然从右下角裁剪了一个方形部分
(裁剪的部分调整为原始图像尺寸)
您可以使用TensorFlow以下命令执行裁剪。数据增强因子 = 任意
# TensorFlow. 'x' = A placeholder for an image.
original_size = [height, width, channels]
x = tf.placeholder(dtype = tf.float32, shape = original_size)
# Use the following commands to perform random crops
crop_size = [new_height, new_width, channels]
seed = np.random.randint(1234)
x = tf.random_crop(x, size = crop_size, seed = seed)
output = tf.images.resize_images(x, size = original_size)
5. 平移
平移仅涉及将图像沿x或y方向(或两者都移动)。在下面的示例中,我们假设图像具有超出其边界的黑色背景,并且经过适当平移。这种增强方法非常有用,因为大多数对象都可以位于图像中的任何地方。
1:原始图像 2:向右平移 3:向上平移
您可以使用TensorFlow以下命令执行平移。数据增强因子 = 任意
# pad_left, pad_right, pad_top, pad_bottom denote the pixel
# displacement. Set one of them to the desired value and rest to 0
shape = [batch, height, width, channels]
x = tf.placeholder(dtype = tf.float32, shape = shape)
# We use two functions to get our desired augmentation
x = tf.image.pad_to_bounding_box(x, pad_top, pad_left, height + pad_bottom + pad_top, width + pad_right + pad_left)
output = tf.image.crop_to_bounding_box(x, pad_bottom, pad_right, height, width)
6. 高斯噪声
当您的神经网络试图学习高频特征时,通常会发生过拟合。高斯噪声的平均值为零,本质上具有所有频率的数据点,从而有效地扭曲了高频特征。这也意味着较低的频率特征(通常,您的预期数据)也会扭曲。添加适量的噪声可以增强学习能力。
1:原始图像 2:高斯噪声 3:椒盐噪声和胡椒噪声
您可以使用TensorFlow以下命令添加高斯噪声。数据增强因子 = 2
#TensorFlow. 'x' = A placeholder for an image.
shape = [height, width, channels]
x = tf.placeholder(dtype = tf.float32, shape = shape)
# Adding Gaussian noise
noise = tf.random_normal(shape=tf.shape(x), mean=0.0, stddev=1.0,
dtype=tf.float32)
output = tf.add(x, noise)
· END ·
HAPPY LIFE