ラビットチャレンジ　レポート（その9　深層学習Day2 Section1）

# 深層学習Day2

## Section1 勾配消失問題

**＜確認テスト＞**
連鎖律の原理を用い、$\frac{dz}{dx}$を求めよ。

```math
z = t^2 \\
t = x + y \\
\frac{dz}{dx} = \frac{dz}{dt} \frac{dt}{dx} = 2t = 2(x+y)
```

勾配消失問題とは、多層ニューラルネットワークにおいて、誤差逆伝播を進めていくにつれて、勾配がどんどん緩やかになっていき、勾配降下法におけるパラメータの更新量が小さくなりすぎてしまい、重みが収束しなくなる現象。

特に活性化関数でよく使用されるSigmoid関数などは、大きい/小さい値に対して勾配が0に近づくので、勾配消失問題を引き起こしやすいという問題点があった。

**＜確認テスト＞**
シグモイド関数を微分した時、入力値が0の時に最大値を取る。その値として正しいものを選択肢から選べ。

⇒実際にシグモイド関数を微分して、0を代入してみる。

```math
\sigma(x) = \frac{1}{1+\exp(-x)} \\

\frac{d \sigma}{dx} = (1- \sigma(x))\sigma(x) \\
\therefore (1-0.5)*0.5 = 0.25
```
よって、答えは(2)0.25

勾配消失問題に対する一般的な解決方法は、

  * 活性化関数の選択
  * 重みの初期値設定
  * バッチ正規化

の3つ。

### 活性化関数の選択
層が深いときに勾配消失問題を起こしやすいSigmoid関数に代わってよく用いられるのが、ReLU関数。
数式で書くと以下。

```math
f(x)=
\begin{cases}
x \ \ (x>0) \\
0 \ \ (x\le 0)
\end{cases}
```

サンプルコードは以下

```python
import numpy as np

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

このReLU関数を用いることによって、勾配消失問題に対処でき、微分計算の簡略化もできる。

### 重みの初期値設定
重みの初期値を、活性化関数に合わせて変える。
使用されるのは主に2種類。

#### Xavierの初期値
  * 重みの要素を、前の層のノード数の平方根で除算した値にする。
  * 使用する活性化関数は以下。
    * ReLU関数
    * Sigmoid関数
    * 双曲線正接関数

#### Heの初期値
  * 重みの要素を、前の層のノード数の平方根で除算した値に対し$\sqrt{2}$を掛け合わせた値にする。
  * 使用する活性化関数は以下。
    * ReLU関数

**＜確認テスト＞**
重みの初期値を0に設定すると、どのような問題が発生するか？
簡潔にまとめよ。

⇒入力値に対して0をかけて伝播していくことになるので、全く学習が進まない。

### バッチ正規化
ミニバッチ単位で入力値のデータの偏りを抑制する方法。
活性化関数に値を渡す前後に、バッチ正規化の処理を含んだ層を加える。

**確認テスト**
一般的に考えられるバッチ正規化の効果を2点挙げよ。

⇒以下の2点

  * 安定して学習できるようになる
  * 計算の効率化

### 実装演習
MNISTデータセットの分類のためのニューラルネットワークを学習させてみる。

```python
import numpy as np
from commonDSpackage import layers
from collections import OrderedDict
from commonDSpackage import functions
from commonDSpackage.mnist import load_mnist
import matplotlib.pyplot as plt

# mnistをロード
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True, one_hot_label=True)
train_size = len(x_train)

print("データ読み込み完了")

# 重み初期値補正係数
wieght_init = 0.01
#入力層サイズ
input_layer_size = 784
#中間層サイズ
hidden_layer_1_size = 40
hidden_layer_2_size = 20

#出力層サイズ
output_layer_size = 10
# 繰り返し数
iters_num = 2000
# ミニバッチサイズ
batch_size = 100
# 学習率
learning_rate = 0.1
# 描写頻度
plot_interval=10

# 初期設定
def init_network():
    network = {} 
    network['W1'] = wieght_init * np.random.randn(input_layer_size, hidden_layer_1_size)
    network['W2'] = wieght_init * np.random.randn(hidden_layer_1_size, hidden_layer_2_size)
    network['W3'] = wieght_init * np.random.randn(hidden_layer_2_size, output_layer_size)
    
    #xavierの初期値
    network['W1'] = np.random.randn(input_layer_size, hidden_layer_1_size) / (np.sqrt(input_layer_size))
    network['W2'] = np.random.randn(hidden_layer_1_size, hidden_layer_2_size) / (np.sqrt(hidden_layer_1_size))
    network['W3'] = np.random.randn(hidden_layer_2_size, output_layer_size) / (np.sqrt(hidden_layer_2_size))

    network['b1'] = np.zeros(hidden_layer_1_size)
    network['b2'] = np.zeros(hidden_layer_2_size)
    network['b3'] = np.zeros(output_layer_size)

    return network

# 順伝播
def forward(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
    
    #活性化関数の変更
    #hidden_f = functions.sigmoid
    hidden_f = functions.relu
    
    u1 =  np.dot(x, W1) + b1
    z1 = hidden_f(u1)
    u2 =  np.dot(z1, W2) + b2
    z2 = hidden_f(u2)
    u3 =  np.dot(z2, W3) + b3
    y = functions.softmax(u3)
 
    return z1, z2, y

# 誤差逆伝播
def backward(x, d, z1, z2, y):
    grad = {}
    
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
    
    #活性化関数の変更
    hidden_d_f = functions.d_relu
    
    last_d_f = functions.d_softmax_with_loss
    
    
    # 出力層でのデルタ
    delta3 = last_d_f(d, y)
    # b3の勾配
    grad['b3'] = np.sum(delta3, axis=0)
    # W3の勾配
    grad['W3'] = np.dot(z2.T, delta3)
    # 2層でのデルタ
    delta2 = np.dot(delta3, W3.T) * hidden_d_f(z2)
    # b2の勾配
    grad['b2'] = np.sum(delta2, axis=0)
    # W2の勾配
    grad['W2'] = np.dot(z1.T, delta2)
    # 1層でのデルタ
    delta1 = np.dot(delta2, W2.T) * hidden_d_f(z1)
    # b1の勾配
    grad['b1'] = np.sum(delta1, axis=0)
    # W1の勾配
    grad['W1'] = np.dot(x.T, delta1)

    return grad

# パラメータの初期化
network = init_network()

accuracies_train = []
accuracies_test = []

# 正答率
def accuracy(x, d):
    z1, z2, y = forward(network, x)
    y = np.argmax(y, axis=1)
    if d.ndim != 1 : d = np.argmax(d, axis=1)
    accuracy = np.sum(y == d) / float(x.shape[0])
    return accuracy

for i in range(iters_num):
    # ランダムにバッチを取得    
    batch_mask = np.random.choice(train_size, batch_size)
    # ミニバッチに対応する教師訓練画像データを取得    
    x_batch = x_train[batch_mask]
    # ミニバッチに対応する訓練正解ラベルデータを取得する
    d_batch = d_train[batch_mask]


    
    z1, z2, y = forward(network, x_batch)
    grad = backward(x_batch, d_batch, z1, z2, y)

    if (i+1)%plot_interval==0:
        accr_test = accuracy(x_test, d_test)
        accuracies_test.append(accr_test)
        
        accr_train = accuracy(x_batch, d_batch)
        accuracies_train.append(accr_train)

        #print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        #print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))

    # パラメータに勾配適用
    for key in ('W1', 'W2', 'W3', 'b1', 'b2', 'b3'):
        network[key]  -= learning_rate * grad[key]


lists = range(0, iters_num, plot_interval)
plt.plot(lists, accuracies_train, label="training set")
plt.plot(lists, accuracies_test,  label="test set")
plt.legend(loc="lower right")
plt.title("accuracy")
plt.xlabel("count")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
# グラフの表示
plt.show()
```
コードの途中で、重みの初期値・活性化関数を変更して実験を行った。

①Sigmoid、初期値ランダム
![深層学習Day2-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/f81bbfa5-d4e7-5f63-2c27-318d5c79f92b.png)

②ReLU、初期値ランダム
![深層学習Day2-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/fc5a88b4-6123-9e21-ea03-34e81f168714.png)

③ReLU、Xavierの初期値使用
![深層学習Day2-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2437549/45c074c4-5a47-54f6-060f-6a9ef667e4ca.png)

この実験結果からも、活性化関数の変更と重みの初期値の変更は、共に効果があることが分かった。