データ分析ガチ勉強アドベントカレンダー 18日目。
Kerasの使い方を復習したところで、今回は時系列データを取り扱ってみようと思います。 時系列を取り扱うのにもディープラーニングは用いられていて、RNN(Recurrent Neural Net)が主流。
今回は、RNNについて書いた後、Kerasで実際にRNNを実装してみます。
今日の参考書はこの本!
詳解 ディープラーニング ~TensorFlow・Kerasによる時系列データ処理~
- 作者:巣籠 悠輔
- 発売日: 2017/05/30
- メディア: 単行本(ソフトカバー)
RNNとは
通常のNeural Networkとの違い
Recurrent Neural Netの略。再帰的ニューラルネット。何が再帰かは下図
入力層、隠れ層、出力層の構造はDay-15で説明したNeural Netとほとんど同じだが、最も大きな違いは隠れ層同士がつながっているところ。これによって時系列に対応できるようにしている。順伝播の計算も、再帰性の要素が入ってくる。(添え字等はDay15参照)
誤差逆伝播法のアルゴリズム
基本的にニューラルネットと同じだが、隠れ層の時間方向に微分がつながっていくので、時間を遡りながら誤差の影響を伝えていく、しかし、本来は一番初めの時刻まで遡るものだが、遡りすぎると時間方向の微分がかけ粟さて勾配消失/爆発を引き起こし、うまく学習されないことも多くある。シンプルなRNNではBPTTを途中で打ち切る場合も多い。
下記にアルゴリズムを記す
勾配消失の工夫 : LSTMやGRU
時間を遡ってBackpropagationを行うため、勾配消失が起こる。それを解消する工夫として、LSTM(Long-Short Term Memory)とGRU(Gated Recurrent Unit)がある。上の図の隠れ層に置ける、○の中身を工夫しながら、何を忘れて何を覚えておくか**まで賢く学習する。
LSTM
正直、本の図はちょっと分かりにくい、ウェブをあさってて一番厳密かつ分かりやすかったのは A Beginner’s Guide to Recurrent Networks and LSTMsというサイトの図
複雑...一番のポイントは
これにより、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というのがにぎわっているように見受けられる。
勉強不足でまだまだ全然分かっていないので、勉強用のソースだけ張っておく。勉強。
- 今更ながらchainerでSeq2Seq(2)〜Attention Model編〜 - Qiita
- Attention and Augmented Recurrent Neural Networks
- TensorFlowで単純なseq2seqモデルとattention seq2seqモデルを比較してみた - Galapagos Blog
Keras実装
練習がてら、KerasでRNNを実装してみる。 githubにもあげているのでそちらも参考に。
データの入力の仕方がちょっと難しかったので、そこだけ重点的に。
データの変形、入力
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:数式載せようかと思ったけど、異常にめんどくさいからやめますごめんなさい。