2024年2月1日木曜日

Keras で mnist の画像学習を CNN を使ってやってみる

Keras で mnist の画像学習を CNN を使ってやってみる

概要

前回 Keras + mnist のチュートリアルをやりました
今回は CNN を使った方法を紹介します
チュートリアルはこちらになります

勉強のために Conv2D のレイヤーを増やしたバージョンも紹介します

環境

  • macOS 14.2.1
  • Python 3.11.6
  • tenforflow 2.15.0
  • keras 2.15.0

サンプルコード

import numpy as np
import tensorflow as tf

mnist = tf.keras.datasets.mnist
# 60000個の学習データ
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 学習データとテストデータを3次元から4次元に変換、Conv2D を使う場合は大抵の場合4次元必要、前回 https://hawksnowlog.blogspot.com/2024/01/keras-mnist-tutorial.html Dense のみは3次元のデータのままいける
x_train = x_train.reshape((60000, 28, 28, 1))
x_test = x_test.reshape((10000, 28, 28, 1))
# ピクセル情報は0-255の範囲なので255で割ることで0-1の範囲に変換する
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(32, (3, 3), activation='relu', input_shape=(28, 28, 1)),  # 3x3 の複数のフィルタを32枚使って画像にフィルタをかけ新たな行列を作成
    tf.keras.layers.MaxPooling2D((2, 2)),  # 作成した新たな行列に対して 2x2 の領域で最大の値を取得、更にそれを新たな行列とする
    # ここで更に Conv2D + MaxPooling2D のレイヤーを増やしても OK、ただし学習時間は長くなる
    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
    # ここから出力用のレイヤー
    tf.keras.layers.Flatten(),  # 3次元->1次元への変換
    tf.keras.layers.Dense(64, activation='relu'),  # 64ノードの全結合->10ノードの全結合
    tf.keras.layers.Dense(10, activation='softmax'),  # 最後はソフトマックスで10個ある出力の合計が1になるように変換する (定番
])

# モデルコンパイル
model.compile(optimizer='adam',  # 最適化関数は Adaptive moment
              loss='sparse_categorical_crossentropy',  # 損失関数は交差エントロピー誤差
              metrics=['accuracy'])  # 評価関数は正解率

# # 学習
model.fit(x_train, y_train, epochs=5)


# 評価
model.evaluate(x_test, y_test)
# 予測と結果表示
results = model.predict(x_test[:5])
# 結果はDense(10)なので10個の配列の予測値から成り立っている
# その配列10個中で一番値の大きいインデックスが予測した手書きの数字になる
# np.argmax はそれを一発でやってくれる便利メソッド
print(np.argmax(results, axis=1))  # [7 2 1 0 4] になるはず
print(y_test[:5])  # [7 2 1 0 4] こっちは答え

Conv2D のレイヤーを増やしてみる

前のコードのレイヤーを増やしてみます
最後の Conv2D(64, (3, 3), activation='relu') で (3, 3, 64) となりこれに更に conv2D でフィルタをかけると (1, 1, 128) などになり

ValueError: One of the dimensions in the output is <= 0 due to downsampling in conv2d_3. Consider increasing the input size. Received input shape [None, 1, 1, 128] which would produce output shape with a zero or negative value in a dimension.

というエラーになるので少しフィルタの次元数を調整します
具体的に (3, 3) を (2, 2) のフィルタにして次のレイヤに行く際の変換した画像の縦横の次元数を少しずつ減らすようにしています

import numpy as np
import tensorflow as tf

mnist = tf.keras.datasets.mnist
# 60000個の学習データ
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 学習データとテストデータを3次元から4次元に変換、Conv2D を使う場合は大抵の場合4次元必要、前回 https://hawksnowlog.blogspot.com/2024/01/keras-mnist-tutorial.html Dense のみは3次元のデータのままいける
x_train = x_train.reshape((60000, 28, 28, 1))
x_test = x_test.reshape((10000, 28, 28, 1))
# ピクセル情報は0-255の範囲なので255で割ることで0-1の範囲に変換する
x_train, x_test = x_train / 255.0, x_test / 255.0

model = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(32, (2, 2), activation='relu', input_shape=(28, 28, 1)),  # 3x3 の複数のフィルタを32枚使って画像にフィルタをかけ新たな行列を作成
    tf.keras.layers.MaxPooling2D((2, 2)),  # 作成した新たな行列に対して 2x2 の領域で最大の値を取得、更にそれを新たな行列とする
    # ここで更に Conv2D + MaxPooling2D のレイヤーを増やしても OK、ただし学習時間は長くなる
    # 以下では 128 個のフィルタで Conv2D するレイヤーを増やしている
    tf.keras.layers.Conv2D(64, (2, 2), activation='relu'),
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Conv2D(128, (2, 2), activation='relu'),  # レイヤーを増やす場合変換した行列の縦横の次元数が途中のレイヤで (1, 1) にならないように Conv2D のフィルタの次元数を調整する必要がある
    tf.keras.layers.MaxPooling2D((2, 2)),
    tf.keras.layers.Conv2D(128, (2, 2), activation='relu'),
    # ここから出力用のレイヤー
    tf.keras.layers.Flatten(),  # 3次元->1次元への変換
    tf.keras.layers.Dense(64, activation='relu'),  # 64ノードの全結合->10ノードの全結合
    tf.keras.layers.Dense(10, activation='softmax'),  # 最後はソフトマックスで10個ある出力の合計が1になるように変換する (定番
])


# モデルコンパイル
model.compile(optimizer='adam',  # 最適化関数は Adaptive moment
              loss='sparse_categorical_crossentropy',  # 損失関数は交差エントロピー誤差
              metrics=['accuracy'])  # 評価関数は正解率

# # 学習
model.fit(x_train, y_train, epochs=5)


# 評価
model.evaluate(x_test, y_test)
# 予測と結果表示
results = model.predict(x_test[:5])
# 結果はDense(10)なので10個の配列の予測値から成り立っている
# その配列10個中で一番値の大きいインデックスが予測した手書きの数字になる
# np.argmax はそれを一発でやってくれる便利メソッド
print(np.argmax(results, axis=1))  # [7 2 1 0 4] になるはず
print(y_test[:5])  # [7 2 1 0 4] こっちは答え

model.summary()

 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 27, 27, 32)        160       
                                                                 
 max_pooling2d (MaxPooling2  (None, 13, 13, 32)        0         
 D)                                                              
                                                                 
 conv2d_1 (Conv2D)           (None, 12, 12, 64)        8256      
                                                                 
 max_pooling2d_1 (MaxPoolin  (None, 6, 6, 64)          0         
 g2D)                                                            
                                                                 
 conv2d_2 (Conv2D)           (None, 5, 5, 128)         32896     
                                                                 
 max_pooling2d_2 (MaxPoolin  (None, 2, 2, 128)         0         
 g2D)                                                            
                                                                 
 conv2d_3 (Conv2D)           (None, 1, 1, 128)         65664     
                                                                 
 flatten (Flatten)           (None, 128)               0         
                                                                 
 dense (Dense)               (None, 64)                8256      
                                                                 
 dense_1 (Dense)             (None, 10)                650       
                                                                 
=================================================================
Total params: 115882 (452.66 KB)
Trainable params: 115882 (452.66 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________

最後に

Keras + CNN で画像解析を試してみました
前回はただの Dense レイヤーだけでしたが今回は CNN という方法を使って判定しています
精度はどちらも良かったのですが画像解析の主流は CNN かなと思います
単純にレイヤーを増やすだけで精度がどんどんよくなるのが摩訶不思議な感じなしますがそれが DeepLearning のすごいところです

CNN 自体の説明は以下の動画が非常に参考になりました
https://www.youtube.com/watch?v=xzzTYL90M8s

参考サイト

0 件のコメント:

コメントを投稿