深層学習　DAY3

###自分用メモ

####Section1 再帰型ニューラルネットワークの概念

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

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

#####RNNの全体像


![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653571/3829a51c-d4e2-a4a3-6d24-0bb82bba326c.jpeg)

入力層からの入力と中間層から中間層に戻す経過が加わる。
それら時間経過に合わせて出力される。

#####RNNの数学的記述

```math
u^t = W_{(in)}x^t + W_{(out)}z^{t-1} + b \\
z^t = f(W_{(in)}x^t + Wz^{t-1} + b) \\
v^t = W_{(out)}z^t + c \\
y^t = g(W_{(out)}z^t + c) \\


```

コードでは

```mathematica

u[:,t+1] = np.dot(X,W_in) + np.dot(z[:,t].reshape(1,-1), W)

z[;,t+1] = functions.sigmoid(u[:,t+1])

np.dot([:,t+1].reshape(1,-1),W_out)

y[:,t] = functions.sigmoid(np.dot(z[;,t+1].reshape(1,-1),W_out))

```

- 確認テスト

Q：
RNNのネットワークには大きく分けて３つの重みがある。一つは入力から現在の中間層を定義する際にかけられる重み、一つは中間層から出力を定義する際にかけられる重みである。残り一つの重みについて説明せよ。

A：中間層から中間層の重み




#####RNNの特徴

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

- 演習チャレンジ

Q:
以下は再帰型ニューラルネットワークに構文木を入力として再帰的に文全体の表現ベクトルを得るプログラムである。ただし、ニューラルネットワークの重みパラメータはグローバル変換として定義してあるものとし、_activation関数は何らかの活性化関数であるとする。木構造は再帰的な辞書で定義してあり、rootが最も外側の辞書であると仮定する。
（く）に当てはまるのはどれか。

```
def traverse(node):
    """
    node: tree node, recursive dict, {left: node', right: node''}
          if leaf, world embeded vector, (embed_size,)

    W: Weights, global variable, (embed_size, 2*embed_size)
    b: bias, global variable, (embed_size,)
    """
    if not isinstance(node, dict):
        v = node
    else:
        left = traverse(node['left'])
        right = traverse(node['right'})
        v = _activation(（く））
    return v

```
A:

W.dot(np.concatenate([left,fight])



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


```math
\frac{\partial E}{\partial W_{(in)}} = \frac{\partial E}{\partial u^t}\biggl[\frac{\partial u^t}{\partial W_{(in)}}\biggr]^T = \delta^t [x^t]^T \\
\frac{\partial E}{\partial W_{(out)}} = \frac{\partial E}{\partial v^t}\biggl[\frac{\partial v^t}{\partial W_{(out)}}\biggr]^T = \delta^{out,t} [z^t]^T \\
\frac{\partial E}{\partial W} = \frac{\partial E}{\partial u^t}\biggl[\frac{\partial u^t}{\partial W}\biggr]^T = \delta^{t} [z^{t-1}]^T \\

\frac{\partial E}{\partial b} =
\frac{\partial E}{\partial u^t}\frac{\partial u^t}{\partial b} =  \delta^{t}
\\
\frac{\partial E}{\partial c} =
\frac{\partial E}{\partial v^t}\frac{\partial v^t}{\partial c} =  \delta^{out,t}

```

コードは以下
バイアスは省略

```
np.dot(X.T, delta[;,t].reshape(1,-1))
np.dot(z[:,t+1].reshape(-1,1),delta_out[:,t].reshape(-1,1))
np.dot(z[:,t].reshape(-1,1),delta[:,t].reshape(1,-1))


```

- 確認テスト

Q:
下図のy1をx・z0・z1・Win・W・Woutを用いて数式で表せ。
＊バイアスは任意の文字で定義せよ。
＊また中間層の出力にシグモイド関数g(x)を作用させよ。

![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653571/fb32fdaa-7552-e6ee-a606-bc883e44580e.jpeg)

A:

y1= g(Wout・z1 +c)
z1 = Win・x1 + w・z0 +b

BPTTの数学的記述


```math

\frac{\partial E}{\partial u^t}= \frac{\partial E}{\partial v^t}\frac{\partial v^t}{\partial u^t}=\frac{\partial E}{\partial v^t} \frac{\partial W_{out}f(u^t)+c}{\partial u^t}= f^{'}(u^t)W^T_{(out)}\delta^{out,t}=\delta^t \\

\delta^{t-1} = \frac{\partial E}{\partial u^{t-1}} = \frac{\partial E}{\partial u^t}\frac{\partial u^t}{\partial u^{t-1}}
=\delta\biggl( \frac{\partial u^t}{\partial z^{t-1}}\frac{\partial z^{z-1}}{\partial u^{t-1}}\biggr)
=\delta^t{Wf^{'}(u{t-1})} \\
\delta^{t-z-1}=\delta^{t-z}{Wf^{'}(u^{t-z-1})}


```

コード

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

```

パラメータの更新式

![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653571/360433ea-f0f6-5f4c-c223-ac1983dedbbb.jpeg)



- コード演習問題

Q:
下図はBPTTを行うプログラムである。なお簡単化のため活性化関数は恒等関数であるとする。
また、calculate_dout関数は損失関数を出力にして偏微分した値を返す関数であるとする。
（お）にあてはまるのはどれか。



```
def bptt(xs, ys, W, U, V):
    """
    ys: labels, (batch_size, n_seq, output_size)
    """

    hiddens, outputs = rnn_net(xs, W, U, V)

    dW = np.zeros_like(W)
    dU = np.zeros_like(U)
    dV = np.zeros_like(V)

    do = _calculate_do(outputs, ys)

    batch_size, n_seq = ys.shape[:2]

    for t in reversed(range(n_seq)):
        dv += np.dot(do[:, t].T, hiddens[:, t] / batch_size
        delta_t = do[:, t].dot(V)

        for bptt_step in reversed(range(t+1)):
            dw += np.dot(delta_t.T, xs[:, bptt_step]) / batch_size
            dU += np0.dot(delta_t.T, hiddens[:, bptt\step-1]) / batch_size
            delta_t = （お）
    return dW, dU, dV

```

A:
delta_t.dot(U)



####Section2 LSTM
勾配消失の問題を構造自体を変えて解決したものがLSTM

- 演習チャレンジ

Q:
RNNや深いモデルでは勾配の消失または爆発が起こる傾向がある。勾配爆発を防ぐために勾配のクリッピングを行うという手法がある。具体的には勾配のノルムが閾値を超えたら、勾配のノルムを閾値に正規化するというものである。以下は勾配のクリッピングを行う関数である。（さ）にあてはまるのはどれか。

```
def gradient_clipping(grad, threshold):
    """
    grad: gradient
    """
    norm = np.linalg.norm(grad)
    rate = threshold / norm
    if rate < 1:
        return　（さ）
    return grad

```

A:
gradient*rate

勾配のノルムがしきい値より大きいときは、勾配のノルムをしきい値に正規化するので、クリッピングした勾配は、勾配×（しきい値/勾配のノルム）と計算される。つまり、gradient*rateである。



#####LSTMモデルの定式化

![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653571/d1e8e877-4e69-d059-abb7-04be769c8f08.jpeg)

##### CEC

勾配消失および勾配爆発の解決方法として、勾配が、１であれば解決できる。

```math
\delta^{t-z-1}= \delta^{t-z}(Wf^{'}(u^{t-z-1}))=1 \\
\frac{\partial E}{\partial c^{t-1}}= \frac{\partial E}{\partial c^t}\frac{\partial c^t}{\partial c^{t-1}}=\frac{\partial E}{\partial c^t}\frac{\partial [a^t-c^{t-1}]}{\partial c^{t-1}}=\frac{\partial E}{\partial c^t}


```

課題　→ニューラルネットワークの学習特性が無いということ。


入力層→隠れ層への重み

入力重み衝突。

隠れ層→出力層への重み

出力層重み衝突。

- 入力ゲート

入力された情報を付加してCECに覚えてもらう機能


- 出力ゲート

CECの情報をどのように出力に使うかという機能

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

- 忘却ゲート

課題→過去の情報が要らなくなった場合、削除することができず、保管され続ける。

解決策→過去の情報が要らなくなった場合、そのタイミングで情報を忘却する機能が必要。


- 確認テスト

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

A:
忘却ゲート

- 演習チャレンジ

Q:
以下のプログラムはLSTMの順伝播を行うプログラムである。ただし、sigmoid関数は要素ごとにシグモイド関数を作用させる関数である。
（け）にあてはまるのはどれか。

```
def lstm(x, prev_h, prev_c, W, U, b):
    """
    x: inputs, (batch_size, input_size)
    prev_h: outputs at the previous time step, (batch_size, state_size)
    prev_c: cell states at the previous time step, (batch_size, state\size)
    W: upward weights, (4*state_size, input_size)
    U: lateral weights, (4*state_size, state_size)
    b: biass, (4*state_size,)
    """
    lstm_in = _activation(x.dot(W.T) + prev_h.dot(U.T) + b)
    a, i, f, o = np.hsplit(lstm_in, 4)

    a = np.tanh(a)
    input_gate = _sigmoid(i)
    forget_gate = _sigmoid(f)
    output_gate = _sigmoid(o)

    c = （け）
    h = output_gate * np.tanh(c)
    return c, h

```

A:
input_gate*a + forget_gate*c


- 覗き穴結合

CEC自身の値に、重み行列を介して伝播可能にした構造。
 



####Section3 GRU

課題

LSTMではパラメータが多く、計算負荷が高くなる問題があった。

→GRUではパラメータを大幅に削減し、精度は同等またはそれ以上が望める様になった構造。

![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653571/da804b04-6e6f-b9db-ba7f-5f64c4834445.jpeg)

- 確認テスト

Q:
LSTMとCECが抱える課題について、それぞれ簡潔に述べよ・

A:
LSTMはパラメータ数が多くなり、計算量が多くなる。
CECは学習能力がない。


- 演習チェレンジ

Q:
GRUもLSTMと同様にRNNの一種である、単純なRNNにおいて問題となる勾配消失問題を解決し、長期的な依存関係を学習することができる。LSTMに比べ変数の数やゲートの数が少なく、より単純なモデルであるが、タスクによってはLSTMより良い性能を発揮する。以下のプログラムはGRUの順伝播を行うプログラムである。ただし_sigmoid関数は要素ごとにシグモイド関数を作用させる関数である。
（こ）にあてはまるのはどれか。

```
def gru(x, h, W_r, U_r, W_z, U_z, W, U):
    """
    x: inputs, (batch_size, input_size)
    h: outputs at the previous time step, (batch_size, state_size)
    W_z, U_r: weights for reset gate
    W_z, U_z: weights for update gate
    U, W: weights for new state
    """

    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 = （こ）
    return h_new

```

A:
(1-z)*h+z*h_bar

- 確認テスト

Q:
LSTMとGRUの違いを簡潔に述べよ。

A:
GRUはCECがなく、更新ゲート、リセットゲートがあり、LSTMよりも計算量が少ない。



####Section4 双方向RNN

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

実用例：文章の推敲や、機械翻訳等



![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653571/6279b870-effa-5955-baa2-8803d4458f75.jpeg)

- 演習チャレンジ

Q:
以下は双方向RNNの順伝播を行うプログラムである。順方向については、入力から中間層への重みW_f,1ステップ前の中間層出力から中間層への重みをU_f,逆方向に関しては同様にパラメータW_b,U_bを持ち、両者の中間層表現を合わせた特徴から出力層への重みはVである。_rnn関数はRNNの順伝播を表し中間層の系列を変える関数であるとする。（か）にあてはまるのはどれか。

```
def bidirectional_rnn_net(xs, W_f, U_f, W_b, U_b, V):
    """
    W_f, U_f: forward rnn weights, (hidden_size, input_size)
    W_b, U_b: backward rnn weights, (hidden_size, input_size)
    V: output weights, (output_size, 2*hidden_size)
    """
    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 = [（か）for h_f, h_b in zip(hs_f, hs_b)]
    ys = hs.dot(V.T)
    return = ys



```

A: 
np.concatenate([h_f,h_b[::-1]],axis=1)


####Section5 Seq2Seq

Encorder-Decorderモデルの一種

#####Encorder RNN

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

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653571/e879a857-ee22-b330-c832-7f46535e2d79.png)

Talking: 文章を単語等のトークン毎に分割し、トークン毎のIDに分割する。

Embedding: IDからそのトークンを表す分散表現ベクトルに変換。

Encorder RNN: ベクトルを順番にRNNに入力していく。


vec1をRNNに入力し、hidden stateを出力。このhidden stateと次の入力vec2をまたRNNに入力してきたhidden stateを出力という流れを繰り返す。

最後のvecを入れたときのhidden stateをfinal stateとしてとっておく。このfinal stateがthought vectorと呼ばれ、入力した文の意味を表すベクトルとなる。

#####Decorder RNN

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

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653571/ddec9045-cda0-78ee-91de-b0237d652cb2.png)

1.Encorder RNNのfinal state(thought vector)から、各tokenの生成確率を出力していきます。final stateをDecorder RNNのinitial stateととして設定し、Embeddingを入力。

2.Sampling:生成確率にもとづいてtokenをランダムに選びます。

3.Embeding: 2で選ばれたtokenをEmbeddingいしてDecorder RNNへの次の入力とします。

4.Dectokenize:1-3を繰り返し、２で得られたtokenを文字列に直します。

- 確認テスト

Q: 
下記の選択肢からseq2seqについて説明しているものを選べ。

A:
(2)RNNを用いたEncorder-Decorderモデルの一種であり、機械翻訳などのモデルに使われる。


- 演習チャレンジ

Q:
機械翻訳タスクにおいて、入力は複数の単語から成る文（文章）であり、それぞれの単語はone-hotベクトルで表現されている。Endorderにおいて、それらの単語は単語埋め込みにより特徴量に変換され、そこからRNNによって（一般的にはLSTMを使うことが多い）時系列の情報をもつ特徴へとエンコードされる。以下は、入力である文（文章）を時系列の情報をもつ特徴量へとエンコードする関数である。ただし、_activation関数はなんらかの活性化関数を表すとする。
（き）にあてはまるのはどれか。

```
def encode(words, E, W, U, b):
    """
    words: sequence words (sentence), one-hot vector, (n_words, vocab_size)
    E: word embedding matrix, (embed_size, vocab_size)
    W: upward weights, (hidden_size, hidden_size)
    U: lateral weights, (hidden_size, embed_size)
    b: bias, (hidden_size,)

    """
    hidden_size = W.shape[0]
    h = np.zero(hidden_size)
    for w in words:
        e = （き）
        h = _activation(W.dot(e) + U.dot(h) +b)
    return h 

```


A:
E.dot(w)

#####seq2seqの課題
一問一答しかできない。→問に対して文脈も何もなく、ただ応答が行われ続ける。→HRED

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

→seq2seqでは会話の文脈無視で、応答がなされたが、HREDでは、前の単語の流れに即して応答されるため、より人間らしい文章が生成される。

課題
→同じコンテキスト（発話リスト）を与えられても、答えの内容が毎回会話の流れとしては同じものしか出せない。

→短いよくある答えを学ぶ傾向がある。

#####VHRED
HREDにVAEの潜在変数の概念を追加したもの

- 確認テスト

Q:
seq2seqとHRED,HREDとVHREDの違いを簡潔に述べよ。

A:
seq2seqは一つの時系列データから別の時系列データを得るネットワーク。HREDはseq2seqにそれまでの意味ベクトルを解釈に加えられるようにすることで、文脈の意味を汲み取った文の変換encoder,decoderにしたもの。
VHREDはHREDが文脈に対して当たり障りのない回答しか作れなくなったことに対しての解決策。オートエンコーダ、VAEの考え方を取り入れて当たり障りのない以上の出力をほどこしたモデル。

#####オートエンコーダ

教師なし学習のひとつ。

入力データから潜在変数zに変換するニューラルネットワークをEncorder
逆に潜在変数zをインプットとして元画像を復元するニューラルネットワークをDecorder

メリットは次元削減が行えること。

#####VAE

通常のオートエンコーダーの場合、何かしら潜在変数zにデータを押し込めているものの、その構造がどのような状態かわからない。→VAEはこの潜在変数zに確率分布z-N(0,1)を仮定したもの。
→VAEはデータを潜在変数zの確率分布という構造に押し込めることを可能にします。



####Section6　Word2vec

課題：RNNでは、単語のような可変長の文字列をRNNに与えることはできない。
→固定長形式で単語を表す必要がある。

学習データからボキャブラリを作成
→番号順に振る→行列を作る。

メリット

大規模データの分散表現の学習が、現実的な計算速度とメモリ量で実現可能にした。

→ボキャブラリ×任意の単語ベクトル次元で重み行列が誕生。



####Section7　Attention Mechanism

課題：seq2seqの問題は長い文章への対応が難しい。
seq2seqでは、２単語でも、100単語でも。固定次元ベクトルの中に入力しなければならない。

解決策：
文章が長くなるほどそのシーケンスの内部表現の次元も大きくなっていく、仕組みが必要になります。

→Attention Mechanism→「入力と出力のどの単語が関連しているのか」の関連度を学習する仕組み。

- 確認テスト

Q:
RNNとword2vec,seq2seqとAttentionの違いを簡潔に述べよ。

A:
RNNは時系列データを処理するのに適したネットワーク。
word2vecは単語の分散ベクトルを得る手法。
seq2seqは一つの時系列データから別の時系列データを得るネットワーク。
Attentionは時系列データの中身に対してそれぞれの関連性に重みをつける手法。