画像分類 EfficientNet ファインチューニング・転移学習 サンプルコードを初心者向けに解説。tensorflow keras

データサイエンス

 画像分類の中でも、安定の精度をたたき出してくれる、EfficientNetのサンプルコードを初心者の方向けに解説します。あまり深入りはしないので、動作させることを目的に読んでください!

はじめに

 EfficientNetは、画像分類をメモリ効率よく高い精度を発揮できるモデルです。7種類提供されています。B7だと、なんと600pixまで対応できているのです。それだけ、メモリも消費するので、高価なGPUが必要になりますが。。。

  • B0 : 入力画像 224×224pix
  • B1:入力画像 240×240pix
  • B2:入力画像 260×260pix
  • B3:入力画像 300×300pix
  • B4 : 入力画像 380×380pix
  • B5: 入力画像 456×456pix
  • B6: 入力画像 528×528pix
  • B7: 入力画像 600×600pix

こちらが、サンプルコードです。↓↓↓
そのまま実行できるので、colaboが便利ですよ。

Keras documentation: Image classification via fine-tuning with EfficientNet
Keras documentation

サンプルコード解説

imageNetの1000クラス画像分類

from tensorflow.keras.applications import EfficientNetB0
model = EfficientNetB0(weights='imagenet')

学習済みのEfficientB0モデルを読み込んでいます。

  • 1行目:kerasのEfficientNetB0をインポート
  • 2行目:モデルオブジェクト作成。重みはイメージネットで学習したものです。

model = EfficientNetB0(include_top=False, weights='imagenet')

転移学習の(手持ちのデータセットに合わせる)場合は、最後の層を削除したモデルを使います。特徴抽出器の部分だけ使う感じです。自分が使いたい分だけのクラス数に付け替えれば、OKですね。

  • 1行目:EfficientNetB0のモデルで、inculued_topをFaslseにすることで、最後の層がない状態です。

model = EfficientNetB0(weights='imagenet', drop_connect_rate=0.4)

正則化の強さをコントロールするパラメータもあります。drop_connect_rateです。正則化は、ロバスト性の高さで、オーバフィッテイング(過学習)を防いでくれます。

Example: EfficientNetB0 for Stanford Dogs.(スタンフォードの犬へのファインチューニングの例)

画像のデータセット

# IMG_SIZE is determined by EfficientNet model choice
IMG_SIZE = 224

画像サイズは224pixです。

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

画像のデータセット準備です。tnsorflowに実装されています。

  • 1行目:tensorflow_datasetsをインポート
  • 3行目:バッチサイズは64
  • 4行目:データセットの名前は、”スタンフォードの犬”
  • 6~7行目:データをロードする。
    出力は、学習用データセット、テスト用データセット、データセットの情報。
    引数は、
    データセットの名前と、
    splitで学習・テストへ分ける、
    with_infoで情報も出力する、
    as_supervisedがTrueで教師あり(正解ラベル付きですね)
  • 9行目:データセットのクラス数を変数にいれおく。

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

画像をモデルに合わせてリサイズしておきます

  • 1行目:モデルに入力できる画像のサイズ。224pixですね。
  • 2行目:学習データのリサイズ、ラムダ関数を使って、行っています。
    tf.image.resizeでリサイズ実行です。
    (リサイズ後の画像、ラベル)のタプルを作成していますね。
  • 3行目:同様に、テストデータをリサイズです。

画像データを見てみよう

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")
  • 1行目:matplotインポート
  • 4~6行目:ラベルを作成する関数です。
    5行目:label_infoに格納されているラベルを数値から文字列に変換します。グラフに表示させるので。。。
    6行目:ラベルを ”ー” で分けて 後ろ側だけ返します。
  • 9行目:データセットの情報からラベルだけ取得します
  • 10行目:学習データの9データから、for文で1個ずつ取り出します
  • 11行目:3行、3列のグラフのi+1番目を使うよう
  • 12行目:imshowで画像を表示します。astypeでuint8に型変換することを忘れずに。
  • 13行目:タイトルを表示します。タイトルはラベルですね。
  • 14行目:グラフじゃないから、軸はいらないよ。

Data augumentation(データ拡張)

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

おなじみのデータ拡張(水増し)です。

  • 1行目:tensorflowのSequentialをインポート
  • 2行目:tensorflowのlayerをインポート
  • 4~11行目:データ拡張のシーケンスを作成。
  • 6行目:ランダムに回転。最大0.15度
  • 7行目:ランダムに水平移動。高さ方向最大10%、幅方向最大10%
  • 8行目:ランダムに上下、左右反転
  • 9行目:ランダムにコントラスといじる。最大0.1まで
  • 10行目:名前は、img_augmentationだよ

データ準備

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

データの準備です

  • 2~4行目:ラベルをonehotエンドーダで変換する関数です。猫は001、犬は010、馬は100みたいな感じです。クラス分箱を用意して、それぞれ1を立てる箱をかえていく感じです。
  • 7~9行目:one-hotエンコーダ実行
  • 10行目:学習データのバッチの設定。
  • 11行目:プリフェッチの設定。要領よく前処理を行ってくれる設定です。
  • 12行目:テストデータone-hot実行
  • 13行目:テストデータのバッチ設定。

Training a model from scratch(学習済みの重みを使わずにゼロから学習)

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)

いよいよ学習です

  • 1行目:EfficientNetのB0をインポート
  • 4行目:画像のインプットサイズを定義 224pixですね
  • 5行目:データ拡張
  • 6行目:出力は、EfficientNetB0に画像を入れた出力だよ。
  • 8行目:入出力を決めてモデルを定義
  • 9行目:モデルをコンパイル。
    引数は
    最適化手法がAdam
    損失lossはカテゴリカルクロスエントロピー
    精度mettricsはaccuracy正答率ですね
  • 13行目:モデルのサマリを表示
  • 14行目:エポック数は40
  • 15行目:学習実行。
    学習データと、テストデータと、エポック数と、verbose表示設定が引数です。
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)

学習履歴のプロットです

  • 1行目:matplotインポート
  • 4行目:関数作成します
  • 5行目:学習データの精度をプロット
  • 6行目:検証データの精度をプロット
  • 7行目:タイトルはmodel_accuracy
  • 8行目:y軸ラベル
  • 9行目:x軸ラベル
  • 10行目:レジェンド(線の説明)
  • 11行目:グラフを見えるように表示
  • 14行目:実施に実行

Transfer learning from pre-trained weights(学習済みのモデルから学習、転移学習です)

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

ファインチューニングです。こっちの方が簡単に精度だせます。

  • 1行目:関数をつくりますよ
  • 2行目:入力層定義
  • 3行目:データ拡張
  • 4行目:EfficientNetB0のモデル定義、include_topをFalseにして、最終層はつけない
  • 7行目:学習済みの重みは変えない
  • ここから、全結合層を新たに作ります。。。。。。。。。。。
  • 10行目:GlobalAveragePoolingで畳み込みの出力をベクトルデータにします
  • 11行目:バッチで正規化しときます。
  • 12行目:ドロップアウトが0.2、2割つながっていない。。。
  • 13行目:全結合層を作成
    出力がクラス数で
    活性化関数は、softmaxです。
  • 18行目:入出力を定義して、モデル作成
  • 19行目:オプティマイザーはAdam学習率は1e-2
  • 20~22行目:モデルコンパイル
  • 23行目:モデルを返します
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)

学習実行です

  • 2行目:先ほどの関数でモデルを作って
  • 4行目:エポック数設定。スライダーのUIで動かせます。 こんなことできるのですね。
  • 5行目:モデル学習実行
  • 6行目:モデルの学習履歴表示

途中の重みも学習(ファインチューニング)

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)

学習済みもでるを凍結せずに、学ばせるやりかたです。個人的にこっちの方がいいと思います。ファインチューニングですね。

  • 1行目:関数ですよ
  • 3行目:後ろから20層をfor文で1個ずつ取り出します
  • 4行目:バッチノーマリゼーションとか、じゃなかったら、その層は、学習するように設定(凍結しないよ)
  • 7行目:オプティマイザ。最適化の設定。
  • 8行目:モデルコンパイル
  • 13行目:関数を使ってモデルをつくって
  • 14行目:実際に学習
  • 15行目:学習履歴表示

まとめ

画像認識で安定の精度を出してくれる、EfficientNetの転移学習・ファインチューニング サンプルコードを解説しました。EfficientNetは様々な画像サイズに対応した、便利なモデルですので、ぜひ身に着けてください。

タイトルとURLをコピーしました