【ラビットチャレンジ】 深層学習（前編・day1）

##ニューラルネットワークの全体像

### 確認テスト
- ディープラーニングは，結局何をやろうとしているのか，何を最適化するのが最終目的か．
- 入力値から適切な出力値を得るために，学習を行い，重み，バイアスを最適化する．
→ 出力誤差を最小化するパラメータを発見すること．

### 確認テスト
ネットワークを紙にかけ．
![IMG_0027.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375106/4ff257dd-cca2-ddfb-ded4-aae42274aea2.jpeg)

# Section1：入力層～中間層
入力 $\boldsymbol x$ に対して重み $\boldsymbol W$ を作用させ（内積），バイアス$\boldsymbol b$を加算したものが中間層の各ユニットへ入力される．
$$
\boldsymbol u = 
\begin{bmatrix}
u_1  \\\ 
\vdots  \\\
u_J 
\end{bmatrix},\boldsymbol x = 
\begin{bmatrix}
x_1  \\\ 
\vdots  \\\
x_I 
\end{bmatrix},\boldsymbol b = 
\begin{bmatrix}
b_1  \\\ 
\vdots  \\\
b_J 
\end{bmatrix},
$$

$$
\boldsymbol W = 
\begin{bmatrix}
w_{11} & \cdots & w_{1I}  \\\ 
\vdots & \ddots & \vdots \\\
w_{J1} & \cdots & w_{JI}
\end{bmatrix}
$$

$$ \boldsymbol u = \boldsymbol{Wx}+\boldsymbol b $$

```python:python
# 中間層ユニット1への入力u1
u1 = np.dot(x, W1) + b1
```

### 上記ソースコードは，確認テスト込み

# Section2：活性化関数
## 活性化関数
- 次の層への出力の大きさを決める非線形の関数
- 入力値によって，次の層への信号のON/OFFや強弱を定める．

$$ \boldsymbol f^{(l)}(\boldsymbol u) = 
\begin{bmatrix}
f^{(l)}(u_1^{(l)}) & \cdots & f^{(l)}(u_j^{(l)})
\end{bmatrix}
$$

###確認テスト
線形，非線形
![Image-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375106/3bef154b-0512-d782-ddb0-0688138b4115.jpeg)
![Image-1-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375106/f058fe83-ffef-aefa-1346-05e11d842e17.jpeg)


#### 中間層の出力

$$ \boldsymbol z^{(l)} = 
\begin{bmatrix}
z_1^{(t)} & \cdots & z_K^{(t)}
\end{bmatrix} =
\boldsymbol f^{(l)}(\boldsymbol u) $$ 


### 確認テスト 中間層の出力を定義しているソースコード
```python:python
    # 中間層出力
    z = functions.relu(u)
```

### 中間層用の活性化関数
- ReLU関数
- シグモイド（ロジスティック）関数
- ステップ関数

### 出力層用の活性化関数
- ソフトマックス関数
- 恒等写像
- シグモイド（ロジスティック）関数

## 各活性化関数の特徴
### ステップ関数

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

- パーセプトロンで利用された関数．
- 0-1の間を表現できず，線形分離可能なものしか学習できない．

### シグモイド関数

$$ f(u) = \frac{1}{1+e^{-u}}$$

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

- 0~1の間を緩やかに変化する関数
- 大きな値では出力の変化が微小なので，郊外消失問題を引き起こすことがある．
- $0$ を取らないので，スパース化できず計算リソースを食う．

### ソフトマックス関数

$$ f(\boldsymbol i, \boldsymbol u) = \frac{e^{u_i}}{\sum_{k=1}^Ke^{u_k}}$$

```python:python
def softmax(x):
    if x.ndim == 2:
        x = x.T
        x = x - np.max(x, axis=0)    # axis=0 は列ごとの最大値． axis=1で行ごと．
        y = np.exp(x) / np.sum(np.exp(x), axis=0)
        return y.T

    x = x - np.max(x) # オーバーフロー対策
    return np.exp(x) / np.sum(np.exp(x))
```

- 多値分類に用いる．

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

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

- 勾配消失問題の回避とスパース化に貢献することで，良い成果をもたらしている．
- 初手ReLUはよいが，最適というわけではない．

### 確認テスト
中間層出力zを求める箇所を示せ
```python:python
    z = functions.sigmoid(u)
```

# Section3：出力層

$$ \boldsymbol y_n^{(t)} = 
\begin{bmatrix}
y_{n1}^{(t)} & \cdots & y_{nK}^{(t)}
\end{bmatrix}
= \boldsymbol z ^{(L)}
$$

- 人間が理解できる数値を出力する．
    - 入力画像が，犬である確率:85%，猫である確率:10%，鼠である確率:5%
        - ばらつきが少ない場合は，データが足りない，もしくは学習がうまく行っていないことを疑う．



## 誤差関数
訓練データと出力の誤差を評価する関数．

- 二乗誤差
$$E_n(\boldsymbol w)=\frac{1}{2}\sum_{j=1}^J (y_j-d_j)^2$$
    - 2乗しているのは，値を正にするため．
    - $\frac{1}{2}$ は，微分して下りてくる$2$が打ち消されるようにするため．
    
### 上記は確認テスト込み

```python:python
def mean_squared_error(d, y):
    return np.mean(np.square(d - y)) / 2    # Sigmaをmeanで処理している
```

<p>

- 交差エントロピー
$$E_n(\boldsymbol w)=-\sum_{j=1}^J d_j \log y_j$$
    - 分類問題に用いられる活性化関数であるシグモイド関数およびソフトマックス関数と相性が良い．

```python:python
def cross_entropy_error(d, y):
    if y.ndim == 1:
        d = d.reshape(1, d.size)
        y = y.reshape(1, y.size)
        
    # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換
    if d.size == y.size:
        d = d.argmax(axis=1)
             
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size
```

# Section4：勾配降下法
### 深層学習の目的
学習を通して誤差を最小にするネットワークを作成すること．
→ 誤差 $E(\boldsymbol w)$ を最小化するパラメータ $\boldsymbol w$ を発見すること．
勾配降下法を利用してパラメータを最適化する．

## 勾配降下法

- 学習率 $\epsilon$ の値によって学習の効率が大きく異なる．
    - 学習率が大きすぎると，最小値にいつまでもたどり着かずに発散してしまう．
    - 学習率が小さすぎると，収束するまでに時間がかかってしまう． 

$$ \boldsymbol w^{(t+1)}=\boldsymbol w^{(t)}-\epsilon \nabla E$$
$$ \nabla E = \frac{\partial E}{\partial \boldsymbol w} = 
\begin{bmatrix}
\frac{\partial E}{\partial w_1} & \cdots & \frac{\partial E}{\partial w_M}
\end{bmatrix}
$$

### 確認テスト
勾配降下法の該当するソースコードを探せ

```python:python
    grad = backward(x, d, z1, y)
    for key in ('W1', 'W2', 'b1', 'b2'):
        network[key]  -= learning_rate * grad[key]
```

#### 勾配降下法
- 1回のパラメータの更新に全サンプルの平均誤差を用いる．
 
#### 確率的勾配降下法(SGD)
- ランダムに抽出したサンプルの誤差によりパラメータを更新する．
    - データが冗長な場合に計算コストを軽減できる．
    - 望まない局所極小解に収束するリスクが軽減される．
    - オンライン学習ができる．
        - オンライン学習：すでに学習済みのモデルに対して，新規ユーザのデータのみで学習を行うこと．

#### ミニバッチ勾配降下法
- ランダムに分割したデータの集合（ミニバッチ） $D_t$ に属するサンプルの平均誤差を用いる．
    - 確率的勾配項ッカ法のメリットを損なわず，計算機の計算資源を有効利用できる．
        - CPUを利用したスレッド並列化や，GPUを利用したSIMD並列化

### 確認テスト
ミニバッチ勾配降下法のEtの意味するところを図示せよ
![Image-1-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/375106/d6e11a2e-7678-b21b-7a2f-ee6a3dbbf480.jpeg)


# Section5：誤差逆伝播法

$$ \nabla E = \frac{\partial E}{\partial \boldsymbol w} = 
\begin{bmatrix}
\frac{\partial E}{\partial w_1} & \cdots & \frac{\partial E}{\partial w_M}
\end{bmatrix}
$$
をどう計算するか．

## 誤差逆伝播法
- 算出された誤差を，出力側から順に微分して前の層へと伝播する．
- 最小限の計算で各パラメータでの微分値を解析的に計算する手法．
    - 計算結果(=誤差)から微分を逆算することで，不要な再帰計算を避けて微分を算出できる．

#### 誤差逆伝播法による誤差勾配の計算の例
誤差関数を二乗誤差関数，
$$E(\boldsymbol y)=\frac{1}{2}\sum_{j=1}^J(y_j-d_j)^2=\frac{1}{2}||\boldsymbol y-\boldsymbol d||^2$$

出力層の活性化関数を恒等写像とする．
$$\boldsymbol y = \boldsymbol u^{(L)}$$

総入力は，
$$\boldsymbol u ^{(l)}=\boldsymbol w^{(l)}\boldsymbol z^{(l-1)}+\boldsymbol b^{(l)}$$

誤差勾配
$$ 
\frac{\partial E}{\partial w_{ji}} =
\frac{\partial E}{\partial \boldsymbol y} 
\frac{\partial \boldsymbol y}{\partial \boldsymbol u}
\frac{\partial \boldsymbol u}{\partial w_{ji}}
$$
を求める．

- 誤差関数の偏微分：
$$\frac{\partial E}{\partial \boldsymbol y} =
\frac{\partial}{\partial \boldsymbol y} \frac{1}{2}||\boldsymbol y-\boldsymbol d||^2 = \boldsymbol y-\boldsymbol d$$
<p>

- 活性化関数（恒等写像）の偏微分：
$$\frac{\partial \boldsymbol y}{\partial \boldsymbol u}=
\frac{\partial \boldsymbol u}{\partial \boldsymbol u}=1$$
<p>

### 確認テスト
ソースコードでは，

```python:python
delta2 = functions.d_mean_squared_error(d, y)
```

- 総入力の微分（$w_{ji}$の要素のみが残る）

```math
\frac{\partial \boldsymbol u}{\partial w_{ji}} = \frac{\partial}{\partial w_{ji}}(\boldsymbol w^{(l)}\boldsymbol z^{(l-1)}+\boldsymbol b^{(l)}) = 
\begin{bmatrix}
0\\
\vdots\\
z_i\\
\vdots\\
0
\end{bmatrix}

```

以上より，誤差勾配は，

```math
\frac{\partial E}{\partial w_{ji}} =
\frac{\partial E}{\partial \boldsymbol y} 
\frac{\partial \boldsymbol y}{\partial \boldsymbol u}
\frac{\partial \boldsymbol u}{\partial w_{ji}}
= (\boldsymbol y-\boldsymbol d) \cdot \begin{bmatrix}
0\\
\vdots\\
z_i\\
\vdots\\
0
\end{bmatrix}
= (y_j-d_j)z_i
```

### 確認テスト
ソースコードでは，

```python:python
grad['W2'] = np.dot(z1.T, delta2)
```

<br>
<br> 

[DeepLearning ラビットチャレンジ](http://ai999.careers/rabbit/　"ラビットチャレンジリンク")
<img src="http://ai999.careers/tools/wp/wp-content/uploads/2018/12/card.png" width=50%>