ゼロから作るDeep Learning Pythonで学ぶディープラーニングの理論と実装　第3章

![deeplearning.PNG](https://qiita-image-store.s3.amazonaws.com/0/113494/2c87f7be-8a66-bd2b-b6c2-4ca1848493ed.png)

[ゼロから作るDeep Learning Pythonで学ぶディープラーニングの理論と実装](https://www.amazon.co.jp/%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89%E4%BD%9C%E3%82%8BDeep-Learning-Python%E3%81%A7%E5%AD%A6%E3%81%B6%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0%E3%81%AE%E7%90%86%E8%AB%96%E3%81%A8%E5%AE%9F%E8%A3%85-%E6%96%8E%E8%97%A4-%E5%BA%B7%E6%AF%85/dp/4873117585/ref=sr_1_1?s=digital-text&ie=UTF8&qid=1483316946&sr=8-1&keywords=deep+learning+%E3%82%BC%E3%83%AD%E3%81%8B%E3%82%89)

# sigmoid function


```python
from sigmoid import *
```


![cd03_test_1_0.png](https://qiita-image-store.s3.amazonaws.com/0/113494/7aff5fe0-81c3-6d6c-f9cb-2ad627a4dc96.png)


```python
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
```


```python
sigmoid(7)
```




    0.9990889488055994




```python
x = np.array([4,6,-2,-1, 2])
sigmoid(x)
```




    array([ 0.98201379,  0.99752738,  0.11920292,  0.26894142,  0.88079708])



# step function


```python
from step_function import *
```


![cd03_test_6_0.png](https://qiita-image-store.s3.amazonaws.com/0/113494/30b9a046-f3e5-3757-d11c-163dc6b56488.png)


```python
def step_function(x):
    return np.array(x > 0, dtype=np.int)
```


```python
step_function(5)
```




    array(1)




```python
step_function(-5)
```




    array(0)




```python
x = np.array([3,-6,4,-1])
step_function(x)
```




    array([1, 0, 1, 0])




```python
x = np.random.randn(2,3)
print(x)
print(step_function(x))
```

    [[ 0.21780529 -0.05316613  1.28802155]
     [-0.55119659 -1.23515555  0.6576237 ]]
    [[1 0 1]
     [0 0 1]]
    

# ReLU function
Rectified Linear Unit

入力が0を超えていれば、その 入力をそのまま出力し、0 以下ならば 0 を 出力する関数です


```python
from relu import *
```


![cd03_test_13_0.png](https://qiita-image-store.s3.amazonaws.com/0/113494/de1e2ede-21c3-b867-6510-5ca8f1a16db8.png)


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

# 多次元配列


```python
B = np.array([[1,2], [3,4], [5,6]]); B
```




    array([[1, 2],
           [3, 4],
           [5, 6]])




```python
np.ndim(B)
```




    2




```python
B.shape
```




    (3, 2)




```python
A = np.array([[3,2,1], [6,5,4]])
```


```python
A.dot(B)
```




    array([[14, 20],
           [41, 56]])



$$
A \cdot B=
\left(
\begin{array}{cc}
3 & 2& 1\\\\
6 & 5 & 4
\end{array}
\right)
\cdot
\left(
\begin{array}{cc}
1 & 2\\\\
3 & 4\\\\
5 & 6
\end{array}
\right)
$$

# 3層ニューラルネットワークの実装

## 入力層から第1層への信号の伝達

![fig3_18.PNG](https://qiita-image-store.s3.amazonaws.com/0/113494/db260fd1-c012-cc9f-e0a0-8c4b0da14dfd.png)


```python
x = np.array([1., .5])
W1 = np.array([[.1, .3, .5], [.2, .4, .6]])
B1 = np.array([.1, .2, .3])
print('入力信号x=', x)
print('重みW1=', W1)
print('バイアスB1=', B1)
```

    入力信号x= [ 1.   0.5]
    重みW1= [[ 0.1  0.3  0.5]
     [ 0.2  0.4  0.6]]
    バイアスB1= [ 0.1  0.2  0.3]
    


```python
x.shape, W1.shape
```




    ((2,), (2, 3))



x, W1の次元の要素数を合わせる！


```python
A1 = np.dot(x, W1) + B1; A1
```




    array([ 0.3,  0.7,  1.1])



隠れ層での重み付き和(重み付き信号とバイアスの総和)を$a$で表す。


```python
Z1 = sigmoid(A1); Z1
```




    array([ 0.57444252,  0.66818777,  0.75026011])



活性化関数で変換された信号を$z$で表す。

sigmoid関数が図中の$h$()にあたる。

---

## 第1層から第2層への信号の伝達

![fig3_19.PNG](https://qiita-image-store.s3.amazonaws.com/0/113494/0a1842e4-db66-94b4-70e3-6424625da01a.png)


```python
W2 = np.array([[.1, .4], [.2, .5], [.3, .6]])
B2 = np.array([.1, .2])
print('W2=', W2)

print('B2=', B2)
```

    W2= [[ 0.1  0.4]
     [ 0.2  0.5]
     [ 0.3  0.6]]
    B2= [ 0.1  0.2]
    


```python
A2 = np.dot(Z1, W2) + B2; A2
```




    array([ 0.51615984,  1.21402696])




```python
Z2 = sigmoid(A2); Z2
```




    array([ 0.62624937,  0.7710107 ])



---

## 第2層から出力層への信号の伝達

![fig3_20.PNG](https://qiita-image-store.s3.amazonaws.com/0/113494/e27b48a3-184b-372a-40cb-555b96b1738e.png)


```python
def identity_function(x):
    """恒等関数と呼ばれる、入力をそのまま出力する、何もしない関数"""
    return x
```


```python
W3 = np.array([[.1, .3], [.2, .4]])
B3 = np.array([.1, .2])
print('W3=', W3)
print('B3=', B3)
```

    W3= [[ 0.1  0.3]
     [ 0.2  0.4]]
    B3= [ 0.1  0.2]
    


```python
A3 = np.dot(Z2, W3) + B3
Y= identity_function(A3); Y
```




    array([ 0.31682708,  0.69627909])



identity_function()という関数を出力層の活性化関数として利用する。

出力層の活性化関数は$\sigma$()で表し、隠れ層の活性化関数$h$()とは区別する。

# 実装まとめ


```python
def init_network():
    """重みとバイアスの初期化"""
    network = {}
    network['W1'] = np.array([[.1, .3, .5], [.2, .4, .6]])
    network['W2'] = np.array([[.1, .4], [.2, .5], [.3, .6]])
    network['W3'] = np.array([[.1, .3], [.2, .4]])
    network['b1'] = np.array([.1, .2, .3])
    network['b2'] = np.array([.1, .2])
    network['b3'] = np.array([.1, .2])
    return network
```


```python
def forward(network, x):
    """入力信号が出力へと変換されるプロセス"""
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
    
    # 入力層
    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    
    # 隠れ層
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    
    # 出力層
    a3 = np.dot(z2, W3) + b3   
    return identity_function(a3)
```


```python
x
```




    array([ 1. ,  0.5])




```python
network = init_network()
x = np.array([1., .5])
forward(network, x)
```




    array([ 0.31682708,  0.69627909])



# 3.5 出力層の設計

## ソフトマックス関数
$$y_k = \frac{\exp{(a_k)}}{\sum_{i=1}^n\exp{(a_i)}}$$


```python
def softmax(a):
    """入力値aを確率で返す"""
    exp_a = np.exp(a - np.max(a))  # オーバーフロー対策
    return  exp_a/ np.sum(exp_a)
```

あまりに大きな値は`inf`="無限大"になって（**オーバーフロー**と呼ぶ。）天井打ちしてしまい、正確な計算ができない。
そのため、以下に示すように数学的に等価となるように、$e^a$を小さい値に直してから合計値で割る。

$$
\begin{align}
y_k = \frac{\exp{(a_k)}}{\sum_{i=1}^n \exp{(a_i)}}
&= \frac{C\exp{(a_k)}}{C\sum_{i=1}^n \exp{(a_i)}}\\\\
&= \frac{\exp{(a_k+\log C)}}{\sum_{i=1}^n\exp{(a_i+\log C)}}\\\\
&= \frac{\exp{a_k + C'}}{\sum_{i=1}^n \exp{(a_i + C')}}
\end{align}
$$

上で説明していることは、つまりは$e^{a_k}$にどんな値を足しても、それを足し算した分母も同じ値足してやれば$y_k$の値に変化はないということ。


```python
softmax(np.array([.3, 2.9, 4.]))
```




    array([ 0.01821127,  0.24519181,  0.73659691])



## データセットの取り出し


```python
import os, sys
sys.path.append(os.pardir)  # 親ディレクトリappend
from dataset.mnist import load_mnist
```

以下を実行するとmnistデータセットを.gzファイルをダウンロードし、解凍してからpklファイルに納める。

終わるのに数分を要する。


```python
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)
```

### MNIST画像の表示
ソースはch03.mnist_show.py

.BMP形式で画像が表示される。


```python
# %load mnist_show.py
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from dataset.mnist import load_mnist
from PIL import Image


def img_show(img):
    pil_img = Image.fromarray(np.uint8(img))
    pil_img.show()

(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False)

img = x_train[0]
label = t_train[0]
print(label)  # 5

print(img.shape)  # (784,)
img = img.reshape(28, 28)  # 形状を元の画像サイズに変形
print(img.shape)  # (28, 28)

img_show(img)

```

    5
    (784,)
    (28, 28)
    

## ニューラルネットワークの推論処理


```python
# %load neuralnet_mnist.py
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import pickle
from dataset.mnist import load_mnist
from common.functions import sigmoid, softmax


def get_data():
    """学習データのロード"""
    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
    return x_test, t_test


def init_network():
    """sample_weight.pklに保存された学習済み重みパラメータの読み込み"""
    with open("sample_weight.pkl", 'rb') as f:
        network = pickle.load(f)
    return network


def predict(network, x):
    """neuralnetworkの実装
    出力層が恒等関数ではなく
    ソフトマックス関数になっている。"""
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = softmax(a3)

    return y
```


```python
%%timeit
x, t = get_data()
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):
    y = predict(network, x[i])
    p= np.argmax(y) # 最も確率の高い要素のインデックスを取得
    if p == t[i]:
        accuracy_cnt += 1

print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
```

    Accuracy:0.9352
    Accuracy:0.9352
    Accuracy:0.9352
    Accuracy:0.9352
    1 loop, best of 3: 1.25 s per loop
    

## バッチ処理

![fig3_27.PNG](https://qiita-image-store.s3.amazonaws.com/0/113494/d62f6e0d-614e-22ae-c1f6-d6643e6121a9.png)

バッチ処理における配列の形状の推移

バス帯域の負荷を軽減する、すなわち、データの読み込みに対して演算の割合を多くするバッチ処理によって1枚当たりの処理時間を短縮できる。

大きな配列を読み込み、一度に大きな配列を計算するほうが、分割された小さな配列を少しずつ計算するよりも演算の終了が早い。


```python
%%timeit
# %load neuralnet_mnist_batch.py
x, t = get_data()
network = init_network()

batch_size = 100 # バッチの数
accuracy_cnt = 0

for i in range(0, len(x), batch_size):
    x_batch = x[i:i+batch_size]
    y_batch = predict(network, x_batch)
    p = np.argmax(y_batch, axis=1)
    accuracy_cnt += np.sum(p == t[i:i+batch_size])

print("Accuracy:" + str(float(accuracy_cnt) / len(x)))

```

    Accuracy:0.9352
    Accuracy:0.9352
    Accuracy:0.9352
    Accuracy:0.9352
    1 loop, best of 3: 269 ms per loop
    

バッチ処理によって約5倍速く実行できた。