作者: lukewood
建立日期 2022/04/08
最後修改日期 2022/04/08
說明:使用 KerasCV 使用 CutMix、MixUp、RandAugment 等方法增強影像。
KerasCV 可以輕鬆地為影像分類和物件偵測任務組裝最先進的、產業級的資料增強流程。KerasCV 提供了廣泛的預處理層,實作了常見的資料增強技術。
其中三個最實用的圖層可能是 keras_cv.layers.CutMix
、keras_cv.layers.MixUp
和 keras_cv.layers.RandAugment
。這些圖層幾乎用於所有最先進的圖像分類管道中。
本指南將向您展示如何將這些圖層組合到您自己的圖像分類任務數據增強管道中。本指南還將引導您完成自訂 KerasCV 數據增強管道的過程。
KerasCV 使用 Keras 3 來處理任何 TensorFlow、PyTorch 或 Jax。在下面的指南中,我們將使用 jax
後端。本指南可以在 TensorFlow 或 PyTorch 後端中以零更改運行,只需更新下面的 KERAS_BACKEND
。
!pip install -q --upgrade keras-cv
!pip install -q --upgrade keras # Upgrade to Keras 3.
我們首先導入所有需要的套件
import os
os.environ["KERAS_BACKEND"] = "jax" # @param ["tensorflow", "jax", "torch"]
import matplotlib.pyplot as plt
# Import tensorflow for [`tf.data`](https://tensorflow.dev.org.tw/api_docs/python/tf/data) and its preprocessing map functions
import tensorflow as tf
import tensorflow_datasets as tfds
import keras
import keras_cv
本指南使用 102 類別花卉數據集 進行演示。
首先,我們加載數據集
BATCH_SIZE = 32
AUTOTUNE = tf.data.AUTOTUNE
tfds.disable_progress_bar()
data, dataset_info = tfds.load("oxford_flowers102", with_info=True, as_supervised=True)
train_steps_per_epoch = dataset_info.splits["train"].num_examples // BATCH_SIZE
val_steps_per_epoch = dataset_info.splits["test"].num_examples // BATCH_SIZE
Downloading and preparing dataset 328.90 MiB (download: 328.90 MiB, generated: 331.34 MiB, total: 660.25 MiB) to /usr/local/google/home/rameshsampath/tensorflow_datasets/oxford_flowers102/2.1.1...
Dataset oxford_flowers102 downloaded and prepared to /usr/local/google/home/rameshsampath/tensorflow_datasets/oxford_flowers102/2.1.1. Subsequent calls will reuse this data.
接下來,我們將圖像調整為固定大小 (224, 224)
,並對標籤進行 one-hot 編碼。請注意,keras_cv.layers.CutMix
和 keras_cv.layers.MixUp
預計目標是 one-hot 編碼的。這是因為它們修改目標值的方式無法通過稀疏標籤表示來實現。
IMAGE_SIZE = (224, 224)
num_classes = dataset_info.features["label"].num_classes
def to_dict(image, label):
image = tf.image.resize(image, IMAGE_SIZE)
image = tf.cast(image, tf.float32)
label = tf.one_hot(label, num_classes)
return {"images": image, "labels": label}
def prepare_dataset(dataset, split):
if split == "train":
return (
dataset.shuffle(10 * BATCH_SIZE)
.map(to_dict, num_parallel_calls=AUTOTUNE)
.batch(BATCH_SIZE)
)
if split == "test":
return dataset.map(to_dict, num_parallel_calls=AUTOTUNE).batch(BATCH_SIZE)
def load_dataset(split="train"):
dataset = data[split]
return prepare_dataset(dataset, split)
train_dataset = load_dataset()
讓我們檢查數據集中的某些樣本
def visualize_dataset(dataset, title):
plt.figure(figsize=(6, 6)).suptitle(title, fontsize=18)
for i, samples in enumerate(iter(dataset.take(9))):
images = samples["images"]
plt.subplot(3, 3, i + 1)
plt.imshow(images[0].numpy().astype("uint8"))
plt.axis("off")
plt.show()
visualize_dataset(train_dataset, title="Before Augmentation")
太好了!現在我們可以進入增強步驟。
RandAugment 已被證明可以在眾多數據集中提供改進的圖像分類結果。它對圖像執行一組標準的增強。
要在 KerasCV 中使用 RandAugment,您需要提供一些值
value_range
描述圖像中涵蓋的值範圍magnitude
是介於 0 到 1 之間的值,描述應用擾動的強度augmentations_per_image
是一個整數,告訴圖層對每個單獨圖像應用多少次增強magnitude_stddev
允許從標準差為 magnitude_stddev
的分佈中隨機採樣 magnitude
rate
表示在每一層應用增強的概率。您可以在 RandAugment
API 文檔 中閱讀有關這些參數的更多信息。
讓我們使用 KerasCV 的 RandAugment 實現。
rand_augment = keras_cv.layers.RandAugment(
value_range=(0, 255),
augmentations_per_image=3,
magnitude=0.3,
magnitude_stddev=0.2,
rate=1.0,
)
def apply_rand_augment(inputs):
inputs["images"] = rand_augment(inputs["images"])
return inputs
train_dataset = load_dataset().map(apply_rand_augment, num_parallel_calls=AUTOTUNE)
最後,讓我們檢查一些結果
visualize_dataset(train_dataset, title="After RandAugment")
嘗試調整強度設置以查看更多種類的結果。
CutMix
和 MixUp
允許我們生成類間範例。 CutMix
隨機裁剪一張圖像的部分並將其放置在另一張圖像上,而 MixUp
在兩張圖像之間插值像素值。這兩種方法都可以防止模型過度擬合訓練分佈,並提高模型泛化到分佈外範例的可能性。此外,CutMix
可以防止您的模型過度依賴任何特定特徵來執行其分類。您可以在他們各自的論文中閱讀有關這些技術的更多信息
在本例中,我們將在手動創建的預處理管道中獨立使用 CutMix
和 MixUp
。在最先進的管道中,圖像通常通過 CutMix
、MixUp
或兩者都不進行隨機增強。以下函數實現了這兩種方法。
cut_mix = keras_cv.layers.CutMix()
mix_up = keras_cv.layers.MixUp()
def cut_mix_and_mix_up(samples):
samples = cut_mix(samples, training=True)
samples = mix_up(samples, training=True)
return samples
train_dataset = load_dataset().map(cut_mix_and_mix_up, num_parallel_calls=AUTOTUNE)
visualize_dataset(train_dataset, title="After CutMix and MixUp")
太好了!看起來我們已經成功地將 CutMix
和 MixUp
添加到我們的預處理管道中。
也許您想從 RandAugment
中排除某個擴增,或者您想將 keras_cv.layers.GridMask
作為默認 RandAugment
擴增的選項之一。
KerasCV 允許您使用 keras_cv.layers.RandomAugmentationPipeline
圖層構建生產級的自定義數據擴增管道。此類的操作方式與 RandAugment
類似;為每個影像選擇一個隨機圖層應用 augmentations_per_image
次。RandAugment
可以被視為 RandomAugmentationPipeline
的一個特例。事實上,我們的 RandAugment
實現在內部繼承自 RandomAugmentationPipeline
。
在本範例中,我們將通過從標準 RandAugment
策略中移除 RandomRotation
圖層,並用 GridMask
圖層替換它,來創建一個自定義的 RandomAugmentationPipeline
。
作為第一步,讓我們使用輔助方法 RandAugment.get_standard_policy()
來創建一個基本管道。
layers = keras_cv.layers.RandAugment.get_standard_policy(
value_range=(0, 255), magnitude=0.75, magnitude_stddev=0.3
)
首先,讓我們過濾掉 RandomRotation
圖層
layers = [
layer for layer in layers if not isinstance(layer, keras_cv.layers.RandomRotation)
]
接下來,讓我們將 keras_cv.layers.GridMask
添加到我們的圖層中
layers = layers + [keras_cv.layers.GridMask()]
最後,我們可以將我們的管道組合起來
pipeline = keras_cv.layers.RandomAugmentationPipeline(
layers=layers, augmentations_per_image=3
)
def apply_pipeline(inputs):
inputs["images"] = pipeline(inputs["images"])
return inputs
讓我們看看結果!
train_dataset = load_dataset().map(apply_pipeline, num_parallel_calls=AUTOTUNE)
visualize_dataset(train_dataset, title="After custom pipeline")
太棒了!如您所見,沒有任何影像被隨機旋轉。您可以根據自己的喜好自定義管道
pipeline = keras_cv.layers.RandomAugmentationPipeline(
layers=[keras_cv.layers.GridMask(), keras_cv.layers.Grayscale(output_channels=3)],
augmentations_per_image=1,
)
此管道將應用 GrayScale
或 GridMask
train_dataset = load_dataset().map(apply_pipeline, num_parallel_calls=AUTOTUNE)
visualize_dataset(train_dataset, title="After custom pipeline")
看起來很棒!您可以隨意使用 RandomAugmentationPipeline
。
作為最後一個練習,讓我們來試試這些圖層。在本節中,我們將使用 CutMix
、MixUp
和 RandAugment
在牛津花卉數據集上訓練一個最先進的 ResNet50
影像分類器。
def preprocess_for_model(inputs):
images, labels = inputs["images"], inputs["labels"]
images = tf.cast(images, tf.float32)
return images, labels
train_dataset = (
load_dataset()
.map(apply_rand_augment, num_parallel_calls=AUTOTUNE)
.map(cut_mix_and_mix_up, num_parallel_calls=AUTOTUNE)
)
visualize_dataset(train_dataset, "CutMix, MixUp and RandAugment")
train_dataset = train_dataset.map(preprocess_for_model, num_parallel_calls=AUTOTUNE)
test_dataset = load_dataset(split="test")
test_dataset = test_dataset.map(preprocess_for_model, num_parallel_calls=AUTOTUNE)
train_dataset = train_dataset.prefetch(AUTOTUNE)
test_dataset = test_dataset.prefetch(AUTOTUNE)
接下來,我們應該創建模型本身。請注意,我們在損失函數中使用了 label_smoothing=0.1
。當使用 MixUp
時,*強烈*建議使用標籤平滑。
input_shape = IMAGE_SIZE + (3,)
def get_model():
model = keras_cv.models.ImageClassifier.from_preset(
"efficientnetv2_s", num_classes=num_classes
)
model.compile(
loss=keras.losses.CategoricalCrossentropy(label_smoothing=0.1),
optimizer=keras.optimizers.SGD(momentum=0.9),
metrics=["accuracy"],
)
return model
最後,我們訓練模型
model = get_model()
model.fit(
train_dataset,
epochs=1,
validation_data=test_dataset,
)
32/32 ━━━━━━━━━━━━━━━━━━━━ 103s 2s/step - accuracy: 0.0059 - loss: 4.6941 - val_accuracy: 0.0114 - val_loss: 10.4028
<keras.src.callbacks.history.History at 0x7fd0d00e07c0>
這就是使用 KerasCV 組裝最先進的影像擴增管道的全部內容!
作為讀者的額外練習,您可以
RandomAugmentationPipeline
對象。目前,在 Keras 核心和 KerasCV 之間有 28 個影像擴增圖層!這些圖層中的每一個都可以獨立使用,或者在管道中使用。查看它們,如果您發現缺少您需要的擴增技術,請在 KerasCV 的 GitHub 上提交 issue。