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

前回の記事[「Tensorfrowで画像分類など学習してみる（手書き文字認識編1）」](https://qiita.com/kazuboy/items/b898b380fca716cf35b4)の続きになります。
前回は、MNISTデータの読み込みと、読み込んだデータの内容を確認しました。今回はその続きで、モデルを作成するまでを書いていきます。

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

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

##順伝播型ニューラルネットワークの実装
###実装その3　画像データのスケール変換

読み込んだMNISTデータは下記のようなものでした。このままでは利用できないのでデータの前処理を行います。
x_train.shape: (60000, 28, 28)
x_test.shape: (10000, 28, 28)
y_train.shape: (60000,)
y_test.shape: (10000,)

画像データを変換します。28×28の2次元行列を784の1次元行列に変換します。あくまでも単に画像を1次元で扱うようにするために変換しています。

```python:code
x_train = x_train.reshape(60000, 784) 
x_test = x_test.reshape(10000, 784) 

#変換後の構造を確認する。
print('x_train.shape:', x_train.shape)
print('x_test.shape:', x_test.shape)
```

結果
x_train.shape: (60000, 784)
x_test.shape: (10000, 784)

次に各ピクセルのデータを0～1の間で扱えるように変換します。
これを正規化（0～1の範囲に収める）という用語で表現します。ただし標準化と似た内容の言葉があり、場面によって標準化と表現する場合もあるようです。ここでは正規化とします。

```python:code
x_train = x_train / 255.
x_test = x_test / 255.
```

1ピクセル毎の画像の濃淡を表す値が0～255までの整数で表現されているものを、0～1の間での表現に変換するということで255で割ります。

これによってどのようにデータが変換されているか確認しましょう。

```python:code
print(x_train[0])
```
結果として下記のようになります。（一部抜粋しています）
正規化前
![正規化前.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659138/5ed0fedb-7b10-4f17-9d88-52184eaefeeb.png)


正規化後
![正規化後.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659138/7acffc83-5682-12e1-6af1-7586f0f97c2e.png)



###実装その4　ラベルデータの変換
ラベルデータについても変更します。ラベルデータは10種の0，1からなる行列に変換します。クラスラベルは整数で表現されていますが、ある値は1、その他は0で表現される形へと変換します。これを1hotベクトルといいます。
変換にはkerasのUtilsモジュールに含まれているto_categoricalメソッドを用います。

```python:code
from tensorflow.keras.utils import to_categorical
y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)
```

###実装その5　モデル構築
この手順でモデルを構築していきます。モデル構築ではkerasで構築する手法の一つである。Sequential APIを利用します。

```python:code
from tensorflow.keras.models import Sequential
model = Sequential()
```

次のステップとして中間層を追加していきます。Denseレイヤーを用いて全結合層を追加します。全結合層はすべての入力がすべてのニューロンと結合している構造を持つ層です。
Sequential APIのaddメソッドを用いて追加します。

```python:code
from tensorflow.keras.layers import Dense

model.add(
    Dense(
        units=64,
        input_shape=(784,),
        activation='relu'
    )
)
```
ここでDeneレイヤー追加時に引数を設定しています。unitsは出力する次元の数を表します。中間層として出力するニューロンの数を設定します。ここでは64としています。中間層にいくつの出力次元を設定するべきかというのは答えがありません。中間層を増やす、もっと出力次元数を大きくするなど手法はたくさんあります。結果として最終的な分類精度などを鑑みてベストな状態を探すということになります。
input_shapeは入力される行列の形を指定します。入力次元は画像のデータを一次元配列としましたが、その数である784を指定しています。
activationはそれぞれのユニットの出力時に利用する活性化関数を指定するものです。今回はreluという関数を指定します。

次のステップとして出力層を追加します。同じくDenseレイヤーをもう一つ追加します。

```python:code
model.add(
    Dense(
        units=10,
        activation='softmax'
    )
)
```



ここまでで、3層の順伝播型ニューラルネットワークの準備ができました。モデルの学習のためにオプティマイザと損失関数を設定し構築します。

```python:code
model.compile(
    optimizer='adam',
    loss='categorical_crossentropy',
    metrics=['accuracy']
)
```
kerasではoptimizerを指定することで最適化が可能です。今回はAdam（Adaptive Moment Estimation)を設定しています。lossは損失関数を選択します。ここでは交差エントロピーを利用します。kerasではcategorical_crossentropyなどいくつか指定ができます。
metricsは評価関数の指定をするものです。ここではaccuracyを選択しています。評価関数も種類がありますので最適なものを利用しましょう。

次回は学習を行うのと、モデルを利用して分類ができるかを試します。