深層学習 day1 NN・day2 CNN【ラビットチャレンジ】

# 概要
本記事は「JDLA E資格」の取得に必要なJDLA認定プログラムのひとつであるラビットチャレンジの受講レポートとして投稿したものです。

E資格について
https://www.jdla.org/certificate/engineer/

# 目次
day1 NN
[Section1：入力層～中間層](#Section1：入力層～中間層)
[Section2：活性化関数](#Section2：活性化関数)
[Section3：出力層](#Section3：出力層)
[Section4：勾配降下法](#Section4：勾配降下法)
[Section5：誤差逆伝播法](#Section5：誤差逆伝播法)
[day1実装](#day1実装)

day2 CNN
[Section1：勾配消失問題](#Section1：勾配消失問題)
[Section2：学習率最適化手法](#Section2：学習率最適化手法)
[Section3：過学習](#Section3：過学習)
[Section4：畳み込みニューラルネットワークの概念](#Section4：畳み込みニューラルネットワークの概念)
[Section5：最新のCNN](#Section5：最新のCNN)
[day2実装](#day2実装)

# day1
# Section1：入力層～中間層
### 深層学習（ディープラーニング）
人間が明示的にプログラムする代わりに多数の中間層を持つニューラルネットワークを用いて
入力値から目的とする出力値に変換する数学モデルを構築し、重みとバイアスを最適化する。

### 層とノード
入力値を与えるところを入力層、出力値が出てくるところを出力層、その間にあるものを中間層という。
深層学習は中間層が多いもの、特に4層以上のものを指すことが多い。
また、各層の値を受け取る部分のことをノード（ニューロン）という。

### ニューラルネットワーク(NN)の対象
・回帰（連続値）→株価予想など
・分類（離散値）→動物の画像分類など

対象を数値に置き換えることで、ニューラルネットワークで扱えるようにする。

### 入力層と中間層
入力されたデータ$\boldsymbol{x}$と重み$\boldsymbol{W}$のドット積をとり、バイアス$b$を足し合わせる。
更にその値を活性化関数に通した結果を次の層で処理する。

# Section1：入力層～中間層・確認テスト
・ディープラーニングは、結局何をやろうとしているか2行以内で述べよ。
また、次の中のどの値の最適化が最終目的か。全て選べ。

→人間が明示的にプログラムする代わりに多数の中間層を持つニューラルネットワークを用いて
入力値から目的とする出力値に変換するを数学モデルを構築すること。
重み[W]
バイアス[b]

・次のネットワークを紙にかけ。
入力層︓2ノード1層
中間層︓３ノード2層
出力層︓1ノード1層

![test1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/76512034-c7cc-a54f-9e6a-76221ca03bca.png)

・この図式に動物分類の実例を入れてみよう。

![test2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/865e9bef-862e-4953-6cfc-058a293c6e39.png)

・この数式をPythonで書け。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/48a76563-879f-524a-7539-0a587fb60d89.png" width=50%>

```python:解答
u1 = np.dot(x, W1) + b1
```

・1-1のファイルから中間層の出力を定義しているソースを抜き出せ。

```python:解答
# 2層の総入力
u2 = np.dot(z1, W2) + b2

# 2層の総出力
z2 = functions.relu(u2)
```

# Section2：活性化関数
・ニューラルネットワークにおいて、次の層への出力の大きさを決める非線形の関数。

### 中間層用の活性化関数

・ステップ関数
出力は常に１か０。パーセプトロン（ニューラルネットワークの前身）で利用された関数。

```python:ステップ関数
def step_function(x):
    if x > 0:
        return 1
    else:
        return 0
```

・シグモイド関数
0と1の間を緩やかに変化。勾配消失問題を引き起こす事があった。

```python:シグモイド関数
defsigmoid(x):
    return 1/(1 + np.exp(-x))
```

・ReLU関数
勾配消失問題の回避とスパース化に貢献

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

# Section2：活性化関数・確認テスト
・線形と非線形の違いを図にかいて簡易に説明せよ。
![test4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/d29ed013-377c-6516-c13c-a29474931c1b.png)
線形：加法性･斉次性を満たす
非線形：加法性･斉次性を満たさない

・布されたソースコードより該当する箇所を抜き出せ。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/a5bdc609-57b6-56ed-09bf-40e36f53eea9.png" width=50%>

```python:解答
z1 = functions.sigmoid(u)
```

# Section3：出力層

中間層は次の層の入力として適切なデータを出力するが、出力層は人間が必要なデータを出力する必要がある。

### 誤差関数
出力結果と訓練データを比較して精度を評価する。

・平均二乗誤差

```math
E_n(\boldsymbol{w}) = \frac{1}{2} \sum_{j=1}^{J} (y_j - d_j)^2 = \frac{1}{2}||(y-d)||^2 
```

```python:平均二乗誤差
loss = functions.mean_squared_error(d, y)
```

・交差エントロピー

```math
E_n(w) = - \sum_{i=1}^I d_i \log y_i
```

```python:交差エントロピー
loss = cross_entropy_error(d, y)
```

### 中間層と出力層
中間層：しきい値の前後で信号の強弱を調整
出力層：信号の大きさ（比率）はそのままに変換
・分類問題の場合、出力層の出力は0 ~ 1 の範囲に限定し、総和を１とする必要がある
・出力層と中間層で利用される活性化関数が異なる

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/1671641f-2987-7b21-ba47-93d2c6c3af21.png" width=50%>

・ソフトマックス関数

```math
f(i, u) = \frac{e^{u_i}}{\sum_{k=1}^{K} e^{u_k}}
```

# Section3：出力層・確認テスト

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/c5dca79b-746d-cd4f-b477-4aedc23bd9e5.png" width=50%>
・正負を合わせるため。
・微分したときに計算しやすくするため。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/1cab9d2e-f74b-d44d-be52-1fa1bad59603.png" width=50%>
・if文はミニバッチとして取り扱う場合
・下から2行目はプログラムを安定化させるため
・return文がソフトマックス関数

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/51f5816a-bbf1-1098-7b9f-fa5d1a8d2547.png" width=50%>
・return文が交差エントロピー
・1e-7($= 1 × 10^{-7}$)というごく僅かな値を加えることで0にならないようにしている

# Section4：勾配降下法
・誤差が最小になる重みやバイアスを見つけるために利用
・学習率が大きすぎると発散してしまう
・学習率が小さすぎると収束までに時間がかかりすぎてしまったり極小値に収束してしまったりする
・誤差関数の値をより小さくする方向に重みとバイアスを更新し、次の周（エポック）に反映

### 確率的勾配降下法(SGD)

```math
W^{(t+1)} = W^{(t)} - \varepsilon \nabla E_n
```
・全データから一部をランダムに抽出したサンプルの誤差
・データが冗⻑な場合の計算コストの軽減
・望まない局所極小解に収束するリスクの軽減
・オンライン学習ができる

### ミニバッチ勾配降下法

```math
W^{(t+1)} = W^{(t)} - \varepsilon \nabla E_t
```
・メモリ消費を抑えられるオンライン学習の特徴をバッチ学習で利用できるようにした手法
・ランダムに分割したデータの集合（ミニバッチ）$D_t$に属するサンプルの平均誤差
・並列処理ができる

### 誤差勾配

```math
\nabla E = \frac{\partial E}{\partial \boldsymbol{w}} = \left[ \frac{\partial E}{\partial w_1} ... \frac{\partial E}{\partial w_M} \right]
```

・数値微分
→プログラムで微小な数値を生成し擬似的に微分を計算する一般的な手法

```math
\frac{\partial E}{\partial w_m} \approx \frac{E(w_m + h) - E(w_m - h)}{2h}
```
・負荷が大きいというデメリットがあるため、誤差逆伝播法を用いる

# Section4：勾配降下法・確認テスト
・オンライン学習とは何か2行でまとめよ
→学習データが入ってくるたびに都度パラメータを更新し、学習を進めていく方法。
　バッチ学習では一度にすべての学習データを使ってパラメータ更新を行う。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/ad9ab0a2-b605-92f9-4c36-b607d3867f16.png" width=50%>

$\boldsymbol{w}^t$ → ($- \varepsilon \nabla E_1$) → $\boldsymbol{w}^{t+1}$ → ($- \varepsilon \nabla E_2$) → $\boldsymbol{w}^{t+2}$

# Section5：誤差逆伝播法

・算出された誤差を、出力層側から順に微分し、前の層前の層へと伝播。
　最小限の計算で各パラメータでの微分値を解析的に計算する手法。
・微分の連鎖律を利用
・一度計算した結果を再利用することができるので、効率的に入力に近い側の微分値を求めることができる

# Section5：誤差逆伝播法・確認テスト
・誤差逆伝播法では不要な再帰的処理を避ける事が出来る。既に行った計算結果を保持しているソースコードを抽出せよ。

```python:解答
delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1)
```
delta2を再利用している

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/ce86c454-477e-0452-5115-15fb7b4f7ef1.png" width=50%>

```python:解答
# 出力層でのデルタ
delta2 = functions.d_mean_squared_error(d, y)

## 試してみよう
delta1 = np.dot(delta2, W2.T) * functions.d_sigmoid(z1)
```

# day1実装
### 順伝播
https://github.com/kyo-git/rabbit-challenge/blob/main/1_1_forward_propagation.ipynb

### 誤差逆伝番法
https://github.com/kyo-git/rabbit-challenge/blob/main/1_2_back_propagation.ipynb

### 確率的勾配降下法(SGD)
https://github.com/kyo-git/rabbit-challenge/blob/main/1_3_stochastic_gradient_descent.ipynb

# day2
# Section1：勾配消失問題
逆伝播で勾配の値が小さくなっていってしまう問題。
シグモイド関数の微分値は最大でも0.25となり、掛け合わせると小さな値となってしまう。
解決策として次のものを見直すことが考えられる。
・活性化関数の選択
・重みの初期値設定（Xavier）
・バッチ正規化

### 活性化関数の選択

・ReLU関数
勾配消失問題の回避とスパース化に貢献

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

### 重みの初期値設定
#### Xavier
・初期値の設定方法
　重みの要素を、前の層のノード数の平方根で除算した値
・S字カーブの関数で用いる

```python:Xavierの初期値 
network['W1'] = np.random.randn(input_layer_size, hidden_layer_size) / np.sqrt(input_layer_size)
network['W2'] = np.random.randn(hidden_layer_size, output_layer_size) / np.sqrt(hidden_layer_size)
```

### He
・初期値の設定方法
　重みの要素を、前の層のノード数の平方根で除算した値に対し$\sqrt{2}$をかけ合わせた値
・S字カーブでない関数で用いる

### バッチ正規化
・ミニバッチ単位で、入力値のデータの偏りを抑制する手法
・活性化関数に値を渡す前後に、バッチ正規化の処理を孕んだ層を加える
・統計的正規化を行い、NNで扱いやすいように調整

# Section1：勾配消失問題・確認テスト
・連鎖律の原理を使い、$dz/dx$を求めよ。

```math
z = t^2 \\
t = x + y \\
```

```math
\frac{dz}{dt} = 2t \\
\frac{dt}{dx} = 1 \\
\begin{align}
\frac{dz}{dx} &= \frac{dz}{dt}  \frac{dt}{dx} \\
&= 2t \\
&= 2(x + y)
\end{align}
```

・シグモイド関数を微分した時、入力値が0の時に最大値をとる。
　その値として正しいものを選択肢から選べ。
　0.25

・重みの初期値に0を設定すると、どのような問題が発生するか。簡潔に説明せよ。
→全ての重みの値が均一に更新され、多数の重みを持つ意味がなくなってしまう（正しく学習が行えない）。

・一般的に考えられるバッチ正規化の効果を2点挙げよ。
→重みの更新が安定化することによる学習の高速化
→過学習を抑えられる

# Section1：勾配消失問題・例題チャレンジ
![test12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/4c628ebe-036f-61ac-8442-bf9fdc009012.png)
→(1)
スライスでミニバッチの最初と最後のインデックスを指定

# Section2：学習率最適化手法
学習率が大きすぎると発散し、小さすぎると時間がかかりすぎたり大域局所最適値に収束しづらくなる

初期の学習率設定方法の指針
・初期の学習率を大きく設定し、徐々に学習率を小さくしていく
・パラメータ毎に学習率を可変させる
→学習率最適化手法を利用して学習率を最適化

### モメンタム
・誤差をパラメータで微分したものと学習率の積を減算した後、
　現在の重みに前回の重みを減算した値と慣性の積を加算する誤差を
　パラメータで微分したものと学習率の積を減算する

```math
V_t = \mu V_{t-1} - \epsilon \nabla E
```

```python
self.v[key] = self.momentum * self.v[key] - self.learning_rate * grad[key]
```

```math
\boldsymbol{w}^{(t + 1)} = \boldsymbol{w}^{(t)} + V_t
```

```python
params[key] += self.v[key]
```

メリット
・局所的最適解にはならず、大域的最適解となる。
・谷間についてから最も低い位置(最適値)にいくまでの時間が早い。

### AdaGrad
・誤差をパラメータで微分したものと再定義した学習率の積を減算する

```math
h_0 = \theta
```

```python
# 何かしらの値でhを初期化
self.h[key] = np.zeros_like(val)
```

```math
h_t = h_{t-1} + (\nabla E)^2
```

```python
# 計算した勾配の2乗を保持
self.h[key] += grad[key] * grad[key]
```

```math
w^{(t + 1)} = w^{(t)} - \epsilon \frac{1}{\sqrt{h_t} + \theta} \nabla E
```

```python
# 現在の重みを適応させた学習率で更新
params[key] -= self.learning_rate * grad[key] / (np.sqrt(self.h[key]) + 1e-7)
```

メリット
・勾配の緩やかな斜面に対して、最適値に近づける
課題
・学習率が徐々に小さくなるので、鞍点問題を引き起こす事があった

### RMSProp
・誤差をパラメータで微分したものと再定義した学習率の積を減算する

```math
h_t = \alpha h_{t-1} + (1 - \alpha) (\nabla E)^2
```

```python
self.h[key] *= self.decay_rate
self.h[key] += (1 -self.decay_rate) * grad[key] * grad[key]
```

```math
w^{(t + 1)} = w^{(t)} - \epsilon \frac{1}{\sqrt{h_t} + \theta} \nabla E
```

```python
params[key] -= self.learning_rate * grad[key] / (np.sqrt(self.h[key]) + 1e-7)
```

メリット
・局所的最適解にはならず、大域的最適解となる。
・ハイパーパラメータの調整が必要な場合が少ない

### Adam
上記手法のいいとこ取りをした最適化アルゴリズム
・モメンタムの、過去の勾配の指数関数的減衰平均
・RMSPropの、過去の勾配の2乗の指数関数的減衰平均

# Section2：学習率最適化手法・確認テスト
・モメンタム・AdaGrad・RMSPropの特徴をそれぞれ簡潔に説明せよ。
　モメンタム：局所的最適解にはならず、大域的最適解となる。
　AdaGrad：勾配の緩やかな斜面に対して、最適値に近づける。
　RMSProp：局所的最適解にはならず、大域的最適解となる。

# Section3：過学習
ネットワークの自由度が高すぎることによって起こる
→正則化手法を利用して過学習を抑制する

### Weight decay(荷重減衰)
重みが大きすぎる（過大評価している）場合に過学習が発生することがある
→誤差に対して正則化項を加算することで重みを抑制する

### L1、L2正則化
・誤差関数に、pノルムを加える

```math
E_{n}(\boldsymbol{w}) + \frac{1}{p}\lambda ||x||_p
```

```python
weight_decay += weight_decay_lambda * np.sum(np.abs(network.params['W' + str(idx)]))
loss = network.loss(x_batch, d_batch) + weight_decay
```

・pノルムの計算

```math
||x||_p = (|x_1|^p + \cdots + |x_n|^p)^{\frac{1}{p}} = (\sum_{i=1}^{n} |x_i|^p)^{\frac{1}{p}}
```

```python
np.sum(np.abs(network.params['W' + str(idx)]))
```

p = 1の場合、L1正則化（ラッソ回帰）
p = 2の場合、L2正則化（リッジ回帰）
と呼ぶ。

### ドロップアウト
ノードを不活性化させて学習を進めていく
→過学習の抑制、複数モデルの組み合わせによる精度の向上

# Section3：過学習・確認テスト
![test15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/82f215c0-4c1e-86c4-66fe-725f6fe457fa.png)
(a)
→(b)線形回帰、(c)バイアス項は正則化されない、(d)誤差関数に対して正則化項を加える

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/dd581468-3510-268a-e606-2c64adbb1ce5.png" width=50%>
右側
誤差関数の等高線と正則化項の等高線の接点が最小

# Section3：過学習・例題チャレンジ
![test14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/c271ab7b-c59c-07e6-19db-51afc26bcc98.png)
(4)
→L2ノルム||param||^2の勾配が誤差の勾配に加えられるが、
　係数の2は正則化の係数に吸収されても変わらない。

![test16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/01fbdb34-81c7-2a31-e6bd-48869e554b5b.png)
sign(param)
→L1ノルム|param|の勾配が誤差の勾配に加えられる。つまり、符号関数sign(param)。

# Section4：畳み込みニューラルネットワークの概念
画像の識別などによく用いられるが、
汎用性が高く画像以外でも次元間で繋がりのあるデータを扱える。
これまで見てきたNNは隣接する層の全てのノード間で結合があった（**全結合**）。
CNNでは新たに畳み込み層、プーリング層が登場する。

### 畳み込み層
全結合層では3次元のデータであれば1次元のデータとして入力する必要があり、
データの「形状」が無視されてしまっていた。
一方で畳み込み層では3次元のデータを3次元のまま入力として受け取り、出力することができる。

### 畳み込み演算（バイアス）
入力を取る範囲をスライドさせながら、フィルターとの積の和をとりバイアス項を加えて出力する。

### 畳み込み演算（パディング）
出力サイズが小さくなりすぎてしまうことを防ぐため、入力データの周辺に固定データを埋める。

### 畳み込み演算（ストライド）
フィルターを適用する位置の間隔をストライドという。

### 畳み込み演算（チャンネル）
フィルターの数

### プーリング層
畳み込み演算と違って重み（フィルターのパラメーター）がなく、処理範囲の最大値や平均値をとっていく。

# Section4：畳み込みニューラルネットワークの概念・確認テスト
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/842404/3708e08c-2729-4996-0084-60258f7bbcc5.png" width=50%>
高さ = (画像の高さ + パディング高 × 2 - フィルター高) / ストライド + 1 = 7
横も同様のため、7×7

# Section5：最新のCNN
### AlexNet
・モデルの構造
5層の畳み込み層およびプーリング層など、それに続く3層の全結合層から構成される。

・過学習を防ぐ施策
サイズ4096の全結合層の出力にドロップアウトを使用している 

# day2実装
https://github.com/kyo-git/rabbit-challenge/blob/main/2_1_network_modified.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_2_1_vanishing_gradient.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_2_2_vanishing_gradient_modified.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_3_batch_normalization.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_4_optimizer.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_4_optimizer_after.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_5_overfiting.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_6_simple_convolution_network.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_6_simple_convolution_network_after.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_7_double_comvolution_network.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_7_double_comvolution_network_after.ipynb
https://github.com/kyo-git/rabbit-challenge/blob/main/2_8_deep_convolution_net.ipynb

# 参考図書
・斎藤 康毅『ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装』オライリー・ジャパン