プロクラシスト

今日の寄り道 明日の近道

【Day-18】時系列のディープラーニング、RNNのまとめとKeras実装


スポンサーリンク

f:id:imslotter:20171218194346p:plain

データ分析ガチ勉強アドベントカレンダー 18日目。

Kerasの使い方を復習したところで、今回は時系列データを取り扱ってみようと思います。 時系列を取り扱うのにもディープラーニングは用いられていて、RNN(Recurrent Neural Net)が主流。

今回は、RNNについて書いた後、Kerasで実際にRNNを実装してみます。

今日の参考書はこの本!

詳解 ディープラーニング ~TensorFlow・Kerasによる時系列データ処理~

詳解 ディープラーニング ~TensorFlow・Kerasによる時系列データ処理~

  • 作者:巣籠 悠輔
  • 発売日: 2017/05/30
  • メディア: 単行本(ソフトカバー)

RNNとは

通常のNeural Networkとの違い

Recurrent Neural Netの略。再帰ニューラルネット。何が再帰かは下図

入力層、隠れ層、出力層の構造はDay-15で説明したNeural Netとほとんど同じだが、最も大きな違いは隠れ層同士がつながっているところ。これによって時系列に対応できるようにしている。順伝播の計算も、再帰性の要素が入ってくる。(添え字等はDay15参照)

誤差逆伝播法のアルゴリズム

基本的にニューラルネットと同じだが、隠れ層の時間方向に微分がつながっていくので、時間を遡りながら誤差の影響を伝えていく、しかし、本来は一番初めの時刻まで遡るものだが、遡りすぎると時間方向の微分がかけ粟さて勾配消失/爆発を引き起こし、うまく学習されないことも多くある。シンプルなRNNではBPTTを途中で打ち切る場合も多い。

下記にアルゴリズムを記す

1. 順伝播計算により{z}の更新式、出力{y}を求める

2 ラベル{d_{nk}^{t}}との誤差を計算

3. 重みを最小化する


4. デルタはBPTTを行うことで再帰的に求められる

5. 何らかのオプティマイザにより重みを更新

勾配消失の工夫 : LSTMやGRU

時間を遡ってBackpropagationを行うため、勾配消失が起こる。それを解消する工夫として、LSTM(Long-Short Term Memory)とGRU(Gated Recurrent Unit)がある。上の図の隠れ層に置ける、○の中身を工夫しながら、何を忘れて何を覚えておくか**まで賢く学習する。

LSTM

正直、本の図はちょっと分かりにくい、ウェブをあさってて一番厳密かつ分かりやすかったのは A Beginner’s Guide to Recurrent Networks and LSTMsというサイトの図

複雑...一番のポイントは

  • 中心のcellと書かれている箇所(CECという)+forget gateで、過去情報をどのくらい記憶するかを調整する
  • これにより、BTPP中の勾配消失を数式的にも抑えることができた*1

    GRU

    LSTMの難点は複雑でパラメータが多いところ。上図で言えばinput/forget/blockと、それぞれのGateに入れるときの重みを最適化しないといけない。一方でGRUはLSTMベースだが、入力ゲートがLSTMよりも断然少ない。

    ゆえに計算量も少ない。さらに、タスクによってはLSTMより良い性能を発揮するということで、注目されている

    見た目の違いが論文に載っている。(Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling)

    だいぶシンプル。計算も軽いわけだから、とりあえずGRUから試してみるのがいいのかもしれない。

    自然言語をにぎわすAttention Model

    NLP(Natural Language Process)のRNN界隈では、Attention Modelというのがにぎわっているように見受けられる。

    勉強不足でまだまだ全然分かっていないので、勉強用のソースだけ張っておく。勉強。

    Keras実装

    練習がてら、KerasでRNNを実装してみる。 githubにもあげているのでそちらも参考に。

    github.com

    データの入力の仕方がちょっと難しかったので、そこだけ重点的に。

    データの変形、入力

    BPTTの都合もあり、投げるデータの配列をすこしいじってやらないといけない。いじり方について図でまとめてみた。このような3次のテンソル的な配列になる。

    コードをみる
    %matplotlib inline
    import matplotlib.pyplot as plt
    import pandas as pd
    import numpy as np
    import keras
    df = pd.read_csv("AirportPassengers.csv",delimiter=";").dropna()
    data = []
    target = []
    max_len = 24
    dim = 1
    # 正規化
    maximum = df.Passengers.max()
    minimum = df.Passengers.min()
    df["Passengers"] = (df.Passengers-minimum)/(maximum-minimum)
    # データを箱に入れる
    for i in range(len(df)-max_len-1):
        data.append(df.Passengers.values[i:i+max_len])
        target.append(df.Passengers.values[i+max_len+1])
    # データの整形
    data = np.array(data).reshape(len(data),max_len,dim)
    target = np.array(target).reshape(-1,1)
    # データの分割
    from sklearn.model_selection import train_test_split
    N_train = int(len(data)*0.7)
    N_test = len(data) - N_train
    X_train, X_validation, Y_train, Y_validation = train_test_split(data, target, test_size=N_test)
    

    学習モデル構築

    keras.layers.recurrentの中に、学習モデルが入っている。 LSTM, GRUも実装されていて、model.addするだけで使えるようになる。Optimizerの設定は普通のニューラルネットと同じ。Day-17にOptimizerについて書かれている

    コードをみる
    from keras.models import Sequential
    from keras.layers import  Dense
    #一番シンプルなやつ
    from keras.layers.recurrent import SimpleRNN
    from keras.layers.recurrent import LSTM, GRU #改良版
    model = Sequential()
    # ネットワーク構成
    input_shape=(max_len, dim)
    # model.add(SimpleRNN(units=64, kernel_initializer="random_uniform",input_shape=input_shape))
    #model.add(LSTM(units=64, input_shape=input_shape))
    model.add(GRU(units=64, input_shape=input_shape))
    
    model.add(Dense(input_shape[1],activation="linear"))
    # optimizerの設定
    from keras.optimizers import Adam
    model.compile(loss="mse", optimizer=Adam())
    

    学習と予測

    7割を学習用データにして、残りの3割を予測することにした。

    コードをみる
    from keras.callbacks import EarlyStopping
    early_stopping = EarlyStopping(monitor='val_loss', patience=10, verbose=1)
    epochs = 500
    batch_size = 10
    model.fit(X_train, Y_train,
              batch_size=batch_size, epochs=epochs,
              validation_data=(X_validation, Y_validation))
    prediction = []
    data_in = data.reshape(data.shape[0],data.shape[1])[N_train]
    
    iteration = len(data) - N_train
    for _ in range(iteration):
    #     print(data_in)
        pred = model.predict(data_in.reshape(1,-1,1))
        data_in = np.delete(data_in, 0)
        data_in = np.hstack((data_in, pred[0]))
        prediction.append(pred[0,0])
    passenger = list(df.Passengers)
    data_num = len(passenger)
    plt.plot(passenger)
    plt.plot(range(N_train+max_len, N_train+max_len+iteration), prediction)
    

    結果

    上記の学習を3回行ってみて、LSTMと普通のRNNとGRUを比べた。

    今回の結果は、LSTMが良かった。

    きちんとトレンド/周期共に捕らえられているようだった。ただ、時間を追うごとに値がずれていっている。もう少し精度を上げたいなら、前処理をしっかりしたほうがいいのかもしれない。前処理については以前時系列まとめで書いた(【Day-12】時系列分析の良リソースまとめ&基礎チュートリアル)

    まとめ

    せっかくKerasを学んだので、時系列もディープでしておこうと思い、まとめた。 ちょっと苦労したけど、これで学習器に入れられるようになった。予測がちゃんとできれば楽しい。

    明日はPyTorchやりたいなぁ。ではでは。

    *1:数式載せようかと思ったけど、異常にめんどくさいからやめますごめんなさい。

    PROCRASIST