目录
定义预处理方法
keras内置的预处理层
自定义keras预处理层
tf.image方法
应用预处理方法
预处理层包含在模型中
预处理应用到数据集
数据增强是通过对训练数据进行随机的变换,增加训练数据多样性的一种方法。对训练数据进行的随机变换最好是可能真实存在的。比如,对训练图片的亮度进行随机变换,因为实际使用中,获取到的图片亮度可能会发生变化。由于数据增强使得训练数据更为多样,通过这种方法训练的模型可以在一定程度上防止过拟合。由于数据增强是对数据的一种变换,也算是数据预处理的一种,后文中提到的数据预处理也可做数据增强理解。
在tensorflow2中定义数据增强(预处理)的方法有2类
- 使用keras的预处理层。包含tf.keras.layers.Resizing(尺寸缩放), tf.keras.layers.Rescaling(数值缩放), tf.keras.layers.RandomFlip(随机翻转), tf.keras.layers.RandomRotation(随机旋转)。还可以自定义预处理层。
- 使用tf.image里面的方法。包含tf.image.flip_left_right(左右翻转),tf.image.rgb_to_grayscale(rgb彩图转成灰度图),tf.image.adjust_brightness(改变亮度),tf.image.central_crop(中心裁剪),tf.image.stateless_random*(随机变换的一类方法)。
keras内置的预处理层都是通过tf.keras.layers里面的类定义的。内置的预处理层有如下19种。
序号 | 预处理层 | 作用 |
1 | tf.keras.layers.CategoryEncoding | 对整型特征进行编码 |
2 | tf.keras.layers.CenterCrop | 裁剪图片 |
3 | tf.keras.layers.Discretication | 对连续特征进行离散化 |
4 | tf.keras.layers.Hashing | hashes and bins categorical features |
5 | tf.keras.layers.IntegerLookup | 将整型特征映射到相邻的区域 |
6 | tf.keras.layers.Normalization | 对连续特征进行归一化 |
7 | tf.keras.layers.RandomBrightness | 训练过程中随机改变亮度 |
8 | tf.keras.layers.RandomContrast | 训练过程中随机改变对比度 |
9 | tf.keras.layers.RandomCrop | 训练过程中随机裁剪图片 |
10 | tf.keras.layers.RandomFlip | 训练过程中随机翻转图片 |
11 | tf.keras.layers.RandomHeight | 训练过程中随机改变图片的高度 |
12 | tf.keras.layers.RandomRotation | 训练过程中随机旋转图片 |
13 | tf.keras.layers.RandomTranslation | 训练过程中随机地上下左右移动图片 |
14 | tf.keras.layers.RandomWidth | 训练过程中随机改变图片的宽度 |
15 | tf.keras.layers.RandomZoom | 训练过程中沿着水平和垂直方向随机zoom(个人理解zoom没有改变图片的长宽比例,resize有可能会改变长宽比例) |
16 | tf.keras.layers.Rescaling | 将输入值变换到新的范围,比如输入数据(0~255)都乘以1/255.0,变换到0~1 |
17 | tf.keras.layers.Resizing | 缩放图片 |
18 | tf.keras.layers.StringLookup | 将字符串特征映射成整数索引 |
19 | tf.keras.layers.TextVectorization | 将文本特征映射成整数序列 |
只在训练过程中生效的预处理层,是指这种层只在model.fit(...)这个训练过程中起作用。在测试和预测,即model.evaluate(...)和model.predict(...)的时候是不生效的。
定义这些内置的预处理层时,就和定义模型很类似。例如:
imgsize = 256
resize_and_rescale = tf.keras.Sequential(
[
tf.keras.layers.Resizing(imgsize,imgsize, name='resize'),
tf.keras.layers.Rescaling(1/255.0, name='rescale')
]
)
augment = tf.keras.Sequential(
[
tf.keras.layers.RandomFlip('horizontal_and_vertical', name='flip'),
tf.keras.layers.RandomBrightness(0.2, name='cg_brightness'),
tf.keras.layers.RandomRotation(0.2, name='rotation')
]
)
原始图片是一些郁金香,如下所示
经过设置预处理层之后, 可以通过这张图片得到很多变换后的图,如下所示。
如果keras内置的预处理层中,缺少自己需要的,可以通过继承tf.keras.layers.Layer类,定义自己的预处理层。比如,自定义一个对图片值取反的预处理层。定义之后,可以把这个使用类似keras内置预处理层一样使用。
def random_invert_img(x,factor=0.5):
if tf.random.uniform([])>factor:
x = 255-x
else:
x
return x
class RandomInvert(tf.keras.layers.Layer):
def __init__(self, factor=0.5, **kwargs):
super().__init__(**kwargs)
self.factor = factor
def call(self, x):
return random_invert_img(x)
aug_and_preprocess = tf.keras.Sequential(
[
tf.keras.layers.RandomFlip('horizontal_and_vertical', name='flip'),
RandomInvert(name='invert'),
tf.keras.layers.Resizing(imgsize,imgsize, name='resize'),
tf.keras.layers.Rescaling(1/255.0, name='rescale')
]
)
与keras中内置的预处理层相比,tf.image包含的预处理方法更为丰富,都是与图像相关的。tf.image中的方法都是函数类型,使用起来比较直接。调用时输入图片及一些必须的参数,就能得到预处理之后的结果。比如使用左右翻转的函数,直接输入image(3D [height,width,channels]或4D [batch,height,width,channels]的tensor类型),就可得到翻转后的图。
flipped = tf.image.flip_left_right(image)
使用tf.image方法对图片进行随机的变换时,需要注意一点。就是tf.image里面的随机变换方法都需要接收一个seed值。如果接收到相同的seed值,变换时改变的量是相同的。比如,使用tf.image.stateless_random_brightness随机改变图片的亮度,这个函数需要接收(image, max_delta(最大变化量的绝对值),seed)。如果进行随机变换时,始终传入相同的seed值,那么变换后的图片亮度会是一样的。
aug1 = tf.image.stateless_random_brightness(image,0.2,[1,1])
aug2 = tf.image.stateless_random_brightness(image,0.2,[1,1])
cmp = (aug1==aug2)
if cmp.numpy().all():
print('True')
所以在使用tf.image的方法进行随机的变换时,需要有一些用于生成不断变化的seed的函数。我们可以使用以下2种方法。
1. 用tf.data.experimental.Counter()实例化一个计数器。计数器每使用一次,值都会增加1,这样可以使得每个数据对应唯一的counter值。将(counter,counter)作为seed,即可获得不同的seed值。
counter = tf.data.experimental.Counter()
mycounter = iter(counter)
for i in range(10):
print(next(mycounter))
'''
tf.Tensor(0, shape=(), dtype=int64)
tf.Tensor(1, shape=(), dtype=int64)
tf.Tensor(2, shape=(), dtype=int64)
tf.Tensor(3, shape=(), dtype=int64)
tf.Tensor(4, shape=(), dtype=int64)
tf.Tensor(5, shape=(), dtype=int64)
tf.Tensor(6, shape=(), dtype=int64)
tf.Tensor(7, shape=(), dtype=int64)
tf.Tensor(8, shape=(), dtype=int64)
tf.Tensor(9, shape=(), dtype=int64)
'''
2. 用 tf.random.Generator.from_seed创建一个初始的seed值生成器,然后每次使用tf.image的随机方法前,在生成器使用make_seeds方法,可以得到新的唯一的seed值。
rng = tf.random.Generator.from_seed(123)
for i in range(10):
seed = rng.make_seeds(2)[0]
print(seed)
'''
tf.Tensor([5919633356954535739 -951714271365450276], shape=(2,), dtype=int64)
tf.Tensor([ -453839084984763860 -5269652775470047458], shape=(2,), dtype=int64)
tf.Tensor([3324081568862093398 338411679161901569], shape=(2,), dtype=int64)
tf.Tensor([ 735933093175394342 7568651755187461736], shape=(2,), dtype=int64)
tf.Tensor([-6599800806376293318 8325175453814895127], shape=(2,), dtype=int64)
tf.Tensor([6538145408201356853 6515059242282190947], shape=(2,), dtype=int64)
tf.Tensor([-1748562438191994256 806493109845881294], shape=(2,), dtype=int64)
tf.Tensor([ 1767612030351502899 -2750390509976653635], shape=(2,), dtype=int64)
tf.Tensor([-3255503542003047460 7984825190919377993], shape=(2,), dtype=int64)
tf.Tensor([-4107093102530856592 -8020944335037123116], shape=(2,), dtype=int64)
'''
应用预处理方法到模型训练有2类实现思路,一种是将预处理层包含在模型中,另一种是将预处理方法直接应用到数据集上。
预处理层包含在模型中,就是把预处理层当作是模型中的层来定义和使用。以mnist分类数据为例,观察一下其使用方法
import os
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds
from tensorflow.keras import layers
# load data
(x_train,y_train),(x_test,y_test) = tf.keras.datasets.mnist.load_data()
# 定义预处理层
resize_and_rescale = tf.keras.Sequential(
[
tf.keras.layers.Rescaling(1/255.0, name='rescale')
]
)
augment = tf.keras.Sequential(
[
tf.keras.layers.RandomFlip('horizontal_and_vertical', name='flip'),
tf.keras.layers.RandomBrightness(0.2, name='cg_brightness'),
tf.keras.layers.RandomRotation(0.2, name='rotation')
]
)
# 定义模型,模型中包含预处理层
model = tf.keras.Sequential(
[
augment,
resize_and_rescale,
layers.Flatten(name='flatten'),
layers.Dense(units=128, activation='relu', name='dense_1'),
layers.Dense(units=10, name='logits')
]
)
# compile and train
model.compile(
optimizer = tf.keras.optimizers.Adam(),
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics = [tf.keras.metrics.SparseCategoricalAccuracy()]
)
model.fit(
x_train,
y_train,
epochs = 5
)
更为常用的方法是把预处理应用到数据集上, 还是以mnist分类为例,注意观察应用到数据集时的map函数。
import os
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
import tensorflow_datasets as tfds
from tensorflow.keras import layers
# load data
(x_train,y_train),(x_test,y_test) = tf.keras.datasets.mnist.load_data()
# 定义预处理层
imgsize = 32
resize_and_rescale = tf.keras.Sequential(
[
tf.keras.layers.Resizing(imgsize,imgsize, name='resize'),
tf.keras.layers.Rescaling(1/255.0, name='rescale')
]
)
augment = tf.keras.Sequential(
[
tf.keras.layers.RandomFlip('horizontal_and_vertical', name='flip'),
tf.keras.layers.RandomBrightness(0.2, name='cg_brightness'),
tf.keras.layers.RandomRotation(0.2, name='rotation')
]
)
# 预处理应用到数据集
train_ds = tf.data.Dataset.from_tensor_slices((np.expand_dims(x_train,-1),y_train)) # numpy array转换成dataset
batch_size = 32
AUTOTUNE = tf.data.AUTOTUNE
def prepare(ds, shuffle=False, aug=False):
# Resize and rescale all datasets
ds = ds.map(lambda x,y: (resize_and_rescale(x),y),
num_parallel_calls=AUTOTUNE)
if shuffle:
ds = ds.shuffle(1000)
# Batch all datasets
ds = ds.batch(batch_size)
# Use data augmentation only on training set
if aug:
ds = ds.map(lambda x,y: (augment(x, training=True), y),
num_parallel_calls = AUTOTUNE)
return ds.prefetch(buffer_size=AUTOTUNE)
train_ds = prepare(train_ds, shuffle=True, aug=True)
# 定义模型,模型中包含预处理层
model = tf.keras.Sequential(
[
layers.Conv2D(filters=16, kernel_size=3, activation='relu', name='conv_1'),
layers.MaxPooling2D(name='pooling_1'),
layers.Flatten(name='flatten'),
layers.Dense(units=128, activation='relu', name='dense_1'),
layers.Dense(units=10, name='logits')
]
)
# compile and train
model.compile(
optimizer = tf.keras.optimizers.Adam(),
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics = [tf.keras.metrics.SparseCategoricalAccuracy()]
)
model.fit(
train_ds,
epochs = 5
)