ラビットチャレンジ - 深層学習 Day3 Section1 再帰型ニューラルネットワークの概念

# 0．概要
本記事は日本ディープラーニング協会認定の講座プログラムである「ラビット・チャレンジ」が提供している科目の1つである深層学習のレポートである。
記事タイトルに記載のとおり、Day3 Section1 再帰型ニューラルネットワークの概念について以下にまとめる。

# 1．再帰型ニューラルネットワーク(RNN)
時系列データを扱うことが出来るニューラルネットワーク。
前$t-1$の隠れ層の値が次$t$の隠れ層の計算へと使われていく。

基本の考え方はこれまでと変わらないが、前の層からの$Wz^{t-1}$が増えていることが下記の式から分かる。

```math
u^{t}=W_{(in)}x^{t}+Wz^{t-1}+b
```

```math
z^{t}=f(W_{(in)}x^{t}+Wz^{t-1}+b)
```

```math
v^{t}=W_{(out)}z^{t}+c
```

```math
v^{t}=g(W_{(out)}z^{t}+c)
```

参考：http://hiro2o2.hatenablog.jp/entry/2016/06/17/232429

# 2．時系列データ
時間的順序を追って一定時間ごとに観測され、かつ相互に統計的依存関係があるデータ。

例）

* テキスト(単語を構成する文字の順、文章を構成する単語の順等)
* 音声
* 株価等の各値がもつ時間(いつの値か)に意味があるもの。

# 3．Backpropagation Through Time(BPTT)
RNNにおける誤差逆伝播法の1つで時系列の考え方を取り入れたもの。

重み

```math
\frac{\partial E}{\partial W_{(in)}}=\frac{\partial E}{\partial u^{t}}
\left[
\frac{\partial u^{t}}{\partial W_{(in)}}
\right]^{T}=
\delta^{t}[x^{t}]^{T}
```

```math
\frac{\partial E}{\partial W_{(out)}}=\frac{\partial E}{\partial v^{t}}
\left[
\frac{\partial v^{t}}{\partial W_{(out)}}
\right]^{T}=
\delta^{out, t}[z^{t}]^{T}
```

```math
\frac{\partial E}{\partial W}=\frac{\partial E}{\partial u^{t}}
\left[
\frac{\partial u^{t}}{\partial W}
\right]^{T}=
\delta^{t}[z^{t-1}]^{T}
```

バイアス

```math
\frac{\partial E}{\partial b}=\frac{\partial E}{\partial u^{t}}
\frac{\partial u^{t}}{\partial b}=\delta^{t}
```

```math
\frac{\partial E}{\partial c}=\frac{\partial E}{\partial v^{t}}
\frac{\partial v^{t}}{\partial c}=\delta^{out, t}
```

パラメータの更新
重み

```math
W_{(in)}^{t+1}=W_{(in)}^{t}-\epsilon\frac{\partial E}{\partial W_{(in)}}=W_{(in)}^{t}-\epsilon\sum_{z=0}^{T_{t}}{\delta^{t-z}[x^{t-z}]^{T}}
```

```math
W_{(out)}^{t+1}=W_{(out)}^{t}-\epsilon\frac{\partial E}{\partial W_{(out)}}=W_{(out)}^{t}-\epsilon \delta^{out,t}[x^{t}]^{T}
```

```math
W^{t+1}=W^{t}-\epsilon\frac{\partial E}{\partial W}=W^{t}-\epsilon\sum_{z=0}^{T_{t}}{\delta^{t-z}[x^{t-z-1}]^{T}}
```

バイアス

```math
b^{t+1}=b^{t}-\epsilon\frac{\partial E}{\partial b}=b^{t}-\epsilon\sum_{z=0}^{T_{t}}{\delta^{t-z}}
```

```math
c^{t+1}=c^{t}-\epsilon\frac{\partial E}{\partial c}=c^{t}-\epsilon \delta^{out,t}
```

参考：https://book.mynavi.jp/manatee/detail/id=76172

詳解 ディープラーニングは読んだことがありますが、改めて読むともう少し数式もなんとなく理解できそう。。。

# 4．確認テスト

## 4.1．確認テスト1
サイズ5x5の入力画像をサイズ3x3のフィルタで畳み込んだ時の出力画像のサイズを答えよ。
なおストライドは2、パディングは1とする。

回答：
(5+2*1-3)/2+1=3
(画像の高さor幅+2*パディング-フィルタの高さor幅)/ストライド+1
高さ、幅ともに同じため3x3となる。

## 4.2．確認テスト2
RNNのネットワークには大きく分けて3つの重みがある。
1つは入力から現在の中間層を定義する際に掛けられる重み、もう1つは中間層から出力を定義する際に掛けられる重みである。
残り1つの重みについて説明せよ。

回答：
前の中間層から現在の中間層(または現在の中間層から次の中間層)を定義する際に掛けられる重み。
各結果の誤差は10000回繰り返した最終結果。

## 4.3．確認テスト3
連鎖律の原理を使い、dz/dxを求めよ
$z=t^2$
$t=x+y$

回答
連鎖律によって以下のような式となる。

```math
\frac{dz}{dx}=\frac{dz}{dt}\frac{dt}{dx}
```

それぞれ解くと

```math
\frac{dz}{dt}=2t
```

```math
\frac{dt}{dx}=1
```

となり、連鎖律で積を取れば良いので

```math
2t・1=2(x+y)
```

## 4.4．確認テスト4
下図の$y_{1}$を数式で表わせ。
中間層の活性化関数にはシグモイド関数を使用する。
※図は2,3...と続くが基本的に同じため省略

![bptt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389699/8965e161-0c11-aa42-cf9d-f05a006b6457.png)

回答

```math
z_{1}=sigmoid(s_{0}W+x_{1}W{in}+b)
```

$b$：中間層へのバイアス

```math
y_{1}=sigmoid(z_{1}W{out}+c)
```

$c$：出力層へのバイアス

# 5．Jupter演習

下記で扱うのは2進数の加算。

初期値の標準偏差：1
学習率：0.1
隠れ層のノード数：16

> Loss:0.0004398720261803661

![rnn1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389699/1bd6572e-a930-4431-4087-28ec87e40ff6.png)

## 5.1．ハイパーパラメータの変更
パラメータを以下のように変えて実行してみる。

初期値の標準偏差：1.1
学習率：0.2
隠れ層のノード数：32

> Loss:0.00017657173012791788

![rnn2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389699/01830f3b-b36a-2423-1c6a-24f14f7e3a86.png)

再び大きく誤差が出ている部分もあるが全体的に少し良化している。
最初に標準偏差を2や0.01にしてみたら大きく悪化したのでやはり手動でパラメータを調整するのは難しいし大きくすれば良いのか小さくすれば良いのか判断が難しく感じる…

## 5.2．Xavier
重みの初期化にXavierを使用してみる。

```Python:
W_in = np.random.randn(input_layer_size, hidden_layer_size) / (np.sqrt(input_layer_size))
W_out = np.random.randn(hidden_layer_size, output_layer_size) / (np.sqrt(hidden_layer_size))
W = np.random.randn(hidden_layer_size, hidden_layer_size) / (np.sqrt(hidden_layer_size))
```

学習率等のパラメータは4で記載している初期値としている。

> Loss:0.005251943346257851

![rnn_xa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389699/adfe2ff2-63eb-1530-08c4-13a7d00cd7f4.png)

収束しているがXavierを使っていなかったときより遅い。

## 5.3．He
重みの初期化にHeを使用してみる。

```Python:
W_in = np.random.randn(input_layer_size, hidden_layer_size) / (np.sqrt(input_layer_size)) * np.sqrt(2)
W_out = np.random.randn(hidden_layer_size, output_layer_size) / (np.sqrt(hidden_layer_size)) * np.sqrt(2)
W = np.random.randn(hidden_layer_size, hidden_layer_size) / (np.sqrt(hidden_layer_size)) * np.sqrt(2)
```

学習率等のパラメータは4で記載している初期値としている。

> Loss:0.004737374724243496

![rnn_he.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389699/09c62918-35bc-a866-06f5-7237b8240346.png)

収束しているがこちらもHeを使っていなかったときより遅い。
必ずしもXavierやHeを使えば良いというものではない(HeはそもそもReLU関数向きでシグモイド関数向きではないが)。

## 5.4．ReLU関数
活性化関数をReLU関数に変更してみる。

```Python:
z[:,t+1] = functions.relu(u[:,t+1])
# z[:,t+1] = functions.sigmoid(u[:,t+1])
```

```Python:
delta_out[:,t] = functions.d_mean_squared_error(dd, y[:,t]) * functions.d_relu(y[:,t])
# delta_out[:,t] = functions.d_mean_squared_error(dd, y[:,t]) * functions.d_sigmoid(y[:,t])
```

```Python:
delta[:,t] = (np.dot(delta[:,t+1].T, W.T) + np.dot(delta_out[:,t].T, W_out.T)) * functions.d_relu(u[:,t+1])
# delta[:,t] = (np.dot(delta[:,t+1].T, W.T) + np.dot(delta_out[:,t].T, W_out.T)) * functions.d_sigmoid(u[:,t+1])
```

学習率等のパラメータは4で記載している初期値としている。重みの初期化もXavierやHeは使っていない。

> Loss:2.125

![rnn_relu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389699/004ca49a-d213-4637-6857-60071c059fcb.png)

勾配爆発していることが確認できる。

## 5.5．tanh関数
活性化関数をtanh関数に変更してみる。

tanh関数

```math:
f(x)=\frac{e^{x}-e^{-x}}{e^{x}+e^{-x}}
```

導関数

```math:
f'(x)=\frac{4}{(e^{x}+e^{-x})^2}
```

参考：https://gcbgarden.com/2017/12/18/tanh-function/

```Python:
def d_tanh(x):
  return 4 / (np.exp(x) + np.exp(-x)) ** 2

z[:,t+1] = np.tanh(u[:,t+1])
# z[:,t+1] = functions.sigmoid(u[:,t+1])
```

```Python:
delta_out[:,t] = functions.d_mean_squared_error(dd, y[:,t]) * d_tanh(y[:,t])
# delta_out[:,t] = functions.d_mean_squared_error(dd, y[:,t]) * functions.d_sigmoid(y[:,t])
```

```Python:
delta[:,t] = (np.dot(delta[:,t+1].T, W.T) + np.dot(delta_out[:,t].T, W_out.T)) * d_tanh(u[:,t+1])
# delta[:,t] = (np.dot(delta[:,t+1].T, W.T) + np.dot(delta_out[:,t].T, W_out.T)) * functions.d_sigmoid(u[:,t+1])
```

学習率等のパラメータは4で記載している初期値としている。重みの初期化もXavierやHeは使っていない。

> Loss:1.3301564794393226

![rnn_tanh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389699/13c6c4c7-8469-3e47-8b40-8743bce5f409.png)

ReLU関数よりいくらかマシではあるが勾配爆発している。

# X．ラビットチャレンジとは
ラビットチャレンジとは、日本ディープラーニング協会認定の講座プログラムの1つ。
E資格を受験するためにはこのラビットチャレンジ等、いずれかの講座プログラムを修了しなければならない。

ラビットチャレンジの特徴は「現場で潰しが効くディープラーニング講座」の通学講座録画ビデオを編集した教材を使用した自習スタイルであるという点。
サポートは他の講座より少なく、受け身ではなく自主的に学んでいく姿勢でなければ進められないが、その分、他の講座に比べると安価であり、手が出しやすい。
ある程度知識がある人、自力で頑張るぞというガッツのある人向けではないかと感じる。

![bnr_jdla.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/389699/bce66671-a2f5-a7f9-de6f-3f87e15c253b.png)