宮塩のお勉強部屋

某大学でARまわりの研究をしている学生です。

【Coursera】Deep Learning Specialization - Course 5: Week1 Recurrent Neural Networks

今回からCourse 5のSequence Modelsをやっていきます。 まずはWeek 1です。前回の記事はこちら

smygw.hatenablog.com

Recurrent Neural Networks

Why sequence models

  • シーケンス(Sequence)データを扱う例
    • 音声認識(Speech recognition)、音楽生成(Music generation)、感情分類(Sentiment classification)、DNA分析、機械翻訳(Machine translation)、動画行動認識(Video activity recognition)、固有表現抽出(Name entity recognition)
    • 全て教師あり学習.    
  • 入力と出力は少なくともどちらか一方はシーケンスデータである.

Notation

  • 今後は以下の表記を用いる,
    • 入出力データにおける t 番目の要素を x^{\lt t>},\ y^{\lt t>}と表す.
    •  i番目のトレーニングデータについて、入力データと出力データの長さを T_x^{(i)},\ T_y^{(i)}と表す(ただし T_x^{(i)} = T_y^{(i)}とは限らない.)
    • つまり i番目のトレーニングデータの t 番目の要素は x^{(i)\lt t>},\ y^{(i)\lt t>}と書ける.
  • どのように各単語をデータとして扱うか?
    • 頻出の単語の上位リストやオンライン辞書を用いて辞書のリストを構築する(商業用ではふつう10万語くらいのサイズに及ぶ.)
    • 各単語はOne-hotベクトル(1つの要素のみ"1"で残りが全て"0"なベクトル)にして扱う.
  • 辞書にない単語が現れたらどうするのか?
    • "Unknown word"というトークンを用意してそれを割り当てればよい.

Recurrent Neural Network Model

  • NNをそのまま適用するには2つの問題がある.RNNはこれらの問題を解決する.
    • 入力および出力はデータごとに異なる長さを持つ.
    • テキストの異なる位置の特徴を共有できない(つまり、ConvNetのように異なる位置にあっても似た特徴は同じように学習されてほしい.)
  • RNN(Recurrent Neural Network, 再帰ニューラルネットワーク)では活性化関数の出力 a^{\lt t>}を次のタイムステップの活性化関数に渡している.
    •  x^{\lt 1>}の活性化関数に渡す値 a^{\lt 0>}はランダムに初期化しても良いが、0でpaddingしたベクトルを用いるのが一般的.
  • RNNの弱点は、ある入力に対してそれ以前のシーケンスの情報しか見ないという点である.
    • ふつう特定の単語に対して、その単語の後ろの文からも意味を推測するはずである.
    • この問題はBRNN(Bidirectional RNN, 双方向再帰ニューラルネットワーク)を扱う際に説明する.
  • 2種類の活性化関数を計算する.
    •  a^{\lt t>} = g(w_{aa} a^{\lt t-1>} + w_{ax} x^{\lt t>} + b_a ) = g(w_{a} [a^{\lt t-1>},\ x^{\lt t>}] + b_a )
      •  w_a = [a^{\lt t-1>},\ x^{\lt t>}]のように書くとシンプルな表現で済む(行列を横に並べただけ)
      • ReLUでもよいがtanhを用いるのが一般的.
    •  \hat{y}^{\lt t>} = g(w_{ya} a^{\lt t>} + b_y ) =g(w_{y} a^{\lt t>} + b_y )
      • 目的に応じて関数を選ぶ(2値問題であればsigmoid.)

Backpropagation through time

  • ロス関数は次のように定義できる.
    •  L(\hat{y},\ y) = \Sigma_{t=1}^{T_x} L^{\lt t>}(\hat{y}^{\lt t>},\ y^{\lt t>})
    •  L^{\lt t>}(\hat{y}^{\lt t>},\ y^{\lt t>}) = -y^{\lt t>} log\hat{y}^{\lt t>} -(1 - y^{\lt t>}) log(1 - \hat{y}^{\lt t>})
  • 時間軸とは逆方向に伝播することからBackpropagation through time(BPTT)と呼ばれる.

Different types of RNNs

  • 入力の次元 T_xと出力の次元 T_yが一致しない場合はどうするか?
  • RNNアーキテクチャは4種類存在する.
    • (One-to-one:これは単なるNNなのでRNNにする意味はない)
    • One-to-many:各タイムステップの出力を次のタイムステップの入力に用いる(ex. 音楽生成)
    • Many-to-one:最後のタイムステップでのみ出力する(ex. 感情分類)
    • Many-to-many(入力と出力に対応がある場合):各タイムステップで \hat{y}を出力(ex. 固有表現抽出)
    • Many-to-many(入力と出力に対応がない場合):全ての入力した(Encoder層)後に出力(Decoder層)を設計する(ex. 機械翻訳

Language model and sequence generation

  • 音声認識などで同じ発音(ex. pair or pear)を区別するために、文章のそれらしさの確率を出力する言語モデルが必要.
  • RNNを用いて言語モデルを構築するにはどうすればよいか?
    1. まず、トレーニングデータ(文章)を単語ごとに区切ってOne-hotなベクトル([tex: y^{\lt t>}を作る.
      • 文章の終わりでトークンを入れる.
      • 辞書にない単語(固有名詞など)はトークンに置き換える.
    2. 各タイムステップで x^{\lt t>} = y^{\lt t-1>}を入力し、その次にあり得そうな単語 y^{\lt t>}を予測する.
      •  t=1のときは x^{\lt 1>}=0として入力する.
    3. Softmaxのロス関数 L(\hat{y}^{\lt t>}, y^{\lt t>}) = -\Sigma_i y_i^{\lt t>} log\hat{y}_i^{\lt t>}を全てのタイムステップについて足し合わせて学習する.
    4. 文章のそれらしさを計算するには、以下のように各タイムステップでの予測の確率を掛ければよい.
      •  P(y^{\lt 1>},\ y^{\lt 2>},\ y^{\lt 3>}) = \hat{y}^{\lt 1>} \hat{y}^{\lt 2>} \hat{y}^{\lt 3>}

Sampling novel sequences

  • 構築した言語モデルがどのようになっているかを直感的に理解するにはサンプリングを行うのが良い.
    • 各タイムステップにおける出力から確率の高いものをランダムに選び、それを次のタイムステップの入力とする.
    • これを繰り返して出来上がった文章を見ると、トレーニングデータの文章の雰囲気に近いことが分かる.
  • アプリケーションによっては、単語単位ではなく文字単位で言語モデルを構築することもある.
    • 辞書を作らなくてよいのでを考慮する必要がなくなるメリットがある.
    • 文章のように多くの単語を幅広く見る際には適さず、学習する計算コストも高くなる.
    • そのため大抵は単語レベルでの言語モデルが一般的.

Vanishing gradients with RNNs

  • RNNはどのレイヤでも同じパラメータを採用しているため、勾配消失や勾配爆発の問題が起きやすい.そのため、1ステップ前や2ステップ前の影響は大きいが、早い段階のタイムステップからの影響は小さくなるため、広範囲のタイムステップでの学習は難しい.
    • 勾配が爆発しているときは計算結果がNaNとなってしまうため発見が容易である.この場合は、勾配がしきい値以上になったらスケーリングを行う手法(Gradient clipping)を用いることで対応できる.
    • 勾配消失問題を解決する方法は次のビデオから説明していく.

Gated Recurrent Unit (GRU)

  • 新しい変数 c^{\lt t>}を導入する.この変数は浅い層の情報を深い層に伝える役割があり、Memory cellと呼ばれる.これを用いることで勾配消失の問題を解決することが出来る.
  • 新しい入力のたびに以下の式に基づいて c^{\lt t>}を更新する.
    •  \tilde{c}^{\lt t>} = tanh(w_c [c^{\lt t-1>},\ x^{\lt t>}] + b_c)
      • 入力の記憶した方が良い部分を考慮した c^{\lt t>}の候補値(ex. 主語が単数形から複数形になった.)
    •  \Gamma_u = \sigma (w_u [c^{\lt t-1>},\ x^{\lt t>}] + b_u)
      • [tex: \tilde{c}^{\lt t>}をどの程度考慮して更新(Update)するかを表す更新ゲートを意味し、0から1の値をとる.
    •  c^{\lt t>} = \Gamma_u * \tilde{c}^{\lt t>} + (1 - \Gamma_u) * c^{\lt t-1>}
      • 実際に行う更新. *はelement-wiseな掛け算.
    •  a^{\lt t>} = c^{\lt t>}
  •  c^{\lt t>},\ \tilde{c}^{\lt t>} ,\ \Gamma_uはすべて同じ次元である.そのため、ベクトルの各要素ごとに記憶の維持or更新をすることができる.
  • 実際にはこれに加えて関連ゲート(Relevance gate)と呼ばれる \Gamma_rを計算する必要があり、上記の式は以下のように修正される.
    •  \tilde{c}^{\lt t>} = tanh(w_c [\Gamma_r * c^{\lt t-1>},\ x^{\lt t>}] + b_c)
    •  \Gamma_r = \sigma (w_r [c^{\lt t-1>},\ x^{\lt t>}] + b_r)
  • 多くの研究者が様々なバージョンのunitやその組み合わせを設計し、より良いRNNを模索してきた.LSTM (Long Short Term Memory)もその1つである.

Long Short Term Memory (LSTM)

  • LSTMはGRUを一般化したもので、より強力である.GRUとの相違点は以下の通り.
    •  a^{\lt t>} \neq c^{\lt t>}
    • 関連ゲートが存在しない.
    • 忘却ゲート(Forget gate) \Gamma_f出力ゲート(Output gate) \Gamma_oが存在する.
  • LSTMの更新式は次のように書ける.
    •  \tilde{c}^{\lt t>} = tanh(w_c [a^{\lt t-1>},\ x^{\lt t>}] + b_c)
    •  \Gamma_u = \sigma (w_u [c^{\lt t-1>},\ x^{\lt t>}] + b_u)
    •  \Gamma_f = \sigma (w_f [c^{\lt t-1>},\ x^{\lt t>}] + b_f)
    •  \Gamma_o = \sigma (w_o [c^{\lt t-1>},\ x^{\lt t>}] + b_o)
    •  c^{\lt t>} = \Gamma_u * \tilde{c}^{\lt t>} + \Gamma_f * c^{\lt t-1>}
    •  a^{\lt t>} = \Gamma_o * c^{\lt t>}
  • この他にも、例えばゲートの計算に c^{\lt t-1>}も用いるのぞき穴接続(Peephole connection)と呼ばれるバリエーションもある.
  • 歴史的にはLSTMの方が早く、GRUはLSTMを簡略化したものとして登場した.一般的にどちらが良いかの結論はない.
    • GRUはシンプルなモデルなので複雑で大きなモデルを構築するのに適しており、計算も少し早い.
    • LSTMはゲートが3つあるので複雑な処理が可能.

Bidirectional RNN

  • これまで議論したRNNは初めのタイムステップから前方向にのみ計算を行っていたため、それよりも後ろのタイムステップからの寄与は考えられていなかった.そこで、前方向からの計算の次に後ろ方向の計算も行い、両方の寄与を用いて出力を計算する.
  • Bidirectional RNN(BRNN)の式は以下のように書ける.
    •  \hat{y}^{\lt t>} = g(w_y [a_{→}^{\lt t>},\ a_{←}^{\lt t>} ] + b_y)
  • RNNのBlockはGRUやLSTMを用いても良い.自然言語処理ではLSTMを用いたBRNNが一般的である.
  • ただしデータ全体が入力として必要になるため、音声認識のようなリアルタイムで処理が必要な場合にBRNNが適するかどうかは考える必要がある.

Deep RNNs

  • より複雑な処理をするためにRNNを積み重ねるDeep RNNという方法もある.
    • 時間軸方向の同じ層ではパラメータ w_a^{\lt t>},\ b_a^{\lt t>}は共有される.
    • 時間軸方向で既に層が深いので、ふつうはRNNを2, 3層積み重ねる程度である.
    • もちろん、ブロックはGRUやLSTMを使っても良い.
  • 各タイムステップの出力を一般的なDNNの入力にする方法もある.