【TensorFlow】全結合層を使ったmnistの画像分類をやってみる

スポンサーリンク
【TensorFlow】全結合層を使った画像分類をやってみるAIを作ってみる
この記事を読んで分かること
  • tensorflowを使った画像分類の実装方法

 

今回はtensorflowを使ってmnistの画像分類をやっていきます。

DeepLearningのチュートリアル的な内容となります。

 

事前準備

必要なライブラリのインポート

まずは必要なライブラリを読み込みます。

import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation
from tensorflow.keras import utils
import matplotlib.pyplot as plt
from tensorflow.keras.utils import plot_model
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, confusion_matrix
import numpy as np
import plotly.express as px

print("Tensorflow version " + tf.__version__)
実行結果(クリックして表示)
Tensorflow version 2.9.1

GPUの読み込み

GPUがある場合は認識して使えるようにします。

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'    # Suppress TensorFlow logging (1)
tf.get_logger().setLevel('ERROR')           # Suppress TensorFlow logging (2)

if tf.test.is_gpu_available():
    gpus = tf.config.experimental.list_physical_devices('GPU')

    for gpu in gpus:
        tf.config.experimental.set_memory_growth(gpu, True)
    
    print("GPUの読み込みが完了しました")

else:
    print("GPUが存在していません")
    device_lib.list_local_devices()

 

データセットの準備

データセットのダウンロード

mnistのデータセットはtensorflowの関数を使って簡単にダウンロードすることができます。

# kerasのMNISTデータの取得
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()

 

データセットの確認

mnistデータセットの中身を確認していきましょう。

print(train_images.shape)
print(test_images.shape)
実行結果(クリックして表示)
(60000, 28, 28)
(10000, 28, 28)

28×28ピクセルの画像が70000枚(学習用60000枚、テスト用10000枚)入っていることがわかります。

 

ラベルには書いた数字が何なのか答えが格納されています。

print(train_labels.shape)
train_labels
実行結果(クリックして表示)
(60000,)
array([5, 0, 4, ..., 5, 6, 8], dtype=uint8)

試しに1枚目の画像とラベルの組み合わせを表示してみました。

plt.imshow(train_images[0], cmap='gray')
plt.title(train_labels[0])
plt.show()

mnist_5

 

5と書いてある画像とラベルが格納されていることがわかります。

 

データセットを扱いやすい形式に変換

正解ラベルをonehot形式に変換します。

# 正解ラベルをonehotに変換
train_labels_onehot = utils.to_categorical(train_labels)
test_labels_onehot = utils.to_categorical(test_labels)

print(train_labels_onehot)
実行結果(クリックして表示)
[[0. 0. 0. ... 0. 0. 0.]
[1. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
...
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 0. 0.]
[0. 0. 0. ... 0. 1. 0.]]

 

モデルの作成

モデルの定義

全結合層を使った簡単なモデルを作成します。

# ネットワークの定義
model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(input_shape=(28, 28)),
    tf.keras.layers.Dense(128, activation='sigmoid'),
    tf.keras.layers.Dense(10, activation='softmax')
])

model.summary()
実行結果(クリックして表示)
_________________________________________________________________
Layer (type) Output Shape Param # 
=================================================================
flatten_5 (Flatten) (None, 784) 0 

dense_24 (Dense) (None, 128) 100480 

dense_25 (Dense) (None, 10) 1290 

=================================================================
Total params: 101,770
Trainable params: 101,770
Non-trainable params: 0
_________________________________________________________________

インプットは画像サイズの28×28として、最終層はクラス数の10を指定しています。

 

モデルのコンパイル

損失関数や最適化アルゴリズムを設定して、モデルのコンパイルを行います。

# 損失関数,最適化アルゴリズムなどの設定 + モデルのコンパイルを行う
model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])

 

モデルの学習

エポック数5で学習をします。

epoch_num = 5

# validation_split=0.1 ---> 0.1(10%)の訓練データが交差検証に使われる
hist = model.fit(train_images, train_labels_onehot, batch_size=200, verbose=1, epochs=epoch_num, validation_split=0.1)
実行結果(クリックして表示)
Epoch 1/5
270/270 [==============================] - 1s 3ms/step - loss: 1.2092 - accuracy: 0.6813 - val_loss: 0.7096 - val_accuracy: 0.8625
Epoch 2/5
270/270 [==============================] - 1s 2ms/step - loss: 0.6482 - accuracy: 0.8503 - val_loss: 0.4886 - val_accuracy: 0.9028
Epoch 3/5
270/270 [==============================] - 1s 2ms/step - loss: 0.5072 - accuracy: 0.8791 - val_loss: 0.3993 - val_accuracy: 0.9148
Epoch 4/5
270/270 [==============================] - 1s 2ms/step - loss: 0.4339 - accuracy: 0.8917 - val_loss: 0.3512 - val_accuracy: 0.9202
Epoch 5/5
270/270 [==============================] - 1s 2ms/step - loss: 0.3905 - accuracy: 0.9012 - val_loss: 0.3210 - val_accuracy: 0.9267

 

バリデーションデータで90%の正解率まで学習できました。

 

学習の推移を可視化

エポックごとのloss関数正解率の推移を確認していきます。

まずはloss関数です。

# loss関数の描画
loss = hist.history['loss']
val_loss = hist.history['val_loss']

# lossのグラフ
plt.plot(range(epoch_num), loss, marker='.', label='loss')
plt.plot(range(epoch_num), val_loss, marker='.', label='val_loss')
plt.legend(loc='best', fontsize=10)
plt.grid()
plt.xlabel('epoch')
plt.ylabel('loss')
plt.show()

mnist_DNN_logloss

 

学習を進めるごとにloglossが下がっていき、うまく学習できていることが確認できました。

 

次に正解率です。

# 正解率の描画
acc = hist.history['accuracy']
val_acc = hist.history['val_accuracy']

# accuracyのグラフ
plt.plot(range(epoch_num), acc, marker='.', label='accuracy')
plt.plot(range(epoch_num), val_acc, marker='.', label='val_accuracy')
plt.legend(loc='best', fontsize=10)
plt.grid()
plt.xlabel('epoch')
plt.ylabel('accuracy')
plt.show()

mnist_DNN_accuracy

正解率も順調に上がっていることが確認できます。

 

テストデータの推論

テストデータの推論

完成したモデルを使ってテストデータに対して推論を行います。

predict_result = model.predict(test_images)
print(predict_result)
実行結果(クリックして表示)
[[1.3324723e-03 7.2508166e-04 9.8155148e-04 ... 9.7192615e-01
2.4663615e-03 5.0377846e-03]
[5.5571616e-02 1.3370248e-02 6.5627718e-01 ... 1.1471154e-03
2.3358773e-02 1.4776604e-03]
[2.8840281e-04 9.7607714e-01 4.6196203e-03 ... 7.3603224e-03
3.6270157e-03 1.6609039e-03]
...
[2.1129617e-04 3.0572866e-03 1.9090319e-03 ... 9.0373568e-03
2.6114585e-02 7.5502887e-02]
[9.6354848e-03 2.1289717e-02 7.3354621e-03 ... 1.3247397e-02
2.5448701e-01 5.1907566e-03]
[6.6589685e-03 3.9037457e-04 6.8264054e-03 ... 4.1297846e-04
1.1598573e-03 1.4709566e-03]]

各クラスに所属する確率が推論結果として返されます。

 

推論結果の確認

このままだと分かりづらいので、一番確率の高い結果のみ抽出します。

cm_predictions = np.argmax(predict_result, axis=-1)
print(cm_predictions)
実行結果(クリックして表示)
[7 2 1 ... 4 5 6]

一枚目の画像は7と予測していました。

 

画像を確認してみましょう。

plt.imshow(test_images[0], cmap='gray')
plt.title(f"ans:{test_labels[0]} pred:{cm_predictions[0]}")
plt.show()

mnist_DNN_pred

 

きちんと予測できていることがわかりました。

 

推論精度の確認

テストデータに対してどのくらいの精度で予測できていたのか計算します。

それぞれの指標値の意味はこちらで解説しています。

正解率(Accuracy)

まず、正解率を算出します。

accuracy = accuracy_score(
    test_labels,
    cm_predictions
)

print(f"Accuracy: {accuracy}")
実行結果(クリックして表示)
Accuracy: 0.9071

 

正解率約90%の精度で予測できていました。

 

適合率(Precision)

次に適合率を算出します。

CLASSES = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
labels = range(len(CLASSES))

precision = precision_score(
    test_labels,
    cm_predictions,
    labels=labels,
    average='macro',
)
print(f"Precision: {precision}")
実行結果(クリックして表示)
Precision: 0.9058395241614962

適合率約90%の精度で予測できていました。

 

再現率(Recall)

続いて再現率を算出します。

recall = recall_score(
    test_labels,
    cm_predictions,
    labels=labels,
    average='macro',
)
print(f"Recall: {recall}")
実行結果(クリックして表示)
Recall: 0.9060706833822063

再現率約90%の精度で予測できていました。

 

F値

最後にF値を求めていきます。

score = f1_score(
    test_labels,
    cm_predictions,
    labels=labels,
    average='macro',
)

print(f"F1-score: {score}")
実行結果(クリックして表示)
F1-score: 0.9056416311891787

F値も約0.90で、全体的にバランスよく予測できていることがわかります。

 

推論結果の分析

混同行列を作り、予測の傾向を分析してみます。

cmat = confusion_matrix(
    test_labels,
    cm_predictions,
    labels=labels,
)

cmat_normalize = (cmat.T / cmat.sum(axis=1)).T  # normalize
print(cmat)
実行結果(クリックして表示)
[[ 958 0 2 3 0 4 7 2 4 0]
[ 0 1113 3 4 1 1 4 1 8 0]
[ 18 6 893 21 22 3 13 22 33 1]
[ 6 2 23 893 1 33 3 16 24 9]
[ 0 1 4 0 918 2 16 1 10 30]
[ 17 1 2 34 10 775 19 7 21 6]
[ 14 2 6 2 15 14 898 2 5 0]
[ 3 14 29 4 13 1 1 927 5 31]
[ 6 7 17 30 11 34 15 15 826 13]
[ 7 6 5 12 61 13 2 26 7 870]]

 

混同行列を作成しましたが、分かりづらいので可視化してみます。

fig = px.imshow(
    cmat,
    text_auto=True,
    labels=dict(x="予測値", y="正解", color="Productivity"),
    x=CLASSES,
    y=CLASSES
)

fig.update_xaxes(side="top")
fig.show()

mnist_DNN_confusion_matrix

 

大半が正解できていますが、3と5を間違えやすいというような傾向を読み取ることができます。

 

おまけ(個別で画像を推論)

testデータをまとめて10000枚一気に推論していきましたが、自分で用意した画像などを1枚推論する場合は画像の形式を整えてから推論する必要があります。

target_image = tf.keras.preprocessing.image.img_to_array(test_images[0]).reshape(1, 28, 28, 1)
target_image.shape
実行結果(クリックして表示)
(1, 28, 28, 1)

 

 

推論は同様にpredict関数で行うことができます。

predict_result = model.predict(target_image)
print(predict_result)
実行結果(クリックして表示)
array([[1.33247231e-03, 7.25081656e-04, 9.81551479e-04, 1.33040855e-02,
3.12581751e-03, 9.71819041e-04, 1.28860513e-04, 9.71926153e-01,
2.46636150e-03, 5.03778458e-03]], dtype=float32)

 

まとめ

tensorflowを使ってmnistの推論を行うことができました。

モデルを作り、学習し、推論し、分析といった流れはどんなAIを作る上でも共通の進め方となります。

 

参考文献

Training a neural network on MNIST with Keras  |  TensorFlow Datasets

 

コメント

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