蟹者

@kanimono

単純なRNN vs LSTMしたかったおじさんの覚え書き

前回のあらすじ


 仕事でLSTMを使うため、TensorFlowを触り始めたおじさんは、迷宮に迷い込む → わからないLSTM 〜最近の機械学習はホントどうかしてる - 蟹者

 なんやかんやあってお金の力で迷宮を抜け出すための武具(詳解 ディープラーニング ~TensorFlow・Kerasによる時系列データ処理~)を手に入れたおじさんであったが・・・


とりあえず本の通りRNN書いてみた

 

コード

$ python3 -i RNNpractice_TF.py

epoch: 0 validation loss: 0.194182
epoch: 1 validation loss: 0.0267386
epoch: 2 validation loss: 0.00253485
epoch: 3 validation loss: 0.00234999

・・・(中略)
epoch: 28 validation loss: 0.00140957
epoch: 29 validation loss: 0.00171765
epoch: 30 validation loss: 0.00193409
epoch: 31 validation loss: 0.00193226
early stopping

とりあえず動いた。

環境

 ・MacBookPro ローカル
 ・Python 3.6.3(ローカルにそのまま入れた)
 ・TensorFlow 1.4.1
 ・TensorFlowは公式ドキュメント通りにvertualenvで切り分けたフォルダ内の仮想環境にインストール
 ・source hogehoge/activate して仮想環境内で実行
 
 ・まずTensorFlowを入れるまでにいろいろ手こずった。
 ・本の通りのコードでも一部動かない部分あったので修正。
 ・RNNの呼び出し方もTensorFlow 0系から変わっているようだし、いろいろ細々修正あるよう。

 ・たぶん1年後には同じコードで動かない(気がする)。

解説、というかメモ

 モデルの定義、誤差関数の定義、最適化手法の定義をそれぞれ関数にして、main部分から読みだして使う(そうするとなんか効率的らしい)。

・データの入れ方とRNNモデル*1

 複数の時系列*2を重ね、同じ時刻tの値をベクトル x(t)とする。
 f:id:russENG:20171231233848p:plain

 遡る時間数 τ (タウ)を設定し、τ だけ遡った時間を1個ずつずらしながらたくさん切り分けてくる。この切り分けた数がサンプル数となる。
 f:id:russENG:20171231233910p:plain

 なので、入れるデータの構造体は3次元のテンソルになる。(絵の次元は、コード内のX( , , )の属性順(次元)と対応。)
 f:id:russENG:20171231234617p:plain


 RNNは理論上、全ての前時間の情報を参照している。具体的には、前の時間の隠れ層の状態 h(t-1) を現在の隠れ層の状態 h(t) に伝達する。

 コードだと、inference関数内のfor構文で囲まれた (cell_output, state) = cell(x[:, t, :], state)の部分。
 f:id:russENG:20171231234638p:plain

 (絵はめんどくさいので奥行き軸(系列数)を省略、h(t)も変数 n_hidden の数だけユニットを持つベクトルとなります。)

・最適化の方法

 本通り、Adamというのを使った。

 学習率を、学習の度合いに応じていい感じに変えていくものらしいです。詳細は以下で。
 
 勾配降下法の最適化アルゴリズムを概観する | コンピュータサイエンス | POSTD

 初期値があらかじめ決まっている(良さげな値が提唱されている)、人力で探索しなくてもよいのは便利ですね。

 あとearly stopping使いました(これも本通り)。

・LSTMは?

 コードのinference関数内の、cell = tf.contrib.rnn.BasicRNNCell(n_hidden) となっているのを

 cell = tf.contrib.rnn.BasicLSTMCell(n_hidden, forget_bias=1.0) もしくは

 cell = tf.contrib.rnn.LSTMCell(n_hidden, forget_bias=1.0) としてやればよいそうです。

 つまりLTSMは、h(t-1) と x(t) から、h(t)の重みを更新する過程をめっちゃ賢くしただけで、その他の点については単純なRNNと共通ということです。
 
 いろいろ調べていくとRNNもLSTMも単層ではなく積み上げていくモデルもあるそうで、そうなるとinference部分を書き換える必要があるのだと思われます。

・その他

 人の書いたコードを写すのは勉強になるなあ。
 
 sklearnでやってたSVM*3とかと比べて、今回の件では、意外とパラメータの探索・調整とかいらないんだなあ、と思いました(それが大変なのでAdamとかが発明されているのかも知れません。)

 まあ隠れ層のユニット数は、問題や規模に応じて変える必要があるのでしょうね。


長期依存性とはなんぞや


 上で、RNNは理論的に τ 時間だけ遡った以前の情報を参照している、と書きました。

 しかしながら実際は、勾配消失問題や、前に長期依存した情報は学習できない(昔すぎることは覚えていられない)などの欠点があり、それを解決するためにLSTMなどが考案されてきた、というのは過去記事のリンクにもありましたね。
 
 marumaru.tonkotsu.jp

 これだと単純なRNNでは10ステップ前までくらいが限界、とあります。


 さて、そうなると長期依存した学習とやらの実力を見たくなるのが人情です。


 本ではトイ・プロブレム(アルゴリズムの性能を実験的に調べる模擬的な課題)としてAdding Problem というのが使われていて、単純なRNNでは学習できなかったが、LSTMでは学習できた、とありました。

 Adding Problemについては、以下がわかりやすかったです。
 
 qiita.com

 しかし、普通に時系列モデルの同定が頭の中にあったので(サインカーブの同定など)、こんな課題、現実にあるの?というか、出来たとして役に立つことになるの?とか思ってしまいました。

 ひょっとすると翻訳とかにRNNを使う向きでは、このトイ・プロブレムのような必要性があるのかも知れません。



今後どうするか


 ・LSTMについて

  ・誤差にどんくらい強いのか、とか性能について調べてみたい

    ・というか、関係ない変数にどれくらい強いのか。

    ・ニューラルネットで時系列解析をするということは統計的なモデルを作るのが大変な場合だったりして、とりあえずなんでも変数としてぶち込むような使い方をすることが想定されるので、ここの議論は重要だと思う。

  ・どの変数が利いてるとか、可視化する方法について調べたい。
  
   ・上のリンクではRWAの学習の様子を可視化していた。

   ・関係ないけど、やっぱりmatplotlib使いずらいので、plotly使いたい。
    
    matplotlib使いづらくない?plotlyで可視化しようよ - MATHGRAM


 ・TensorFlowとこの界隈への慣れ

  ・やっぱり論文とか英語のドキュメントをもっと抵抗なく読めるようにならないと。

  ・とりあえず最新論文の要約とかしてくれるすごい人たち(現人神)のtwitterリストを作った。

  ・まあ論文読めって話なのかも知れないけど、そのモデルの利点を理解するということは、トイ・プロブレムを理解するということがすごく効いてくるな、と。

  ・要は、試行回数を増やして慣れていくしかないなー。どんどんやっていこう!


 とりあえず、明けましておめでとうございます。

*1:ここの部分の理解に時間がかかったので絵を描きました

*2:1つでもよい。コード内では1つ

*3:過去記事:今さら新しみのないサポートベクターマシン(SVM)のサンプルコード(Python,Scikit-learn) - 蟹者