# Handwritten Digit Recognition using Two-Layer Feedforward Neural Networks in TensorFlow with Keras

```python
import os
from time import time
from datetime import datetime, timedelta, timezone
import math
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import np_utils
from keras.callbacks import EarlyStopping, TensorBoard
```

    Using TensorFlow backend.

```python
(X_train, y_train), (X_test, y_test) = mnist.load_data()
```

MNISTの手書き数字画像の特徴量は、28×28の2次元配列だ。その全ての行をつなげて1行にする。こうしてできた1次元配列(28×28=784次元ベクトル)を、ニューラルネットワークの各入力とする。

```python
img_rows, img_cols = 28, 28
input_dim = img_rows * img_cols # = 784
```

```python
X_train = X_train.reshape(-1, input_dim).astype('float32') / 255
X_test = X_test.reshape(-1, input_dim).astype('float32') / 255
```

入力の各要素(8ビットの非負整数値)を255(=2^8-1)で割ることで、0〜1に正規化している。

```python
X_train.shape
```

    (60000, 784)

```python
# X_train[0]
```

各出力は10次元ベクトルとする。そのベクトルの各次元は、分類されるクラスに対応している。今回は[0..9]の整数だ。

```python
output_dim = 10
```

ラベルは、あらかじめ10次元ベクトルに変換しておく。該当クラスの要素だけ1で、他の要素は0にする。この表現方法はone-hotエンコーディング(OHE)と呼ばれる。

```python
Y_train = np_utils.to_categorical(y_train.astype('int32'), output_dim)
Y_test = np_utils.to_categorical(y_test.astype('int32'), output_dim)
```

```python
Y_train[0]
```

    array([0., 0., 0., 0., 0., 1., 0., 0., 0., 0.], dtype=float32)

中間層(隠れ層)のユニット数(ニューロン数)を16とする。ユニット数を増やせば正解率(accuracy)が上がるが、学習に時間がかかる。また、パラメータの数が増えて過学習が起きやすくなる。

```python
UNITS = 16
# UNITS = 128
# UNITS = 512
```

中間層の活性化関数にはSigmoidを使う。これをReLUに変えるだけでも、正解率が上がることを確認しておく。ReLUの方が学習にかかる時間も短くなる。

```python
ACTIVATION = 'sigmoid'
# ACTIVATION = 'relu'
```

最適化アルゴリズムにはAdamを使う。

```python
# OPTIMIZER = 'sgd'
# OPTIMIZER = 'rmsprop'
OPTIMIZER = 'adam'
```

また、ミニバッチ学習を行う。つまり、ランダムに選択したトレーニングデータの一部(ミニバッチ)を用いた学習(パラメータ更新)を繰り返す。今回、ミニバッチのサイズ(batch_size)を128とする。

```python
BATCH_SIZE = 128
```

トレーニングデータが60,000個だとすると、60,000 / 128 = 468.75なので、468回のパラメータ更新で、データを全て使ったとみなすことができる。この1サイクルのことをエポックという。

今回のように、トレーニングデータ数がバッチサイズで割り切れない場合、実際には、使われずに残るデータがある。さらに、ランダムに選択するがゆえに選択されずに残るデータもある。それでも、(トレーニングデータ数 / バッチサイズ)の小数点以下を切り捨てた整数を、1エポックのイテレーション数とする。

今回、エポック数は300とする。

```python
EPOCHS = 300
```

最後までいけば、468 × 300 = 140,400回のパラメータ更新が行われる。

しかし、学習すればするほど正解率が上がる訳ではない。トレーニングデータに適合し過ぎて、未知データに適合できなくなることがある。これは、オーバーフィッティング(過学習)と呼ばれる。

最適なエポック数を試行錯誤で見つけてもいいが、early stoppingを使うことにする。つまり、クロスエントロピー誤差がほとんど変わらなくなった時点で、学習を終了させる。

この検証時に使われるデータは、バリデーションデータと呼ばれる。バリデーションデータを用意するために、Kerasでは、model.fitの引数validation_splitを渡す。

今回は、

```python
VALIDATION_SPLIT = 0.1
```

としておく。すると、自動的にトレーニングデータの10%をバリデーションデータとして使ってくれる。バリデーションデータをよけておくので、トレーニングデータは、実際には60,000個より少なくなる。

バリデーションデータは、自分でトレーニングデータから一部をよけておいてもいい。この場合、model.fitの引数validation_dataに、そのデータを渡すことになる。

validation_dataにテストデータを渡しているソースコードもあるが、本当は良くない。テストの時に、実際よりも高い正答率が出てしまう。テストデータを使ってチューニングをするのだから、当然そうなる。テストデータは、最後にパフォーマンスを見るためだけにとっておく。

出力層の活性化関数にはsoftmax関数を使う。

\begin{equation}
    \require{color}
    \color{black}
    y = \frac{\exp(x)}{\sum^n_{k=1}\exp(x_k)} \nonumber
\end{equation}

すると、出力は、全てのユニットの出力の和が1であるような正数となる。このことが、確率的解釈を可能とする。つまり、出力層の各ユニットから出力される値は、そのユニットに対応するクラスに分類される確率だと考えることができるようになる。

損失関数、つまり誤差としては、カテゴリカルクロスエントロピーを使う。

\begin{equation}
    \require{color}
    \color{black}
    E = -\sum_kt_k\log(y_k) \nonumber
\end{equation}

$ y_k $: 出力層のk番目のユニットの出力

$ t_k $: 出力層のk番目のユニットのターゲット出力(正解値)

ラベルはOHEで表現されているので、$ t_k $は0か1だ。このことから、損失関数は、

\begin{equation}
    \require{color}
    \color{black}
    E = -\log(y_\hat{k})|\tiny{\hat{k}\mbox{は、正解のクラスに対応するユニットのインデックス}} \nonumber
\end{equation}

ということになる。

この関数は単調減少で、$ y_\hat{k} $が1に近づくほど0に近づく。

では、Kerasで2層のニューラルネットワークを作る。中間層が1層と出力層が1層の構成だ。入力層は、実質的には中間層の1層目に対する入力なので、実装上は出現しない。

```python
model = Sequential()
model.add(Dense(UNITS, activation=ACTIVATION, input_dim=input_dim))
model.add(Dense(output_dim, activation='softmax'))

model.compile(loss='categorical_crossentropy',
             optimizer=OPTIMIZER,
             metrics=['accuracy'])
```

ログを出すには、verbose=1とする。TensorBoardを作成するためのコールバックも用意しておく。

```python
VERBOSE = 1
# VERBOSE = 0

def make_tensorboard(log_dir_base):
    jst = timezone(timedelta(hours=+9), 'JST')
    jst_now = datetime.now(jst)
    log_dir = log_dir_base + '_' + jst_now.strftime('%Y%m%d%H%M%S')
    os.makedirs(log_dir, exist_ok=True)
    tensorboard = TensorBoard(log_dir=log_dir)
    return tensorboard

CALLBACKS = [
    EarlyStopping(patience=0, verbose=1),
    make_tensorboard(log_dir_base='log/tlffnn_keras_mnist')
]
```

実行時間を計測しながら、学習させる。

```python
start_time = time()

history = model.fit(X_train,
                    Y_train,
                    batch_size=BATCH_SIZE,
                    epochs=EPOCHS,
                    verbose=VERBOSE,
                    validation_split=VALIDATION_SPLIT,
                    callbacks=CALLBACKS)

print('Executed in {0:.3f}s'.format(time() - start_time))
```

    Train on 54000 samples, validate on 6000 samples
    Epoch 1/300
    54000/54000 [==============================] - 1s 18us/step - loss: 1.2513 - acc: 0.7514 - val_loss: 0.7045 - val_acc: 0.8877
    Epoch 2/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.5960 - acc: 0.8830 - val_loss: 0.4252 - val_acc: 0.9170
    Epoch 3/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.4251 - acc: 0.9020 - val_loss: 0.3320 - val_acc: 0.9237
    Epoch 4/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.3533 - acc: 0.9104 - val_loss: 0.2832 - val_acc: 0.9290
    Epoch 5/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.3137 - acc: 0.9166 - val_loss: 0.2550 - val_acc: 0.9337
    Epoch 6/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.2882 - acc: 0.9213 - val_loss: 0.2368 - val_acc: 0.9372
    Epoch 7/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.2692 - acc: 0.9251 - val_loss: 0.2237 - val_acc: 0.9405
    Epoch 8/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.2547 - acc: 0.9283 - val_loss: 0.2126 - val_acc: 0.9432
    Epoch 9/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.2432 - acc: 0.9316 - val_loss: 0.2043 - val_acc: 0.9438
    Epoch 10/300
    54000/54000 [==============================] - 1s 16us/step - loss: 0.2332 - acc: 0.9342 - val_loss: 0.1978 - val_acc: 0.9468
    Epoch 11/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.2251 - acc: 0.9367 - val_loss: 0.1919 - val_acc: 0.9482
    Epoch 12/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.2178 - acc: 0.9381 - val_loss: 0.1887 - val_acc: 0.9507
    Epoch 13/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.2112 - acc: 0.9396 - val_loss: 0.1838 - val_acc: 0.9512
    Epoch 14/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.2055 - acc: 0.9411 - val_loss: 0.1805 - val_acc: 0.9497
    Epoch 15/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.2005 - acc: 0.9427 - val_loss: 0.1781 - val_acc: 0.9502
    Epoch 16/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.1958 - acc: 0.9439 - val_loss: 0.1761 - val_acc: 0.9518
    Epoch 17/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.1914 - acc: 0.9450 - val_loss: 0.1746 - val_acc: 0.9515
    Epoch 18/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.1874 - acc: 0.9466 - val_loss: 0.1724 - val_acc: 0.9525
    Epoch 19/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.1840 - acc: 0.9470 - val_loss: 0.1713 - val_acc: 0.9527
    Epoch 20/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.1805 - acc: 0.9480 - val_loss: 0.1705 - val_acc: 0.9523
    Epoch 21/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.1776 - acc: 0.9491 - val_loss: 0.1671 - val_acc: 0.9540
    Epoch 22/300
    54000/54000 [==============================] - 1s 16us/step - loss: 0.1748 - acc: 0.9500 - val_loss: 0.1657 - val_acc: 0.9533
    Epoch 23/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.1718 - acc: 0.9506 - val_loss: 0.1648 - val_acc: 0.9528
    Epoch 24/300
    54000/54000 [==============================] - 1s 15us/step - loss: 0.1692 - acc: 0.9513 - val_loss: 0.1635 - val_acc: 0.9533
    Epoch 25/300
    54000/54000 [==============================] - 1s 16us/step - loss: 0.1669 - acc: 0.9523 - val_loss: 0.1638 - val_acc: 0.9540
    Epoch 00025: early stopping
    Executed in 21.327s

学習曲線を見て、オーバーフィッティングが起きていないか確認する。

```python
plt.figure(1, figsize=(10, 4))
plt.subplots_adjust(wspace=0.5)

plt.subplot(1, 2, 1)
plt.plot(history.history['loss'], label='train', color='black')
plt.plot(history.history['val_loss'], label='validation', color='dodgerblue')
plt.legend()
# plt.ylim(0, 10)
plt.grid()
plt.title('Loss')
plt.xlabel('epoch')
plt.ylabel('loss')

plt.subplot(1, 2, 2)
plt.plot(history.history['acc'], label='train', color='black')
plt.plot(history.history['val_acc'], label='validation', color='dodgerblue')
plt.legend(loc='lower right')
# plt.ylim(0, 1)
plt.grid()
plt.title('Accuracy')
plt.xlabel('epoch')
plt.ylabel('acc')

plt.show()
```

テストデータを使って結果を確認する。

```python
score = model.evaluate(X_test, Y_test, verbose=1)
print('Test loss: ', score[0])
print('Test accuracy: ', score[1])
```

    10000/10000 [==============================] - 0s 18us/step
    Test loss:  0.19434047244638206
    Test accuracy:  0.941

正解率を出すだけではなく、最初の50個の具体的な結果も出す。画像の上に予測を表示し、間違えている場合は括弧つきで正解も表示する。背景色を赤にすることで、間違いを見つけやすくしておく。

```python
TEST_COUNT = 50

Y_predict = model.predict(X_test)

plt.figure(1, figsize=(5, 14))
plt.subplots_adjust(hspace=0.4)

for i in range(TEST_COUNT):
    plt.subplot(TEST_COUNT / 5, 5, i + 1)
    X = X_test[i, :].reshape(img_rows, img_cols)
    Y = Y_predict[i, :]
    prediction = np.argmax(Y)
    y = np.argmax(Y_test[i, :])
    if prediction == y:
        plt.title(prediction, pad=10)
    else:
        plt.title('{}({})'.format(prediction, y), pad=10,
                  backgroundcolor='red')
    plt.axis('off')
    plt.imshow(X, cmap='gray')

plt.show()
```

最後に、パラメータ(weights)を視覚化しておくのも面白い。

```python
weights = model.layers[0].get_weights()[0]

columns = 4
rows = math.ceil(UNITS / columns)
plt.figure(1, figsize=(columns * 1.5, rows * 1.5))
plt.subplots_adjust(wspace=0.25, hspace=0.25)

for i in range(weights.shape[-1]):
    plt.subplot(rows, columns, i + 1)
    w = weights[:, i].reshape(img_rows, img_cols)
    plt.axis('off')
    plt.imshow(w, cmap='gray')

plt.show()
```

白い部分に文字の一部があると、そのユニットは活性し、黒い部分だと抑制する。いくつかは、数字の"2"や"3"の形が浮かび上がっているように見える。それらの数字を認識するのに役立っているのかもしれない。


# Thoughts while solving a nasty integral
*Nhat Le, Aug 4, 2017*

Today, I had to solve an integral like this:

$$\int_0^\infty x^N \ \exp\left(-Mx^2\right) \ \mathrm{d}x $$

It is probably solvable by parts, but being lazy and being a fan of Feynman, I decided to differentiate under the integral sign

I started with the integral:

\begin{align}
		\int_0^\infty x \ \exp\left(-Mx^2\right) \ \mathrm{d}x = \frac{-1}{2M} \ \left.\exp\left(-Mx^2\right) \right|_0^\infty = \frac{1}{2M}
	\end{align}

Now comes the crucial step: I differentiated both sides *with respect to M*, several times...

\begin{align}
	\int_0^\infty x^3 \ \exp\left(-Mx^2\right) \ \mathrm{d}x = \frac{1}{2M^2} \\[1em]
	\int_0^\infty x^5 \ \exp\left(-Mx^2\right) \ \mathrm{d}x = \frac{2}{2M^3} \\[1em]
	\int_0^\infty x^7 \ \exp\left(-Mx^2\right) \ \mathrm{d}x = \frac{6}{2M^4}
\end{align}

Have you noticed a pattern? It is easy to see (and trivial to prove by induction) that

\begin{align}
	\int_0^\infty x^{2n-1} \ \exp\left(-Mx^2\right) \ \mathrm{d}x = \frac{(n-1)!}{2M^n}
\end{align}

We're done! (At least for the case $N$ is odd...)

But the fun doesn't stop there. Let's put $M=1$ to get

$$(n-1)! = 2\int_0^\infty x^{2n-1} \ \exp\left(-x^2\right) \ \mathrm{d}x$$

A simple change of variable $t=x^2$ gets us:

$$(n-1)! = \int_0^\infty t^{n-1} \ \exp\left(-t\right) \ \mathrm{d}t$$

Guess what, the right hand side is exactly the Gamma function, which is the natural extension of the factorial function to real numbers.

$$(n-1)! = \Gamma(n) = \int_0^\infty t^{n-1} \ e^{-t} \ \mathrm{d}t$$

And *that* is pretty amazing.


```python
import matplotlib.pyplot as plt
import numpy as np
import sympy as sym
```

```python
def f(x, power, coeff):
    summ = 0
    xk = 1
    coeff = list(map(float, coeff))
    for i in range(int(power), -1, -1):
        summ += float(xk) * coeff[i]
        xk *= x
    return summ
```

```python
def f2(x, power, coeff):
    summ = 0
    xk = 1
    coeff = list(map(float, coeff))
    for i in range(int(power), -1, -1):
        summ += xk * coeff[i]
        xk *= x
    return summ
```

```python
def fprime(x, power, coff):
    return sym.diff(f2(x, power, coff), x)
```

```python
def grad_descent(X, Y, power, coff, eps=0.05, max_iter=200, precision=0.00001):
    X_old = 0
    X_new = 4.9
    Xs = np.zeros(max_iter)
    Ys = np.zeros(max_iter)
    i = 0
    x = sym.symbols('x')
    dfr = fprime(x, power, coff) #diferansiyel

    while abs(X_new - X_old) > precision and max_iter > i:
        Xs[i] = X_new
        Ys[i] = f(X_new, power, coff)

        X_old = X_new

        df = dfr.evalf(subs={x: X_old})
        X_new = X_old - eps * df
        eps *= 0.99
        i += 1
    return Xs, Ys
```

```python
X = np.arange(-5, 5, 0.1)
Y = np.zeros(len(X))
power = input("Enter power of the function: ")
coeff = input("Enter the function's coefficients: ")
coeff = coeff.split()
for i in range(len(X)):
    Y[i] = f(X[i], power,  coeff)
```

    Enter power of the function: 2
    Enter the function's coefficients: 1 0 0

```python
x_list, y_list = grad_descent(X, Y, power, coeff)
```

```python
plt.subplot(1,2,2)
plt.scatter(x_list,y_list,c="g")
plt.plot(X, Y, c="r")
plt.title("Gradient descent")
plt.show()
```

```python
plt.subplot(1,2,1)
plt.scatter(x_list,y_list,c="g")
plt.plot(X, Y, c="r")
plt.xlim([-3.0,0.3])
plt.title("Zoomed in Gradient descent to Key Area")
plt.show()
```

```python

```


```
import numpy as np
import scipy as sp
import sympy

# Pylab combines the pyplot functionality (for plotting) with the numpy
# functionality (for mathematics and for working with arrays) in a single namespace
# aims to provide a closer MATLAB feel (the easy way). Note that his approach
# should only be used when doing some interactive quick and dirty data inspection.
# DO NOT USE THIS FOR SCRIPTS
#from pylab import *

# the convienient Matplotib plotting interface pyplot (the tidy/right way)
# use this for building scripts. The examples here will all use pyplot.
import matplotlib.pyplot as plt

# for using the matplotlib API directly (the hard and verbose way)
# use this when building applications, and/or backends
import matplotlib as mpl
```

```
# activate pop up plots
#%matplotlib qt
# or change to inline plots
%matplotlib inline
```

```
# some sample data
x = np.arange(-10,10,0.1)
```

```
page_width_cm = 13
dpi = 200
inch = 2.54 # inch in cm
# setting global plot configuration using the RC configuration style
plt.rc('font', family='serif')
plt.rc('xtick', labelsize=12) # tick labels
plt.rc('ytick', labelsize=20) # tick labels
plt.rc('axes', labelsize=20)  # axes labels
# If you don’t need LaTeX, don’t use it. It is slower to plot, and text
# looks just fine without. If you need it, e.g. for symbols, then use it.
#plt.rc('text', usetex=True) #<- P-E: Doesn't work on my Mac
```

```
# create a figure instance, note that figure size is given in inches!
fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(8,6))
# set the big title (note aligment relative to figure)
fig.suptitle("suptitle 16, figure alignment", fontsize=16)

# actual plotting
ax.plot(x, x**2, label="label 12")

# set axes title (note aligment relative to axes)
ax.set_title("title 14, axes alignment", fontsize=14)

# axes labels
ax.set_xlabel('xlabel 12')
ax.set_ylabel(r'$y_{\alpha}$ 12', fontsize=8)

# legend
ax.legend(fontsize=12, loc="best")

# saving the figure in different formats
fig.savefig('figure-%03i.png' % dpi, dpi=dpi)
fig.savefig('figure.svg')
fig.savefig('figure.eps')
```

```
# following steps are only relevant when using figures as pop up windows (with %matplotlib qt)
# to update a figure with has been modified
fig.canvas.draw()
# show a figure
fig.show()
```

```
plt.plot(x,x**2)
#Write code to show grid in plot here
plt.grid()
```

```
plt.plot(x,x**2, label="label 12")
plt.legend(fontsize=12, loc="upper center")
```

```
stride = max( int(len(x) / 10), 1)
plt.plot(x,x**2, 'o-', markevery=stride)
```

```
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(16,6))
ax[0].plot(x,x**2)
ax[1].plot(x,x**2)
```

```
X, Y = np.meshgrid(x,x)
```

```
Z=X**2+Y**2
CS = plt.contour(X, Y, Z)
```

```

```

```
fig, ax1 = plt.subplots()
ax1.plot(x,x**2)
ax2 = ax1.twinx()
ax2.plot(x,x**4, 'r')
plt.show()
```

```
plt.plot(x,x**2)
plt.axvline(5)
plt.axhline(50)
```

```
import datetime
dates = np.array([datetime.datetime.now() + datetime.timedelta(days=i) for i in xrange(24)])
fig, ax = plt.subplots(nrows=1, ncols=1)

ax.plot(dates,range(0,24))
fig.autofmt_xdate()
```

```
x=np.linspace(0,5*np.pi,1000)
y=np.sin(x)
yrand=y+np.random.normal(0,0.3,1000)
fig, ax = plt.subplots(nrows=1, ncols=1)
ax.plot(x,y,linewidth=4.0,linestyle='dashed',color='black',label="y = sin(x)")
ax.plot(x,yrand,'b.',markersize=5)
plt.ylim([-1.5,1.5])

n=10
cmap = mpl.cm.Accent
for i in range(1,n+1):
    coef = np.polyfit(x, y, i)
    polynomial = np.poly1d(coef)
    yFit = polynomial(x)
    labelFit="deg "+str(i-1)
    ax.plot(x,yFit,linewidth=2.0,color=cmap(i / float(n)),label=labelFit)

plt.legend(fontsize=12,  bbox_to_anchor=(1.02, 1), loc=2)
```

```

```