初等深層学習・手書き文字認識編

##環境
Microsoftが提供するAzureNotebook下のJupyterNotebookを使います。
ですので、ブラウザさえあれば実践できます。

##実装
早速やっていきましょう！
AzureのJupyterの使い方は省略します。おそらく直感でほぼいけるので…

ざっくりとした説明をその都度していきますが、私個人の理解であり、もしかしたら間違っているかもしれません。(そんなこと無いよう気をつけますが・・・)

まず、セルにこれを入れてRun(実行)しましょう。

```python:
!pip install keras
!pip install numpy
!pip install matplotlib
```

pipコマンドの先頭はJupyterでは「!」を使用します。

次に、必要なモジュールのインポートです。

```python:
import keras
from keras.models import Sequential
from keras.layers import Conv2D,MaxPooling2D,Activation,Flatten,Dense
from keras.optimizers import Adadelta
from keras.datasets import mnist

import numpy as np
import matplotlib.pyplot as plt
```

各種モジュールがインポートされました！
この子達を使って、Sequentialモデルという単入力・単出力のモデルを構築します。

```python:
model = Sequential()

model.add(Conv2D(3,(2,2),input_shape = (28,28,1)))
model.add(Activation("relu"))

model.add(Conv2D(6,(2,2)))
model.add(Activation("relu"))

model.add(MaxPooling2D(pool_size = (2,2)))

model.add(Conv2D(9,(2,2)))
model.add(Activation("relu"))

model.add(Conv2D(12,(2,2)))
model.add(Activation("relu"))

model.add(MaxPooling2D(pool_size = (2,2)))

model.add(Flatten())

model.add(Dense(1024))
model.add(Activation("relu"))

model.add(Dense(512))
model.add(Activation("relu"))

model.add(Dense(256))
model.add(Activation("relu"))

model.add(Dense(10))
model.add(Activation("softmax"))

model.compile(loss = keras.losses.categorical_crossentropy,
              optimizer = Adadelta(),
              metrics = ["accuracy"])
```

さて、解説といきましょう！

最初にmodel変数にSequentialクラスのインスタンスを生成しました。。
このインスタンスにはまだ何も加えられていません。これに層を加えていくことで、モデルが構築されていきます。

まず、Conv2D()についてです。これはConvolution層と言われ、畳み込み層と訳されます。画像データを空間情報を維持したまま計算し、その特徴量を検出します。今回は手書き文字なので、線の形などに留まりますが、例えば犬の特徴量を検出する際は、体表の模様に始まり、次第に輪郭→目・鼻・口→顔→犬といった風に、だんだんと情報の幅を広くしていきます。

こうすることで、より容易に物を識別することが可能となり、精度が飛躍的に向上しました。

各層の下には、Activationという活性化関数をつけています。これなしに、人工知能は成り立たないといっても過言ではありません。ReLUという関数を今回は使用していますが、理由としては「便利だから」とだけ言っておきます。今はその認識でOKです。

特徴量を抽出したら、次はそれを1次元にします。つまり、空間情報を扱うのをやめ、次のDense層に渡せるようにします。Flatten()の役割がこれです。

あとはDense(ノード数）で、ニューラルネットワークを作り、最後に10クラス分類のためにDense(10)として、クラス分類のためのSoftmax()を重ねて終わりです。

Softmax()は、クラスごとの確率を求めたいときに使われます。計算式は割愛しますが、総和分のあるクラスに相当する値を返します。
これを確率とみなせるのです。

因みに、MaxPooling2D()は、計算量を削減する層です。(2,2)のフィルタの中の最大値を抜き出すことで、表す情報はほとんど変化しないままに、情報量を4分の1に減らしてくれます。

##データセット

モデルの学習に使われるデータ群をデータセットと言います。以下のコードを実行して、データを取得し、可視化までしてみましょう。

```python:
(xTrain,yTrain),(xTest,yTest) = mnist.load_data()

xTrain = xTrain.reshape(-1,28,28,1) / 255
xTest = xTrain.reshape(-1,28,28,1) / 255

yTrain = keras.utils.to_categorical(yTrain,10)
yTest = keras.utils.to_categorical(yTest,10)

plt.imshow(xTrain[0].reshape(28,28))
print(yTrain[0])
```

これを実行すると、手書きの数字と、0と1が出力されるはずです。
1が左から何番目にあるか、「0、1、2、・・・」と数えてみましょう。
n番目と、表示されている数字（n）が一致していますね？

これが学習用データと解答データです。

また、255で割っているのは0〜255の値で表されている色情報を0〜1の間に押し込むためです。

##学習
さらりとデータの整形を済ませてしまいましたが、いよいよ学習させてみます。
コードは以下の通りです。

```
log = model.fit(xTrain,yTrain,
                batch_size = 256,
                epochs = 16,
                validation_data = (xTest,yTest))
```

xTrain,yTrainというのは、先程の学習データとその解答データです。

batch_sizeというのは、一度に数万のデータとにらめっこするのは大変ですので、ちっちゃい塊にして、与える時の塊の大きさです。計算時間や、精度にまで影響する値です。

epochsというのは、学習を繰り返す回数です。本来はだんだん大きな値を指定するものですが、今回は16でいいでしょう。

validation_dataというのは、モデルの汎化度を検証するためのデータです。モデルがきちんと色々な問題に対応できるかをこれで測ります。これは、学習には全く影響しません。

##可視化
ここまで苦労してきたのに、その成果がわからないのはつまらないものです。
というわけで、その軌跡をグラフで示しましょう！

```python:
plt.plot(log.history["acc"],label = "Train Accuracy")
plt.plot(log.history["val_acc"],label = "Test Accuracy")
plt.legend()
```

Train Accuracyが上がるにつれて、Testの方も上がっていっていますね！(たぶん）
この2線がよっぽど離れていない限り、モデルは正常に学習しています。
この2線が離れてしまうと、モデルは過学習という状態に陥っています。
これは、偏学習とも言われ、今までいい結果を出していたモデルが、いきなりアホの子になってしまうこともあります。これをいかに回避するかが重要となってきます。

#最後に
ここまで読んでいただき、本当にありがとうございます！
ただ、この記事は過不足なく情報を網羅しているわけではありません…
演算式や、詳細な原理などは省いています。
ですので、もしこの記事で興味をそそられたということがございましたら、ぜひ色々なサイトを見てみてください！
世界は広いです、色々な人が色々な方法で色々な問題にアプローチしています！

この記事が貴方のAIライフへの糸口となることを祈っています！