数据增强的作用
深度学习有3个核心要素,分别是:
- 优秀的算法设计
- 高性能的计算能力
- 大数据
因此在我们拥有优秀的算法设计和高性能的计算能力的同时,我们也需要大量的高质量数据。
但是,对于个人,学校团队甚至普通的工程师团队来说,数据的搜集能力都是十分有限的。缺乏大量高质量的训练样本,便难以训练处一个具有很好泛化能力的模型。因此,我们就需要一些数据集扩充的方法,即我们常说的数据增强。
数据增强的概述
对于图像处理的场景,数据增强自然是对图片而言。数据增强就是指对图片数据进行变换、修改,使其变成新的图片数据,从而扩充我们的图片数据集。
图片数据增强的方法主要包括以下几个:
- 随机裁剪
- 颜色改变
- 水平或竖直翻转
- 随机改变大小
- 加入噪声
- 对图片进行仿射变换
通过使用数据增强的方法来实现数据集的扩充,可以使训练后的模型具有更好的鲁棒性,尤其是对噪声的抵抗能力得到强化,进而提高训练后模型的泛化能力。
鲁棒性:
1.模型具有较高的精度或有效性,这也是对于机器学习中所有学习模型的基本要求;
2.对于模型假设出现的较小偏差,只能对算法性能产生较小的影响; 主要是:噪声(noise)
3.对于模型假设出现的较大偏差,不可对算法性能产生“灾难性”的影响;主要是:离群点(outlier)
泛化能力
在机器学习方法中,泛化能力通俗来讲就是指学习到的模型对未知数据的预测能力。在实际情况中,我们通常通过测试误差来评价学习方法的泛化能力。如果在不考虑数据量不足的情况下出现模型的泛化能力差,那么其原因基本为对损失函数的优化没有达到全局最优。
Keras实现数据增强
Keras为我们提供的数据增强功能是通过ImageDataGenerator类来实现的。ImageDataGenerator类主要通过生成器(generator)的方式来产生经过变化的新图片。生成器是Python提供的一个高级特性,对于list这样的容器,其中的元素是预先存储在内存中的。而生成器的特点是其中的一个元素一边生成,一边使用。换句话说,当我们遍历list时,遍历的是已经存在于list中的元素,而生成器中的元素是在遍历的过程中同时生成的,这是一种流式的效果。
keras.preprocessing.image.ImageDataGenerator(
featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False,
zca_epsilon=1e-6,
rotation_range=0,
width_shift_range=0.,
height_shift_range=0.,
brightness_range=None,
shear_range=0.,
zoom_range=0.,
channel_shift_range=0.,
fill_mode='nearest',
cval=0.,
horizontal_flip=False,
vertical_flip=False,
rescale=None,
preprocessing_function=None,
data_format='channels_last',
validation_split=0.0,
interpolation_order=1,
dtype='float32')
- featurewise_center:布尔值,是否将输入数据的均值设置为0。
- samplewise_center:布尔值,是否将每个样本的均值设置为0。
- featurewise_std_normalization:布尔值,是否将输入特征除以其标准值。
- samlewise_std_normalization:布尔值,是否将每个输入样本除以其标准差。
- zca_epsilon:ZCA白化的ε值,默认为1e-6。
- zca_whitening:布尔值,决定是否应用ZCA白化的方式进行数据增强。
- rotation_range:整数值,决定随机旋转的度数。
- width_shift_range:浮点数、整数或者其数组,水平位置平移值。
- height_shift_range:浮点数、整数或者其数组,垂直位置平移值。
- shear_range:浮点数,图片剪切的强度。
- zoom_range:浮点数或者一个区间,表示随机缩放的范围,形如[0.1, 0.2],分别表示其上界和下界。
- channel_shift_range:浮点数,随机通道偏移的幅度。
- fill_mode:“constant” “nearest” “reflect”或“wrap”之一,默认为“nearest”。当进行变换时,超出边界的点将根据本参数给定的方法进行处理。假设填充序列“abcd”两侧的数值,其中每个参数的填充方式如下。
- constant:kkkkkkkk|abcd|kkkkkkkk(假设cval参数值为k时)
- nearest:aaaaaaaa|abcd|dddddddd
- reflect:abcddcba|abcd|dcbaabcd
- wrap:abcdabcd|abcd|abcdabcd
- cavl:浮点数或整型,详见fill_mode参数;
- horizontal_flip:布尔值,是否随机水平翻转;
- vertical_flip:布尔值,是否随机垂直翻转;
- rescale:重缩放因子的数值,将数据乘以所提供的参数值。如果不指定该参数则默认为None。如果是None或0,则不进行缩放;
- preprocessing_function:指定一个回调函数,这个函数会在其它任何操作之前运行。这个函数需要一个参数——图片数据(秩为3的Numpy张量)
- data_format:图像数据格式。
- validation_split:是一个在[0, 1]区间的浮点数,表示保留用于验证的图像比例。
keras.preprocessing.image.ImageDataGenerator.flow( x,
y=None,
batch_size=32,
shuffle=True,
sample_weight=None,
seed=None,
save_to_dir=None,
save_prefix='',
save_format='png',
subset=None):
接收numpy数组和标签为参数,生成经过数据提升或标准化后的batch数据,并在一个无限循环中不断的返回batch数据
-
x:样本数据,秩应为4.在黑白图像的情况下channel轴的值为1,在彩色图像情况下值为3
-
y:标签
-
batch_size:整数,默认32
-
shuffle:布尔值,是否随机打乱数据,默认为True
-
save_to_dir:None或字符串,该参数能让你将提升后的图片保存起来,用以可视化
-
save_prefix:字符串,保存提升后图片时使用的前缀, 仅当设置了save_to_dir时生效
-
save_format:“png"或"jpeg"之一,指定保存图片的数据格式,默认"jpeg”
-
yields:形如(x,y)的tuple,x是代表图像数据的numpy数组.y是代表标签的numpy数组.该迭代器无限循环.
-
seed: 整数,随机数种子
使用示例:
注:本示例是在pycharm下运行,在flow()方法中需要设置正确的路径。
import numpy as np
from keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img
datagen = ImageDataGenerator(
rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, rescale=1 / 255,
shear_range=20, zoom_range=0.2, horizontal_flip=True, fill_mode='nearest')
img = load_img('demo.jpg')
x = img_to_array(img)
# 扩展数组的形状
x = np.expand_dims(x, 0)
# 生成20张图片
i = 0
for batch in datagen.flow(x, batch_size=1, save_to_dir='genreate_images',
save_prefix='new',
save_format='jpg'):
i += 1
if i == 20:
break
print('image generate finished!')
自己实现数据增强
import cv2
import numpy as np
import scipy.ndimage as ndi
def do_random_crop_and_rotation(image_array, translation_factor, zoom_range):
height = image_array.shape[0]
width = image_array.shape[1]
x_offset = np.random.uniform(0, translation_factor * width)
y_offset = np.random.uniform(0, translation_factor * height)
offset = np.array([x_offset, y_offset])
scale_factor = np.random.uniform(zoom_range[0], zoom_range[1])
crop_matrix = np.array([[scale_factor, 0], [0, scale_factor]])
image_array = np.rollaxis(image_array, axis=-1, start=0)
# ndi.interpolation 插值
image_channel =[ndi.interpolation.affine_transform(image_channel, crop_matrix, offset=offset,
order=0, mode='nearest',
cval=0.0) for image_channel in image_array]
image_array = np.stack(image_channel, axis=0)
image_array = np.rollaxis(image_array, 0, 3)
return image_array
if __name__ == '__main__':
raw_img = cv2.imread('demo.jpg')
cropped_and_rotated_img = do_random_crop_and_rotation(raw_img, 0.3, [0.75, 1.25])
cv2.imwrite('new_demo.jpg', cropped_and_rotated_img)
该函数实现的事仿射变换,利用仿射变换对图片进行旋转与裁剪。