在 TensorFlow 之中進行數據增強
在我們之前的學習之中,我們所使用的數據都是進行一些 “簡單的處理”,比如正則化、歸一化、分批次等基本操作;這些操作都有一些特點,那就是在固定的數據集上進行處理,也就是說這些處理并不會改變數據的數量(甚至可能會減少數據的數量,比如數據篩選)。
那么這節課我們便來學習一下如何在 TensorFlow 之中數據增強,它可以增加數據量,從而可以使用更多樣的數據來訓練模型。
1. 什么是數據增強
關于數據增強,我們可以在 TensorFlow API 之中看到相關的定義:
A technique to increase the diversity of your training set by applying random (but realistic) transformations.
翻譯一下就是:
數據增強是一種通過應用隨機(但現實)的變換來增加訓練集的多樣性的技術。
簡單來說,通過數據增強,我們可以將一些已經存在的數據進行相應的變換(可以選擇將這些變換之后的數據增加到新的原來的數據集之中,也可以直接在原來的數據集上進行變換),從而實現數據種類多樣性的增加。
數據增強常見于圖像領域,因此這節課我們會以圖像處理為例來解釋如何在 TensorFlow 之中進行數據增強。
對于圖片數據,常見的數據增強方式包括:
- 隨機水平翻轉:
- 隨機的裁剪;
- 隨機調整明亮程度;
- 其他方式等。
2. 如何在 TensorFlow 之中進行圖像數據增強
在 TensorFlow 之中進行圖像數據增強的方式主要有兩種:
- 使用 tf.keras 的預處理層進行圖像數據增強;
- 使用 tf.image 進行數據增強。
這兩種各有不同的特點,但是因為我們要采用 tf.keras 進行模型的構建,因此我們重點學習如何使用 tf.keras 的預處理層進行圖像數據增強。
1. 如何使用 tf.keras 的預處理層進行圖像數據增強
使用 tf.keras 的預處理層進行圖像數據增強要使用的最主要的 API 包括在一下包之中:
tf.keras.layers.experimental.preprocessing
在這個包之中,我們最常用的數據增強 API 包括:
- tf.keras.layers.experimental.preprocessing.RandomFlip(mode): 將輸入的圖片進行隨機翻轉,一般我們會取 mode=“horizontal” ,因為這代表水平旋轉;而 mode=“vertical” 則代表隨機進行上下翻轉;
- tf.keras.layers.experimental.preprocessing.RandomRotation§: 按照旋轉角度(單位為弧度) p 將輸入的圖片進行隨機的旋轉;
- tf.keras.layers.experimental.preprocessing.RandomContrast§:按照 P 的概率將輸入的圖片進行隨機的圖像色相翻轉;
- tf.keras.layers.experimental.preprocessing.CenterCrop(height, width):使用 height * width 的大小的裁剪框,在數據的中心進行裁剪。
以上介紹的是我們在數據增強處理之中使用的最多的增強方式,在接下來的學習之中,我們會以該方式為例進行程序的演示。
在使用的過程之中,我們只需要將這些數據增強的網絡層添加到網絡的最底層即可。
2. 使用 tf.image 進行數據增強
使用 tf.image 是 TensorFlow 最原生的一種增強方式,使用這種方式可以實現更多、更加個性化的數據增強。
其中包含的數據增強方式主要包括:
- tf.image.flip_left_right (img):將圖片進行水平翻轉;
- tf.image.rgb_to_grayscale (img):將 RGB 圖像轉化為灰度圖像;
- tf.image.adjust_saturation (image, f):將 image 圖像按照 f 參數進行飽和度的調節;
- tf.image.adjust_brightness (image, f):將 image 圖像按照 f 參數進行亮度的調節;
- tf.image.central_crop (image, central_fraction):按照 p 的比例進行圖片的中心裁剪,比如如果 p 是 0.5 ,那么裁剪后的長、寬就是原來圖像的一半;
- tf.image.rot90 (image):將 image 圖像逆時針旋轉 90 度。
可以看到,很多的 tf.image 數據增強方式并不提供隨機化選項,因此我們需要手動進行隨機化。
也正是因為上述特性,tf.image 數據增強主要用在一些自定義的模型之中,從而可以實現數據增強的自定義化。
3. 使用 tf.keras 的預處理層進行數據增強的實例
在這里,我們仍然采用我們熟悉的貓狗分類的例子來進行程序的演示,我們的代碼和之前的代碼相同,只是我們新增加了兩個數據增強的處理層:
tf.keras.layers.experimental.preprocessing.RandomFlip("horizontal_and_vertical",
input_shape=(Height, Width ,3)),
tf.keras.layers.experimental.preprocessing.RandomRotation(0.2),
其中第一個層表示進行隨機的水平和垂直翻轉,而第二個層表示按照 0.2 的弧度值進行隨機旋轉。
整體的網絡程序為:
import tensorflow as tf
import os
import matplotlib.pyplot as plt
dataset_url = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
path_download = os.path.dirname(tf.keras.utils.get_file('cats_and_dogs.zip', origin=dataset_url, extract=True))
train_dataset_dir = path_download + '/cats_and_dogs_filtered/train'
valid_dataset_dir = path_download + '/cats_and_dogs_filtered/validation'
BATCH_SIZE = 64
TRAIN_NUM = 2000
VALID_NUM = 1000
EPOCHS = 15
Height = 128
Width = 128
train_image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
valid_image_generator = tf.keras.preprocessing.image.ImageDataGenerator(rescale=1./255)
train_data_generator = train_image_generator.flow_from_directory(batch_size=BATCH_SIZE,
directory=train_dataset_dir,
shuffle=True,
target_size=(Height, Width),
class_mode='binary')
valid_data_generator = valid_image_generator.flow_from_directory(batch_size=BATCH_SIZE,
directory=valid_dataset_dir,
shuffle=True,
target_size=(Height, Width),
class_mode='binary')
model = tf.keras.models.Sequential([
tf.keras.layers.experimental.preprocessing.RandomFlip("horizontal_and_vertical",
input_shape=(Height, Width ,3)),
tf.keras.layers.experimental.preprocessing.RandomRotation(0.2),
tf.keras.layers.Conv2D(16, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Conv2D(32, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Conv2D(64, 3, activation='relu'),
tf.keras.layers.MaxPooling2D(),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(512, activation='relu'),
tf.keras.layers.Dense(1)
])
model.compile(optimizer='adam',
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
model.summary()
history = model.fit_generator(
train_data_generator,
steps_per_epoch=TRAIN_NUM // BATCH_SIZE,
epochs=EPOCHS,
validation_data=valid_data_generator,
validation_steps=VALID_NUM // BATCH_SIZE)
acc = history.history['accuracy']
loss=history.history['loss']
val_acc = history.history['val_accuracy']
val_loss=history.history['val_loss']
epochs_ran = range(EPOCHS)
plt.plot(epochs_ran, acc, label='Train Acc')
plt.plot(epochs_ran, val_acc, label='Valid Acc')
plt.show()
plt.plot(epochs_ran, loss, label='Train Loss')
plt.plot(epochs_ran, val_loss, label='Valid Loss')
plt.show()
在訓練結束后,我們可以得到如下結果,而這個結果與我們之前的結果有了一個良好的提升,最高達到了 79% 的準確率,因此我們認為我們的數據增強起到了一定的作用。
Found 2000 images belonging to 2 classes.
Found 1000 images belonging to 2 classes.
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
random_flip_1 (RandomFlip) (None, 128, 128, 3) 0
_________________________________________________________________
random_rotation_1 (RandomRot (None, 128, 128, 3) 0
_________________________________________________________________
conv2d_6 (Conv2D) (None, 126, 126, 16) 448
_________________________________________________________________
max_pooling2d_6 (MaxPooling2 (None, 63, 63, 16) 0
_________________________________________________________________
conv2d_7 (Conv2D) (None, 61, 61, 32) 4640
_________________________________________________________________
max_pooling2d_7 (MaxPooling2 (None, 30, 30, 32) 0
_________________________________________________________________
conv2d_8 (Conv2D) (None, 28, 28, 64) 18496
_________________________________________________________________
max_pooling2d_8 (MaxPooling2 (None, 14, 14, 64) 0
_________________________________________________________________
flatten_2 (Flatten) (None, 12544) 0
_________________________________________________________________
dense_4 (Dense) (None, 512) 6423040
_________________________________________________________________
dense_5 (Dense) (None, 1) 513
=================================================================
Total params: 6,447,137
Trainable params: 6,447,137
Non-trainable params: 0
_________________________________________________________________
Epoch 1/15
31/31 [==============================] - 40s 1s/step - loss: 0.7372 - accuracy: 0.5052 - val_loss: 0.6700 - val_accuracy: 0.5583
......
Epoch 11/15
31/31 [==============================] - 41s 1s/step - loss: 0.5219 - accuracy: 0.8213 - val_loss: 0.5480 - val_accuracy: 0.7900
......
同時我們的程序還會輸出以下兩個圖片:
準確率變化曲線:
損失變化曲線:
4. 小結
通過這節課的學習,我們了解到了什么是數據增強,同時也明白了如何在 TensorFlow 之中進行數據增強(兩種不同的實現方式)。最后我們會很據以前的程序進行改進,得到了一個完整的程序示例。