MNISTを使ったKerasの教師有り学習をやってみた

今更ですがDeepLearningを勉強していまして、その最初の一歩としてMNISTで教師有り学習のモデルを自作してみました。環境構築にもプログラム作成にも苦労したので、次回また勉強する時のためにメモを残します。

#環境

今の所、下の環境で一応動作しております。AnacondaとCUDAとCUDNNはホームページからダウンロードしインストールしました。もし、これより最新のドライバが入っていた場合は最新版をアンインストールしてから、下のバージョンをインストールしてください。

* Windows10Home
* Anaconda 4.10.3
* CUDA 11.2
* CUDNN 8.2.1
* Tensorflow 2.5

Anacondaはインストール後、各パッケージが最新になるようにアップデートしました。（アップデートするとAnacondaが持っているパッケージのバージョンが変わるので、この方法でいつもうまくいくかはわかりません！）

```bash:
conda update --all
```

Tensorflowは`conda`でインストールすると何故かGPUを認識しなかったので`pip`でインストールしました。

```bash:
pip install tensorflow-gpu
```

KerasはTensorflowの一部になったので、Tensorflowが入っていれば特にインストールする必要はなくなりました。
なお、TensorflowがGPUを認識しているかを確認するためには、Pythonで下のスクリプトを実行します。

```python:
from tensorflow.python import client
client.device_lib.list_local_devices()
```

実行後に下のようにGPUという文字が出てくれば認識をしているようです。（出てこなければ認識していない）

```:GPUを認識している例
 name: "/device:GPU:0"
 device_type: "GPU"
```

#MNISTとは

MNISTとは手書きの数字とその値のデータをあつめたデータセットです。画像サイズは28×28で、黒背景に白文字で数字がかかれたものが70000枚あり、DeepLearningのチュートリアルでよく使われます。学習用データを作成するのは苦労しそうなので今回はMNISTを使います。

#モデルを学習する

作成したプログラムを記載します。

```python:学習用プログラム
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense,Flatten,BatchNormalization,Conv2D,MaxPooling2D
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical

(x_train,y_train),(x_test,y_test) = mnist.load_data()
x_train = np.float32(x_train.reshape(60000,28,28,1))/255.0
y_train = to_categorical(y_train,10)
x_test = np.float32(x_test.reshape(10000,28,28,1))/255.0
y_test = to_categorical(y_test,10)

model = Sequential([
	Conv2D(32,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu',input_shape=(28,28,1)),
	MaxPooling2D(pool_size=(2,2)),
	Conv2D(32,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu'),
	MaxPooling2D(pool_size=(2,2)),
	Flatten(),
	Dense(128,activation='relu'),
	BatchNormalization(),
	Dense(128,activation='relu'),
	BatchNormalization(),
	Dense(128,activation='relu'),
	BatchNormalization(),
	Dense(10,activation='softmax')
])

model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
model.fit(x_train,y_train,batch_size=100,epochs=20,verbose=1,validation_data=(x_test,y_test))
model.save('mnist')
```

学習が終わると、`mnist`というフォルダに学習されたモデルが保存されます。

#プログラムの説明

他のサイトでも結構やっているので、他と違う点だけ説明します。

```python:
	Conv2D(32,kernel_size=(3,3),strides=(1,1),padding='same',activation='relu',input_shape=(28,28,1)),
```

他では画像データを(784,)の1次元配列に展開してからDNNに入れている場合が多いのですが、本投稿では2次元配列(28,28)のまま扱っています。その際`Conv2D`の中で`input_shape=(28,28)`としたら何故か動作しなかったので`input_shape=(28,28,1)`にしてます。

#学習されたモデルを使う

今回認識させる画像データは下の`4.png`です。

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/593076/8fef88b4-28fd-2e09-0c8f-586df48dc9f7.png)

これを学習させたモデル`mnist`で認識させてみます。

```python:認識用プログラム
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import load_img,img_to_array

model = load_model('mnist')
image_data = load_img('4.png',target_size=(28,28),color_mode='grayscale')
array_data = img_to_array(image_data).reshape(1,28,28,1)
result = model.predict(array_data)
print(result)
```

```:出力結果
[[0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]]
```

配列に10個の数値が入っています。これはモデルが判定した結果を表しており、左から「0と思われる確率」,「1と思われる確率」・・「9と思われる確率」を表しています。今回は左から5番目で1となっていますので、このモデルは確信を持って4と認識したようです。