プーリングとドロップアウト & 集大成のMNIST認識ネットワークモデル

## プーリング
畳み込み層によって、2次元画像が持つ特徴を利用することができる。画像認識にかんして、もう一つ重要なことがある。それは、画像の位置ずれに対する頑強さである。
例えば、手書き文字"2"を1ピクセルだけずらした画像を入力した場合でも、各配列に対する数値は完全に変化する。人間の目からすると、ほとんど同一と言える入力なのに、ネットワークとしては完全に別なパターンと認識されてしまう。これは、CNNを使ったとしても同様である。この問題を解決する方法として、**プーリング**をいう処理がある。

画像を6×6の枠に分け、2×2に切り出して中の数値の最大値を抜き出していくと3×3の画像に抽出できる。
このときサイズが小さくなるが、似たような画像に対して、同じような出力になるため、ずれに対して頑強な識別ができる。これを2×2の最大プーリングと呼ぶ。入力画像内の2×2の小領域に着目し、最も大きい数値を出力値とする。小領域はストライド2でずらし、同様の処理を繰り返していく。結果、出力画像の縦横のサイズは入力画像の半分になる。

プーリング層をネットワークに取り入れることで、位置だけがずれた画像には似た出力を返す性質を付加することができる。

## ドロップアウト
ネットワークの学習を改善する方法として、**ドロップアウト**という方法が応用でよい結果をもたらす。
ドロップアウトは、学習時に入力層のユニットや中間層のニューロンを確率でランダムに選び、それ以外を無効にする方法である。
無力化したニューロンは存在しないものとして学習を更新する。ミニバッチごとにニューロンを選出し直し、この手続きを繰り返す。

学習後に予測する場合には、すべてのニューロンが使われる。学習時えでは確率の割合のニューロンしか存在しない状態で学習していたのに、予測時には全部参加となると出力が大きくなっていく。そこで、予測時にはドロップアウトを施した層の出力先の重みを確率倍し、小さく設定することでつじづまを合わせる。

ドロップアウトは複数のネットワークを別々に学習させ、予測時にはネットワークを平均化して
合体させる効果があると考えられる。

## 集大成のMNIST認識ネットワークモデル
畳み込みネットワークにプーリングとドロップアウトも取り入れ、層の数も増やしたネットワークを構築する。

```python
import numpy as np
np.random.seed(1)

import matplotlib.pyplot as plt

from keras.datasets import mnist
from keras.utils import to_categorical

from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Activation, Dropout, Flatten, Dense
from keras.optimizers import Adam
import time

(x_train, y_train), (x_test, y_test) = mnist.load_data()

x_train = x_train.reshape(60000, 28, 28, 1)
x_train = x_train.astype('float32')
x_train = x_train / 255

num_classes = 10

y_train = to_categorical(y_train, num_classes)

x_test = x_test.reshape(10000, 28, 28, 1)
x_test = x_test.astype('float32')
x_test = x_test / 255

y_test = to_categorical(y_test, num_classes)

# Sequentialモデルの作成
model = Sequential()
model.add(Conv2D(16, (3, 3), input_shape=(28, 28, 1), activation='relu'))
model.add(Conv2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.25))
model.add(Dense(num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer=Adam(), metrics=['accuracy'])

# 学習
startTime = time.time()
history = model.fit(x_train, y_train, batch_size=1000, epochs=20, verbose=1, validation_data=(x_test, y_test))

# モデル評価
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

calculation_time = time.time() - startTime
print("Calculation time:{0:.3f} sec".format(calculation_time))


def show_prediction():
    n_show = 96
    y = model.predict(x_test)
    plt.figure(2, figsize=(12, 8))
    plt.gray()
    for i in range(n_show):
        plt.subplot(8, 12, i + 1)
        x = x_test[i, :]
        x = x.reshape(28, 28)
        plt.pcolor(1 - x)
        wk = y[i, :]
        prediction = np.argmax(wk)
        plt.text(22, 25.5, "%d" % prediction, fontsize=12)
        if prediction != np.argmax(y_test[i, :]):
            plt.plot([0, 27], [1, 1], color='cornflowerblue', linewidth=5)
        plt.xlim(0, 27)
        plt.ylim(27, 0)
        plt.xticks([], "")
        plt.yticks([], "")

show_prediction()
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3850581/341cefdc-763e-bb8e-94ca-28027b07261d.png)

これによって、ほとんど間違いのない画像識別ができた。