0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

深層学習 学習まとめ: 再帰型ニューラルネットワーク

Posted at

再帰型ニューラルネットワーク(RNN, Recurrent NN)

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

  • 時系列データに対応可能なニューラルネットワーク
    • 時系列データとは、時間的順序を追って一定間隔ごとに観察され、しかも相互に統計的依存関係が認められるようなデータの系列
      • 例えば、音声データ、株価データやテキストデータ
      • テキストデータは単語が前の単語とつながっていると考える
  • 入力層〜中間層〜出力層というニューラルネットワークの基本構造は変わらないが、中間層同士に時間的なつながり(数珠つながり)を持つ
    • 時系列モデルを扱うには、初期の状態と過去の時間$ t-1 $の状態を保持し、そこから次の時間での$ t $を再帰的に求める再帰構造が必要となる
    • 中間層の出力を次の時点の中間層の入力として活用する
      • エルマンネットワーク
        • 出力層を戻すジョーダンネットワークというものも存在する
      • 入力$ x_n $と前の時点の中間層 $ z_{n-1} $から、新しい中間層 $ z_n $が得られ、そこから出力$ y_n $を得る
      • 重みは入力の重み$ W_{(in)} $と出力の重み$ W_{(out)} $に加え、前の中間層からの重み$ W $がある
  • 数学的な記述
    • $ u^t = W_{(in)}x^t + Wz^{t-1} + b $
      • $x^t$はt時点の入力、$z^{t-1}$は前の中間層の出力
    • $ z^t = f(W_{(in)}x^t + Wz^{t-1} + b) $
      • $f$は中間層の活性化関数
    • $ v^t = W_{(out)}z^t + c $
    • $ y^t = g(W_{(out)}z^t + c) $
      • $g$は出力層の活性化関数
  • 実装(バイアスb, cは省略)
    • 数式上は現在が$ t $、前が$ t-1 $だが、Pythonでの実装では$ t $が0から始まるため、現在が$ t+1 $、前が$ t $とする
u[:,t+1] = np.dot(X, W_in ) + np.dot(z[:,t].reshape(1, -1), W)
z[:,t+1] = functions.sigmoid (u[:,t+1])
y[:,t] = functions.sigmoid(np.dot(z[:,t+1].reshape(1, -1), W_out))

BPTT(Back Propagation Through Time)

  • RNNにおけるパラメーター調整方法の一種
    • 誤差逆伝播法の一種
  • 数学的な記述
    • Wが3つあるので、3つの偏微分を行う
    • $ \dfrac{∂E}{∂W_{(in)}} = \dfrac{∂E}{∂u^t} \lbrack \dfrac{∂u^t}{∂W_{(in)}} \rbrack ^T = δ^t[x^t]^T$
      • $ δ $は、$E$から$u$までの微分をひとまとめにしたもの
      • RNNにおいて、$ \lbrack   \rbrack ^T $は時間を遡って全て微分することを表す
    • $ \dfrac{∂E}{∂W_{(out)}} = \dfrac{∂E}{∂v^t} \lbrack \dfrac{∂v^t}{∂W_{(out)}} \rbrack ^T = δ^{out,t}[z^t]^T$
    • $ \dfrac{∂E}{∂W} = \dfrac{∂E}{∂u^t} \lbrack \dfrac{∂u^t}{∂W} \rbrack ^T = δ^t[z^{t-1}]^T$
    • バイアスは2つ
    • $ \dfrac{∂E}{∂b} = \dfrac{∂E}{∂u^t} \dfrac{∂u^t}{∂b} = δ^t   as   \dfrac{∂u^t}{∂b} = 1 $
    • $ \dfrac{∂E}{∂c} = \dfrac{∂E}{∂v^t} \dfrac{∂v^t}{∂v} = δ^{out,t}   as   \dfrac{∂v^t}{∂c} = 1 $
  • コードでの記述(バイアスb, cは省略)
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))
  • $δ^t$は、$E$から$u$までの微分をひとまとめにしたもの
    • $ δ^{t-n-1} = δ^{t-n} \cdots $ は一つ前の時点とつながっていることを示す
delta_out[:,t] = functions.d_mean_squared_error(dd, y[:,t]) * functions.d_sigmoid(y[:,t])

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])
  • パラメーターの更新式
# 勾配更新
W_in_grad += np.dot(X.T, delta[:,t].reshape(1,-1))
W_out_grad += np.dot(z[:,t+1].reshape(-1,1), delta_out[:,t].reshape(-1,1))
W_grad += np.dot(z[:,t].reshape(-1,1), delta[:,t].reshape(1,-1))

# 勾配適用
W_in -= learning_rate * W_in_grad
W_out -= learning_rate * W_out_grad
W -= learning_rate * W_grad
- 時間的なつながりを辿る部分は$ \sum $がつく
  • BPTTの全体像
    • $ z^t $を開くと$ z^{t-1} $が出てきて、$ z^{t-1} $を開くと$ z^{t-2} $が出てきてと、zが数珠つながりになっている(=再帰)

LSTM(Long Short-Term Memory)

  • RNNの課題

    • 時系列を遡れば遡るほど、勾配が消失していく
      • 結果的に長い時系列の学習が困難
    • 逆の勾配爆発という現象もある
      • 勾配が層を逆伝搬するごとに指数関数的に大きくなってしまい、結果的に学習がうまく行かない
  • LSTMは構造自体を変えて勾配消失問題を解決するRNNの一種

  • LSTMの構造
    20240215LSTM.png

  • CEC: Constant Error Carousel(記憶セル)

    • CECは記憶機能しかない(考える機能は持たない)
    • 勾配消失および勾配爆発の解決方法として、理屈上、勾配が1であれば解決できる
      • 勾配を1にするために導入されたのがCEC
    • 逆に勾配が1ということは、ニューラルネットワークの学習特性がないということ(覚えるが、学習しない)
      • CECの周りに学習機能を配置する
  • 入力ゲートと出力ゲート

    • CECは記憶機能しか持たず、学習機能を持たないが、入力ゲートと出力ゲートを追加することにより、全体として学習機能を持たせる
      • 入力・出力ゲートを追加することで、それぞれのゲートへの入力値の重みを、重み行列$ \boldsymbol{W_x}, \boldsymbol{U_h} $で可変可能とする
    • 入力ゲートは入力された情報をどのように記憶するかをCECに指示する
    • 出力ゲートはCECが保管している情報(記憶)をどのように使うかを指示する
    • 今回の入力値と前回の出力値(隠れ層)をもとに、入力ゲート、出力ゲート共に学習する
      • 入力ゲートの重み$ \boldsymbol{W_x}^{(i)} $(今回の入力値に対する重み), $ \boldsymbol{U_h}^{(i)} $(前回の出力値に対する重み)を学習する
        • 入力ゲートの出力(どれだけCECに記憶させるか)$ \boldsymbol{I} $
    • 出力ゲートの重み$ \boldsymbol{W_x}^{(o)} $(今回の入力値に対する重み), $ \boldsymbol{U_h}^{(o)} $(前回の出力値に対する重み)を学習する
      • 出力ゲートの出力(CECの記憶をどう使うか)$ \boldsymbol{O} $
  • 忘却ゲート

    • CECには過去の情報が全て保管されているが、過去の情報が入らなくなっても、削除することができず、保管され続ける
      • 忘却ゲートは、過去の情報が要らなくなった時点で、CECからその情報を忘却させる機能を持つ
      • 忘却ゲートの重み$ \boldsymbol{W_x}^{(f)} $(今回の入力値に対する重み), $ \boldsymbol{U_h}^{(f)} $(前回の出力値に対する重み)を学習する
        • 忘却ゲートの出力(どれだけCECに忘却させるか)$ \boldsymbol{F} $
  • 処理の流れ

    • 学習されるLSTMのパラメーターは、入力に対する重み行列である$ \boldsymbol{W_x} = [\boldsymbol{W_x}^{(f)}, \boldsymbol{W_x}^{(g)}, \boldsymbol{W_x}^{(i)}, \boldsymbol{W_x}^{(o)}] $、前の時刻からの隠れ状態に対する重み行列である$ \boldsymbol{U_h} = [\boldsymbol{U_h}^{(f)}, \boldsymbol{U_h}^{(g)}, \boldsymbol{U_h}^{(i)}, \boldsymbol{U_h}^{(o)}] $、バイアスベクトルである$ \boldsymbol{B} = [\boldsymbol{b}^{(f)}, \boldsymbol{b}^{(g)}, \boldsymbol{b}^{(i)}, \boldsymbol{b}^{(o)}] $からなる
    • 入力と前の時刻からの隠れ状態から、忘却ゲートの値を計算する
      • $ \boldsymbol{F} = sigmoid(\boldsymbol{X_t} \boldsymbol{W_x}^{(f)} + \boldsymbol{H_{t-1}} \boldsymbol{U_h}^{(f)} + \boldsymbol{b}^{(f)}) $
        • ゲートは度合い(忘却ゲートの場合は、忘れる度合い)を調整するので、シグモイド関数で0〜1とする
    • 入力と前の時刻からの隠れ状態から、入力の値を計算する
      • $ \boldsymbol{G} = tanh(\boldsymbol{X_t} \boldsymbol{W_x}^{(g)} + \boldsymbol{H_{t-1}} \boldsymbol{U_h}^{(g)} + \boldsymbol{b}^{(g)}) $
        • 入力の値は情報量を一定程度維持しつつ勾配爆発を避けるために、tanh関数で-1〜1とする
    • 入力と前の時刻からの隠れ状態から、入力ゲートの値を計算する
      • $ \boldsymbol{I} = sigmoid(\boldsymbol{X_t} \boldsymbol{W_x}^{(i)} + \boldsymbol{H_{t-1}} \boldsymbol{U_h}^{(i)} + \boldsymbol{b}^{(i)}) $
        • ゲートは度合い(入力ゲートの場合は、入力を加える度合い)を調整するので、シグモイド関数で0〜1とする
    • 入力と前の時刻からの隠れ状態から、出力ゲートの値を計算する
      • $ \boldsymbol{O} = sigmoid(\boldsymbol{X_t} \boldsymbol{W_x}^{(o)} + \boldsymbol{H_{t-1}} \boldsymbol{U_h}^{(o)} + \boldsymbol{b}^{(o)}) $
        • ゲートは度合い(出力ゲートの場合は、出力を伝える度合い)を調整するので、シグモイド関数で0〜1とする
    • 前の時刻の記憶$ C_{t-1} $に対し、忘却ゲートの値(度合い)$ F $を作用させ、続いて入力の値$ G $に対し入力ゲートの値(度合い)$ I $を作用させたものを加え、現時点での記憶$ C_t $を得る
      • $ \boldsymbol{C_t} = \boldsymbol{F} \odot \boldsymbol{C_{t-1}} + \boldsymbol{I} \odot \boldsymbol{G} $
        • ゲートによる作用は、アダマール積であることに注意
    • 現時点での記憶$ C_t $をtanh関数に通した上で、出力ゲートの値(度合い)$ O $を作用させることで、現時点での隠れ状態 $ H_t $を得る
      • $ \boldsymbol{H_t} = \boldsymbol{O} \odot tanh(\boldsymbol{C_t}) $
        • 入力と同様に記憶も情報量を維持するためにtanh関数を使用する
        • ゲートによる作用は、アダマール積であることに注意
    • 実装に際しては、入力に対する重み行列である$ \boldsymbol{W_x} $、前の時刻からの隠れ状態に対する重み行列である$ \boldsymbol{U_h} $、バイアスベクトルである$ \boldsymbol{B} $それぞれを一まとまりの行列として$ \boldsymbol{X_t} \boldsymbol{W_x} + \boldsymbol{H_{t-1}} \boldsymbol{U_h} + \boldsymbol{b} $の計算を行い、結果を$ f, g, i, o $に分割する処理とすることが一般的
      • 前述のように、ゲート$ f, i, o $にはシグモイド関数を、$ g $にはtanh関数を適用し、$ F, G, I, O $を得る
  • 覗き穴(peep hole)結合

    • CEC自体の値は、ゲート(入力/出力/忘却)制御に影響を与えていないが、与えることはできないか
      • 覗き穴結合はCEC自体の値を重み行列を介してゲートに伝播可能にした構造
      • 例えば、入力と前の時刻からの隠れ状態と、前の時刻の記憶から、忘却ゲートの値を計算する
        • $ \boldsymbol{F} = sigmoid(\boldsymbol{X_t} \boldsymbol{W_x}^{(f)} + \boldsymbol{H_{t-1}} \boldsymbol{U_h}^{(f)} + \boldsymbol{C_{t-1}} \odot \boldsymbol{V_c}^{(f)} + \boldsymbol{b}^{(f)}) $
          • 覗き穴の作用は、アダマール積であることに注意
      • ただし出力ゲートの計算においては、出力ゲートがCECより後ろに存在するので、現在の記憶($ C_t $)を作用させる
        • $ \boldsymbol{O} = sigmoid(\boldsymbol{X_t} \boldsymbol{W_x}^{(o)} + \boldsymbol{H_{t-1}} \boldsymbol{U_h}^{(o)} + \boldsymbol{C_{t}} \odot \boldsymbol{V_c}^{(o)} + \boldsymbol{b}^{(o)}) $
      • ただ実際にはあまり大きな効果は得られていない
  • LSTMでは、勾配爆発を避けるために、勾配クリッピングを使うことがある

    • 大きい勾配に対し、ノルムを閾値以下に正規化する

GRU(Gated Recurrent Unit)

  • LSTMの改良版

    • LSTMではパラメーター数が多く、計算負荷が高くなる
    • GRUはパラメーターを減らすことによって、計算負荷が低い
  • CEC、入力ゲート、出力ゲート、忘却ゲートがなく、その代わりに、リセットゲートと更新ゲートがある

  • GRUの構造
    20240215GRU.png

  • 処理の流れ

    • 学習されるGRUのパラメーターは、入力に対する重み行列である$ \boldsymbol{W_x} = [\boldsymbol{W_x}^{(r)}, \boldsymbol{W_x}^{(z)}, \boldsymbol{W_x}^{(\tilde{h})}] $、前の時刻からの隠れ状態に対する重み行列である$ \boldsymbol{U_h} = [\boldsymbol{U_h}^{(r)}, \boldsymbol{U_h}^{(z)}, \boldsymbol{U_h}^{(\tilde{h})}] $、バイアスベクトルである$ \boldsymbol{B} = [\boldsymbol{b}^{(r)}, \boldsymbol{b}^{(z)}, \boldsymbol{b}^{(\tilde{h})}] $からなる
    • 入力と前の時刻からの隠れ状態から、リセットゲートの値を計算する
      • $ \boldsymbol{R} = sigmoid(\boldsymbol{X_t} \boldsymbol{W_x}^{(r)} + \boldsymbol{H_{t-1}} \boldsymbol{U_h}^{(r)} + \boldsymbol{b}^{(r)}) $
        • ゲートは度合い(リセットゲートの場合は、前の隠れ状態を忘れる度合い)を調整するので、シグモイド関数で0〜1とする
    • 入力と前の時刻からの隠れ状態から、更新ゲートの値を計算する
      • $ \boldsymbol{Z} = sigmoid(\boldsymbol{X_t} \boldsymbol{W_x}^{(z)} + \boldsymbol{H_{t-1}} \boldsymbol{U_h}^{(z)} + \boldsymbol{b}^{(z)}) $
        • ゲートは度合い(更新ゲートの場合は、前の隠れ状態を混ぜる度合い)を調整するので、シグモイド関数で0〜1とする
    • 入力と前の時刻からの隠れ状態、およびリセットゲートの値から、仮の隠れ状態の値$ \boldsymbol{\tilde{H}} $を計算する
      • $ \boldsymbol{\tilde{H}} = tanh \lbrace \boldsymbol{X_t} \boldsymbol{W_x}^{(\tilde{h})} + (\boldsymbol{R} \odot \boldsymbol{H_{t-1}}) \boldsymbol{U_h}^{(\tilde{h})} + \boldsymbol{b}^{(\tilde{h})} \rbrace $
        • 入力の値は情報量を一定程度維持しつつ勾配爆発を避けるために、tanh関数で-1〜1とする
        • ゲートによる作用は、アダマール積であることに注意
    • 最後に、(1- 更新ゲートの値$ \boldsymbol{Z}$)と$\boldsymbol{H_{t-1}} $、 $ \boldsymbol{Z} $と$ \boldsymbol{\tilde{H}} $から、新たな隠れ状態の値$ \boldsymbol{H_t} $を計算する
      • $ \boldsymbol{H_t} = (1 - \boldsymbol{Z}) \odot \boldsymbol{H_{t-1}} + \boldsymbol{Z} \odot \boldsymbol{\tilde{H}} $
        • ゲートによる作用は、アダマール積であることに注意
      • なお、$ 1- \boldsymbol{Z} $と$ \boldsymbol{Z} $をひっくり返した数式で記載されていることもあるので、注意が必要(基本的には上記が正しいと思われる)

その他の派生RNNモデル

エコーステートネットワーク

  • 入力の重み$ \boldsymbol{U} $と、隠れ層の重み$ \boldsymbol{V} $をランダムな値で固定し、出力の重み$ \boldsymbol{W} $のみを学習する
    • シンプルな線形回帰の問題となり、勾配消失や勾配爆発が発生しなくなり、また、学習が速くなる

スキップ接続

  • 長期依存性の課題への対策の一つとして、隠れ層を1時刻以上スキップした接続を追加する

Leaky unit

  • 隠れ層に線型結合を導入して移動平均の効果を得る
    • $ h_t = (1 - \frac{1}{τ})h_{t-1} + \frac{1}{τ} f( \boldsymbol{W_h} h_{t-1} + \boldsymbol{W}_{in}x_t) $

双方向RNN

  • 過去の情報だけでなく、未来の情報を加味することで、精度を向上させるためのモデル
    • 文章の推敲や、機械翻訳等に活用される
    • 文章は過去(文の前段)も未来(文の後段)も同時に情報が得られるので、過去から未来への情報の伝達だけでなく、未来から過去への情報の伝達も活用できる
  • 時間に対し順方向に隠れ状態を伝播させる層と、逆方向に隠れ状態を伝播させる層は直接的には結合していない

RNNの応用例

seq2seq

  • 2つのネットワークがつながれている自然言語処理用のネットワーク
    • 系列変換モデル: 単語列などの系列(sequence)を受け取り、別の系列へ変換するモデル
      • 時系列の入力をもとに、出力を時系列で予測する
    • 一つ目のネットワーク(Encoder)で文の意味(=文脈)を抽出・保持し、それを二つ目のネットワーク(Decoder)に渡す
    • seq2seqはEncoder-Decoderモデルの一種で、機械対話や機械翻訳などに使用される
  • Encoder RNN
    • ユーザーがインプットしたテキストデータを、単語等のトークンに区切って渡す構造
    • Tokening: 文章を単語等のトークン毎に分割し、トークンごとのIDに分割する
      • IDはone hot vectorで表現できる
    • Embedding: IDから、そのトークンを表す分散表現ベクトルに変換
      • one hot vectorは数万列(単語数だけ)あるが、それを数百列の数字行列に圧縮する
        • 意味が似ている単語が似た行列になるようにする
        • 結果的に単語の意味を抽出していることになる
    • Encoder RNN: ベクトルを順番にRNNに入力していく
      • 処理手順
        1. vec1をRNNに入力し、hidden stateを出力する
        2. このhidden stateと次の入力vec2をまたRNNに入力し、結果のhidden stateを出力という流れを繰り返す
        3. 最後のvecを入れたときのhidden stateをfinal stateとしてとっておく
        4. このfinal stateがthought vectorと呼ばれ、入力した文の意味を表すベクトルとなる
      • 意味が似ている単語が似た行列になるようにする(=特徴量の抽出)という処理が難しい
        • GoogleのBERTはMLM(Masked Language Model)で
          • 単語に分解した上で、ある単語をマスク、前後の単語から予測させる
          • それによって、似た意味の単語が似たような意味ベクトルになるようになる
          • 人間が学習データを作る必要がないので、大量のデータを学習できる
  • Decoder RNN
    • システムがアウトプットデータを単語等のトークンごとに生成する構造
    • 処理手順
      1. Decoder RNN: Encoder RNNのfinal state(thought vector)から、各tokenの生成確率を出力
      2. Sampling: 生成確率にもとづいてtokenをランダムに選ぶ
      3. Embedding: 2で選ばれたtokenをEmbeddingして Decoder RNNへの次の入力とする
      4. Detokenize: 1~3を繰り返し、2で得られたtokenを文字列に直す

HRED

  • seq2seqでは一問一答しかできない(それまでの文脈が活かされない)
  • HREDでは、過去n-1個の発話から次の発話を生成する
    • 一文一文も単語と同様にseq2seqにかける
    • 文の意味ベクトルを次に引き継ぐ
      • seq2seqでは単語の意味ベクトルだったが、その上にさらに文レベルでのRNNを構築する
  • つまり、HREDとは、seq2seq + Context RNN
    • Context RNN: Encoderのまとめた各文章の系列をまとめて、これまでの会話コンテキスト全体を表すベクトルに変換する構造
      • これによって、過去の発話の履歴を加味した返答をできる
  • HREDの課題
    • HREDは確率的な多様性が字面にしかなく、会話の「流れ」のような多様性が無い
    • HREDは「うん」「そうだね」など、短く情報量に乏しい答えをしがちである
  • VHRED
    • HREDに、VAE(Variational Auto Encoder)の潜在変数の概念を追加したもの
      • HREDの課題をVAEの潜在変数の概念を追加することで解決した構造
      • VAEについては、生成モデルを参照

word2vec

  • RNNでは、単語のような可変長の文字列をNNに与えることができず、固定長形式で単語を表す必要がある
  • word2vecでは、学習データからボキャブラリーを作成
    • 辞書の単語数だけone-hot vectorができる
  • これにボキャブラリ数 × 単語ベクトルの次元数の重み行列を導入することで、現実的な計算速度とメモリ量で大規模データの分散表現の学習を実現可能にした
  • 埋め込み行列を獲得するための方法としてContinuous Bag-of-Words(CBOW)と、skip-gramの二種類のニューラルネットワークがある
    • CBOWは前後の単語からある単語を予測するNN
    • skip-gramはある単語が与えられた時に、その周辺の単語を予測するNN
  • word2vecで巨大なコーパスを扱う場合、通常の他クラス分類を行うと計算速度が問題になるため、負例サンプリング(negative sampling)という手法で、損失関数の計算時間の短縮を図る
    • 通常のCBOWまたはskip-gramにおける出力層では、すべての語彙を対象として、ソフトマックス関数と交差エントロピー誤差関数の計算を行うが、これは非常に重くなる
    • 負例サンプリングを適用した場合の出力層では、1個の正例とk個の負例だけを対象として、シグモイド関数と交差エントロピー誤差関数の計算を行う
      • つまり膨大な多クラス分類問題をk+1個の2クラス分類問題としている
      • k個の負例は、コーパスの中から単語の出現頻度に基づいてサンプリングする
        • kはハイパーパラメーター

Attention Mechanism

  • seq2seqは、文章が長くなるほどそのシーケンスの内部表現の次元も大きくなっていくため、長い文章への対応が難しい
  • Attention Mechanismは、「入力と出力のどの単語が関連しているのか」という関連性の重みを学習する仕組み
    • 一文の中で特に重要な単語を自力で見出す
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?