Kerasによる時系列データ処理 ~その1~

はじめに

こんにちは、がんがんです。無事に年が明けましたね。
2018年も終わり、2019年がやってきました。
2019年は開幕早々自身が起きたり、インフルにかかったりと大変でした…

インフルも治ったので、2018年に勉強していた本を今一度勉強しようと思いました。
今回はこちらの本を再学習していきます。

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

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


今後、何回かに分けて備忘録をまとめていきます。

参考記事

改めてAmazonのリンクを記載しておきます。

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

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

また、公式が挙げられているGitHubのリンクについても貼っておきます。
コードのリンクはその都度貼っていきます。
deeplearning-tensorflow-keras/5 at master · yusugomori/deeplearning-tensorflow-keras · GitHub


時系列モデルについてはこちらがまとまっています。
deepage.net

問題提起・環境について

今回の実験では簡単な時系列データであるsin波を用いていきます。
検証するモデルは以下の通りです。

・simple RNN
・LSTM
・GRU

また、実験環境としては以下を用いました。なお、TensorFlowについての実験はこちらでは扱っていないのでご了承ください。

・Windows 10
・Python 3.6.4
・Keras 2.1.5

Simple RNN

公式のGitHubこちらからどうぞ。

simpleRNNとは出力が入力にフィードバックされるRNNのことです。

実験の結果は以下のようになります。
f:id:gangannikki:20190112211833p:plain

LSTM

公式のGitHubこちらからどうぞ。

LSTMはRNNの欠点を改良したモデルです。LSTMを初めて見たときのイメージは「フリップフロップみたい」でした(笑)

モデルの詳細については以下が参考になります。
LSTMのモデルについては様々な方がまとめられているのでそちらも参考にしてください。
qiita.com

実験の結果は以下のようになりました。
f:id:gangannikki:20190112211853p:plain

GRU

公式のGitHubこちらからどうぞ。

実行結果は以下のようになりました。
f:id:gangannikki:20190112212222p:plain

GRUは使用したコードも載せておきます。

#------------------------------------------------------------
#
#    chap5.
#    kerasを用いてsin波のRNNを行う。GRUを使用
#
#------------------------------------------------------------
#   coding: utf-8
#------------------------------------------------------------
import numpy as np
import matplotlib.pyplot as plt
from keras.models import Sequential
from keras.layers import Dense, Activation
from keras.optimizers import Adam
from keras.initializers import TruncatedNormal	#  切断正規分布を使用
from keras.layers.recurrent import GRU
from keras.callbacks import EarlyStopping
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle


"""
	Deep NN クラス
"""
class DNN(object):
	#  初期化処理
	def __init__( self, n_in, n_hidden, n_out, epochs, batch_size, maxlen ):
		self.n_in = n_in
		self.n_hidden = n_hidden
		self.n_out = n_out
		self.epochs = epochs
		self.batch_size = batch_size
		self.maxlen = maxlen

	"""
		モデルの構築関数
	"""
	def inference( self ):
		#  	LSTM
		model.add( GRU( self.n_hidden, 
                                kernel_initializer=TruncatedNormal(stddev=0.01),
                                input_shape = ( self.maxlen, self.n_in)) )
		#  出力層
		model.add( Dense( self.n_out,
                                  kernel_initializer=TruncatedNormal(stddev=0.01)) )
		model.add( Activation('linear') )

		model.compile( loss='mean_squared_error',
                              optimizer=Adam(lr=0.001, beta_1=0.9, beta_2=0.999) )
	"""
		学習関数
	"""
	def training( self, X_train, Y_train, X_validation, Y_validation ):
		early_stopping = EarlyStopping( monitor='val_loss', patience=10, verbose=1 )
		#  モデルの学習
		hist = model.fit( X_train, Y_train, 
                                  epochs = self.epochs, 
                                  batch_size = self.batch_size,
                                  validation_data = ( X_validation, Y_validation),
                                  callbacks = [early_stopping] )

#	sin波の生成
def sin( x, T=100 ):
	return np.sin( 2.0 * np.pi * x / T )

#	Toy Problem
def toy_problem( T=100, ampl=0.05 ):
	x = np.arange( 0, 2 * T + 1 )
	noise = ampl * np.random.uniform( low=-1.0, high=1.0, size=len(x) )
	return sin(x) + noise


if __name__ == '__main__':
	"""
		データの設定
	"""
	np.random.seed(0)
	T = 100
	f = toy_problem(T)
	length_of_sequences = 2 * T 	#  全時系列の長さ
	maxlen = 25		#  1つの時系列のデータ長

	data = []
	target = []

	for i in range( 0, length_of_sequences - maxlen + 1 ):
		data.append( f[i: i + maxlen] )
		target.append( f[i + maxlen] )

	X = np.array(data).reshape( len(data), maxlen, 1 )
	Y = np.array(target).reshape( len(data), 1 )

	#	データの分割
	N_train = int(len(data) * 0.9)
	N_validation = len(data) - N_train

	X_train, X_validation, Y_train, Y_validation = \
		train_test_split( X, Y, test_size=N_validation )

#	モデル設定
	n_in = len(X[0][0])	#  1
	n_hidden = 20
	n_out = len(Y[0])	#  1
	epochs = 500
	batch_size = 10

	#  インスタンス定義
	DNN = DNN( n_in, n_hidden, n_out , epochs, batch_size, maxlen )

	model = Sequential()
	DNN.inference()

#	モデル学習
	DNN.training( X_train, Y_train, X_validation, Y_validation )			  

#	sin波の出力
	truncate = maxlen
	Z = X[:1]	#  元データの最初の一部だけ切り出し

	original = [f[i] for i in range(maxlen)]
	predicted = [None for i in range(maxlen)]

	for i in range(length_of_sequences - maxlen + 1):
		#  最後の時系列データから未来を予測
		z_ = Z[-1:]
		y_ = model.predict(z_)
		#  予測結果かを用いて新しい時系列データを生成
		sequence_ = np.concatenate(
			( z_.reshape(maxlen, n_in)[1:], y_), axis=0).reshape( 1, maxlen, n_in )
		Z = np.append( Z, sequence_, axis=0 )
		predicted.append( y_.reshape(-1) )

	#  グラフの描画
	plt.rc('font', family='serif')
	plt.figure()
	plt.plot( toy_problem( T, ampl=0), linestyle='dotted', color='#aaaaaa' )
	plt.plot( original, linestyle='dashed', color='blue' )
	plt.plot( predicted, color='black' )
	plt.show()

まとめ

Kerasの時系列モデルは非常に簡単に使えるので、ほかにもいろいろな実験をしていきます。
今後も自身の勉強のために頑張ります。