ディープラーニングで時系列を扱うとなると、
定番なのはLSTM(Long Short Term Memory)と呼ばれるモデルです。
LSTMでは、時間的な関係をニューロンの構造に組み込んだもので、
データに時間の流れなどが含まれる場合に、適したモデルとなります。
今回は、このLSTMを使って、時系列の予測をしてみます。
LSTM とは
LSTM とは、ニューラルネットワークの中間層の構造の一つで、
自身の出力を、再帰的に入力するような構造を持ったものです。
図のように、自分の層の中で、
それぞれのニューロンの出力が、次のニューロンの入力として使われます。
このようにすることで、「ニューロンtさん」の次は「ニューロンt+1さん」という形で、
LSTM層の中のニューロンに順番が生まれることになります。
出力が別のニューロンの入力に「戻ってくる」ニューラルネットワークは、 RNNと呼ばれ、LSTMもこのRNNの一種と言えます。
一般に、ニューロンの出力を戻してくると、学習が不安定になるのですが、
LSTMでは、この学習の問題を避ける仕組みが導入されており、
良い性能を出すように改良されてきたものになります。
準備
ここでは、kerasパッケージを用いてLSTMを実行するので、
kerasをインストールしておく必要があります。
インストール方法は、下記の記事を参照してください。
clean-copy-of-onenote.hatenablog.com
LSTM による時系列の再現
それでは、さっそくLSTMで時系列を学習して、
時系列を再現できるか確かめてみましょう。
データの準備
今回は、Rで標準に用意されているデータセット
UKgas
を使って時系列の学習を行います。
最終的に、
ある一定の時間分のデータを使って、次の時間のデータを予測
するようにデータを学習させるので、
ひとつながりの時系列データを一定期間ごとに区切って、
入力と出力の訓練データを作る必要があります。
library(keras) make_data_for_lstm = function(ts_df,window,rm.na=F){ data.x = NULL data.y = NULL n = dim(ts_df)[2] for(i in 1:n){ ts_x = ts_df[,i] for(j in 1:(length(ts_x)-window)){ if(rm.na){ tmp.x = ts_x[1:window + j -1] tmp.y = ts_x[1:window + j -1] if(sum(c(is.na(tmp.x),is.na(tmp.y))) == 0){ data.x = rbind(data.x,ts_x[1:window + j -1]) data.y = rbind(data.y,ts_x[window + j]) } }else{ data.x = rbind(data.x,ts_x[1:window + j -1]) data.y = rbind(data.y,ts_x[window + j]) } } } return(list(x=array_reshape(data.x,c(dim(data.x),1)), y=data.y)) }
上の関数make_data_for_lstm
では、時系列データts_df
を、
window
で指定した長さ毎に区切って訓練用のデータを生成します。
上の関数を使って、UKgas
をLSTMへの入力用に加工します。
window=10 ts_df = as.matrix(UKgas) data = make_data_for_lstm(ts_df,window,rm.na=T) scale = max(ts_df,na.rm = T) x = data$x / scale y = data$y / scale
最後の三行では、データを最大値で割ってスケーリングしています。
モデルの準備
続いて、モデルを準備していきます。
ここでは、最もシンプルなもので、
LSTMを1層だけ中間層に挟んだモデルを使うことにします。
model = keras_model_sequential() model %>% layer_lstm(units = 64,input_shape = c(dim(x)[2],1)) %>% layer_dropout(rate=0.4) %>% layer_dense(units=1)
LSTM の入力は、三次元になっていて、
訓練データの数×1つの訓練データの入力の長さ×変数の数
となっています。
今の場合、単一の変数の時系列を扱っているので、変数の数は1を設定しています。
※ make_data_for_lstm
でもそれに合わせて、データが生成されます。
モデルを記述したらコンパイルしましょう。
model %>% compile(loss="mean_squared_error", optimizer = optimizer_adam(), metrics = "accuracy")
出力の数値自体を合わせるように学習してほしいので、
ロス関数は、mean_squared_error
を指定して、
最小二乗誤差となるように学習を進めます。
モデルのフィッティング
初めに生成した訓練用のデータでモデルの重みを学習していきます。
model %>% fit(x,y, epochs=1000,batch_size = 10,validation_split = 0.2)
学習の進み方を見ているとかなり上手く学習できてそうですね。
時系列が再現できるかを確認
学習が完了したので、ニューラルネットワークに時系列を出力させてみましょう。
test_LSTM = function(model,ts_df,i=1){ test_x = ts_df[,i] test_x = make_data_for_lstm(as.matrix(test_x),window)$x scale=max(ts_df,na.rm = T) test_x = test_x/scale pred_x = model %>% predict(test_x) ts.plot(ts_df[,i]/scale->a,ylim=c(min(c(a,pred_x)),max(c(a,pred_x))),ylab="y") lines(c(rep(NA,window),pred_x),col=2) } test_LSTM(model,ts_df,1)
下の図は、学習に使った時系列をそのまま入力として与えて、
直前までの入力で、次のデータを予測した結果と、
元のデータを比べたものです。
赤の線が予測の結果ですが、
黒の線とよく一致していることが分かります。
過去のデータから未来のデータを予測
上では、予測とはいっても、
既にモデルの学習に使ったデータを予測しているので、
正確には、時系列の再現をしているだけになります。
本当に予測までできるのかを調べるために、
前半80個のデータだけを使ってモデルを学習して、
学習に使っていないデータを予測してみましょう。
ts_df = as.matrix(UKgas)[1:80,] data = make_data_for_lstm(ts_df,window,rm.na=T) scale = max(ts_df,na.rm = T) x = data$x / scale y = data$y / scale model = keras_model_sequential() model %>% layer_lstm(units = 64,input_shape = c(dim(x)[2],1)) %>% layer_dropout(rate=0.4) %>% layer_dense(units=1) model %>% compile(loss="mean_squared_error", optimizer = optimizer_adam(), metrics = "accuracy") model %>% fit(x,y, epochs=1000,batch_size = 10,validation_split = 0.2) ####################### ts_df = as.matrix(UKgas[-1:(-80+window)]) test_LSTM(model,ts_df,1)
下の図は、1つ先の予測を行った結果を赤線で、
正解を黒線で示していますが、
かなり上手く行ってそうに見えます。