開發者指南 / KerasCV / 使用 KerasCV 進行 CutMix、MixUp 和 RandAugment 影像增強

使用 KerasCV 進行 CutMix、MixUp 和 RandAugment 影像增強

作者: lukewood
建立日期 2022/04/08
最後修改日期 2022/04/08
說明:使用 KerasCV 使用 CutMix、MixUp、RandAugment 等方法增強影像。

在 Colab 中檢視 GitHub 原始碼


概觀

KerasCV 可以輕鬆地為影像分類和物件偵測任務組裝最先進的、產業級的資料增強流程。KerasCV 提供了廣泛的預處理層,實作了常見的資料增強技術。

其中三個最實用的圖層可能是 keras_cv.layers.CutMixkeras_cv.layers.MixUpkeras_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.CutMixkeras_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")

png

太好了!現在我們可以進入增強步驟。


RandAugment

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")

png

嘗試調整強度設置以查看更多種類的結果。


CutMix 和 MixUp:生成高質量的類間範例

CutMixMixUp 允許我們生成類間範例。 CutMix 隨機裁剪一張圖像的部分並將其放置在另一張圖像上,而 MixUp 在兩張圖像之間插值像素值。這兩種方法都可以防止模型過度擬合訓練分佈,並提高模型泛化到分佈外範例的可能性。此外,CutMix 可以防止您的模型過度依賴任何特定特徵來執行其分類。您可以在他們各自的論文中閱讀有關這些技術的更多信息

在本例中,我們將在手動創建的預處理管道中獨立使用 CutMixMixUp。在最先進的管道中,圖像通常通過 CutMixMixUp 或兩者都不進行隨機增強。以下函數實現了這兩種方法。

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")

png

太好了!看起來我們已經成功地將 CutMixMixUp 添加到我們的預處理管道中。


自訂您的增強管道

也許您想從 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")

png

太棒了!如您所見,沒有任何影像被隨機旋轉。您可以根據自己的喜好自定義管道

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")

png

看起來很棒!您可以隨意使用 RandomAugmentationPipeline


訓練 CNN

作為最後一個練習,讓我們來試試這些圖層。在本節中,我們將使用 CutMixMixUpRandAugment 在牛津花卉數據集上訓練一個最先進的 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)

png

接下來,我們應該創建模型本身。請注意,我們在損失函數中使用了 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 組裝最先進的影像擴增管道的全部內容!

作為讀者的額外練習,您可以

  • 對 RandAugment 參數執行超參數搜索,以提高分類器的準確性
  • 用您自己的數據集替換牛津花卉數據集
  • 試驗自定義的 RandomAugmentationPipeline 對象。

目前,在 Keras 核心和 KerasCV 之間有 28 個影像擴增圖層!這些圖層中的每一個都可以獨立使用,或者在管道中使用。查看它們,如果您發現缺少您需要的擴增技術,請在 KerasCV 的 GitHub 上提交 issue