画像関連のAIで最も有名なアルゴリズムは、CNNです。Tensorflow kerasによる実装サンプルを初心者の方向けに解説します。動作させることを目標に参考になさってください。
はじめに
画像認識は、
参照元は、こちらです。
サンプルコード解説
セットアップ
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)が主流になりつつあります。余力がある方は、下記も参考になさってください。