ディープラーニングを実装から学ぶ～ (まとめ１）実装は、実は簡単

MNISTの予測をディープラーニング(ニューラルネットワーク)で行います。実は、ディープラーニング(ニューラルネットワーク)の実装は簡単です。数十ステップで98%程度の精度を達成できます。
（注意事項）
ディープラーニング(ニューラルネットワーク)の学習方法を理解すること目的としたプログラムです。MNISTデータ程度のデータを想定しています。大量のデータやデータによっては、この実装だけは対応できません。。あくまでもディープラーニング(ニューラルネットワーク)の基本を理解するという視点でご覧ください。

# ニューラルネットワーク
以下のような図を見たことがありますか？脳を模倣したニューラルネットワークです。

![NN1.png](https://qiita-image-store.s3.amazonaws.com/0/212365/f2a3d325-1489-d059-ff97-5cb4db8d084e.png)

緑枠が脳細胞を表すニューロンです。ニューロンとニューロンの間はシナプス（青の◆）で結合しています。シナプスでのデータの受け渡し度合いを重み($ w $)で表します。ニューロン間の結合の度合い、太さとも言えます。
図の関係で、中間層の階層数は２階層としています。この階層数をいくらでも深くできることからディープラーニング（深層学習）と言われます。また、各層のノード数（ニューロンの数）も図では２～３個にしていますが、実際にはもっと多くのノード数となります。

# 学習
ニューラルネットワークを通して学習を行います。学習は、正解データを持ったデータで行います。

![NN2.png](https://qiita-image-store.s3.amazonaws.com/0/212365/ec9b28b8-797c-38d9-9217-b45d43f2c316.png)

入力層から入ったデータをもとに各層を通じて予測データを求めます。次に、正解データとの誤差を計算します。最後に、誤差をもとに誤差がより小さくなるように重みの調整を行います。

# 実装
実装は、pythonで行います。行列演算、数値計算を行うことが可能なnumpyを用います。

## affine変換
シナプスによる結合部分の計算になります。図で青枠の部分です。

![NN3.png](https://qiita-image-store.s3.amazonaws.com/0/212365/8a4cc82b-98ce-b6ca-d4c8-53f229c7381c.png)

$ u_1^{(1)} $の計算は、以下の式で表せます。

```math
u_1^{(1)} = w_{11}^{(1)}x_1 + w_{12}^{(1)}x_2 + w_{13}^{(1)}x_3 + b_1^{(1)}
```

入力層と中間層(1)の間を行列で表すと以下になります。

```math
\begin{pmatrix}
u_1^{(1)}\\
u_2^{(1)}
\end{pmatrix}
=
\begin{pmatrix}
w_{11}^{(1)} & w_{12}^{(1)} & w_{13}^{(1)}\\
w_{21}^{(1)} & w_{22}^{(1)} & w_{23}^{(1)}
\end{pmatrix}
\begin{pmatrix}
x_1\\
x_2\\
x_3
\end{pmatrix}
+
\begin{pmatrix}
b_1^{(1)}\\
b_2^{(1)}
\end{pmatrix}
```

行列の内積(ドット積）で表せます。numpyでの実装は以下になります。

```python
def affine(z, W, b):
    return np.dot(z, W) + b
```

## 活性化関数（中間層）
活性化関数は、ニューロン内での発火を表します。図の青枠の部分です。

![NN4.png](https://qiita-image-store.s3.amazonaws.com/0/212365/db3f5779-d1fd-7b78-cb3b-e98718bd81ef.png)

最近は、活性化関数としてReLUが用いられることが多いようです。
ReLUは、以下の関数です。入力が負になれば以降に情報は伝えません。正の場合は、そのままの値を伝えます。

```math
f_{ReLU}(u) = 
\left\{
\begin{array}{ll}
u & (u \gt 0) \\
0 & (u \le 0)
\end{array}
\right.
```

ReLUのグラフです。単純です。

![NN_ReLU.png](https://qiita-image-store.s3.amazonaws.com/0/212365/ed0d59a0-bee8-33a0-2089-8df005e7ac85.png)

実装は、以下になります。

```python
def relu(u):
    return np.maximum(0, u)
```

## 活性化関数（出力層）
MNISTのように0～9の数値に分類する分類問題の場合は、出力層の活性化関数としてsoftmax関数が利用されます。これは、各分類ごとの確率を求めるためです。総和は、1になります。

![NN5.png](https://qiita-image-store.s3.amazonaws.com/0/212365/d74c6c44-b1c7-776f-b18d-bdea621eb491.png)

softmaxの式です。

```math
f(u_i) = \frac{\exp(u_i)}{\sum_{k=1}^{n}\exp(u_k)}
```

実装は、以下になります。

```python
def softmax(u):
    max_u = np.max(u, axis=1, keepdims=True)
    exp_u = np.exp(u-max_u)
    return exp_u/np.sum(exp_u, axis=1, keepdims=True)
```

## 誤差関数
予測データと正解データの比較に誤差関数を用います。分類問題の場合は、交差エントロピー誤差を利用します。

![NN6.png](https://qiita-image-store.s3.amazonaws.com/0/212365/f752f245-89df-e451-bb44-44cc077fb16a.png)

交差エントロピー誤差の式です。

```math
E= -\sum_kt_k\log{y_k}
```

実装は以下です。

```python
def cross_entropy_error(y, t):
    return -np.sum(t * np.log(np.maximum(y,1e-7)))/y.shape[0]
```

1e-7は、0のlogを取らないようにするための定数です。

## 重みの調整
誤差を小さくするように重みを調整します。
調整には、微分を利用します。誤差関数に対して微分することで傾き（勾配）を求めます。青の線を誤差関数とします。下の図のように、オレンジの場合は、勾配は正となり、値を小さくすることで誤差を小さくできます。逆に緑のように勾配が負となった場合は、値を大きくすることで誤差を小さくできます。

![NN_diff.png](https://qiita-image-store.s3.amazonaws.com/0/212365/77f18f3d-1410-c266-8d28-3b6b18af129e.png)

微分値（勾配）が、正となった場合は、重みを小さくするように調整します。負となった場合は、重みを大きくするように調整します。

最終的に、誤差Eに対する重みの微分を求めます。正式には偏微分を用います。
細かい説明は省略しますが、合成微分を用いて各重みの偏微分は以下のように表せます。

```math
\begin{align}
\frac{\partial E}{\partial w^{(3)}} &= 
\frac{\partial E}{\partial y}
\frac{\partial y}{\partial u^{(3)}}
\frac{\partial u^{(3)}}{\partial w^{(3)}}
\\
\frac{\partial E}{\partial w^{(2)}} &= 
\frac{\partial E}{\partial y}
\frac{\partial y}{\partial u^{(3)}}
\frac{\partial u^{(3)}}{\partial z^{(2)}}
\frac{\partial z^{(2)}}{\partial u^{(2)}}
\frac{\partial u^{(2)}}{\partial w^{(2)}}
\\
\frac{\partial E}{\partial w^{(1)}} &= 
\frac{\partial E}{\partial y}
\frac{\partial y}{\partial u^{(3)}}
\frac{\partial u^{(3)}}{\partial z^{(2)}}
\frac{\partial z^{(2)}}{\partial u^{(2)}}
\frac{\partial u^{(2)}}{\partial z^{(1)}}
\frac{\partial z^{(1)}}{\partial u^{(1)}}
\frac{\partial u^{(1)}}{\partial w^{(1)}}
\end{align}
```

難しそうに見えますが、逆から順番に微分値を掛けていくだけです。
各層の勾配を見ていきましょう。

### 誤差関数、活性化関数(出力層）の勾配
交差エントロピー誤差およびsoftmax関数の式は、以下でした。

```math
E= -\sum_kt_k\log{y_k}
```

```math
f(u_i) = \frac{\exp(u_i)}{\sum_{k=1}^{n}\exp(u_k)}
```

交差エントロピー＋softmaxの勾配は、以下で表せます。

```math
\frac{\partial E}{\partial u_i^{(3)}} = y_i - t_i
```

ここでは、微分を求めることが目的ではありませんので、詳細は省略します。詳しく知りたい方は、以下に書いておりますので参照してみてください。
ディープラーニングを実装から学ぶ（4-2）学習（誤差逆伝播法２）

実装は、以下となります。

```python
def softmax_cross_entropy_error_back(y, t):
    return (y - t)/y.shape[0]
```

複数データを同時に処理することを想定していますので、1データ当たりとするよう調整を入れています。

### 活性化関数（中間層）の勾配
ReLU関数は、以下でした。

```math
f_{ReLU}(u) = 
\left\{
\begin{array}{ll}
u & (u \gt 0) \\
0 & (u \le 0)
\end{array}
\right.
```

こちらの微分は簡単ですね。

```math
f_{ReLU}^{'}(u) = 
\left\{
\begin{array}{ll}
1 & (u \gt 0) \\
0 & (u \le 0)
\end{array}
\right.
```

実装です。

```python
def relu_back(dz, u):
    return dz * np.where(u > 0, 1, 0)
```

前の勾配、dzを掛けています。

### Affine変換の勾配
例えば、$ u_1^{(3)} $は、以下の式となります。

```math
u_1^{(3)} = w_{11}^{(3)}z_1^{(2)} + w_{12}^{(3)}z_2^{(2)} + w_{13}^{(3)}z_3^{(2)} + b_1^{(3)}
```

それぞれ偏微分してみます。偏微分を行う変数以外は定数とみなします。

```math
\begin{align}
\frac{\partial u_1^{(3)}}{\partial z_1^{(2)}} &= w_{11}^{(3)}\\
\frac{\partial u_1^{(3)}}{\partial w_{11}^{(3)}} &= z_1^{(2)}\\
\frac{\partial u_1^{(3)}}{\partial b_1^{(3)}} &= 1
\end{align}
```

一般的には、$ u $を$ z $で偏微分すると$ z $に掛けた重みに、重みで偏微分すると重みに掛けた$ z $に、バイアスは、1ですね。
実装は、以下です。

```python
def affine_back(du, z, W, b):
    dz = np.dot(du, W.T)
    dW = np.dot(z.T, du)
    db = np.dot(np.ones(z.shape[0]).T, du)
    return dz, dW, db
```

それぞれ$ u $の勾配を掛けることで、それ以前の勾配を掛けることになります。

### 重み調整
最後に、求めた勾配から重みを調整します。求めた勾配に、学習率lrを掛けて引きます。

```math
\begin{align}
w^{(1)} &= w^{(1)} - lr * \frac{\partial E}{\partial w^{(1)}}\\
w^{(2)} &= w^{(2)} - lr * \frac{\partial E}{\partial w^{(2)}}\\
w^{(3)} &= w^{(3)} - lr * \frac{\partial E}{\partial w^{(3)}}
\end{align}
```

勾配が正であれば、値を小さくします。勾配が負であれば、値を大きくします。


## 学習全体
全体の流れをもう一度示します。

![NN7.png](https://qiita-image-store.s3.amazonaws.com/0/212365/d71cb3b1-9855-6f05-01c0-c1b6d6a128a9.png)

入力データから予測データを求めるまでを順伝播と言います。
中間層の２階層の実装は、以下です。

```python
    # 順伝播
    u1 = affine(x, W1, b1)
    z1 = relu(u1)
    u2 = affine(z1, W2, b2)
    z2 = relu(u2)
    u3 = affine(z2, W3, b3)
    y  = softmax(u3)
```

逆に勾配の計算を求め、重みの更新を行う部分を逆伝播と言います。

```python
    # 逆伝播
    dy = softmax_cross_entropy_error_back(y, t)
    dz2, dW3, db3 = affine_back(dy, z2, W3, b3)
    du2 = relu_back(dz2, u2)
    dz1, dW2, db2 = affine_back(du2, z1, W2, b2)
    du1 = relu_back(dz1, u1)
    dx, dW1, db1 = affine_back(du1, x, W1, b1)
```

重みの更新です。

```python
    # 重み、バイアスの更新
    W1 = W1 - lr * dW1
    b1 = b1 - lr * db1
    W2 = W2 - lr * dW2
    b2 = b2 - lr * db2
    W3 = W3 - lr * dW3
    b3 = b3 - lr * db3
```

今回は、分かりやすくするため２階層固定としました。多階層とするには、W,b,u,zなどを配列とし、for分でループさせると任意の階層に対応できます。

# プログラム全体
今回作成したプログラムの全体です。

関数部分

```python
import numpy as np
# affine変換
def affine(z, W, b):
    return np.dot(z, W) + b
# affine変換勾配
def affine_back(du, z, W, b):
    dz = np.dot(du, W.T)
    dW = np.dot(z.T, du)
    db = np.dot(np.ones(z.shape[0]).T, du)
    return dz, dW, db
# 活性化関数(ReLU)
def relu(u):
    return np.maximum(0, u)
# 活性化関数(ReLU)勾配
def relu_back(dz, u):
    return dz * np.where(u > 0, 1, 0)
# 活性化関数(softmax)
def softmax(u):
    max_u = np.max(u, axis=1, keepdims=True)
    exp_u = np.exp(u-max_u)
    return exp_u/np.sum(exp_u, axis=1, keepdims=True)
# 誤差(交差エントロピー）
def cross_entropy_error(y, t):
    return -np.sum(t * np.log(np.maximum(y,1e-7)))/y.shape[0]
# 誤差(交差エントロピー）＋活性化関数(softmax)勾配
def softmax_cross_entropy_error_back(y, t):
    return (y - t)/y.shape[0]
```

学習部分

```python
def learn(x, t, W1, b1, W2, b2, W3, b3, lr):
    # 順伝播
    u1 = affine(x, W1, b1)
    z1 = relu(u1)
    u2 = affine(z1, W2, b2)
    z2 = relu(u2)
    u3 = affine(z2, W3, b3)
    y  = softmax(u3)
    # 逆伝播
    dy = softmax_cross_entropy_error_back(y, t)
    dz2, dW3, db3 = affine_back(dy, z2, W3, b3)
    du2 = relu_back(dz2, u2)
    dz1, dW2, db2 = affine_back(du2, z1, W2, b2)
    du1 = relu_back(dz1, u1)
    dx, dW1, db1 = affine_back(du1, x, W1, b1)
    # 重み、バイアスの更新
    W1 = W1 - lr * dW1
    b1 = b1 - lr * db1
    W2 = W2 - lr * dW2
    b2 = b2 - lr * db2
    W3 = W3 - lr * dW3
    b3 = b3 - lr * db3

    return W1, b1, W2, b2, W3, b3
```

たったこれだけです。
これで、MNISTで約98%の正解率まで学習が可能です。

予測は、以下で行います。

```python
def predict(x, W1, b1, W2, b2, W3, b3):
    # 順伝播
    u1 = affine(x, W1, b1)
    z1 = relu(u1)
    u2 = affine(z1, W2, b2)
    z2 = relu(u2)
    u3 = affine(z2, W3, b3)
    y  = softmax(u3)
    return y
```

# 学習の実行
## 実行プログラム
実際に、MNISTで98%の正解率となるか確認するためのプログラムです。
今回の目的は、学習プログラムを作成することでしたので、詳細な説明はしません。（今後、ハイパーパラメータについてまとめたいと考えています。）
実行は、中間層２層、１階層目のノード数100、２階層目のノード数50で実行します。学習は、100データごとに、50回（50エポック）行います。

データは、[MNIST](http://yann.lecun.com/exdb/mnist/)のページからダウンロードしてください。

* train-images-idx3-ubyte.gz: 学習用画像
* train-labels-idx1-ubyte.gz: 学習用正解ラベル
* t10k-images-idx3-ubyte.gz: テスト用画像
* t10k-labels-idx1-ubyte.gz: テスト用正解ラベル

データは、c:\\mnist\\に格納することを想定しています。別のフォルダに格納した場合は、load_mnistのパラメータを変更してください。

```python
import gzip
import numpy as np
# MNIST読み込み
def load_mnist( mnist_path ) :
    return _load_image(mnist_path + 'train-images-idx3-ubyte.gz'), \
           _load_label(mnist_path + 'train-labels-idx1-ubyte.gz'), \
           _load_image(mnist_path + 't10k-images-idx3-ubyte.gz'), \
           _load_label(mnist_path + 't10k-labels-idx1-ubyte.gz')
def _load_image( image_path ) :
    # 画像データの読み込み
    with gzip.open(image_path, 'rb') as f:
        buffer = f.read()
    size = np.frombuffer(buffer, np.dtype('>i4'), 1, offset=4)
    rows = np.frombuffer(buffer, np.dtype('>i4'), 1, offset=8)
    columns = np.frombuffer(buffer, np.dtype('>i4'), 1, offset=12)
    data = np.frombuffer(buffer, np.uint8, offset=16)
    image = np.reshape(data, (size[0], rows[0]*columns[0]))
    image = image.astype(np.float32)
    return image
def _load_label( label_path ) :
    # 正解データ読み込み
    with gzip.open(label_path, 'rb') as f:
        buffer = f.read()
    size = np.frombuffer(buffer, np.dtype('>i4'), 1, offset=4)
    data = np.frombuffer(buffer, np.uint8, offset=8)
    label = np.zeros((size[0], 10))
    for i in range(size[0]):
        label[i, data[i]] = 1
    return label

# 正解率
def accuracy_rate(y, t):
    max_y = np.argmax(y, axis=1)
    max_t = np.argmax(t, axis=1)
    return np.sum(max_y == max_t)/y.shape[0]
```

```python
# MNISTデータ読み込み
x_train, t_train, x_test, t_test = load_mnist('c:\\mnist\\')

# 入力データの正規化(0～1)
nx_train = x_train/255
nx_test  = x_test/255

# ノード数設定
d0 = nx_train.shape[1]
d1 = 100 # 1層目のノード数
d2 = 50  # 2層目のノード数
d3 = 10
# 重みの初期化(-0.1～0.1の乱数)
np.random.seed(8)
W1 = np.random.rand(d0, d1) * 0.2 - 0.1
W2 = np.random.rand(d1, d2) * 0.2 - 0.1
W3 = np.random.rand(d2, d3) * 0.2 - 0.1
# バイアスの初期化(0)
b1 = np.zeros(d1)
b2 = np.zeros(d2)
b3 = np.zeros(d3)

# 学習率
lr = 0.5
# バッチサイズ
batch_size = 100
# 学習回数
epoch = 50

# 予測（学習データ）
y_train = predict(nx_train, W1, b1, W2, b2, W3, b3)
# 予測（テストデータ）
y_test = predict(nx_test, W1, b1, W2, b2, W3, b3)
# 正解率、誤差表示
train_rate, train_err = accuracy_rate(y_train, t_train), cross_entropy_error(y_train, t_train)
test_rate, test_err = accuracy_rate(y_test, t_test), cross_entropy_error(y_test, t_test)
print("{0:3d} train_rate={1:6.2f}% test_rate={2:6.2f}% train_err={3:8.5f} test_err={4:8.5f}".format((0), train_rate*100, test_rate*100, train_err, test_err))

for i in range(epoch):
    # 学習
    for j in range(0, nx_train.shape[0], batch_size):
        W1, b1, W2, b2, W3, b3 = learn(nx_train[j:j+batch_size], t_train[j:j+batch_size], W1, b1, W2, b2, W3, b3, lr)

    # 予測（学習データ）
    y_train = predict(nx_train, W1, b1, W2, b2, W3, b3)
    # 予測（テストデータ）
    y_test = predict(nx_test, W1, b1, W2, b2, W3, b3)
    # 正解率、誤差表示
    train_rate, train_err = accuracy_rate(y_train, t_train), cross_entropy_error(y_train, t_train)
    test_rate, test_err = accuracy_rate(y_test, t_test), cross_entropy_error(y_test, t_test)
    print("{0:3d} train_rate={1:6.2f}% test_rate={2:6.2f}% train_err={3:8.5f} test_err={4:8.5f}".format((i+1), train_rate*100, test_rate*100, train_err, test_err))
```

プログラムは、各実行回ごとに、学習データの正解率、テストデータの正解率、学習データの誤差、テストデータの誤差の順に表示しています。

## 実行結果
実行結果です。

```
  0 train_rate= 11.67% test_rate= 12.18% train_err= 2.30623 test_err= 2.30601
  1 train_rate= 92.39% test_rate= 92.38% train_err= 0.24541 test_err= 0.24307
  2 train_rate= 96.78% test_rate= 96.34% train_err= 0.10314 test_err= 0.11774
  3 train_rate= 97.67% test_rate= 97.02% train_err= 0.07395 test_err= 0.10026
  4 train_rate= 98.03% test_rate= 97.06% train_err= 0.06057 test_err= 0.09716
  5 train_rate= 98.34% test_rate= 97.18% train_err= 0.05275 test_err= 0.09951
  6 train_rate= 98.65% test_rate= 97.30% train_err= 0.04209 test_err= 0.09596
  7 train_rate= 98.51% test_rate= 96.95% train_err= 0.04488 test_err= 0.10893
  8 train_rate= 98.79% test_rate= 97.39% train_err= 0.03560 test_err= 0.09737
  9 train_rate= 99.01% test_rate= 97.47% train_err= 0.03033 test_err= 0.09725
 10 train_rate= 99.12% test_rate= 97.57% train_err= 0.02574 test_err= 0.09797
 11 train_rate= 99.16% test_rate= 97.47% train_err= 0.02525 test_err= 0.10192
 12 train_rate= 99.38% test_rate= 97.67% train_err= 0.01840 test_err= 0.09298
 13 train_rate= 99.44% test_rate= 97.70% train_err= 0.01623 test_err= 0.10091
 14 train_rate= 99.17% test_rate= 97.52% train_err= 0.02395 test_err= 0.10848
 15 train_rate= 99.53% test_rate= 97.76% train_err= 0.01352 test_err= 0.10865
 16 train_rate= 99.48% test_rate= 97.68% train_err= 0.01517 test_err= 0.10646
 17 train_rate= 99.63% test_rate= 97.84% train_err= 0.01055 test_err= 0.10101
 18 train_rate= 99.19% test_rate= 97.48% train_err= 0.02559 test_err= 0.12495
 19 train_rate= 99.68% test_rate= 97.83% train_err= 0.01083 test_err= 0.10719
 20 train_rate= 99.72% test_rate= 97.87% train_err= 0.00812 test_err= 0.10383
 21 train_rate= 99.17% test_rate= 97.40% train_err= 0.02394 test_err= 0.13422
 22 train_rate= 99.66% test_rate= 97.90% train_err= 0.00967 test_err= 0.11079
 23 train_rate= 99.64% test_rate= 97.65% train_err= 0.01141 test_err= 0.11507
 24 train_rate= 99.84% test_rate= 97.91% train_err= 0.00505 test_err= 0.11271
 25 train_rate= 99.83% test_rate= 97.81% train_err= 0.00559 test_err= 0.11566
 26 train_rate= 99.97% test_rate= 98.06% train_err= 0.00120 test_err= 0.10587
 27 train_rate=100.00% test_rate= 98.16% train_err= 0.00046 test_err= 0.10365
 28 train_rate=100.00% test_rate= 98.14% train_err= 0.00028 test_err= 0.10521
 29 train_rate=100.00% test_rate= 98.16% train_err= 0.00020 test_err= 0.10636
 30 train_rate=100.00% test_rate= 98.14% train_err= 0.00017 test_err= 0.10702
 31 train_rate=100.00% test_rate= 98.16% train_err= 0.00015 test_err= 0.10763
 32 train_rate=100.00% test_rate= 98.17% train_err= 0.00014 test_err= 0.10802
 33 train_rate=100.00% test_rate= 98.19% train_err= 0.00013 test_err= 0.10863
 34 train_rate=100.00% test_rate= 98.18% train_err= 0.00012 test_err= 0.10904
 35 train_rate=100.00% test_rate= 98.17% train_err= 0.00011 test_err= 0.10938
 36 train_rate=100.00% test_rate= 98.18% train_err= 0.00010 test_err= 0.10971
 37 train_rate=100.00% test_rate= 98.19% train_err= 0.00009 test_err= 0.11007
 38 train_rate=100.00% test_rate= 98.18% train_err= 0.00009 test_err= 0.11035
 39 train_rate=100.00% test_rate= 98.19% train_err= 0.00008 test_err= 0.11066
 40 train_rate=100.00% test_rate= 98.19% train_err= 0.00008 test_err= 0.11096
 41 train_rate=100.00% test_rate= 98.19% train_err= 0.00008 test_err= 0.11121
 42 train_rate=100.00% test_rate= 98.20% train_err= 0.00007 test_err= 0.11134
 43 train_rate=100.00% test_rate= 98.20% train_err= 0.00007 test_err= 0.11166
 44 train_rate=100.00% test_rate= 98.20% train_err= 0.00007 test_err= 0.11189
 45 train_rate=100.00% test_rate= 98.20% train_err= 0.00007 test_err= 0.11215
 46 train_rate=100.00% test_rate= 98.19% train_err= 0.00006 test_err= 0.11237
 47 train_rate=100.00% test_rate= 98.20% train_err= 0.00006 test_err= 0.11256
 48 train_rate=100.00% test_rate= 98.20% train_err= 0.00006 test_err= 0.11272
 49 train_rate=100.00% test_rate= 98.22% train_err= 0.00006 test_err= 0.11297
 50 train_rate=100.00% test_rate= 98.22% train_err= 0.00006 test_err= 0.11314
```

26回目で、テストデータに対する正解率は、98%を超えました。

最後にもう一度言います。信じられないかもしれませんが、たった数十ステップで98%を超える正解率になりました。
別に、1,2などの数字の形状を教えたわけではありません。自分で学習しました。○△×でもアルファベットでもプログラムを変更なしで、そのまま学習できます。また、画像でなくても数値データであれば入力データから予測が可能となります。

最初にも書きましたが、このプログラムは、ディープラーニング(ニューラルネットワーク)を理解するためのものです。大きなデータやデータによっては対応できません。どんなデータでも対応できるわけではありません。その点の理解をお願いします。

**改版履歴**
2018/6/23 入力データの正規化、重みの初期化を単純化