めちゃめちゃ丁寧に誤差を逆伝播するッッッ!

# はじめに
できる限り丁寧に誤差を逆伝播するアルゴリズム（back propagation）についてわかりやすく書いていこうと思います．

## 誤差逆伝播法とは
1986年にDavid E. Rumelhartらによって発表されたアルゴリズムです．
[論文(Nature)](https://www.nature.com/articles/323533a0)
[こっちからも読めるよ](https://www.semanticscholar.org/paper/Learning-representations-by-back-propagating-errors-Rumelhart-Hinton/052b1d8ce63b07fec3de9dbb583772d860b7c769)
基本的な発想としては，より出力層に近い側の「誤差」を使って入力層に近い側の「誤差」を効率よく求めようというアルゴリズムです．
具体的にいうと，
```math
\delta^{\,l+1} \rightarrow \delta^{\,l}
```
てな感じで，一層深い層の誤差を用いて求めたい層の誤差を求めると言った具合です．

## アーキテクチャ

下記の図のような全結合型のNeural Networkを考えます（文字が足りん、、、）．
![NN.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2711911/ee05d491-94bb-90c2-751d-189b9e49f01a.png)

$W^{(l)}$:$l-1$層から$l$層へと向かう重み
$u_j^{(l)}$:$l$層$j$番目の重み付き和
$h_j^{(l)}$:$u_j^{(l)}$の活性化関数後の値
$\mathbf{y}$:$\mathbf{u}^{(L)}$の出力関数後の値
（回帰であれば恒等関数，二値分類であればシグモイド関数，多クラス分類であればソフトマックス関数）

以前ソフトマックス関数の重みに関する微分については求めてあるのでそれを参考にしようと思います．
[参考](https://qiita.com/chersky/items/dc85bdf18609eaeb5244)

## 出力層に関する重みによる誤差関数の微分

$h_i^{(l-1)}$から$u_j^{(l)}$への重みを$W^{(l)}_{i,j}$と書きます．
![def_W_ij.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2711911/63fc9edf-3bdc-11b5-9d85-2d0c921ba154.png)

この時，出力層の重みは，$W^{(L)}_{i,j}$ですね．

### 誤差関数Eの微分

連鎖律より，
```math
\frac{\partial{E}}{\partial{W^{(L)}_{i,j}}}=\sum_k\frac{\partial{E}}{\partial{u^{(L)}_k}}\frac{\partial{u^{(L)}_k}}{\partial{W^{(L)}_{i,j}}}=\frac{\partial{E}}{\partial{u^{(L)}_j}}\frac{\partial{u^{(L)}_j}}{\partial{W^{(L)}_{i,j}}}=\frac{\partial{E}}{\partial{u^{(L)}_j}}h_i^{L-1}
```
と書けます．

[以前の記事](https://qiita.com/chersky/items/dc85bdf18609eaeb5244)では，文字が違うものの下記のようにソフトマックス後の値を使って展開してました．

```math
\frac{\partial{E}}{\partial{W^{(L)}_{i,j}}}=\sum_a\frac{\partial{E}}{\partial{y_a}}\frac{\partial{y_a}}{\partial{W^{(L)}_{i,j}}}=\sum_a\frac{\partial{E}}{\partial{y_a}}\frac{\partial{y_a}}{\partial{u^{(L)}_j}}\frac{\partial{u^{(L)}_j}}{\partial{W^{(L)}_{i,j}}}
```

しかし，これだと

* クロスエントロピー誤差の場合
```math
E = -\sum_c t_c\,log\,y_c
``` 
```math
\frac{\partial{E}}{\partial{y_k}}= -\frac{t_k}{y_k}
```

* 二乗誤差の場合
```math
E = \frac{1}{2}||\mathbf{y}-\mathbf{t}||^2
``` 
```math
\frac{\partial{E}}{\partial{y_k}}= (y_k-t_k)
```

という風に，出力関数によって場合分けが必要です．

しかし，重み付き線形和である$u^{(L)}_j$で微分すると，クロスエントロピー誤差も二乗誤差も$(y_j-t_j)$という形できれいに現れてくれます．（計算してみてください）

そしてここで$(y_j-t_j)$という誤差を，

```math
\delta^{(L)}_j=\frac{\partial{E}}{\partial{u^{(L)}_j}}=(y_j-t_j)
```
という風におきます．

この誤差を中間層に伝播させることがこの誤差逆伝播法のメインとなるアイデアです．

そんなにうまく行くんでしょうか？

やってみましょう．


## 中間層に関する重みによる誤差関数の微分

求めたいものは
```math
\frac{\partial{E}}{\partial{W^{(l)}_{i,j}}}
```
です．
```math
\frac{\partial{E}}{\partial{W^{(l)}_{i,j}}}=\sum_k\frac{\partial{E}}{\partial{u^{(l)}_k}}\frac{\partial{u^{(l)}_k}}{\partial{W^{(l)}_{i,j}}}=\frac{\partial{E}}{\partial{u^{(l)}_j}}\frac{\partial{u^{(l)}_j}}{\partial{W^{(l)}_{i,j}}}=\frac{\partial{E}}{\partial{u^{(l)}_j}}h_i^{(l-1)}
```

先ほど，

```math
\delta^{(L)}_j=\frac{\partial{E}}{\partial{u^{(L)}_j}}
```
とおいたので，$L \rightarrow l$として

```math
\delta^{(l)}_j=\frac{\partial{E}}{\partial{u^{(l)}_j}}
```

とかけますね．なので，

```math
\frac{\partial{E}}{\partial{W^{(l)}_{i,j}}} = \frac{\partial{E}}{\partial{u^{(l)}_j}}h_i^{l-1}=\delta^{(l)}_jh_i^{(l-1)}
```

です．当初の目標を今一度思い出しましょう．

```math
\delta^{(l+1)} \rightarrow \delta^{(l)}
```
と書くことでしたね．

というわけで，

```math
\delta^{(l)}_j =\frac{\partial{E}}{\partial{u^{(l)}_j}}=\sum_p \frac{\partial{E}}{\partial{u^{(l+1)}_p}}\frac{\partial{u^{(l+1)}_p}}{\partial{u^{(l)}_j}}=\sum_p \delta^{(l+1)}_p\frac{\partial{u^{(l+1)}_p}}{\partial{u^{(l)}_j}}
```
と連鎖律を用いて展開してみましょう．

さて，問題は
```math
\frac{\partial{u^{(l+1)}_p}}{\partial{u^{(l)}_j}}
```
ですね．

これも連鎖律を用いて次のように書き換えます．

```math
\frac{\partial{u^{(l+1)}_p}}{\partial{u^{(l)}_j}}=\sum_q \frac{\partial{u^{(l+1)}_p}}{\partial{h^{(l)}_q}}\frac{\partial{h^{(l)}_q}}{\partial{u^{(l)}_j}}=\frac{\partial{u^{(l+1)}_p}}{\partial{h^{(l)}_j}}f^{\prime}(u^{(l)}_j)=W^{(l+1)}_{j,p}f^{\prime}(u^{(l)}_j)
```

よって，

```math
\delta^{(l)}_j = \sum_p \delta^{(l+1)}_pW^{(l+1)}_{j,p}f^{\prime}(u^{(l)}_j)
```

となることがわかります．図示すると下記のようなイメージです．![back_propagation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2711911/fa435b90-93cd-66e2-d01e-5a288cb799bd.png)



というわけでまとめると，

1. 全ての層（というより全てのノード？）の誤差を求める
    ```math
    \delta^{(l)}_j = \sum_p \delta^{(l+1)}_pW^{(l+1)}_{j,p}f^{\prime}(u^{(l)}_j)
    ```     
2. 各重みによる誤差関数の勾配を求める
    ```math
    \frac{\partial{E}}{\partial{W^{(l)}_{i,j}}} = \delta^{(l)}_jh_i^{(l-1)}
    ```
3. 全ての重みを更新する
    ```math
    W^{(l)}_{i,j} = W^{(l)}_{i,j} - \alpha \,\delta^{(l)}_jh_i^{(l-1)}
    ```

で誤差逆伝播法による勾配更新まで行えます．