ラビットチャレンジレポート＿深層学習②-1

**深層学習day3**
**Section1: 再帰型ニューラルネットワークの概念**

RNN:
時系列データに対応可能な、ニューラルネットワーク

時系列データとは:
時間的順序を追って一定間隔ごとに観察され，しかも相互に統計的依存関係が認められるようなデータの系列

RNNの特徴：
時系列モデルを扱うには、初期の状態と過去の時間t-1の状態を保持し、そこから次の時間でのtを再帰的に求める再帰構造が必要になる

＊RNNのネットワークには大きく分けて3つの重みがある
1つは入力から現在の中間層を定義する際に掛けられる重み、
もう1つは中間層から出力を定義する際に掛けられる重み、
残りの１つは前の中間層から現在の中間層を定義する際に掛けられる重み

BPTT：
RNNにおいてのパラメータ調整方法の一種
誤差逆伝播の一種

＊連鎖律の原理を使った計算例。dz/dxを求める
$$z=t^2$$

$$t=x+y$$ 

のとき

$$\frac{dz}{dt}=2t$$

$$\frac{dt}{dx}=1$$

連鎖律で積をとり

$$2t・1=2(x+y)$$


BPTTの数学的記述

重み

$$\frac{∂E}{∂W_{in}} = \frac{∂E}{∂u^t}\left[\frac{∂u^t}{∂W_{in}}\right]^T = δ^t\left[x^t\right]^T$$

$$\frac{∂E}{∂W_{out}} = \frac{∂E}{∂υ^t}\left[\frac{∂υ^t}{∂W_{out}}\right]^T = δ^{out,t}\left[z^t\right]^T$$

$$\frac{∂E}{∂W} = \frac{∂E}{∂u^t}\left[\frac{∂u^t}{∂W}\right]^T = δ^{t}\left[z^{t-1}\right]^T$$


バイアス

$$\frac{∂E}{∂b} = \frac{∂E}{∂u^t}\frac{∂u^t}{∂b} = δ^t$$

$$\frac{∂E}{∂c} = \frac{∂E}{∂υ^t}\frac{∂υ^t}{∂c} = δ^{out,t}$$


パラメータの更新式

重み

$$W_{in}^{t+1}=W_{in}^t−ϵ\frac{∂E}{∂W_{in}}=W_{in}^t−ϵ\sum_{z=0}^{T_t}δ^{t−z}[x^{t−z}]^T$$

$$ W_{out}^{t+1}=W_{out}^t−ϵ\frac{∂E}{∂W_{out}}=W_{out}^t−ϵ\sum_{z=0}^{T_t}δ^{out,t}[x^{t}]^T$$

$$W^{t+1}=W^t−ϵ\frac{∂E}{∂W}=W^t−ϵ\sum_{z=0}^{T_t}δ^{t−z}[x^{t−z-1}]^T$$


バイアス

$$b^{t+1}=b^t−ϵ\frac{∂E}{∂b} = b^t−ϵ\sum_{z=0}^{T_t}δ^{t−z}$$

$$c^{t+1}=c^t−ϵ\frac{∂E}{∂c}=c^t−ϵδ^{out,t}$$


**Section2: LSTM**

RNNの課題
時系列を遡れば遡るほど、勾配が消失していくため、長い時系列の学習が困難
構造自体を変えて解決したものがLSTM

勾配消失問題：
誤差逆伝播法が下位層に進んでいくに連れて、勾配がどんどん緩やかになっていく。そのため、勾配降下法による、更新では下位層のパラメータはほとんど変わらず、訓練は最適値に収束しなくなる。

活性化関数としてシグモイド関数を用いた場合、大きな値では出力の変化が微小なため、勾配消失問題を引き起こす事があった。

*シグモイド関数を微分した時、入力値が0の時に最大値0.25をとる。

シグモイド関数 $f(x)$ を微分すると

$$f'(x)=(1-f(x))・f(x)$$

入力値$x$に0を代入すると

$$sigmoid(x)=\frac{1}{1+e^{-x}}=\frac{1}{2}=0.5$$

微分の式に代入すると

$$f'(x)=(1-0.5)・0.5=0.25$$

勾配爆発：
勾配が、層を逆伝播するごとに指数関数的に大きくなっていくこと

クリッピング：
勾配のノルムがしきい値を超えたら、勾配のノルムをしきい値に正規化するというもの。
勾配爆発を防ぐための手法。


クリッピングの実装例

```
def gradient_clipping(grad, threshould):
  norm = np.linalg.norm(grad)
  rate = threshould / norm

  if rate < 1:
    return grad * rate
  return grad
```

CEC：
勾配消失および勾配爆発の解決方法として、勾配が1であれば解決できる。
課題：入力データについて、時間依存度に関係なく重みが一律であることは、ニューラルネットワークの学習特性が無いということ。

入力ゲート、出力ゲート：
入力・出力ゲートを追加することで、それぞれのゲートへの入力値の重みを、重み行列W,Uで可変可能とすることで、CECの課題を解決する

忘却ゲート：
CECは、過去の情報が全て保管されているため、過去の情報が要らなくなった場合、削除することはできず、保管され続ける。過去の情報が要らなくなった場合、そのタイミングで情報を忘却する機能として生まれた。

＊以下の文章をLSTMに入力し空欄に当てはまる単語を予測したいとする。文中の「とても」という言葉は空欄の予測においてなくなっても影響を及ぼさないと考えられる。このような場合、どのゲートが作用すると考えられるか。「映画おもしろかったね。ところで、とてもお腹が空いたから何か____。」
→忘却ゲート

覗き穴結合：
CECの保存されている過去の情報を、任意のタイミングで他のノードに伝播させたり、あるいは任意のタイミングで忘却させたいために、CEC自身の値に、重み行列を介して伝播可能にした構造。

LSTMの実装例

```
def lstm(x, prev_h, prev_c, W, U, b):
  # セルへの入力やゲートをまとめて計算し、分割
  lstm_in = _activation(x.dot(W.T)) + prev_h.dot(U.T) + b)
  a, i, f, o = np.hsplit(lstm_in, 4)

  # 値を変換、セルへの入力:(-1, 1)、ゲート:(0, 1)
  a = np.tanh(a)
  input_gate = _sigmoid(i)
  forget_gate = _sigmoid(f)
  output_gate = _sigmoid(o)

  # セルの状態を更新し、中間層の出力を計算
  c = input_gate * a + forget_gate * c
  h = output_gate * np.tanh(c)
  return c, h
```

**Section3: GRU**

GRU:
従来のLSTMでは、パラメータが多数存在していたため、計算負荷が大きかった。しかし、GRUでは、そのパラメータを大幅に削減し、精度は同等またはそれ以上が望める様になった構造。計算負荷が低いのがメリット。

GRUの実装例

```
def gru(x, h, W_r, U_r, W_z, U_z, W, U):
  # リセットゲート
  r = _sigmoid(x.dot(W_r.T) + h.dot(U_r.T))
  # 更新ゲート
  z = _sigmoid(x.dot(W_z.T) + h.dot(U_z.T))
  
  h_bar = np.tanh(x.dot(W.T) + (r * h).dot(U.T))
  h_new = (1 - z) * h + z * h_bar
  return h_new
```

＊LSTMとCECが抱える課題
LSTM：パラメータが多いため計算負荷が大きいこと
CEC：重みが一律になって学習ができないこと。任意のタイミングで伝搬や忘却ができず、過去の情報が不要になっても残り続けてしまうこと

＊LSTMとGRUの違い
LSTM：
・ 入力ゲート、出力ゲート、忘却ゲートの3つのゲートがある
・ パラメータ数が多い
GRU：
・リセットゲート、更新ゲートの2つのゲートがある
・パラメータ数は少ない


**Section4: 双方向RNN**

双方向RNN：
過去の情報だけでなく、未来の情報を加味することで、精度を向上させるためのモデル

双方向RNNの実装例

```
def bindirectional_rnn_net(xs, W_f, U_f, W_b, U_b, V):
  # W_f, U_f：入力から中間層、前の中間層から今の中間層の順方向の重み
  # W_b, U_b：W_f, U_fの逆方向
  # V：順方向、逆方向の中間層から出力層の重み
  xs_f = np.zeros_like(xs)
  xs_b = np.zeros_like(xs)

  for i, x in enumerate(xs):
    xs_f[i] = x
    xs_b[i] = x[::-1]

  hs_f = _rnn(xs_f, W_f, U_f)
  hs_b = _rnn(xs_b, W_b, U_b)
  hs = [np.concatenate([h_f, h_b[::-1]], axis=0)] for h_f, h_b in zip(hs_f, hs_b)
  ys = hs.dot(V.T)
  return ys
```


**Section5: Seq2Seq**

Seq2Seq：
Encoder-Decoderモデルの一種。
機械対話や、機械翻訳などに使用されている。

Encoder RNN：
ユーザーがインプットしたテキストデータを、単語等のトークンに区切って渡す構造

Taking :文章を単語等のトークン毎に分割し、トークンごとのIDに分割する。
Embedding :IDから、そのトークンを表す分散表現ベクトルに変換。
Encoder RNN:ベクトルを順番にRNNに入力していく。

Decoder RNN：
システムがアウトプットデータを、単語等のトークンごとに生成する構造。

＊Seq2Seqについて説明しているもの
→RNNを用いたEncoder-Decoderモデルの一種であり、機械翻訳などのモデルに使われる。


HRED：
過去n-1 個の発話から次の発話を生成する

HREDの構造：
Seq2Seq+ Context RNN
Context RNN: 
Encoder のまとめた各文章の系列をまとめて、これまでの会話コンテキスト全体を表すベクトルに変換する構造。
過去の発話の履歴を加味した返答をできる。

HREDの課題：
HRED は確率的な多様性が字面にしかなく、会話の「流れ」のような多様性が無い。

VHRED：
HREDに、VAEの潜在変数の概念を追加したもの。
HREDの課題をVAEの潜在変数の概念を追加することで解決した構造


＊Seq2SeqとHRED、HREDとVHREDの違い
Seq2SeqとHRED：
Seq2seqでは、会話の文脈無視で、応答がなされたが、HREDでは、前の単語の流れに即して応答されるため、より人間らしい文章が生成される。
HREDとVHRED：
HREDは発話に多様性がなく情報量に乏しいが、VHREDはそれらの課題を解決し、多様性ある発話ができる。


VAE
オートエンコーダー：
教師なし学習のひとつ。そのため学習時の入力データは訓練データのみで教師データは利用しない。

オートエンコーダ構造：
入力データから潜在変数zに変換するニューラルネットワーク（Encoder）と、
逆に潜在変数zをインプットとして元画像を復元するニューラルネットワーク（Decoder）からなる
次元削減が行えることがメリット

VAE：
通常のオートエンコーダーの場合、何かしら潜在変数zの構造がどのような状態かわからない。
VAEはこの潜在変数zに確率分布z∼N(0,1)を仮定したもの。

＊VAE
→オートエンコーダーの潜在変数に”確率分布”を導入したもの

Encoderの実装例

```
def encode(words, E, W, U, b):
  # words：文。各単語はone-hotベクトル
  # E：単語の埋め込み行列
  # W：重み
  # U：重み
  # b：バイアス
  hidden_size = W.shape[0]
  h = np.zeros(hidden_size)

  for w in words:
    e = E.dot(w)
    h = _activation(W.dot(e) + U.dot(h) + b)
  return h
```


**Section6: Word2vec**

Word2vec:
推論ベースの手法であり、2層のニューラルネットワークで構成される。
skip-gramモデルとCBOWモデルがある。
重みの再学習ができるため、単語の分散表現の更新や追加が効率的に行える。

skip-gramモデル:
ひとつの単語から複数の単語を推測する

CBOWモデル:
複数の単語からひとつの単語を推測する

いずれも入力層側の重みと出力層側の重みをパラメータとして持つが、一般に入力層側の重みが単語埋め込み行列として用いられる。

**Section7: Attention Mechanism**

Attention Mechanism:
seq2seqでは、単語数によらず、固定次元ベクトルの中に入力しなければならないため、長い文章への対応が難しい。文章が長くなるほどそのシーケンスの内部表現の次元も大きくなっていく仕組みが必要になる。そこで、「入力と出力のどの単語が関連しているのか」の関連度を学習する仕組み。

Attention はふたつの時系列データ間の対応関係をデータから学習。
ベクトルの内積を使って、ベクトル間の類似度を算出し、その類似度を用いた和ベクトルが出力となる。
誤差逆伝播法によって学習できる。

外部メモリによるニューラルネットワークの拡張研究例では、メモリの読み書きにAttentionが用いられたりしている。


＊RNNとword2vec、seq2seqとAttentionの違い

RNNとWord2vec：
RNNはボキャブラリー×ボキャブラリー数だけの重みが必要、
Word2vecはボキャブラリー数×単語ベクトルの次元数だけの重みのみが必要となる。

Word2Vecは分散表現の取得に特化しており、文脈の単語の並びを意識することはできない、
RNNは文脈の単語の並びを意識することはできるが、分散表現を取得するために使用すると計算コストが大きくなり過ぎてしまう。

Seq2SeqとSeq2Seq+Attention：
Seq2Seqは長い文章に対して対応が難しい、
Attentionを取り入れることで入力と出力の関連度に基づく長い文章にも対応できる。

参考
https://proceedings.neurips.cc/paper/2017/file/3f5ee243547dee91fbd053c1c4a845aa-Paper.pdf


この記事の参考図書
「ゼロから作るDeepLearning」 斎藤康毅
「ゼロから作るDeepLearning２」 斎藤康毅
「徹底攻略ディープラーニングE資格エンジニア問題集」スキルアップAI株式会社
「機械学習のエッセンス」加藤公一

~Fin~