Keras 2 : examples : 堅牢性向上のための画像分類用 RandAugment (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 12/09/2021 (keras 2.7.0)
* 本ページは、Keras の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
- Code examples : Computer Vision : RandAugment for Image Classification for Improved Robustness (Author: Sayak Paul)
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- 人工知能研究開発支援
- 人工知能研修サービス(経営者層向けオンサイト研修)
- テクニカルコンサルティングサービス
- 実証実験(プロトタイプ構築)
- アプリケーションへの実装
- 人工知能研修サービス
- PoC(概念実証)を失敗させないための支援
- テレワーク & オンライン授業を支援
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
- 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
- sales-info@classcat.com ; Web: www.classcat.com ; ClassCatJP
Keras 2 : examples : 堅牢性向上のための画像分類用 RandAugment
Description: 堅牢性向上を伴う画像分類モデルを訓練するための RandAugment。
データ増強は非常に有用なテクニックで、畳み込みニューラルネットワーク (CNN) の並進不変性 (= translational invariance) を向上させるのに役立つことができます。RandAugment はビジョンデータのための確率的データ増強ルーチンで RandAugment: Practical automated data augmentation with a reduced search space で提案されました。それはランダムクロップのような従来の増強変換とともに、カラー jitters, ガウスぼかし, 彩度 (= saturations) 等のような強力な増強変換から構成されます。
RandAugment は 2 つのパラメータを持ちます :
- n は順次適用されるランダムに選択された増強変換の数を示します。
- m は総ての増強変換の強度です。
これらのパラメータは与えられたデータセットとネットワーク・アーキテクチャのために調整されます。RandAugment の著者らは原論文で RandAugment の疑似コードも提供しています (図 2)。
最近では、それは Noisy 生徒訓練 と 一貫性訓練のための教師なしデータ増強 のようなワークの主要コンポーネントとなっています。それはまた EfficientNet の成功の中心にもなっています。
このサンプルは TensorFlow 2.4 またはそれ以上、そして imgaug を必要とします、これは次のコマンドを使用してインストールできます :
pip install imgaug
インポート & セットアップ
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras import layers
import tensorflow_datasets as tfds
from imgaug import augmenters as iaa
import imgaug as ia
tfds.disable_progress_bar()
tf.random.set_seed(42)
ia.seed(42)
CIFAR10 データセットのロード
このサンプルのためには、CIFAR10 データセット を使用していきます。
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()
print(f"Total training examples: {len(x_train)}")
print(f"Total test examples: {len(x_test)}")
Total training examples: 50000 Total test examples: 10000
ハイパーパラメータの定義
AUTO = tf.data.AUTOTUNE
BATCH_SIZE = 128
EPOCHS = 1
IMAGE_SIZE = 72
RandAugment オブジェクトを初期化する
さて、imgaug.augmenters モジュールからの RandAugment オブジェクトを RandAugment 著者により提案されたパラメータで初期化します。
rand_aug = iaa.RandAugment(n=3, m=7)
def augment(images):
# Input to `augment()` is a TensorFlow tensor which
# is not supported by `imgaug`. This is why we first
# convert it to its `numpy` variant.
images = tf.cast(images, tf.uint8)
return rand_aug(images=images.numpy())
TensorFlow Dataset オブジェクトの作成
RandAugment は NumPy 配列しか処理できませんので、(TensorFlow テンソルを想定する) Dataset オブジェクトの一部として直接的には適用できません。RandAugment をデータセットの一部にするには、それを [tf.py_function] (https://www.tensorflow.org/api_docs/python/tf/py_function) 内にラップする必要があります。
py_function は TensorFlow 演算で (他の TensorFlow 演算のように引数として TF テンソルを取り TensorFlow テンソルを返します)、任意の Python コードを実行することができます。必然的に、この Python コードは CPU 上でだけ実行できますので (一方で TensorFlow グラフの残りは GPU 上で高速化できますが)、これはある場合には大幅なスローダウンを引き起こす可能性があります — けれども、この場合には、Dataset パイプラインはモデルとともに非同期に実行され、CPU 上で前処理を行えば高いパフォーマンスを維持できます。
train_ds_rand = (
tf.data.Dataset.from_tensor_slices((x_train, y_train))
.shuffle(BATCH_SIZE * 100)
.batch(BATCH_SIZE)
.map(
lambda x, y: (tf.image.resize(x, (IMAGE_SIZE, IMAGE_SIZE)), y),
num_parallel_calls=AUTO,
)
.map(
lambda x, y: (tf.py_function(augment, [x], [tf.float32])[0], y),
num_parallel_calls=AUTO,
)
.prefetch(AUTO)
)
test_ds = (
tf.data.Dataset.from_tensor_slices((x_test, y_test))
.batch(BATCH_SIZE)
.map(
lambda x, y: (tf.image.resize(x, (IMAGE_SIZE, IMAGE_SIZE)), y),
num_parallel_calls=AUTO,
)
.prefetch(AUTO)
)
tf.py_function の使用についての注意 :
- 私達の augment() 関数はネイティブ TensorFlow 演算ではないので、高価な演算になる可能性が高いです。これがデータセットをバッチ処理した後にそれを適用することが遥かに良い理由です。
- tf.py_function は TPU と 互換ではありません。そのため、TPU を使用する TensorFlow 訓練パイプラインを分散させた場合、tf.py_function を使用できません。その場合、マルチ GPU 環境に切り替えるか、関数の中身を純粋な TensorFlow 内で書き変えることを考えてください。
比較目的で、ランダム反転, ランダム回転, そしてランダム zoomings から成る単純な増強パイプラインも定義しましょう。
simple_aug = tf.keras.Sequential(
[
layers.Resizing(IMAGE_SIZE, IMAGE_SIZE),
layers.RandomFlip("horizontal"),
layers.RandomRotation(factor=0.02),
layers.RandomZoom(
height_factor=0.2, width_factor=0.2
),
]
)
# Now, map the augmentation pipeline to our training dataset
train_ds_simple = (
tf.data.Dataset.from_tensor_slices((x_train, y_train))
.shuffle(BATCH_SIZE * 100)
.batch(BATCH_SIZE)
.map(lambda x, y: (simple_aug(x), y), num_parallel_calls=AUTO)
.prefetch(AUTO)
)
RandAugment で増強されたデータセットの可視化
sample_images, _ = next(iter(train_ds_rand))
plt.figure(figsize=(10, 10))
for i, image in enumerate(sample_images[:9]):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(image.numpy().astype("int"))
plt.axis("off")
様々なバリエーションを見るために上記のコードブロックを何回か実行することを勧めます。
simple_aug で増強されたデータセットの可視化
sample_images, _ = next(iter(train_ds_simple))
plt.figure(figsize=(10, 10))
for i, image in enumerate(sample_images[:9]):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(image.numpy().astype("int"))
plt.axis("off")
モデル構築ユティリティ関数の定義
さて、ResNet50V2 アーキテクチャ に基づく CNN モデルを定義します。また、ネットワークは既にその内部の層をリスケールしていることに気付いてください。これはデータセット上で別の前処理を行なう必要性をなくして特に配備目的で非常に便利です。
def get_training_model():
resnet50_v2 = tf.keras.applications.ResNet50V2(
weights=None,
include_top=True,
input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3),
classes=10,
)
model = tf.keras.Sequential(
[
layers.Input((IMAGE_SIZE, IMAGE_SIZE, 3)),
layers.Rescaling(scale=1.0 / 127.5, offset=-1),
resnet50_v2,
]
)
return model
print(get_training_model().summary()
Model: "sequential_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= rescaling (Rescaling) (None, 72, 72, 3) 0 _________________________________________________________________ resnet50v2 (Functional) (None, 10) 23585290 ================================================================= Total params: 23,585,290 Trainable params: 23,539,850 Non-trainable params: 45,440 _________________________________________________________________
データセットの 2 つの異なるバージョンでこのネットワークを訓練します。
- RandAugment で増強されたもの。
- simple_aug で増強された別のもの。
RandAugment は一般的な摂動と破損に対してモデルの堅牢性を拡張するとして知られていますので、CIFAR-10-C データセット上でもモデルを評価します、これは Benchmarking Neural Network Robustness to Common Corruptions and Perturbations by Hendrycks et al. で提案されました。CIFAR-10-C dataset は 19 の異なる画像破損と摂動 (例えば、スペックル雑音, fog, ガウスぼかし, 等) から構成され、その重要度 (= severity levels) も様々です。このサンプルについては次の設定を使用します : cifar10_corrupted/saturate_5。この設定からの画像は以下のように見えます :
再現性の向上のために、浅いネットワークの初期ランダム重みをシリアライズします。
initial_model = get_training_model()
initial_model.save_weights("initial_weights.h5")
RandAugment でモデルを訓練する
rand_aug_model = get_training_model()
rand_aug_model.load_weights("initial_weights.h5")
rand_aug_model.compile(
loss="sparse_categorical_crossentropy", optimizer="adam", metrics=["accuracy"]
)
rand_aug_model.fit(train_ds_rand, validation_data=test_ds, epochs=EPOCHS)
_, test_acc = rand_aug_model.evaluate(test_ds)
print("Test accuracy: {:.2f}%".format(test_acc * 100))
391/391 [==============================] - 1199s 3s/step - loss: 2.0652 - accuracy: 0.2744 - val_loss: 1.6281 - val_accuracy: 0.4319 79/79 [==============================] - 46s 580ms/step - loss: 1.6281 - accuracy: 0.4319 Test accuracy: 43.19%
simple_aug でモデルを訓練する
simple_aug_model = get_training_model()
simple_aug_model.load_weights("initial_weights.h5")
simple_aug_model.compile(
loss="sparse_categorical_crossentropy", optimizer="adam", metrics=["accuracy"]
)
simple_aug_model.fit(train_ds_simple, validation_data=test_ds, epochs=EPOCHS)
_, test_acc = simple_aug_model.evaluate(test_ds)
print("Test accuracy: {:.2f}%".format(test_acc * 100))
391/391 [==============================] - 1169s 3s/step - loss: 1.7628 - accuracy: 0.3862 - val_loss: 1.3458 - val_accuracy: 0.5305 79/79 [==============================] - 42s 527ms/step - loss: 1.3458 - accuracy: 0.5305 Test accuracy: 53.05%
CIFAR-10-C dataset をロードしてパフォーマンスを評価する
# Load and prepare the CIFAR-10-C dataset
# (If it's not already downloaded, it takes ~10 minutes of time to download)
cifar_10_c = tfds.load("cifar10_corrupted/saturate_5", split="test", as_supervised=True)
cifar_10_c = cifar_10_c.batch(BATCH_SIZE).map(
lambda x, y: (tf.image.resize(x, (IMAGE_SIZE, IMAGE_SIZE)), y),
num_parallel_calls=AUTO,
)
# Evaluate `rand_aug_model`
_, test_acc = rand_aug_model.evaluate(cifar_10_c, verbose=0)
print(
"Accuracy with RandAugment on CIFAR-10-C (saturate_5): {:.2f}%".format(
test_acc * 100
)
)
# Evaluate `simple_aug_model`
_, test_acc = simple_aug_model.evaluate(cifar_10_c, verbose=0)
print(
"Accuracy with simple_aug on CIFAR-10-C (saturate_5): {:.2f}%".format(
test_acc * 100
)
)
Accuracy with RandAugment on CIFAR-10-C (saturate_5): 35.90% Accuracy with simple_aug on CIFAR-10-C (saturate_5): 47.34%
このサンプルのためには、モデルを単一エポックだけ訓練しました。CIFAR-10-C データセット上、RandAugment によるモデルは simple_aug (e.g., 64.80%) で訓練されたモデルに比較して、より高い精度 (例えば、1 度の実験で 76.64%) でより良く遂行できます。RandAugment はまた訓練を安定させるのにも役立てます。結果の幾つかを確認するためにこの ノートブック を調べることができます。
ノートブックでは、RandAugment による増えた訓練時間の犠牲により、CIFAR-10-C データセット上で遥かに良いパフォーマンスを引き出せることに気付くかもしれません。同じ CIFAR-10-C データセットの実行による他の破損と摂動の設定で実験して RandAugment が役立つかどうか見ることができます。
RandAugment オブジェクトの n と m の異なる値でも実験できます。原論文では、著者らは特定のタスクと広範囲な ablation 研究に対する個々の増強変換の影響を示しています。You are welcome to check them out.
RandAugment は、Noisy Student 訓練 と FixMatch のようなワークで示されたように、コンピュータビジョンのための深層学習モデルの堅牢性の向上に素晴らしい進展を示しました。これは RandAugment を様々なビジョンモデルを訓練するために非常に有用なレシピにしています。
以上