深層学習 Day 2 - Section 2 学習率最適化手法 のまとめ

この記事は個人的なお勉強用のメモです。

# このお勉強の位置

深層学習 Day 2

Section 1 勾配消失問題
**Section 2 学習率最適化手法**　←　これ
Section 3 過学習
Section 4 畳み込みニューラルネットワークの概念
Section 5 最新のCNN

# 講義

### 学習率の決め方

- 初期の学習率を大きく設定し、徐々に学習率を小さくする。
- パラメータごとに学習率を可変させる。

学習率最適化手法を利用
（自動で学習率を最適な値にする方法のこと）

### 学習率最適化手法の一覧

- モメンタム
- AdaGrad
- RMSProp
- Adam

### モメンタム

モーメンタムや、Momentumとも。


```math
V_t=\mu V_{t-1} - \epsilon \nabla E\\
w^{(t+1)}=w^{(t)}+V_t
```

$\mu$：慣性
$V$：速度のようなもの（初期値は $0$）
$\epsilon$：学習係数

※ $\mu$ について。
　DN25動画では「$0.05$ ～ $0.09$」、DN26動画では「$0.5$ ～ $0.9$」という話。
　桁が違うけどどっちが正しい？ 
　常識で考えると $0.1$ 以下は小さすぎるので、後者が正しいように思える。

勾配降下法との違いは、$V$ の部分。

メリット
・大域的最適解になる（局所的最適解にならない）。
・谷間に着いてから最適値に着くまで早い。

### AdaGrad

```math
\begin{align}
h_0&=\theta\\
h_t&=h_{t-1}+(\nabla E)^2\\
w^{(t+1)}&=w{(t)}-\epsilon \frac{1}{\sqrt{h_t}+\theta}\nabla E
\end{align}
```

$\theta$：微小な値

勾配降下法でいう学習率の部分 $\biggl(\epsilon \frac{1}{\sqrt{h_t}+\theta}\biggr)$ が変わっていく。

メリット
・勾配の緩やかな斜面に対して、最適値に近づける。

課題
・鞍点問題（極小値のつもりが実は極大値、多次元での話）の可能性

### RMSProp

```math
\begin{align}
h_t&=\alpha h_{t-1}+(1-\alpha) (\nabla E)^2\\
w^{(t+1)}&=w{(t)}-\epsilon \frac{1}{\sqrt{h_t}+\theta}\nabla E
\end{align}
```

$\alpha$：ハイパーパラメータ

メリット
・大域的最適解になる（局所的最適解にならない）。
・ハイパーパラメータの調整が少ない。

ソースコードの変数名に decay とあるが、減衰という意味。
decay_rate であれば、減衰率か。

### Adam

数式は難解。

モメンタムとRMSPropのいいとこ取りなアルゴリズム。


# 実装演習



### SGD、学習率=0.01

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810819/c60078ac-e9c5-9652-b804-f8caeb9195e8.png)

全く学習が進んでいない。
学習率を0.1にしても傾向は同じだった。

### SGD、学習率=1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810819/13932b92-f076-17db-3fa6-e0c34f46828e.png)

学習率を大きくすると、学習が進むようになった。

### SGD、学習率=10

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810819/035d9f85-4acd-f04d-d7d3-de1fcd91f0fa.png)

学習率を更に10倍にすると、急激に学習が進むようになった。

### SGD、学習率=100

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810819/10bbf59f-b875-3785-b412-068026e31fd3.png)

さらに学習率を大きくすると、逆に学習しなくなった。
SGDの場合は、人間による適切な学習率の設定が必要ということか。
学習率を手動で調整するのは難しい。


### モメンタム、慣性=0.9、学習率=0.01

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810819/5381ec2a-48b2-cdf1-6269-3770a9d20b09.png)

モメンタムは学習率最適化手法の一つなので、学習が進むかと思ったが
意外にも学習が進まない。

### モメンタム、慣性=0.5、学習率=0.01

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810819/629fb542-8a4d-172e-4e7f-d697577b6209.png)

慣性を適切な範囲の下限と言われている0.5にしてみたが、0.9のときと
あまり傾向は変わらず。
慣性を0.1にしてもあまり変わらなかった。
そもそも、学習率が0.01という低い値では、慣性にどんな値を指定しても効果がないのか。

### モメンタム、慣性=0.9、学習率=0.1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810819/802ef459-a217-1d4a-d6f7-66ca71d46b11.png)

学習率を0.1にしてみたら学習が進むようになった。
しかし、学習の傾向はSGDと同じであるため、
モメンタムのおかげというわけではなく、
学習率の変更のおかげ、というのが適切っぽい。
加えて、活性化関数にシグモイド関数を指定しているのも、
学習が進まない要因の一つであろう。

### モメンタム、慣性=0.9、学習率=0.1、活性化関数=ReLU

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/810819/babcb3b9-6fbd-418f-a00f-1cd2137e31e7.png)

こちらはReLU関数の場合。
シグモイド関数に比べて非常によく学習が進んでいる。
もはや、モメンタムがどこまで効果があるのか把握できかねている。

### AdaGradの作成

自分の解答

```python
         if i == 0:
             h[key] = np.zeros_like(network.params[key]) + 1e-7
         h[key] += grad[key] * grad[key]
         network.params[key] -= learning_rate * grad[key] / np.sqrt(h[key])
```

模範解答

```python
         if i == 0:
             h[key] = np.full_like(network.params[key], 1e-4)
         else:
             h[key] += np.square(grad[key])
         network.params[key] -= learning_rate * grad[key] / (np.sqrt(h[key]))
```

np.full_like：すべての要素を指定した値で初期化した配列を生成
np.square：各要素の二乗した配列を返す

そこまで大きく外してはいないと思う。

# 確認テスト

### モメンタム・AdaGrad・RMSPropの特徴

モメンタム（これは講義の内容そのまま）

- 大域的最適解になる（局所的最適解にならない）。
- 谷間に着いてから最適値に着くまで早い。

AdaGrad

- 学習が進むにつれて、学習係数を小さくする。
- 最小値に向かって効率的に動く。

RMSProp

- 主な処理の流れはAdaGradと同じ。
- RMSPropでは過去の勾配を徐々に忘れて、新しい勾配の情報が大きく反映される。

# 修了テスト～練習問題～

### 問題26（AdaGrad）

ソースコード

```python
for key in params.keys():
  self.h[key] += grads[key] * grads[key]
  params[key] = self.lr * grads[key] * (1 / (np.sqrt(self.h[key]) + 1e-7)) 
```

説明文より
勾配の絶対値の大きい要素には h の調整によって更新量を小さくする。
勾配の絶対値の小さい要素には h の調整によって更新量を大きくする。
すなわち、h の逆数を掛ける。

### 問題27（モメンタム）

ソースコード

```python
for key in params.keys():
  self.v[key] = self.momentum * self.v[key] - self.lf * grads[key]
  params[key] += self.v[key]
```

### 問題28（RMSProp）

RMSPropの特徴

1. grads[key]の要素の絶対値が大きい場合、パラメータの更新幅は大きくなる。  
ではない。grads[key]が大きいとself.h[key]も大きくなるが、self.h[key]の逆数が使われるので  
パラメータの更新幅は小さくなる。
2. self.lfが大きいほど、パラメータの更新幅は大きくなる。  
self.lfは掛け算して足すだけ。
3. grads[key]が0に近い場合、更新回数が多くなるほど、パラメータの更新幅は大きくなる。  
1と逆のことを言っている。self.h[key]の逆数が使われるので正しい。
4. self.decay_rateは0から1の値をとる。  
  減衰率なので、これは割合。つまり0から1の間をとるのは正しい。

### 問題82（Adam）

AdamはモメンタムとRMSPropのいいとこどりをしたアルゴリズム。

```math
m_t \leftarrow \beta_1 ・ m_{t-1} + (1-\beta_1)・g_t
```

$(1-\beta_1)$：RMSPropで見かける式
$g_t$：二乗しない理由は、その後の式で $m_t$ をそのまま使うため。
　（その一方、$v_t$ は二乗して、その後の式で平方根をとる。）