Tensorfrowで画像分類など学習してみる（手書き文字認識編3）

前回の記事[「Tensorfrowで画像分類など学習してみる（手書き文字認識編2）」](https://qiita.com/kazuboy/items/c762abb6863f27dd0461)の続きになります。
前回は、データの前処理（スケール変換・1hotベクトル変換と、3層の順伝播型ニューラルネットワークをkerasを用いて構築しました。今回はその続きとしてモデルの学習を進めていきます。

## 前提/環境
前提となる環境とバージョンは下記となります。
・Anaconda3
・Python3.7.7
・pip 20.0
・TensorFlow 2.0.0

この記事ではJupyter Notebookでプログラムを進めていきます。コードの部分をJupyter Notebookにコピー＆ペーストし実行することで同様の結果が得られると思います。前回分のコードは前回の記事を参照してもらえるとありがたいです。

## モデルの学習
機械学習では大まかにモデル構築⇒モデルを用いて訓練⇒未知のデータの分類または予測　という流れで進めます。モデルの訓練は次のコードで行います。keras ではfit関数を利用します。fitは固定のエポック数でモデルを訓練するものになります。

```python:code
model.fit(
    x_train,
    y_train,
    batch_size=32,
    epochs=20,
    validation_split=0.2
)
```
訓練ではデータすべてを使うわけではありません、手書き文字のデータセットの内60000枚を利用します。残りの10000枚のデータは未知のデータへの検証や予測のために利用します。

fit関数を用いるにあたり引数を指定しています。それぞれは下記の意味合いとなります。

<dl>
  <dt>x_train</dt>
  <dd> 訓練データの画像に手書き文字セットの画像の行列に対応します。 </dd>
  <dt>y_train</dt>
  <dd> 訓練データのラベルに対応する行列に対応します。 </dd>
  <dt>batch_size</dt>
  <dd>整数またはNoneを指定します。動作としては設定したサンプル数ごとに勾配の更新を行います。指定しなければデフォルトで32で更新します。機械学習の原理として与えられた関数の傾き（勾配）を計算して、勾配の傾きが大きいほうへパラメーターをずらしていきます。この動きに対する指定となります。 </dd>
  <dt>epochs</dt>
  <dd> 整数で，モデルを訓練するエポック数を指定します。 </dd>
  <dt>validation_sprit</dt>
  <dd> 訓練データ全体を訓練にのみ利用するのではなく、一部を訓練中の検証用データとして用いる場合に設定します。kerasでは0から1までの浮動小数点数で指定します。この設定値は訓練データの中で検証データとして使う割合を示します。この記事では0.2を定していますが、訓練に使われるのは全体の8割、残りの2割は検証用データとして利用するということになります。ここで設定した検証データは訓練には利用されません。 </dd>
</dl>

###モデルの訓練の実行
fitを実行すると下記のような結果が得られます。（一部抜粋）注：結果中のaccuracyなどの値は全く同じにならない場合があります

Train on 48000 samples, validate on 12000 samples
Epoch 1/20
48000/48000 [==============================] - 230s 5ms/sample - loss: 0.1753 - accuracy: 0.9567 - val_loss: 0.3396 - val_accuracy: 0.9468
Epoch 2/20
48000/48000 [==============================] - 246s 5ms/sample - loss: 0.1696 - accuracy: 0.9575 - val_loss: 0.3084 - val_accuracy: 0.9444
・
・
・
Epoch 20/20
48000/48000 [==============================] - 317s 7ms/sample - loss: 0.1477 - accuracy: 0.9636 - val_loss: 0.4187 - val_accuracy: 0.9446

得られる結果としてデータセット数、loss、accuracy、val_loss、val_accuracyというものが得られます。手書き文字セットの訓練データは60000枚でしたがその8割を利用するので48000枚を利用して訓練をしています。残りの2割データで検証を行っています。
loss、accuracy、val_loss、val_accuracyそれぞれの意味合いは下記になります。

<dl>
  <dt>loss</dt>
  <dd>訓練データを用いた場合の損失関数の値。損失関数の値が減少することが望ましい</dd>
  <dt>accuracy</dt>
  <dd>訓練データに対しての分類精度（正解率）この値が高くなることが望ましい。</dd>
  <dt>val_loss</dt>
  <dd>訓練データの一部を検証用データとして利用した場合に得られる。検証用データ（未知のデータ）に対する損失関数の値</dd>
  <dt>val_accuracy</dt>
  <dd>訓練データの一部を検証用データとして利用した場合に得られる。検証用データ（未知のデータ）に対する正解率</dd>
</dl>

基本的にエポックを重ねるごとにlossが減少し、accuracyが増加します。またval_loss,val_accuracyの値に比べ正解率が高く、損失値が小さくなります。訓練用データでのエポックを過剰に進めると、未知のデータに対する正解率が上がらず損失値が減少しない場合があります。これを過学習といいます。

###モデルの正解率の評価

```python:code
test_loss, test_acc = model.evaluate(x_test, y_test, verbose=0)
print('\nTest accuracy:', test_acc)
```
結果
Test accuracy: 0.9414

手書き文字セットの内、10000枚のデータで評価しました。値は訓練データでの正解率に比べ現象しています。

###モデルを使い予測してみる。
predict関数を用いて、手書き文字セットの評価用データを用いて予測してみます。

```python:code
predictions = model.predict(x_test)
predictions[0]
```

結果
array([0.0000000e+00, 1.4220487e-26, 1.4102146e-16, 1.2095399e-13,
       5.7181305e-13, 1.5182375e-28, 0.0000000e+00, 1.0000000e+00,
       0.0000000e+00, 3.0219263e-18], dtype=float32)

この結果として予測した結果の値が得られます。いくつかの候補に値があります。

```python:code
np.argmax(predictions[0])
```
結果
7

予測したもので最も可能性が高いと思われるものが7であるという結果が得られました。
ラベルを確認しましょう。

```python:code
y_test[0]
```
結果
array([0., 0., 0., 0., 0., 0., 0., 1., 0., 0.], dtype=float32)

正解を示すラベルでは7番目の要素が1となっています。この結果から正しいことがわかりました。念のため画像を見ましょう

```python:code
x_test_image = x_test[0].reshape(28, 28) 

plt.figure()
plt.imshow(x_test_image)
plt.colorbar()
plt.grid(False)
plt.show()
```
ここでは一度784に画像を変更しているので、28×28に再度reshapeしなおして画像として表示しています。
そうすると７という文字の手書き画像が表示されます。

##まとめ
ここまで、MNISTの手書きデータセットを用いて順伝播型ニューラルネットワークによる訓練と予測をしてみました。
tensorflowとkerasを用いると簡便にいろいろな機械学習が行えます。
初歩的な内容でしたが、次回以降はCNNによる画像分類を書いていきたいと思います。

2020年7月12日追記
CNNの実装をしてみようということで下記の記事も書き始めました。
[Tensorflow・kerasでCNNを構築して画像分類してみる（概要編）](https://qiita.com/kazuboy/items/b08b1bfacdc6ec393d93)