JDLA E資格対策 深層学習day2　レポート


JDAL　E資格の受験用に、深層学習day2を勉強した備忘録


#Section１：勾配消失問題

活性化関数にシグモイド関数を使う場合、勾配消失問題が起きやすい
　⇒誤差逆伝播法を用いて、パラメータ更新する場合
　　シグモイド関数を微分していくが、何層もある場合、シグモイド関数の微分を繰り返すこととなり
　　勾配が小さくなっていき、上流の層になればなるほど勾配がゼロに近づいてしまう。
　　（１以下の数字を繰り返し掛け算することになるため）

勾配消失問題の解決策として下記のような手法がある

　・活性化関数の選択
　　　⇒ReLU関数を用いられることが多い

```math
{{f(x) = \left\{
\begin{array}{ll}
ｘ & (x \geq 0) \\
0 & (x \lt 0)
\end{array}
\right.
}
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/235e836e-6ddd-334a-fc0f-d76fd08aa89e.png)


　・重みの初期値設定
   ⇒設定方法としてXavierとHeが用いられる

　　　　＜Xavier＞
　　　　設定方法：重みの要素を、前の層のノード数の平方根で除算した値を用いる
　　　　活性化関数：
　　　　　　　　　　ReLU関数
　　　　　　　　　　シグモイド(ロジスティック)関数
　　　　　　　　　　双曲線正接関数
　　　　＜He＞
　　　　設定方法：重みの要素を、前の層のノード数の平方根で除算した値に$√2$をかけた値
　　　　   活性化関数：
　　　   　　　　　　　ReLu関数


　・バッチ正規化
　　⇒ミニバッチ単位で、入力値のデータの偏りを抑制する手法

　　　バッチ正規化の使い所：活性化関数に値を渡す前後に、バッチ正規化の処理を孕んだ層を加える


＜確認テスト1＞
　連鎖率の原理を使い、$dz/dxdz/dx$を求めよ。

　　$z=t^2$
　　$t=x+y$
　　$dz/dx=dz/dt･dt/dx=2t×1=2(x+y)$

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

＜確認テスト3＞
　重みの初期値に0を設定すると、どのような問題が発生するか。完結に説明せよ。
　全ての重みが均一に更新されてしまい、正しく学習を行うことができない。

＜確認テスト4＞
　一般的に考えられるバッチ正規化の効果を2点挙げよ。
　・勾配消失問題が起こりづらくなる。
　・計算を高速に行うことができる。


#Section２：学習率最適化手法

　　　1) 学習率とは

　　　　　　勾配降下法で学習を収束させるための係数
　　　　　　学習率が大きいと収束が早いが、最適値にいつまでもたどり着かず発散してしまう
　　　　　　学習率が小さいと発散することはないが収束するまでに時間がかかる。大域局所最適値に収束しづらい

　　　2) 学習率最適化手法

　　　　　・モメンタム
　　　　　　　誤差をパラメータで微分したものと学習率の積を減算した後、
　　　　　　　現在の重みに前回の重みを減算した値と慣性の積を加算する
　　　　　　　局所的最適解にはならず、大域的最適解となる
　　　　　　　谷間についてから最も低い位置(最適値)にいくまでの時間が早い

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/2a5fd1c9-e412-1b64-2ce0-5e629dc232a5.png)


＜コード実装＞

```python
class Momentum:

    """Momentum SGD"""

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None
        
    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():                                
                self.v[key] = np.zeros_like(val)
                
        for key in params.keys():
            self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] 
            params[key] += self.v[key]
```


　　　　　・AdaGrad
　　　　　　　誤差をパラメータで微分したものと再定義した学習率の積を減算する
　　　　　　　勾配の緩やかな斜面に対して、最適値に近づける
　　　　　　　学習率が徐々に小さくなるので、鞍点問題を引き起こす可能性がある

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/ca3f3abd-e608-1018-a38a-b3fd0c2da1eb.png)


＜コード実装＞

```python
class AdaGrad:

    """AdaGrad"""

    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None
        
    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
```


　　　　　・RMSProp
　　　　　　　誤差をパラメータで微分したものと再定義した学習率の積を減算する
　　　　　　　局所的最適解にはならず、大域的最適解となる
　　　　　　　ハイパーパラメータの調整が必要な場合が少ない

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/927cd2cc-0ccd-3500-be2c-38ed97165fe9.png)

＜コード実装＞

```python
class RMSprop:

    """RMSprop"""

    def __init__(self, lr=0.01, decay_rate = 0.99):
        self.lr = lr
        self.decay_rate = decay_rate
        self.h = None
        
    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] *= self.decay_rate
            self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

```


　　　　　・Adam
　　　　　　　モメンタムの、過去の勾配の指数関数的減衰平均
　　　　　　　RMSPropの、過去の勾配の2乗の指数関数的減衰平均
　　　　　　　モメンタムおよびRMSPropのメリットを持つアルゴリズム

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/dc70971d-a649-c08a-286d-ab74bc77678a.png)

＜コード実装＞

```python
class Adam:

    """Adam (http://arxiv.org/abs/1412.6980v8)"""

    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None
        
    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)
        
        self.iter += 1
        lr_t  = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)         
        
        for key in params.keys():

            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])
            
            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
```

＜確認テスト5＞
　モメンタム・AdaGrad・RMSPropの特徴をそれぞれ簡潔に説明せよ。

　　　・モメンタム：局所最適解にならず、大域的最適解になる。最適値に達するまでの時間が早い。
　　　・AdaGrad：勾配の緩やかなものでも最適値に近づくことができる。
　　　・RMSProp：局所最適解にならず大域的最適解になる。ハイパーパラメータの調整が必要な場合が少ない。

#Section３：過学習

　　　　1) 過学習とは

　　　　　テスト誤差と訓練誤差とで学習曲線が乖離すること
　　　　　特定の訓練サンプルに対して、特化して学習する

　　　　　　対応方法：下記の方法
　　　　　　　・正則化
　　　　　　　・ドロップアウト
2)正則化

ネットワークの自由度を制約すること
重みが大きい値を取らないように正則化項を加えて過学習を抑える
L1正則化とL2正則化手法がある

```math
\begin{eqnarray}  L_p=\|x \|_p =\left(|x_1|^p+| x_2 |^p+| x_3 |^p+\cdots +| x_n|^p\right)^{\frac{1}{p}}=\left(\sum_{i=1}^n|x_i|^p\right)^{\frac{1}{p}}  \end{eqnarray}
```

```math
\begin{eqnarray}  E_n(w)+\frac{1}{p}\lambda \|x\|_p  \end{eqnarray}
```

Pが1であればL1正則化、2であればL2正則化です。
λは減衰させるスケーリングのための値です。


3)ドロップアウト

ランダムにノードを削除して学習させること
データ量を変化させずに、異なるモデルを学習させていると解釈できる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/b55a6369-7994-742c-3f6a-78beb8eacde6.png)

＜確認テスト6＞
　機械学習で使われる線形モデルの正則化は、モデルの重みを制限することで可能となる、
　線形モデルの正則化手法の中にリッジ回帰という手法があり、その特徴として正しい物を選択せよ
　⇒(a) ハイパーパラメータを大きな値に設定すると、すべての重みが限りなく0に近づく。

＜確認テスト7＞
　下図について、L1正則化を表しているグラフはどちらか答えよ
　⇒Lasso推定量のグラフ

＜実装:過学習＞


```python
import numpy as np
from collections import OrderedDict
from common import layers
from data.mnist import load_mnist
import matplotlib.pyplot as plt
from multi_layer_net import MultiLayerNet
from common import optimizer


(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

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

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10)
optimizer = optimizer.SGD(learning_rate=0.01)

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10


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]

    grad = network.gradient(x_batch, d_batch)
    optimizer.update(network.params, grad)

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

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

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()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/676e8a9f-2c7f-da7e-b7e8-d6e0b648513a.png)

・トレーニング結果が100%になっており、過学習している。

＜実装:L2正則化＞

```python
from common import optimizer

(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

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

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]


network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10)


iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate=0.01

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10
hidden_layer_num = network.hidden_layer_num

# 正則化強度設定 ======================================
weight_decay_lambda = 0.1
# =================================================

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]

    grad = network.gradient(x_batch, d_batch)
    weight_decay = 0
    
    for idx in range(1, hidden_layer_num+1):
        grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * network.params['W' + str(idx)]
        grad['b' + str(idx)] = network.layers['Affine' + str(idx)].db
        network.params['W' + str(idx)] -= learning_rate * grad['W' + str(idx)]
        network.params['b' + str(idx)] -= learning_rate * grad['b' + str(idx)]        
        weight_decay += 0.5 * weight_decay_lambda * np.sqrt(np.sum(network.params['W' + str(idx)] ** 2))

    loss = network.loss(x_batch, d_batch) + weight_decay
    train_loss_list.append(loss)        
        
    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)
        
        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))               


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()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/45e04339-9948-87b3-13e3-392011073ee1.png)

＜実装：L1正則化＞

```python
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

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

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10)


iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100
learning_rate=0.1

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10
hidden_layer_num = network.hidden_layer_num

# 正則化強度設定 ======================================
weight_decay_lambda = 0.005
# =================================================

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]

    grad = network.gradient(x_batch, d_batch)
    weight_decay = 0
    
    for idx in range(1, hidden_layer_num+1):
        grad['W' + str(idx)] = network.layers['Affine' + str(idx)].dW + weight_decay_lambda * np.sign(network.params['W' + str(idx)])
        grad['b' + str(idx)] = network.layers['Affine' + str(idx)].db
        network.params['W' + str(idx)] -= learning_rate * grad['W' + str(idx)]
        network.params['b' + str(idx)] -= learning_rate * grad['b' + str(idx)]        
        weight_decay += weight_decay_lambda * np.sum(np.abs(network.params['W' + str(idx)]))

    loss = network.loss(x_batch, d_batch) + weight_decay
    train_loss_list.append(loss)        
        
    if (i+1) % plot_interval == 0:
        accr_train = network.accuracy(x_train, d_train)
        accr_test = network.accuracy(x_test, d_test)
        accuracies_train.append(accr_train)
        accuracies_test.append(accr_test)
        
        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))               
                
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()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/bc8aadba-52f4-7290-d6a0-5e1388557f50.png)

＜実装：ドロップアウト＞

```python
class Dropout:
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)

    def backward(self, dout):
        return dout * self.mask
```

```python
from common import optimizer
(x_train, d_train), (x_test, d_test) = load_mnist(normalize=True)

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

# 過学習を再現するために、学習データを削減
x_train = x_train[:300]
d_train = d_train[:300]

# ドロップアウト設定 ======================================
use_dropout = True
dropout_ratio = 0.15
# ====================================================

network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100], output_size=10,
                        weight_decay_lambda=weight_decay_lambda, use_dropout = use_dropout, dropout_ratio = dropout_ratio)
optimizer = optimizer.SGD(learning_rate=0.01)
# optimizer = optimizer.Momentum(learning_rate=0.01, momentum=0.9)
# optimizer = optimizer.AdaGrad(learning_rate=0.01)
# optimizer = optimizer.Adam()

iters_num = 1000
train_size = x_train.shape[0]
batch_size = 100

train_loss_list = []
accuracies_train = []
accuracies_test = []

plot_interval=10


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]

    grad = network.gradient(x_batch, d_batch)
    optimizer.update(network.params, grad)

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

        print('Generation: ' + str(i+1) + '. 正答率(トレーニング) = ' + str(accr_train))
        print('                : ' + str(i+1) + '. 正答率(テスト) = ' + str(accr_test))        
        
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()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/fd94ab14-4e65-c2df-926e-164c6e2f0140.png)

・ドロップアウトにより、過学習は抑えられているが、学習は収束していないので
　もう少し学習進めたほうがよい。


#Section４：畳み込みニューラルネットワークの概念

・中間層に畳み込み層とプーリング層をもつニューラルネットワーク
・主に画像認識で用いられることが多い
・計算量が多い

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/c179ecf6-50fa-9597-11d9-e7fb76bdedf1.png)

##畳み込み層

＜バイアス＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/0d206944-0fd3-0305-c548-de5c2d3ee259.png)


＜（ゼロ）パディング＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/691baa00-1024-1c53-51fb-bb2c8a0bcb38.png)


＜ストライド＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/c6f736a8-279c-3576-7ce2-5f41cafd881b.png)


＜チャンネル＞
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/833437cf-e992-365d-e029-60f6ec273e8c.png)

なお、入力サイズをW×H、フィルタサイズをFw×Fh、パディングをp、ストライドをsとし、
畳み込み層の出力サイズをOW×OHとすると、OWおよびOHは次式により求められる。

```math
OW=\frac{W+2p-Fw}{s}+1, \quad OH=\frac{H+2p-Fh}{s}+1
```


##プーリング層

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2456345/923b18e4-23ac-2df9-5541-c02b6e939128.png)


＜確認テスト8＞
　サイズ6*6の入力画像を、サイズ2*2のフィルタで畳み込んだ時の出力画像のサイズを答えよ。なおストライドとパディングは1とする。
　(6 + 2*1 -2) / 1 + 1 = 7
　したがって、サイズは7*7




#Section５：最新のCNN

＜AlexNet＞
2012年にILSVRC（ImageNet Large Scale Visual Recognition Challenge）で優勝したモデル。
このモデルの優勝から深層学習に注目が集まったと思われる。

AlexNetでは畳み込み層及びプーリング層（Max Pooling）が5層あり、全結合層が3層ある。初期に考案されたCNNに比べると層が深くなっているため、過学習の対策としてサイズ4096の全結合層の出力でドロップアウトを利用している。

＜VGG-16＞
13層の畳み込み層と３層の全結合層により構成される。畳み込み層とプーリング層を単純に何層も
重ねただけであるにもかかわらず、高い予測精度を持つ。畳み込み層が16層であるものはVGG-19と呼ぶ。

＜GoogleNet＞

2014年に提案されたモデル。名前は最初期の「LeNet」に由来。
22層で構成されており、従来手法とは異なる手法をいくつか用いている

　　・Inceptionモジュール：異なるサイズの畳み込み層を並列につなぐ
　　・GAP：全結合層を重ねる代わりにGlobal Average Pooling(GAP)を導入

＜ResNet＞

shortcut connectionを用いた残差ブロックを導入し勾配消失問題に対処することで、
152層まで層を増やすことに成功した。shortcut connectionは畳み込み層を重ねたものをまたぐように、
入力値をそのまま伝える。