Tensorflow keras CNN(convolutional neural network)のサンプルコード説明 初心者向けです。

データサイエンス

 画像関連のAIで最も有名なアルゴリズムは、CNNです。Tensorflow kerasによる実装サンプルを初心者の方向けに解説します。動作させることを目標に参考になさってください。

はじめに

 画像認識は、

参照元は、こちらです。

Keras documentation: Image classification from scratch
Keras documentation

サンプルコード解説

セットアップ

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

tensorflowとkeras、kerasのkayersをimportしておきます。

layersはCNNの各層の構造を作成するときに使います。

サンプルデータのダウンロード

!で始まると心は、google colabo以外では、コマンドラインで入力してください。

!curl -O https://download.microsoft.com/download/3/E/1/3E1C3F21-ECDB-4869-8368-6DEBA77B919F/kagglecatsanddogs_5340.zip

今回使う画像サンプルデータをダウンロードします。猫と犬の2クラスデータです。

!unzip -q kagglecatsanddogs_5340.zip
!ls

zipファイルを解凍します。lsでファルダ表示。

!ls PetImages

JFIFデータを削除

画像データの中で、ヘッダーにJFIFと記載があるものを削除します。不具合が出るみたいです。

ここは、とりあえず実行しておきましょう。私も初めて知りました。。。

import os

num_skipped = 0
for folder_name in ("Cat", "Dog"):
    folder_path = os.path.join("PetImages", folder_name)
    for fname in os.listdir(folder_path):
        fpath = os.path.join(folder_path, fname)
        try:
            fobj = open(fpath, "rb")
            is_jfif = tf.compat.as_bytes("JFIF") in fobj.peek(10)
        finally:
            fobj.close()

        if not is_jfif:
            num_skipped += 1
            # Delete corrupted image
            os.remove(fpath)

print("Deleted %d images" % num_skipped)

データセットを作成する

image_size = (180, 180)
batch_size = 128

train_ds, val_ds = tf.keras.utils.image_dataset_from_directory(
    "PetImages",
    validation_split=0.2,
    subset="both",
    seed=1337,
    image_size=image_size,
    batch_size=batch_size,
)
  • 1行目、画像のサイズを定義しておきます
  • 2行目、バッチサイズを定義します。1回でメモリにのせて計算させる画像の数です。
  • 4~11行目、画像のデータセットを定義しています。ディレクトリから逐次読み込むタイプです。大量の画像データを扱うときはこちらですね。
  • 5行目 画像データが保存されているフォルダは、”PetImages”
  • 6行目 検証データへ回すのは2割
  • 7行目 subsetは”both”にしておきます。学習データと検証データ両方つくります。
  • 8行目 乱数の種は1337番に固定
  • 9行目 画像サイズ
  • 10行目 バッチサイズ

画像を表示して確認

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 10))
for images, labels in train_ds.take(1):
    for i in range(9):
        ax = plt.subplot(3, 3, i + 1)
        plt.imshow(images[i].numpy().astype("uint8"))
        plt.title(int(labels[i]))
        plt.axis("off")

matplotで画像を表示して確認します。ここも本題ではないので、実行して確認しましょう。

データ拡張(水増し)

data_augmentation = keras.Sequential(
    [
        layers.RandomFlip("horizontal"),
        layers.RandomRotation(0.1),
    ]
)

学習時のデータ拡張をおこないます。

  • 3行目、水平方向の反転をランダムに行います。
  • 4行目、最大0.1度の回転をランダムに行います

データ拡張した画像を確認

データ拡張した画像を確認します。ここも、本題ではないので実行して確認する程度にしておきましょう。

plt.figure(figsize=(10, 10))
for images, _ in train_ds.take(1):
    for i in range(9):
        augmented_images = data_augmentation(images)
        ax = plt.subplot(3, 3, i + 1)
        plt.imshow(augmented_images[0].numpy().astype("uint8"))
        plt.axis("off")

画像前処理の方法2種類

データの正規化の方法を2種類紹介しています。

一つ目は、モデルの中で定義する方法

inputs = keras.Input(shape=input_shape)
x = data_augmentation(inputs)
x = layers.Rescaling(1./255)(x)
...  # Rest of the model

上記のように書くことで、モデル学習時に、学習に使われるデータがランダムに拡張されます。

二つ目は、データ自身を処理してしまう

augmented_train_ds = train_ds.map(
    lambda x, y: (data_augmentation(x, training=True), y))

上記のようなラムダ関数を使って、データ拡張した画像を作成します。

毎回、画像の前処理を行う時間がなくなるので、こちらの方がおすすめのようです。

データ拡張実行

実際のデータ拡張の実行です。

# Apply `data_augmentation` to the training images.
train_ds = train_ds.map(
    lambda img, label: (data_augmentation(img), label),
    num_parallel_calls=tf.data.AUTOTUNE,
)
# Prefetching samples in GPU memory helps maximize GPU utilization.
train_ds = train_ds.prefetch(tf.data.AUTOTUNE)
val_ds = val_ds.prefetch(tf.data.AUTOTUNE)
  • 2~5行目、画像データを拡張(水増し)しておきます。
  • 7行目、8行目、学習データと検証データをプリフェッチしておきます。

モデル構築

一番大事なモデル構築部分です。公開モデルの流用である、ファインチューニングではなく、スクラッチで1から構築していきます。少々長いですがお付き合いください。Resnetをシンプルにしたモデルです。

def make_model(input_shape, num_classes):
    inputs = keras.Input(shape=input_shape)

    # Entry block
    x = layers.Rescaling(1.0 / 255)(inputs)
    x = layers.Conv2D(128, 3, strides=2, padding="same")(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation("relu")(x)

    previous_block_activation = x  # Set aside residual

    for size in [256, 512, 728]:
        x = layers.Activation("relu")(x)
        x = layers.SeparableConv2D(size, 3, padding="same")(x)
        x = layers.BatchNormalization()(x)

        x = layers.Activation("relu")(x)
        x = layers.SeparableConv2D(size, 3, padding="same")(x)
        x = layers.BatchNormalization()(x)

        x = layers.MaxPooling2D(3, strides=2, padding="same")(x)

        # Project residual
        residual = layers.Conv2D(size, 1, strides=2, padding="same")(
            previous_block_activation
        )
        x = layers.add([x, residual])  # Add back residual
        previous_block_activation = x  # Set aside next residual

    x = layers.SeparableConv2D(1024, 3, padding="same")(x)
    x = layers.BatchNormalization()(x)
    x = layers.Activation("relu")(x)

    x = layers.GlobalAveragePooling2D()(x)
    if num_classes == 2:
        activation = "sigmoid"
        units = 1
    else:
        activation = "softmax"
        units = num_classes

    x = layers.Dropout(0.5)(x)
    outputs = layers.Dense(units, activation=activation)(x)
    return keras.Model(inputs, outputs)


model = make_model(input_shape=image_size + (3,), num_classes=2)
keras.utils.plot_model(model, show_shapes=True)
  • 2行目、データのインプット
  • 6行目、リスケーリング。255でわって、0~1の32bitデータにします。
  • 7行目、畳み込み
  • 8行目、バッチごとの正規化
  • 9行目、活性化関数relu
  • 11行目、出力を変数でとっておく(後で残差計算に使うため)
  • 13行目、256、512、728で下記のループを3回回します
  • 14行目、活性化関数relu
  • 15行目、少し特殊なSeparableConv2Dを行います。(初心者のかたは、こんなのもあるのだなくらいでOK)
  • 16行目、バッチごとの正規化
  • 18~20行目、同じことを繰り返して
  • 22行目、max pooling、stride2なので、半分の大きさになります。
  • 25~26行目、残差を作成。
  • 27行目、残差を加える
  • 29行目、出力を変数でとっておく。(後で残差計算につかう)
  • 31~33行目、またいつものセットを繰り返して
  • 35行目、グローバルアベレージプーリングで、1次元データ化。
  • 36~41行目、クラスの数によって、活性化関数を変えます。2クラスはsigmoid、3クラス以上は、softmaxです
  • 43行目、5割をdropout
  • 44行目、モデルを返して関数終了
  • 48行目、上記関数を使って、モデルオブジェクト作成
  • 49行目、モデルを書いてみます。

おつかれさまです。。。長いですね。。。

モデル学習

epochs = 25

callbacks = [
    keras.callbacks.ModelCheckpoint("save_at_{epoch}.keras"),
]
model.compile(
    optimizer=keras.optimizers.Adam(1e-3),
    loss="binary_crossentropy",
    metrics=["accuracy"],
)
model.fit(
    train_ds,
    epochs=epochs,
    callbacks=callbacks,
    validation_data=val_ds,
)
  • 1行目:エポック数設定
  • 3~5行目:学習の途中結果を保存する、コールバックの設定
  • 6~10行目:モデルのコンパイルです。 optimizer:adamaで学習率1e-3、loss:binary_crossentropy(2クラスなのでこれです)、metrics:accuracy(正解率ですね)
  • 11~16行目:モデルの学習実行。学習データセット(train_ds)と、バリデーションデータセット(val_ds)を指定しています。

新しい(学習で使っていない)データで推論実行

img = keras.preprocessing.image.load_img(
    "PetImages/Cat/6779.jpg", target_size=image_size
)
img_array = keras.preprocessing.image.img_to_array(img)
img_array = tf.expand_dims(img_array, 0)  # Create batch axis

predictions = model.predict(img_array)
score = float(predictions[0])
print(f"This image is {100 * (1 - score):.2f}% cat and {100 * score:.2f}% dog.")
  • 1~3行目:画像データを読み込みます。猫みたいですね
  • 4行目:画像データをnumpy形式に変換
  • 5行目:画像データに次元を追加(バッチデータ数の次元を追加します)
  • 7行目:推論実行
  • 8行目:スコア計算
  • 9行目:猫と、犬の確率を表示

まとめ

画像認識の王道である、CNNのサンプルコードを紹介しました。いかがでしたかね、少し長かったですね。。。

CNN以外に最近では、ViT(Vision Transoformer)が主流になりつつあります。余力がある方は、下記も参考になさってください。

Vision transformer (ViT)を用いた画像認識のコード解説。初心者向 けにtensorflow keras APIのコードをわかりやすく解説します。
画像認識のアルゴリズムで最近注目されている、Vision Transformer(ViT)のサンプルコードを解説します(Tensorflow keras API)。初心者の方にも理解しやすいように、必要以上に情報を詰め込まずに平易な文章で説明します。まずは手軽に実行してみましょう!
タイトルとURLをコピーしました