【Python】TensorFlow(Keras)で画像分類モデルを作る方法

Python
スポンサーリンク

今回はTensorFlow(Keras)でEfficientNetを使い、画像分類をする方法を解説します。

モデル作成の基礎的なことはこちらの記事に書いています。

データセット

CIFAR10

import tensorflow as tf

(X_train, y_train), (X_test, y_test) = tf.keras.datasets.cifar10.load_data()
print(X_train.shape, X_test.shape)
print(y_train.shape, y_test.shape)

# ========== output ==========
# (50000, 32, 32, 3) (10000, 32, 32, 3)
# (50000, 1) (10000, 1)

“cifar10″をダウンロードしましょう。

“train”が学習用データ、”test”が検証用データです。

import matplotlib.pyplot as plt

plt.imshow(X_train[0])
plt.title(y_train.reshape(-1)[0])
plt.show()

1枚目の画像を表示しました。カエルですね。ラベルは6番です。

plt.figure()
for i in range(9):
    plt.subplot(3, 3, i + 1)
    plt.imshow(X_train[i])
    plt.title(y_train.reshape(-1)[i])
plt.tight_layout()
plt.show()

このように写真の種類によってバラバラなラベルが与えられています。

ラベルは合計で10種類あります。

Dataset

train_dataset = tf.data.Dataset.from_tensor_slices((X_train, y_train))
test_dataset = tf.data.Dataset.from_tensor_slices((X_test, y_test))

train_dataset = train_dataset.batch(32)
test_dataset = test_dataset.batch(32)

train_dataset = train_dataset.shuffle(1024 * 50)

batch = next(iter(train_dataset))
print(batch[0].shape)
print(batch[1].shape)

# ========== output ==========
# (32, 32, 32, 3)
# (32, 1)

“from_tensor_slices”にデータを渡すとデータセットが作れます。

batch_sizeは画像を小出しにするサイズです。

学習用データはシャッフルしておきましょう。

()に入れる値はデータ数以上にしておけば無難です。

1バッチだけ取り出してみると、確かに32個のデータが入っていますね。

モデル作成

keras.applications

import tensorflow.keras.layers as L
import tensorflow.keras.models as M

def get_model(image_size, num_classes):
    # 画像を受け取る層
    inputs = L.Input(shape = image_size)
    # EfficientNetB0を作成
    x = tf.keras.applications.efficientnet.EfficientNetB0(include_top = False)(inputs)
    # 1次元に変換する層
    x = L.Flatten()(x)
    # 最終出力層
    outputs = L.Dense(units = num_classes, activation = "softmax")(x)

    # 定義した層を使ってモデル作成
    model = M.Model(inputs = inputs, outputs = outputs)
    # 最適化手法と損失関数を定義
    model.compile(
        optimizer = tf.keras.optimizers.Adam(learning_rate = 5e-5),
        loss = "sparse_categorical_crossentropy"
    )
    return model

# モデルの呼び出し
tf.keras.backend.clear_session()
model = get_model(X_train.shape[-3:], 10)
model.summary()

モデルは”EfficientNetB0″を使います。

これは大規模なデータセットで事前学習されたものなので、最初からそこそこ精度が良いです。

それに加えて今回のデータで転移学習をさせます。

出力を”Flatten”で1次元に変換してから”Dense”でラベル数に合わせましょう。

損失関数を”sparse_categorical_crossentropy”にするとOhe-Hot変換しなくて済みます。

tfimm

#!pip install -q tfimm timm # <- installしていない場合は実行

import tfimm
[m for m in tfimm.list_models() if "swin" in m]

# ========== output ==========
# ['efficientnet_b0',
#  'efficientnet_b0_ap',
#  'efficientnet_b0_ns',
#  'efficientnet_b1',
#  ...

“tfimm”を使うとpytorchのように様々なモデルを扱うことができます。

“swin_base”などtransformerを利用したモデルも使用可能です。

import tensorflow.keras.layers as L
import tensorflow.keras.models as M

def get_model(image_size, num_classes):
    inputs = L.Input(shape = image_size)

    # ============================================================
    # EfficientNetB0を作成
    x = tfimm.create_model("efficientnet_b0", pretrained = True, nb_classes = 0)(inputs)
    # ============================================================

    x = L.Flatten()(x)
    outputs = L.Dense(units = num_classes, activation = "softmax")(x)
    model = M.Model(inputs = inputs, outputs = outputs)
    model.compile(
        optimizer = tf.keras.optimizers.Adam(learning_rate = 5e-5),
        loss = "sparse_categorical_crossentropy"
    )
    return model

# モデルの呼び出し
tf.keras.backend.clear_session()
model = get_model(X_train.shape[-3:], 10)
model.summary()

このように”EfficientNetB0″の部分を”tfimm”のモデルに変えましょう。

以上、どちらの方法で使ってモデルを作ってもOKです。

学習と検証

checkpoint = tf.keras.callbacks.ModelCheckpoint(
    "best_weight.h5",     # ファイル名
    monitor = "val_loss", # 参照するloss
    mode = "min",         # monitorが最小のときに保存
    save_best_only = True,
    save_weights_only = True
)

history = model.fit(
    train_dataset,
    validation_data = test_dataset,
    epochs = 10,
    callbacks = [checkpoint]
)

“fit”で学習しましょう。

検証データで最も精度が良い状態を保存するために”ModelCheckpoint”を使用します。

plt.plot(history.history["loss"], label = "train")
plt.plot(history.history["val_loss"], label = "valid")
plt.legend()
plt.show()

このように徐々にlossが下がっています。

予測

model.load_weights("best_weight.h5")
preds = model.predict(test_dataset)
print(preds[0])
print(y_test[0])

# ========== output ==========
# [2.6435443e-04 8.1145071e-04 1.2745401e-04 9.4737655e-01 4.0097401e-04
#  4.4634677e-02 7.0899853e-04 1.0307332e-03 3.5173900e-03 1.1272756e-03]
# [3]

保存した最も精度の良い重みを読み込み、”predict”で予測しましょう。

各列がそれぞれのラベルに該当する確率になっています。

pred_labels = preds.argmax(axis = 1)
print(pred_labels[:5])
print(y_test[:5].reshape(-1))

# ========== output ==========
# [3 8 8 8 6]
# [3 8 8 0 6]

“argmax”で確立が最も大きい列番号を予測ラベルとしました。

だいたいあっていそうですね。

from sklearn.metrics import accuracy_score

print(accuracy_score(pred_labels, y_test.reshape(-1)))

# ========== output ==========
# 0.7213

正解率は72.1%でした。

Data Augmentation

def augmentation(image, label):
    image = tf.image.random_flip_left_right(image) # 左右反転
    image = tf.image.random_flip_up_down(image)    # 上下反転
    return image, label

image = X_train[4]

plt.figure()
for i in range(9):
    image = augmentation(image, _)[0]
    plt.subplot(3, 3, i + 1)
    plt.imshow(image)
plt.show()

画像を水平方向や上下方向に反転させることで、実質的にデータ量を増やすことができます。

例えば上図でいうと、車が左右どちらを向いていても、車であることは変わりません。

しかし、もしデータ内に左向きの車の画像しかない場合、右向きの画像を車と判定しにくくなります。

“random_flip_left_right”が左右、”random_flip_up_down”が上下に50%の確立で反転させます。

train_dataset = tf.data.Dataset.from_tensor_slices((X_train, y_train))
train_dataset = train_dataset.batch(32)
train_dataset = train_dataset.shuffle(1024 * 50)
train_dataset = train_dataset.map(augmentation)

データセットを作り直しました。”map”で画像変換を適用させています。

# モデルリセット
tf.keras.backend.clear_session()
model = get_model(X_train.shape[-3:], 10)

checkpoint = tf.keras.callbacks.ModelCheckpoint(
    "best_weight.h5",     # ファイル名
    monitor = "val_loss", # 参照するloss
    mode = "min",         # monitorが最小のときに保存
    save_best_only = True,
    save_weights_only = True
)

history = model.fit(
    train_dataset,
    validation_data = test_dataset,
    epochs = 15,
    callbacks = [checkpoint]
)

plt.plot(history.history["loss"], label = "train")
plt.plot(history.history["val_loss"], label = "test")
plt.legend()
plt.show()

モデルを再度呼び出して学習させましょう。

データが増えたからなのか収束が遅くなったので、”epochs”を増やしています。

さっきよりもlossが下がっていますね。

model.load_weights("best_weight.h5")
preds = model.predict(test_dataset)
pred_labels = preds.argmax(axis = 1)
print(pred_labels[:5])
print(y_test[:5].reshape(-1))
print(accuracy_score(pred_labels, y_test.reshape(-1)))

# ========== output ==========
# [3 8 8 8 6]
# [3 8 8 0 6]
# 0.782

正解率も78.2%まで伸びました。

ほかにも色を変えたり画像を回転させたりと、様々な変換方法があります。

まとめ:Kerasで画像分類してみよう

今回はTensorFlow(Keras)で画像分類モデルを作成しました。

モデル作成自体はEfficientNetを使うだけなので簡単ですね。

>>【無料説明会あり】キカガクのAI人材育成コースで勉強する

コメント

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