転移学習おためしコード - you1025/my_something_flagments GitHub Wiki

概要

Inception-V3 モデルを用いて犬猫の 2 値判別を行う。
データ画像は Tensorflow Datasets の "cats_vs_dogs" を用いる。

約 2,300 枚の学習データを用いて最終的に 99% ほどの精度を実現。

参考

データセットの作成

データの取得

builder = tfds.builder("cats_vs_dogs")
builder.download_and_prepare()

# 訓練・検証・テスト 画像をそれぞれ全体の 10%(=約2,300枚) ずつ用意する
ds_train, ds_valid, ds_test = builder.as_dataset(split=["train[:10%]", "train[80%:90%]", "train[90%:]"], as_supervised=True)

画像の前処理

  • スケール変換: [0, 255] -> [0, 1]
  • サイズ変換: 縦横それぞれ (300, 300) に揃える
BATCH_SIZE = 32
INPUT_SHAPE = (300, 300, 3)

# データセットに含まれる画像の前処理(スケール変換&サイズ変換)
def resize(image, label):
    image = tf.image.resize(tf.cast(image, tf.float32) / 255.0, INPUT_SHAPE[:2])
    return (image, label)

# 各データセットの前処理を実施
ds_train = ds_train.map(resize).shuffle(buffer_size=10000).batch(batch_size=BATCH_SIZE)
ds_valid = ds_valid.map(resize).repeat(1).batch(batch_size=BATCH_SIZE)
ds_test  = ds_test.map(resize).batch(batch_size=BATCH_SIZE)

転移学習

モデルの作成

include_top=False により Inception-V3 の最上位層を除去し特徴抽出器として用いる。

# Inception-V3 のロード(上位層は対象外)
base_model = InceptionV3(weights="imagenet", include_top=False, input_shape=INPUT_SHAPE)

# Inception-V3 の全層を学習不可に設定
base_model.trainable = False

# Inception-V3 の後続に全結合層を接続
model = Sequential([
    base_model,
    GlobalAveragePooling2D(),
    Dense(units=1024, activation="relu"),
    Dense(units=1, activation="sigmoid")
])

model.compile(loss="binary_crossentropy", optimizer="rmsprop", metrics=["accuracy"])

学習の実行

データセットを渡すだけで良いのは便利。
Google Colaboratory の無料版でだいたい 6 分くらい。

2 エポック目で止めるのが良さげな感じある。

history_transfer = model.fit(
    ds_train,
    epochs=5,
    validation_data=ds_valid,
    verbose=1
)

## Epoch 1/5
## 73/73 [==============================] - 62s 610ms/step - loss: 0.3574 - accuracy: 0.9338 - val_loss: 0.4040 - val_accuracy: 0.8590
## Epoch 2/5
## 73/73 [==============================] - 46s 553ms/step - loss: 0.0992 - accuracy: 0.9807 - val_loss: 0.0336 - val_accuracy: 0.9923
## Epoch 3/5
## 73/73 [==============================] - 45s 555ms/step - loss: 0.0706 - accuracy: 0.9854 - val_loss: 0.0586 - val_accuracy: 0.9905
## Epoch 4/5
## 73/73 [==============================] - 45s 553ms/step - loss: 0.0321 - accuracy: 0.9901 - val_loss: 0.2565 - val_accuracy: 0.9463
## Epoch 5/5
## 73/73 [==============================] - 45s 555ms/step - loss: 0.0589 - accuracy: 0.9884 - val_loss: 0.1008 - val_accuracy: 0.9837

モデルの評価

テストデータでモデルの評価を行う。
かなり高い精度が出ている。

# テストデータによる評価
score = model.evaluate(ds_test)
print(f"test_loss: {score[0]:.5f}, test_accuracy: {score[1]:.5f}")

## 73/73 [==============================] - 20s 273ms/step - loss: 0.0488 - accuracy: 0.9884
## test_loss: 0.04878, test_accuracy: 0.98839

ファインチューニング

モデルの修正

Inception-V3 上位層のいくつかも学習対象とし、追加で学習を行う。

# 249 層以上のレイヤを学習対象とする
for layer in base_model.layers[249:]:
    layer.trainable = True

# モデルを修正した後は再コンパイルが必要
model.compile(loss="binary_crossentropy", optimizer="rmsprop", metrics=["accuracy"])

学習の追加実行

history_finetune = model.fit(
    ds_train,
    epochs=5,
    validation_data=ds_valid,
    verbose=1
)

## Epoch 1/5
## 73/73 [==============================] - 52s 576ms/step - loss: 0.1149 - accuracy: 0.9673 - val_loss: 0.4514 - val_accuracy: 0.8727
## Epoch 2/5
## 73/73 [==============================] - 46s 559ms/step - loss: 0.1168 - accuracy: 0.9751 - val_loss: 0.1143 - val_accuracy: 0.9695
## Epoch 3/5
## 73/73 [==============================] - 45s 554ms/step - loss: 0.0973 - accuracy: 0.9686 - val_loss: 0.0502 - val_accuracy: 0.9867
## Epoch 4/5
## 73/73 [==============================] - 46s 558ms/step - loss: 0.0874 - accuracy: 0.9768 - val_loss: 0.0414 - val_accuracy: 0.9893
## Epoch 5/5
## 73/73 [==============================] - 45s 554ms/step - loss: 0.0749 - accuracy: 0.9798 - val_loss: 0.0449 - val_accuracy: 0.9893

モデルの評価

Loss / Accuracy が共に転移学習のみのモデルよりも改善している。

# テストデータによる評価
score = model.evaluate(ds_test)
print(f"test_loss: {score[0]:.5f}, test_accuracy: {score[1]:.5f}")

## 73/73 [==============================] - 20s 272ms/step - loss: 0.0426 - accuracy: 0.9910
## test_loss: 0.04262, test_accuracy: 0.99097

全体結果

転移学習とファインチューニングそれぞれの実行結果を表示する。

# 各指標の結合
train_accuracy      = history_transfer.history["accuracy"]     + history_finetune.history["accuracy"]
validation_accuracy = history_transfer.history["val_accuracy"] + history_finetune.history["val_accuracy"]
train_loss          = history_transfer.history["loss"]         + history_finetune.history["loss"]
validation_loss     = history_transfer.history["val_loss"]     + history_finetune.history["val_loss"]


plt.figure(figsize=(18, 12))

# Accuracy
plt.subplot(2, 1, 1)
plt.title("Accuracy")
plt.plot(train_accuracy,      label="train_acc", marker="o")
plt.plot(validation_accuracy, label="test_acc",  marker="o")
plt.xlabel("epoch")
plt.ylabel("accuracy")
plt.legend(loc="upper left")
plt.grid(color="gray", alpha=0.1)
plt.xticks(range(0, 10))

# Loss
plt.subplot(2, 1, 2)
plt.title("Loss")
plt.plot(train_loss,      label="train_loss", marker="o")
plt.plot(validation_loss, label="test_loss",  marker="o")
plt.xlabel("epoch")
plt.ylabel("loss")
plt.legend(loc="upper right")
plt.grid(color="gray", alpha=0.1)
plt.xticks(range(0, 10))

5 エポック目以降がファインチューニングによる結果。
一度悪化した後で徐々にフィットしていっている様子が見て取れる。

転移学習の結果プロット