Keras 2 : examples : EfficientNet を使用した再調整による画像分類 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 11/21/2021 (keras 2.7.0)
* 本ページは、Keras の以下のドキュメントを翻訳した上で適宜、補足説明したものです:
- Code examples : Computer Vision : Image classification via fine-tuning with EfficientNet (Author: Yixing Fu)
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- 人工知能研究開発支援
- 人工知能研修サービス(経営者層向けオンサイト研修)
- テクニカルコンサルティングサービス
- 実証実験(プロトタイプ構築)
- アプリケーションへの実装
- 人工知能研修サービス
- PoC(概念実証)を失敗させないための支援
- テレワーク & オンライン授業を支援
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- ウェビナー運用には弊社製品「ClassCat® Webinar」を利用しています。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
- 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
- E-Mail:sales-info@classcat.com ; WebSite: www.classcat.com ; Facebook
Keras 2 : examples : EfficientNet を使用した再調整による画像分類
Description: Stanford Dogs 分類のために imagenet 上で事前訓練された重みを持つ EfficientNet を使用します。
イントロダクション: EfficientNet とは何か
Tan and Le, 2019 で最初に紹介された EfficientNet は最も効率的なモデル (i.e. 推論のために最少の FLOPS を必要とします) の一つで、imagenet と一般的な画像分類転移学習タスクの両方で最先端の精度に到達しています。
最少の基底モデルは MnasNet に類似しています、これは大幅に小さいモデルで SOTA 近くに達しました。モデルをスケールするための発見的な (ヒューリスティックな) 方法を導入することにより、EfficientNet は様々なスケールで効率性と精度の良い組合せを表すモデルのファミリ (B0 〜 B7) を提供します。そのようなスケールする発見的問題解決法 (compound-scaling, 詳細は Tan and Le, 2019 参照) は、ハイパーパラメータの広範囲に渡るグリッドサーチを回避しながら、効率性指向の基底モデル (B0) が総てのスケールのモデルを超えることを可能にします。
モデルの最新アップデートの概要は ここ で利用可能です、そこではモデルの imagenet パフォーマンスを更に改良するために様々な増強スキームと半教師あり学習アプローチが適用されています。モデルのこれらの拡張はモデルアーキテクチャを変更することなく重みを更新することにより利用できます。
EfficientNet の B0 〜 B7 のバリエーション
(This section provides some details on “compound scaling”, and can be skipped if you’re only interested in using the models)
元の論文 に基づけば、EfficientNet は論文の Eq.(3) 内のスケーリング因子を任意に選択することにより作成されたモデルの連続的なファミリであるという印象を持つかもしれません。けれども、解像度、深さと幅の選択もまた多くの要因で制限されています :
- 解像度 : 8, 16, 等で割り切れない解像度は幾つかの層の境界近くでゼロパディングを引き起こします、これは計算リソースを浪費します。これは特にモデルの小さいバリエーションに当てはまりますので、B0 と B1 のための入力解像度は 224 と 240 として選択されます。
- 深さと幅 : EfficientNet のビルディングブロックはチャネルサイズが 8 の倍数であることを要求します。
- リソース制限 : メモリ制限は深さと幅がまだ増加するとき解像度のボトルネックになるかもしれません。そのような状況では、解像度を維持しながら深さ and/or 幅を増やせばパフォーマンスを改良できます。
結果として、EfficientNet モデルの各バリエーションの深さ, 幅と解像度は手動で選択されて良い結果を生成することが証明されています、それらは compound スケーリングの式から大きく外れる可能性はありますが。従って、keras 実装 (下で詳細) は、width / depth / resolution パラメータの任意の選択を許容する代わりに、これらの 8 モデル, B0 〜 B7 だけを提供します。
EfficientNet の Keras 実装
EfficientNet B0 〜 B7 の実装は TF2.3 から tf.keras とともに出荷されています。imagenet からの 1000 クラスの画像を分類するために EfficientNetB0 を使用するには、次を実行します :
from tensorflow.keras.applications import EfficientNetB0
model = EfficientNetB0(weights='imagenet')
このモデルは shape (224, 224, 3) の入力画像を取り、そして入力データは範囲 [0, 255] であるべきです。正規化はモデルの一部として含まれています。
ImageNet 上の EfficinetNet の訓練は膨大な量のリソースと (モデルアーキテクチャ自身の一部ではない) 幾つかのテクニックを必要とします。そのため Keras 実装は AutoAugment による訓練を通して得られた事前訓練済みの重みをデフォルトでロードします。
B0 から B7 のベースモデルについては、入力 shape は様々です。ここに各モデルのために想定される入力 shape のリストがあります :
基底モデル | 解像度 |
---|---|
EfficientNetB0 | 224 |
EfficientNetB1 | 240 |
EfficientNetB2 | 260 |
EfficientNetB3 | 300 |
EfficientNetB4 | 380 |
EfficientNetB5 | 456 |
EfficientNetB6 | 528 |
EfficientNetB7 | 600 |
モデルが転移学習を目的としているとき、Keras 実装はトップ層を削除するオプションを提供しています :
model = EfficientNetB0(include_top=False, weights='imagenet')
このオプションは、 最後から 2 番目の層の1280 特徴を 1000 ImageNet クラスの予測に変える最後の Dense 層を除外します。トップ層をカスタム層に置き換えることは、EfficientNet を転移学習ワークフローにおける特徴抽出機として使用することを可能にします。
注目に値するモデル・コンストラクタのもう一つの引数は drop_connect_rate で、これは確率的 depth の責任を負う dropout 率を制御します。このパラメータは再調整における追加の正則化のためのトグルとして機能しますが、ロードされた重みには影響しません。例えば、より強い正則化が望まれるときには、次を試してください :
model = EfficientNetB0(weights='imagenet', drop_connect_rate=0.4)
デフォルト値は 0.2 です。
サンプル: Stanford Dogs のための EfficientNetB0
EfficientNet は広範囲の画像分類タスクに対応可能です。これが転移学習のための良いモデルにしています。end-to-end サンプルとして、Stanford Dogs データセット上で事前訓練済みの EfficientNetB0 を使用することを示します。
# IMG_SIZE is determined by EfficientNet model choice
IMG_SIZE = 224
セットアップとデータロード
このサンプルは TensorFlow 2.3 またはそれ以上を必要とします。
TPU を利用するには、TPU ランタイムが現在動作している TensorFlow のバージョンに一致していなければなりません。不一致がある場合は、以下を試してください :
from cloud_tpu_client import Client
c = Client()
c.configure_tpu_version(tf.__version__, restart_type="always")
import tensorflow as tf
try:
tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
print("Device:", tpu.master())
strategy = tf.distribute.TPUStrategy(tpu)
except ValueError:
print("Not connected to a TPU runtime. Using CPU/GPU strategy")
strategy = tf.distribute.MirroredStrategy()
Not connected to a TPU runtime. Using CPU/GPU strategy INFO:tensorflow:Using MirroredStrategy with devices ('/job:localhost/replica:0/task:0/device:GPU:0',)
データのロード
ここでは tensorflow_datasets (以後 TFDS) からデータをロードします。Stanford Dogs は stanford_dogs として TFDS で提供されます。120 クラスの犬種 (= dog breeds) に属する 20,580 画像を特集しています (訓練用に 12,000 画像とテストのために 8,580 画像)。
下で dataset_name を単純に変更することで、cifar10, cifar100, food101 等のような TFDS の他のデータセットに対してこのデータセットを試すことも可能です。画像が EfficientNet 入力のサイズよりも遥かに小さいときは、入力画像を単純にアップサンプリングすることができます。転移学習の結果は、入力画像が小さいままでも、増加した解像度に対してより良いことを Tan and Le, 2019 で示されています。
TPU のために: TFDS データセットを使用する場合、データセットを保存するために GCS バケットの場所が必要です。例えば :
tfds.load(dataset_name, data_dir="gs://example-bucket/datapath")
また、現在の環境と TPU サービスアカウントの両方はバケットへの適切なアクセスを持ちます。代わりに、小さいデータセットに対しては、データをメモリにロードすることを試みて tf.data.Dataset.from_tensor_slices() を使用できるかもしれません。
import tensorflow_datasets as tfds
batch_size = 64
dataset_name = "stanford_dogs"
(ds_train, ds_test), ds_info = tfds.load(
dataset_name, split=["train", "test"], with_info=True, as_supervised=True
)
NUM_CLASSES = ds_info.features["label"].num_classes
データセットが様々なサイズの画像を含むとき、それらを共有サイズにリサイズする必要があります。Stanford Dogs データセットは少なくともサイズ 200×200 ピクセルの画像だけを含みます。ここでは EfficientNet のために必要な入力サイズに画像をリサイズします。
size = (IMG_SIZE, IMG_SIZE)
ds_train = ds_train.map(lambda image, label: (tf.image.resize(image, size), label))
ds_test = ds_test.map(lambda image, label: (tf.image.resize(image, size), label))
データの可視化
以下のコードは最初の 9 画像をラベルと共に表示します。
import matplotlib.pyplot as plt
def format_label(label):
string_label = label_info.int2str(label)
return string_label.split("-")[1]
label_info = ds_info.features["label"]
for i, (image, label) in enumerate(ds_train.take(9)):
ax = plt.subplot(3, 3, i + 1)
plt.imshow(image.numpy().astype("uint8"))
plt.title("{}".format(format_label(label)))
plt.axis("off")
データ増強
画像増強のために前処理層 API を利用できます。
from tensorflow.keras.models import Sequential
from tensorflow.keras import layers
img_augmentation = Sequential(
[
layers.RandomRotation(factor=0.15),
layers.RandomTranslation(height_factor=0.1, width_factor=0.1),
layers.RandomFlip(),
layers.RandomContrast(factor=0.1),
],
name="img_augmentation",
)
このシーケンシャルモデル・オブジェクトは後で構築するモデルの一部としても、モデルに供給する前にデータを前処理する関数としても利用できます。それらを関数と使用すれば増強された画像を可視化することを簡単にします。ここでは 与えられた図の増強結果の 9 サンプルをプロットします。
for image, label in ds_train.take(1):
for i in range(9):
ax = plt.subplot(3, 3, i + 1)
aug_img = img_augmentation(tf.expand_dims(image, axis=0))
plt.imshow(aug_img[0].numpy().astype("uint8"))
plt.title("{}".format(format_label(label)))
plt.axis("off")
入力の準備
入力データと増強が正しく動作していることを検証できたら、訓練用にデータセットを準備します。入力データは一様な IMG_SIZE にリサイズされます。ラベルは one-hot (a.k.a. categorical) エンコーディングに配置されます。データセットはバッチ化されます。
Note : 先取りと AUTOTUNE はある状況ではパフォーマンスを向上させるかもしれませんが、環境と使用される特定のデータセットに依存します。データパイプライン性能に詳細は このガイド を参照してください。
# One-hot / categorical encoding
def input_preprocess(image, label):
label = tf.one_hot(label, NUM_CLASSES)
return image, label
ds_train = ds_train.map(
input_preprocess, num_parallel_calls=tf.data.AUTOTUNE
)
ds_train = ds_train.batch(batch_size=batch_size, drop_remainder=True)
ds_train = ds_train.prefetch(tf.data.AUTOTUNE)
ds_test = ds_test.map(input_preprocess)
ds_test = ds_test.batch(batch_size=batch_size, drop_remainder=True)
モデルをスクラッチから訓練する
120 出力クラスで EfficientNetB0 を構築します、これはスクラッチから初期化されます :
Note: 精度は非常にゆっくりと向上して過剰適合する場合があります。
from tensorflow.keras.applications import EfficientNetB0
with strategy.scope():
inputs = layers.Input(shape=(IMG_SIZE, IMG_SIZE, 3))
x = img_augmentation(inputs)
outputs = EfficientNetB0(include_top=True, weights=None, classes=NUM_CLASSES)(x)
model = tf.keras.Model(inputs, outputs)
model.compile(
optimizer="adam", loss="categorical_crossentropy", metrics=["accuracy"]
)
model.summary()
epochs = 40 # @param {type: "slider", min:10, max:100}
hist = model.fit(ds_train, epochs=epochs, validation_data=ds_test, verbose=2)
Model: "functional_1" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 224, 224, 3)] 0 _________________________________________________________________ img_augmentation (Sequential (None, 224, 224, 3) 0 _________________________________________________________________ efficientnetb0 (Functional) (None, 120) 4203291 ================================================================= Total params: 4,203,291 Trainable params: 4,161,268 Non-trainable params: 42,023 _________________________________________________________________ Epoch 1/40 187/187 - 66s - loss: 4.9221 - accuracy: 0.0119 - val_loss: 4.9835 - val_accuracy: 0.0104 Epoch 2/40 187/187 - 63s - loss: 4.5652 - accuracy: 0.0243 - val_loss: 5.1626 - val_accuracy: 0.0145 Epoch 3/40 187/187 - 63s - loss: 4.4179 - accuracy: 0.0337 - val_loss: 4.7597 - val_accuracy: 0.0237 Epoch 4/40 187/187 - 63s - loss: 4.2964 - accuracy: 0.0421 - val_loss: 4.4028 - val_accuracy: 0.0378 Epoch 5/40 187/187 - 63s - loss: 4.1951 - accuracy: 0.0540 - val_loss: 4.3048 - val_accuracy: 0.0443 Epoch 6/40 187/187 - 63s - loss: 4.1025 - accuracy: 0.0596 - val_loss: 4.1918 - val_accuracy: 0.0526 Epoch 7/40 187/187 - 63s - loss: 4.0157 - accuracy: 0.0728 - val_loss: 4.1482 - val_accuracy: 0.0591 Epoch 8/40 187/187 - 62s - loss: 3.9344 - accuracy: 0.0844 - val_loss: 4.1088 - val_accuracy: 0.0638 Epoch 9/40 187/187 - 63s - loss: 3.8529 - accuracy: 0.0951 - val_loss: 4.0692 - val_accuracy: 0.0770 Epoch 10/40 187/187 - 63s - loss: 3.7650 - accuracy: 0.1040 - val_loss: 4.1468 - val_accuracy: 0.0719 Epoch 11/40 187/187 - 63s - loss: 3.6858 - accuracy: 0.1185 - val_loss: 4.0484 - val_accuracy: 0.0913 Epoch 12/40 187/187 - 63s - loss: 3.5942 - accuracy: 0.1326 - val_loss: 3.8047 - val_accuracy: 0.1072 Epoch 13/40 187/187 - 63s - loss: 3.5028 - accuracy: 0.1447 - val_loss: 3.9513 - val_accuracy: 0.0933 Epoch 14/40 187/187 - 63s - loss: 3.4295 - accuracy: 0.1604 - val_loss: 3.7738 - val_accuracy: 0.1220 Epoch 15/40 187/187 - 63s - loss: 3.3410 - accuracy: 0.1735 - val_loss: 3.9104 - val_accuracy: 0.1104 Epoch 16/40 187/187 - 63s - loss: 3.2511 - accuracy: 0.1890 - val_loss: 3.6904 - val_accuracy: 0.1264 Epoch 17/40 187/187 - 63s - loss: 3.1624 - accuracy: 0.2076 - val_loss: 3.4026 - val_accuracy: 0.1769 Epoch 18/40 187/187 - 63s - loss: 3.0825 - accuracy: 0.2229 - val_loss: 3.4627 - val_accuracy: 0.1744 Epoch 19/40 187/187 - 63s - loss: 3.0041 - accuracy: 0.2355 - val_loss: 3.6061 - val_accuracy: 0.1542 Epoch 20/40 187/187 - 64s - loss: 2.8945 - accuracy: 0.2552 - val_loss: 3.2769 - val_accuracy: 0.2036 Epoch 21/40 187/187 - 63s - loss: 2.8054 - accuracy: 0.2710 - val_loss: 3.5355 - val_accuracy: 0.1834 Epoch 22/40 187/187 - 63s - loss: 2.7342 - accuracy: 0.2904 - val_loss: 3.3540 - val_accuracy: 0.1973 Epoch 23/40 187/187 - 62s - loss: 2.6258 - accuracy: 0.3042 - val_loss: 3.2608 - val_accuracy: 0.2217 Epoch 24/40 187/187 - 62s - loss: 2.5453 - accuracy: 0.3218 - val_loss: 3.4611 - val_accuracy: 0.1941 Epoch 25/40 187/187 - 63s - loss: 2.4585 - accuracy: 0.3356 - val_loss: 3.4163 - val_accuracy: 0.2070 Epoch 26/40 187/187 - 62s - loss: 2.3606 - accuracy: 0.3647 - val_loss: 3.2558 - val_accuracy: 0.2392 Epoch 27/40 187/187 - 63s - loss: 2.2819 - accuracy: 0.3801 - val_loss: 3.3676 - val_accuracy: 0.2222 Epoch 28/40 187/187 - 62s - loss: 2.2114 - accuracy: 0.3933 - val_loss: 3.6578 - val_accuracy: 0.2022 Epoch 29/40 187/187 - 62s - loss: 2.0964 - accuracy: 0.4215 - val_loss: 3.5366 - val_accuracy: 0.2186 Epoch 30/40 187/187 - 63s - loss: 1.9931 - accuracy: 0.4459 - val_loss: 3.5612 - val_accuracy: 0.2310 Epoch 31/40 187/187 - 63s - loss: 1.8924 - accuracy: 0.4657 - val_loss: 3.4780 - val_accuracy: 0.2359 Epoch 32/40 187/187 - 63s - loss: 1.8095 - accuracy: 0.4874 - val_loss: 3.5776 - val_accuracy: 0.2403 Epoch 33/40 187/187 - 63s - loss: 1.7126 - accuracy: 0.5086 - val_loss: 3.6865 - val_accuracy: 0.2316 Epoch 34/40 187/187 - 63s - loss: 1.6117 - accuracy: 0.5373 - val_loss: 3.6419 - val_accuracy: 0.2513 Epoch 35/40 187/187 - 63s - loss: 1.5532 - accuracy: 0.5514 - val_loss: 3.8050 - val_accuracy: 0.2415 Epoch 36/40 187/187 - 63s - loss: 1.4479 - accuracy: 0.5809 - val_loss: 4.0113 - val_accuracy: 0.2299 Epoch 37/40 187/187 - 62s - loss: 1.3885 - accuracy: 0.5939 - val_loss: 4.1262 - val_accuracy: 0.2158 Epoch 38/40 187/187 - 63s - loss: 1.2979 - accuracy: 0.6217 - val_loss: 4.2519 - val_accuracy: 0.2344 Epoch 39/40 187/187 - 62s - loss: 1.2066 - accuracy: 0.6413 - val_loss: 4.3924 - val_accuracy: 0.2169 Epoch 40/40 187/187 - 62s - loss: 1.1348 - accuracy: 0.6618 - val_loss: 4.2216 - val_accuracy: 0.2374
(訳注: 実験結果)
_________________________________________________________________ Epoch 1/40 187/187 - 86s - loss: 4.9901 - accuracy: 0.0110 - val_loss: 4.9468 - val_accuracy: 0.0139 - 86s/epoch - 462ms/step Epoch 2/40 187/187 - 55s - loss: 4.6459 - accuracy: 0.0223 - val_loss: 4.9987 - val_accuracy: 0.0196 - 55s/epoch - 296ms/step Epoch 3/40 187/187 - 56s - loss: 4.4327 - accuracy: 0.0328 - val_loss: 4.5213 - val_accuracy: 0.0317 - 56s/epoch - 298ms/step Epoch 4/40 187/187 - 56s - loss: 4.3056 - accuracy: 0.0438 - val_loss: 4.4932 - val_accuracy: 0.0353 - 56s/epoch - 301ms/step Epoch 5/40 187/187 - 57s - loss: 4.2003 - accuracy: 0.0530 - val_loss: 4.8582 - val_accuracy: 0.0373 - 57s/epoch - 303ms/step Epoch 6/40 187/187 - 57s - loss: 4.1207 - accuracy: 0.0622 - val_loss: 4.2693 - val_accuracy: 0.0611 - 57s/epoch - 303ms/step Epoch 7/40 187/187 - 56s - loss: 4.0349 - accuracy: 0.0720 - val_loss: 4.1088 - val_accuracy: 0.0628 - 56s/epoch - 301ms/step Epoch 8/40 187/187 - 57s - loss: 3.9589 - accuracy: 0.0799 - val_loss: 4.2242 - val_accuracy: 0.0606 - 57s/epoch - 306ms/step Epoch 9/40 187/187 - 56s - loss: 3.8639 - accuracy: 0.0968 - val_loss: 3.9041 - val_accuracy: 0.0899 - 56s/epoch - 301ms/step Epoch 10/40 187/187 - 56s - loss: 3.7922 - accuracy: 0.1057 - val_loss: 3.7818 - val_accuracy: 0.1023 - 56s/epoch - 300ms/step Epoch 11/40 187/187 - 57s - loss: 3.6818 - accuracy: 0.1211 - val_loss: 3.9161 - val_accuracy: 0.1109 - 57s/epoch - 306ms/step Epoch 12/40 187/187 - 56s - loss: 3.6017 - accuracy: 0.1284 - val_loss: 3.8162 - val_accuracy: 0.1160 - 56s/epoch - 302ms/step Epoch 13/40 187/187 - 57s - loss: 3.5148 - accuracy: 0.1484 - val_loss: 4.0796 - val_accuracy: 0.1039 - 57s/epoch - 302ms/step Epoch 14/40 187/187 - 59s - loss: 3.4689 - accuracy: 0.1580 - val_loss: 4.4297 - val_accuracy: 0.1030 - 59s/epoch - 315ms/step Epoch 15/40 187/187 - 58s - loss: 3.3517 - accuracy: 0.1727 - val_loss: 3.6688 - val_accuracy: 0.1378 - 58s/epoch - 312ms/step Epoch 16/40 187/187 - 57s - loss: 3.2756 - accuracy: 0.1839 - val_loss: 3.6092 - val_accuracy: 0.1514 - 57s/epoch - 306ms/step Epoch 17/40 187/187 - 57s - loss: 3.1953 - accuracy: 0.2002 - val_loss: 3.7861 - val_accuracy: 0.1431 - 57s/epoch - 304ms/step Epoch 18/40 187/187 - 56s - loss: 3.1032 - accuracy: 0.2177 - val_loss: 3.6004 - val_accuracy: 0.1599 - 56s/epoch - 302ms/step Epoch 19/40 187/187 - 57s - loss: 3.0438 - accuracy: 0.2233 - val_loss: 4.2866 - val_accuracy: 0.1174 - 57s/epoch - 305ms/step Epoch 20/40 187/187 - 57s - loss: 2.9518 - accuracy: 0.2458 - val_loss: 3.8885 - val_accuracy: 0.1494 - 57s/epoch - 302ms/step Epoch 21/40 187/187 - 56s - loss: 2.8672 - accuracy: 0.2596 - val_loss: 3.7840 - val_accuracy: 0.1565 - 56s/epoch - 301ms/step Epoch 22/40 187/187 - 57s - loss: 2.7877 - accuracy: 0.2797 - val_loss: 3.7096 - val_accuracy: 0.1634 - 57s/epoch - 306ms/step Epoch 23/40 187/187 - 57s - loss: 2.7046 - accuracy: 0.2947 - val_loss: 3.5432 - val_accuracy: 0.1828 - 57s/epoch - 307ms/step Epoch 24/40 187/187 - 57s - loss: 2.6175 - accuracy: 0.3103 - val_loss: 3.5549 - val_accuracy: 0.1875 - 57s/epoch - 306ms/step Epoch 25/40 187/187 - 58s - loss: 2.5492 - accuracy: 0.3259 - val_loss: 3.6091 - val_accuracy: 0.1959 - 58s/epoch - 309ms/step Epoch 26/40 187/187 - 56s - loss: 2.4541 - accuracy: 0.3463 - val_loss: 3.5213 - val_accuracy: 0.2064 - 56s/epoch - 301ms/step Epoch 27/40 187/187 - 57s - loss: 2.4326 - accuracy: 0.3501 - val_loss: 3.5567 - val_accuracy: 0.1852 - 57s/epoch - 307ms/step Epoch 28/40 187/187 - 58s - loss: 2.3325 - accuracy: 0.3689 - val_loss: 4.0466 - val_accuracy: 0.2052 - 58s/epoch - 309ms/step Epoch 29/40 187/187 - 57s - loss: 2.2260 - accuracy: 0.3942 - val_loss: 3.6396 - val_accuracy: 0.2150 - 57s/epoch - 304ms/step Epoch 30/40 187/187 - 57s - loss: 2.1411 - accuracy: 0.4129 - val_loss: 3.5410 - val_accuracy: 0.2219 - 57s/epoch - 306ms/step Epoch 31/40 187/187 - 56s - loss: 2.0663 - accuracy: 0.4265 - val_loss: 3.6396 - val_accuracy: 0.2121 - 56s/epoch - 299ms/step Epoch 32/40 187/187 - 56s - loss: 1.9845 - accuracy: 0.4511 - val_loss: 4.3944 - val_accuracy: 0.1918 - 56s/epoch - 299ms/step Epoch 33/40 187/187 - 57s - loss: 1.8984 - accuracy: 0.4728 - val_loss: 3.6451 - val_accuracy: 0.2261 - 57s/epoch - 303ms/step Epoch 34/40 187/187 - 57s - loss: 1.8056 - accuracy: 0.4923 - val_loss: 3.8113 - val_accuracy: 0.2153 - 57s/epoch - 304ms/step Epoch 35/40 187/187 - 57s - loss: 1.7040 - accuracy: 0.5123 - val_loss: 3.8534 - val_accuracy: 0.2198 - 57s/epoch - 304ms/step Epoch 36/40 187/187 - 57s - loss: 1.6090 - accuracy: 0.5412 - val_loss: 3.9382 - val_accuracy: 0.2232 - 57s/epoch - 305ms/step Epoch 37/40 187/187 - 57s - loss: 1.5113 - accuracy: 0.5606 - val_loss: 4.3693 - val_accuracy: 0.2076 - 57s/epoch - 303ms/step Epoch 38/40 187/187 - 57s - loss: 1.4480 - accuracy: 0.5809 - val_loss: 4.0174 - val_accuracy: 0.2316 - 57s/epoch - 304ms/step Epoch 39/40 187/187 - 56s - loss: 1.3744 - accuracy: 0.5927 - val_loss: 4.1591 - val_accuracy: 0.2254 - 56s/epoch - 302ms/step Epoch 40/40 187/187 - 58s - loss: 1.2814 - accuracy: 0.6190 - val_loss: 4.4588 - val_accuracy: 0.2104 - 58s/epoch - 310ms/step CPU times: user 1h 16min 21s, sys: 4min 19s, total: 1h 20min 41s Wall time: 38min 31s
モデルの訓練は比較的高速です (Colab で利用可能な TPUv2 上で 20 秒/エポックだけかかります)。これは EfficientNet をスクラッチから望まれる任意のデータセット上で単純に訓練することが容易に感じるかもしれません。けれども、より小さいデータセット、特に CIFAR-100 のような低い解像度を持つデータセット上での EfficientNet の訓練は過剰適合の大きな困難に直面します。
そのためスクラッチからの訓練はハイパーパラメータの非常に注意深い選択を要求し、そして適切な正則化を見つけることは困難です。それはまたリソースを遥かに多く必要とします。訓練と検証精度をプロットすれば、検証精度が低い値で停滞していることが明らかになります。
import matplotlib.pyplot as plt
def plot_hist(hist):
plt.plot(hist.history["accuracy"])
plt.plot(hist.history["val_accuracy"])
plt.title("model accuracy")
plt.ylabel("accuracy")
plt.xlabel("epoch")
plt.legend(["train", "validation"], loc="upper left")
plt.show()
plot_hist(hist)
事前訓練済みの重みから転移学習
ここではモデルを事前訓練済みの ImageNet 重みで初期化してそれを私達自身のデータセット上で再調整します。
def build_model(num_classes):
inputs = layers.Input(shape=(IMG_SIZE, IMG_SIZE, 3))
x = img_augmentation(inputs)
model = EfficientNetB0(include_top=False, input_tensor=x, weights="imagenet")
# Freeze the pretrained weights
model.trainable = False
# Rebuild top
x = layers.GlobalAveragePooling2D(name="avg_pool")(model.output)
x = layers.BatchNormalization()(x)
top_dropout_rate = 0.2
x = layers.Dropout(top_dropout_rate, name="top_dropout")(x)
outputs = layers.Dense(NUM_CLASSES, activation="softmax", name="pred")(x)
# Compile
model = tf.keras.Model(inputs, outputs, name="EfficientNet")
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-2)
model.compile(
optimizer=optimizer, loss="categorical_crossentropy", metrics=["accuracy"]
)
return model
転移学習への最初のステップは総ての層を凍結してトップ層だけを訓練することです。このステップのためには、比較的大きい学習率 (1e-2) が使用できます。検証精度と損失は訓練精度と損失よりも通常は良いことに注意してください。これは訓練時のメトリックだけを抑制する正則化が強力であるためです。
収束は学習率の選択に依存して最大 50 エポックまでかかるかもしれないことに注意してください。画像増強層が適用されない場合には、検証精度は ~60% にとどまるかもしれません。
with strategy.scope():
model = build_model(num_classes=NUM_CLASSES)
epochs = 25 # @param {type: "slider", min:8, max:80}
hist = model.fit(ds_train, epochs=epochs, validation_data=ds_test, verbose=2)
plot_hist(hist)
Epoch 1/25 187/187 - 33s - loss: 3.5673 - accuracy: 0.3624 - val_loss: 1.0288 - val_accuracy: 0.6957 Epoch 2/25 187/187 - 31s - loss: 1.8503 - accuracy: 0.5232 - val_loss: 0.8439 - val_accuracy: 0.7484 Epoch 3/25 187/187 - 31s - loss: 1.5511 - accuracy: 0.5772 - val_loss: 0.7953 - val_accuracy: 0.7563 Epoch 4/25 187/187 - 31s - loss: 1.4660 - accuracy: 0.5878 - val_loss: 0.8061 - val_accuracy: 0.7535 Epoch 5/25 187/187 - 31s - loss: 1.4143 - accuracy: 0.6034 - val_loss: 0.7850 - val_accuracy: 0.7569 Epoch 6/25 187/187 - 31s - loss: 1.4000 - accuracy: 0.6054 - val_loss: 0.7846 - val_accuracy: 0.7646 Epoch 7/25 187/187 - 31s - loss: 1.3678 - accuracy: 0.6173 - val_loss: 0.7850 - val_accuracy: 0.7682 Epoch 8/25 187/187 - 31s - loss: 1.3286 - accuracy: 0.6222 - val_loss: 0.8142 - val_accuracy: 0.7608 Epoch 9/25 187/187 - 31s - loss: 1.3210 - accuracy: 0.6245 - val_loss: 0.7890 - val_accuracy: 0.7669 Epoch 10/25 187/187 - 31s - loss: 1.3086 - accuracy: 0.6278 - val_loss: 0.8368 - val_accuracy: 0.7575 Epoch 11/25 187/187 - 31s - loss: 1.2877 - accuracy: 0.6315 - val_loss: 0.8309 - val_accuracy: 0.7599 Epoch 12/25 187/187 - 31s - loss: 1.2918 - accuracy: 0.6308 - val_loss: 0.8319 - val_accuracy: 0.7535 Epoch 13/25 187/187 - 31s - loss: 1.2738 - accuracy: 0.6373 - val_loss: 0.8567 - val_accuracy: 0.7576 Epoch 14/25 187/187 - 31s - loss: 1.2837 - accuracy: 0.6410 - val_loss: 0.8004 - val_accuracy: 0.7697 Epoch 15/25 187/187 - 31s - loss: 1.2828 - accuracy: 0.6403 - val_loss: 0.8364 - val_accuracy: 0.7625 Epoch 16/25 187/187 - 31s - loss: 1.2749 - accuracy: 0.6405 - val_loss: 0.8558 - val_accuracy: 0.7565 Epoch 17/25 187/187 - 31s - loss: 1.3022 - accuracy: 0.6352 - val_loss: 0.8361 - val_accuracy: 0.7551 Epoch 18/25 187/187 - 31s - loss: 1.2848 - accuracy: 0.6394 - val_loss: 0.8958 - val_accuracy: 0.7479 Epoch 19/25 187/187 - 31s - loss: 1.2791 - accuracy: 0.6420 - val_loss: 0.8875 - val_accuracy: 0.7509 Epoch 20/25 187/187 - 30s - loss: 1.2834 - accuracy: 0.6416 - val_loss: 0.8653 - val_accuracy: 0.7607 Epoch 21/25 187/187 - 30s - loss: 1.2608 - accuracy: 0.6435 - val_loss: 0.8451 - val_accuracy: 0.7612 Epoch 22/25 187/187 - 30s - loss: 1.2780 - accuracy: 0.6390 - val_loss: 0.9035 - val_accuracy: 0.7486 Epoch 23/25 187/187 - 30s - loss: 1.2742 - accuracy: 0.6473 - val_loss: 0.8837 - val_accuracy: 0.7556 Epoch 24/25 187/187 - 30s - loss: 1.2609 - accuracy: 0.6434 - val_loss: 0.9233 - val_accuracy: 0.7524 Epoch 25/25 187/187 - 31s - loss: 1.2630 - accuracy: 0.6496 - val_loss: 0.9116 - val_accuracy: 0.7584
Epoch 1/25 187/187 - 20s - loss: 3.5233 - accuracy: 0.3715 - val_loss: 0.9779 - val_accuracy: 0.7093 - 20s/epoch - 108ms/step Epoch 2/25 187/187 - 10s - loss: 1.8707 - accuracy: 0.5251 - val_loss: 0.8400 - val_accuracy: 0.7416 - 10s/epoch - 53ms/step Epoch 3/25 187/187 - 10s - loss: 1.5058 - accuracy: 0.5826 - val_loss: 0.8193 - val_accuracy: 0.7531 - 10s/epoch - 54ms/step Epoch 4/25 187/187 - 10s - loss: 1.4633 - accuracy: 0.5899 - val_loss: 0.8137 - val_accuracy: 0.7552 - 10s/epoch - 54ms/step Epoch 5/25 187/187 - 10s - loss: 1.4025 - accuracy: 0.6068 - val_loss: 0.7838 - val_accuracy: 0.7626 - 10s/epoch - 54ms/step Epoch 6/25 187/187 - 10s - loss: 1.3708 - accuracy: 0.6121 - val_loss: 0.7811 - val_accuracy: 0.7673 - 10s/epoch - 54ms/step Epoch 7/25 187/187 - 10s - loss: 1.3373 - accuracy: 0.6218 - val_loss: 0.7725 - val_accuracy: 0.7712 - 10s/epoch - 54ms/step Epoch 8/25 187/187 - 10s - loss: 1.3100 - accuracy: 0.6275 - val_loss: 0.8296 - val_accuracy: 0.7526 - 10s/epoch - 55ms/step Epoch 9/25 187/187 - 10s - loss: 1.2767 - accuracy: 0.6390 - val_loss: 0.8051 - val_accuracy: 0.7650 - 10s/epoch - 54ms/step Epoch 10/25 187/187 - 10s - loss: 1.2845 - accuracy: 0.6339 - val_loss: 0.8514 - val_accuracy: 0.7569 - 10s/epoch - 54ms/step Epoch 11/25 187/187 - 10s - loss: 1.2651 - accuracy: 0.6419 - val_loss: 0.8002 - val_accuracy: 0.7646 - 10s/epoch - 54ms/step Epoch 12/25 187/187 - 10s - loss: 1.2924 - accuracy: 0.6393 - val_loss: 0.8160 - val_accuracy: 0.7667 - 10s/epoch - 55ms/step Epoch 13/25 187/187 - 10s - loss: 1.2593 - accuracy: 0.6461 - val_loss: 0.8552 - val_accuracy: 0.7592 - 10s/epoch - 54ms/step Epoch 14/25 187/187 - 10s - loss: 1.2637 - accuracy: 0.6428 - val_loss: 0.8439 - val_accuracy: 0.7619 - 10s/epoch - 54ms/step Epoch 15/25 187/187 - 10s - loss: 1.2523 - accuracy: 0.6481 - val_loss: 0.8707 - val_accuracy: 0.7611 - 10s/epoch - 53ms/step Epoch 16/25 187/187 - 10s - loss: 1.2747 - accuracy: 0.6451 - val_loss: 0.8584 - val_accuracy: 0.7592 - 10s/epoch - 53ms/step Epoch 17/25 187/187 - 10s - loss: 1.2437 - accuracy: 0.6518 - val_loss: 0.8351 - val_accuracy: 0.7667 - 10s/epoch - 53ms/step Epoch 18/25 187/187 - 10s - loss: 1.2597 - accuracy: 0.6493 - val_loss: 0.9032 - val_accuracy: 0.7566 - 10s/epoch - 54ms/step Epoch 19/25 187/187 - 10s - loss: 1.2575 - accuracy: 0.6482 - val_loss: 0.8342 - val_accuracy: 0.7717 - 10s/epoch - 53ms/step Epoch 20/25 187/187 - 10s - loss: 1.2524 - accuracy: 0.6499 - val_loss: 0.8600 - val_accuracy: 0.7580 - 10s/epoch - 53ms/step Epoch 21/25 187/187 - 10s - loss: 1.2369 - accuracy: 0.6552 - val_loss: 0.8574 - val_accuracy: 0.7642 - 10s/epoch - 54ms/step Epoch 22/25 187/187 - 10s - loss: 1.2461 - accuracy: 0.6564 - val_loss: 0.8213 - val_accuracy: 0.7729 - 10s/epoch - 53ms/step Epoch 23/25 187/187 - 10s - loss: 1.2501 - accuracy: 0.6540 - val_loss: 0.9247 - val_accuracy: 0.7533 - 10s/epoch - 54ms/step Epoch 24/25 187/187 - 10s - loss: 1.2752 - accuracy: 0.6486 - val_loss: 0.8719 - val_accuracy: 0.7621 - 10s/epoch - 53ms/step Epoch 25/25 187/187 - 10s - loss: 1.2136 - accuracy: 0.6609 - val_loss: 0.8941 - val_accuracy: 0.7661 - 10s/epoch - 54ms/step
2 番目のステップは幾つかの層を凍結解除して、より小さい学習率を使用してモデルを適合させることです。このサンプルでは総ての層を凍結解除して示しますが、特定のデータセットに依存して総ての層のほんの一部だけを凍結解除するのが望ましいかもしれません。
事前訓練済みモデルの特徴抽出が十分に上手く機能するとき、このステップは検証精度において非常に限定的なゲインを与えます。私達のケースでは小さい改善を見るだけです、ImageNet 事前訓練は既にモデルにかなりの量の犬 (画像) を見せているからです。
一方、ImageNet とは異なるデータセットで事前訓練済みの重みを使用するときには、この再調整ステップは重要である可能性があります、特徴抽出器もまたかなりの量で調整される必要があるからです。そのような状況は、代わりに CIFAR-100 データセットを選択する場合に実演できます、そこでは再調整は EfficientNetB0 上で 80% を超えるほどに検証精度をおよそ 10% ブーストします。そのような場合、収束は 50 エポック以上かかるかもしれません。
モデルの凍結/凍結解除についての追記 : Model の trainable の設定は Model に属する総ての層を同じ trainable 属性に同時に設定します。各層は、層自身とそれを含むモデルの両方が trainable である場合に限り trainable です。従ってモデルを部分的に凍結/凍結解除する必要があるとき、モデルの trainable 属性は True に設定されることを確実にする必要があります。
def unfreeze_model(model):
# We unfreeze the top 20 layers while leaving BatchNorm layers frozen
for layer in model.layers[-20:]:
if not isinstance(layer, layers.BatchNormalization):
layer.trainable = True
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-4)
model.compile(
optimizer=optimizer, loss="categorical_crossentropy", metrics=["accuracy"]
)
unfreeze_model(model)
epochs = 10 # @param {type: "slider", min:8, max:50}
hist = model.fit(ds_train, epochs=epochs, validation_data=ds_test, verbose=2)
plot_hist(hist)
Epoch 1/10 187/187 - 33s - loss: 0.9956 - accuracy: 0.7080 - val_loss: 0.7644 - val_accuracy: 0.7856 Epoch 2/10 187/187 - 31s - loss: 0.8885 - accuracy: 0.7352 - val_loss: 0.7696 - val_accuracy: 0.7866 Epoch 3/10 187/187 - 31s - loss: 0.8059 - accuracy: 0.7533 - val_loss: 0.7659 - val_accuracy: 0.7885 Epoch 4/10 187/187 - 32s - loss: 0.7648 - accuracy: 0.7675 - val_loss: 0.7730 - val_accuracy: 0.7866 Epoch 5/10 187/187 - 32s - loss: 0.6982 - accuracy: 0.7833 - val_loss: 0.7691 - val_accuracy: 0.7858 Epoch 6/10 187/187 - 31s - loss: 0.6823 - accuracy: 0.7880 - val_loss: 0.7814 - val_accuracy: 0.7872 Epoch 7/10 187/187 - 31s - loss: 0.6536 - accuracy: 0.7953 - val_loss: 0.7850 - val_accuracy: 0.7873 Epoch 8/10 187/187 - 31s - loss: 0.6104 - accuracy: 0.8111 - val_loss: 0.7774 - val_accuracy: 0.7879 Epoch 9/10 187/187 - 32s - loss: 0.5990 - accuracy: 0.8067 - val_loss: 0.7925 - val_accuracy: 0.7870 Epoch 10/10 187/187 - 31s - loss: 0.5531 - accuracy: 0.8239 - val_loss: 0.7870 - val_accuracy: 0.7836
Epoch 1/10 187/187 - 21s - loss: 0.9869 - accuracy: 0.7091 - val_loss: 0.7475 - val_accuracy: 0.7937 - 21s/epoch - 113ms/step Epoch 2/10 187/187 - 11s - loss: 0.8455 - accuracy: 0.7434 - val_loss: 0.7530 - val_accuracy: 0.7917 - 11s/epoch - 56ms/step Epoch 3/10 187/187 - 11s - loss: 0.7889 - accuracy: 0.7584 - val_loss: 0.7552 - val_accuracy: 0.7919 - 11s/epoch - 57ms/step Epoch 4/10 187/187 - 11s - loss: 0.7539 - accuracy: 0.7676 - val_loss: 0.7524 - val_accuracy: 0.7923 - 11s/epoch - 60ms/step Epoch 5/10 187/187 - 11s - loss: 0.6874 - accuracy: 0.7892 - val_loss: 0.7569 - val_accuracy: 0.7893 - 11s/epoch - 58ms/step Epoch 6/10 187/187 - 11s - loss: 0.6634 - accuracy: 0.7972 - val_loss: 0.7557 - val_accuracy: 0.7889 - 11s/epoch - 59ms/step Epoch 7/10 187/187 - 11s - loss: 0.5926 - accuracy: 0.8097 - val_loss: 0.7601 - val_accuracy: 0.7888 - 11s/epoch - 58ms/step Epoch 8/10 187/187 - 11s - loss: 0.5792 - accuracy: 0.8198 - val_loss: 0.7708 - val_accuracy: 0.7900 - 11s/epoch - 58ms/step Epoch 9/10 187/187 - 11s - loss: 0.5664 - accuracy: 0.8235 - val_loss: 0.7673 - val_accuracy: 0.7853 - 11s/epoch - 59ms/step Epoch 10/10 187/187 - 11s - loss: 0.5250 - accuracy: 0.8344 - val_loss: 0.7612 - val_accuracy: 0.7886 - 11s/epoch - 58ms/step
EfficientNet を再調整するためのヒント
凍結解除してる層上で :
- BathcNormalization 層は凍結しておく必要があります ( 詳細 )。それらも trainable に変えてしまう場合、凍結解除後の最初のエポックが精度を著しく低下させます。
- ある場合には、総ての層を凍結解除する代わりに層の一部だけをオープンすることは有益かもしれません。これは B7 のような大きいモデルに進むとき再調整を遥かに高速にします。
- 各ブロックは総て on か off にする必要があります。これはアーキテクチャが各ブロックに対して最初の層から最後の層へのショートカットを含むためです。ブロックへの配慮がないと最終的なパフォーマンスも大きく損ないます。
EfficientNet を活用するための幾つかの他のヒント :
- EfficientNet の大きなバリエーションは、特に少ないデータや少ないクラスを持つタスクに対して、改善されたパフォーマンスを保証しません。そのような場合、EfficientNet のより大きなバリエーションが選択されれば、ハイパーパラメータの調整もより困難になります。
- EMA (指数平滑移動平均, Exponential Moving Average) は EfficientNet をスクラッチから訓練するのに非常に有用ですが、転移学習のためにはそれほどではありません。
- 転移学習のために原著論文でのように RMSprop セットアップを使用しないでください。モメンタムと学習率は転移学習のためには高すぎます。事前訓練済みの重みを簡単に損ない、損失を吹き飛ばします (= blow up)。素早い確認は、同じエポック後に (categorical 交差エントロピーとしての) 損失が log(NUM_CLASSES) よりも著しく大きくなるかどうかを見ます。もしそうであれば、初期学習率/モメンタムが高すぎます。
- より小さいバッチサイズは検証精度に恩恵を与えますが、多分これは正則化を効果的に提供するからです。
最新の EfficientNet 重みの使用
初期論文から、EfficientNet はデータ前処理と (学習結果を拡大するために) ラベル付けられてないデータを使用するための様々な方法で改良されてきました。これらの改良は再現するのが比較的困難で計算的にコスト高で、追加のコードを必要とします ; しかし重みは TF チェックポイントファイルの形式ですぐに利用可能です。モデルアーキテクチャは変わっていないので、改良されたチェックポイントをロードすることは可能です。
公式モデルレポジトリ で提供されるチェックポイントを利用するには、最初にチェックポイントをダウンロードします。例として、ここでは B1 の noisy-student 版をダウンロードします :
!wget https://storage.googleapis.com/cloud-tpu-checkpoints/efficientnet\
/noisystudent/noisy_student_efficientnet-b1.tar.gz
!tar -xf noisy_student_efficientnet-b1.tar.gz
次に ckpt ファイルを h5 ファイルに変換するためスクリプト efficientnet_weight_update_util.py (訳注: リンク切れ) を使用します。
!python efficientnet_weight_update_util.py --model b1 --notop --ckpt \
efficientnet-b1/model.ckpt --o efficientnetb1_notop.h5
モデルを作成するとき、新しい重みをロードするために以下を使用します :
model = EfficientNetB1(weights="efficientnetb1_notop.h5", include_top=False)
以上