Encoder-Decoder LSTM Modelモデルによる家庭用電気の多段階時間系列予測


このセクションでは、コーデックモデルを使用するために、通常のLSTMを更新することができる.これは、モデルがベクトルシーケンスを直接出力しないことを意味します.逆に、このモデルは、入力シーケンスを読み取り符号化するエンコーダと、符号化された入力シーケンスを読み取り、出力シーケンスの各要素を一歩予測するデコーダとの2つのサブモデルから構成される.この差は,実際には両方の方法でシーケンス出力を予測できるため,微細である.重要な違いは、デコーダがLSTMモデルを使用していることであり、デコーダは、前日にシーケンスで何が予測されたのか、またシーケンスを出力する際に内部状態が蓄積されたのかを知ることができる.このモデルがどのように定義されているかをよく見てみましょう.前述と同様に,200セルを含むLSTM非表示層を定義した.これはデコーダモデルで、入力シーケンスを読み出し、入力シーケンスから特性をキャプチャする200個の要素ベクトル(ユニットごとに出力)を出力します.
14日間の総消費電力を入力として使用します.
# define model
model = Sequential()
model.add(LSTM(200, activation='relu', input_shape=(n_timesteps, n_features)))

簡単なエンコーダ−デコーダアーキテクチャを用いてKerasで容易に実現することができ,LSTM自動エンコーダのアーキテクチャと多くの類似点がある.まず,入力シーケンスの内部表現を複数回繰り返し,出力シーケンスの時間ステップごとに1回繰り返す.このベクトルシーケンスはLSTMデコーダに提示される.
model.add(RepeatVector(7))

次に,デコーダを200セルを含むLSTM非表示層として定義した.重要なのは、エンコーダが行ったように、シーケンスの最後の出力だけでなく、デコーダがシーケンス全体を出力することです.これは、200ユニットの各ユニットが、出力シーケンス内で毎日予測されるコンテンツの基礎を示す7日間の毎日に1つの値を出力することを意味する.
model.add(LSTM(200, activation='relu', return_sequences=True))

次に、完全に接続されたレイヤを使用して、最終出力レイヤの前の出力シーケンスの各時間ステップを説明します.重要なのは、出力レイヤが出力シーケンスのステップを予測することであり、7日間ではありません.これは、出力シーケンスの各ステップに対して同じレイヤを使用することを意味します.同じ完全接続レイヤと出力レイヤが、デコーダが提供する各時間ステップを処理するために使用されることを意味する.これを実現するために、レイヤおよび出力レイヤは、デコーダからステップを実行するたびにカプセル化されたレイヤを使用することを可能にするTimeDistributedパッケージにカプセル化されることを説明する.モデル.(TimeDistributed(密度(100年、活性化='relu'))modelを追加します.add(TimeDistributed(密度(1):
model.add(TimeDistributed(Dense(100, activation='relu')))
model.add(TimeDistributed(Dense(1)))

これにより、LSTMデコーダは、出力シーケンスの各ステップに必要なコンテキストを計算し、各時間ステップのパッケージングされた密層を個別に解釈しながら、同じ重みを再利用して解釈を実行することができる.もう1つの方法は、LSTMデコーダによって作成されたすべての構造を平らにし、ベクトルを直接出力することである.拡張子として使用して、比較方法を確認できます.したがって,ネットワークは入力構造と同じ3次元ベクトルを出力し,次元数[サンプル,時間ステップ,特徴]を持つ.毎日消費される総電力量と、常に7つの機能しかありません.したがって,単一の1週間予測には大きさがある:[1,7,1].したがって,モデルを訓練する際には,前節で用いた「sample,features」の2次元構造ではなく,出力データ(y)を3次元構造で再構成しなければならない.
# reshape output into [samples, timesteps, features]
train_y = train_y.reshape((train_y.shape[0], train_y.shape[1], 1))
# train the model
def build_model(train, n_input):
	# prepare data
	train_x, train_y = to_supervised(train, n_input)
	# define parameters
	verbose, epochs, batch_size = 0, 20, 16
	n_timesteps, n_features, n_outputs = train_x.shape[1], train_x.shape[2], train_y.shape[1]
	# reshape output into [samples, timesteps, features]
	train_y = train_y.reshape((train_y.shape[0], train_y.shape[1], 1))
	# define model
	model = Sequential()
	model.add(LSTM(200, activation='relu', input_shape=(n_timesteps, n_features)))
	model.add(RepeatVector(n_outputs))
	model.add(LSTM(200, activation='relu', return_sequences=True))
	model.add(TimeDistributed(Dense(100, activation='relu')))
	model.add(TimeDistributed(Dense(1)))
	model.compile(loss='mse', optimizer='adam')
	# fit network
	model.fit(train_x, train_y, epochs=epochs, batch_size=batch_size, verbose=verbose)
	return model
# univariate multi-step encoder-decoder lstm
from math import sqrt
from numpy import split
from numpy import array
from pandas import read_csv
from sklearn.metrics import mean_squared_error
from matplotlib import pyplot
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import LSTM
from keras.layers import RepeatVector
from keras.layers import TimeDistributed

# split a univariate dataset into train/test sets
def split_dataset(data):
	# split into standard weeks
	train, test = data[1:-328], data[-328:-6]
	# restructure into windows of weekly data
	train = array(split(train, len(train)/7))
	test = array(split(test, len(test)/7))
	return train, test

# evaluate one or more weekly forecasts against expected values
def evaluate_forecasts(actual, predicted):
	scores = list()
	# calculate an RMSE score for each day
	for i in range(actual.shape[1]):
		# calculate mse
		mse = mean_squared_error(actual[:, i], predicted[:, i])
		# calculate rmse
		rmse = sqrt(mse)
		# store
		scores.append(rmse)
	# calculate overall RMSE
	s = 0
	for row in range(actual.shape[0]):
		for col in range(actual.shape[1]):
			s += (actual[row, col] - predicted[row, col])**2
	score = sqrt(s / (actual.shape[0] * actual.shape[1]))
	return score, scores

# summarize scores
def summarize_scores(name, score, scores):
	s_scores = ', '.join(['%.1f' % s for s in scores])
	print('%s: [%.3f] %s' % (name, score, s_scores))

# convert history into inputs and outputs
def to_supervised(train, n_input, n_out=7):
	# flatten data
	data = train.reshape((train.shape[0]*train.shape[1], train.shape[2]))
	X, y = list(), list()
	in_start = 0
	# step over the entire history one time step at a time
	for _ in range(len(data)):
		# define the end of the input sequence
		in_end = in_start + n_input
		out_end = in_end + n_out
		# ensure we have enough data for this instance
		if out_end < len(data):
			x_input = data[in_start:in_end, 0]
			x_input = x_input.reshape((len(x_input), 1))
			X.append(x_input)
			y.append(data[in_end:out_end, 0])
		# move along one time step
		in_start += 1
	return array(X), array(y)

# train the model
def build_model(train, n_input):
	# prepare data
	train_x, train_y = to_supervised(train, n_input)
	# define parameters
	verbose, epochs, batch_size = 0, 20, 16
	n_timesteps, n_features, n_outputs = train_x.shape[1], train_x.shape[2], train_y.shape[1]
	# reshape output into [samples, timesteps, features]
	train_y = train_y.reshape((train_y.shape[0], train_y.shape[1], 1))
	# define model
	model = Sequential()
	model.add(LSTM(200, activation='relu', input_shape=(n_timesteps, n_features)))
	model.add(RepeatVector(n_outputs))
	model.add(LSTM(200, activation='relu', return_sequences=True))
	model.add(TimeDistributed(Dense(100, activation='relu')))
	model.add(TimeDistributed(Dense(1)))
	model.compile(loss='mse', optimizer='adam')
	# fit network
	model.fit(train_x, train_y, epochs=epochs, batch_size=batch_size, verbose=verbose)
	return model

# make a forecast
def forecast(model, history, n_input):
	# flatten data
	data = array(history)
	data = data.reshape((data.shape[0]*data.shape[1], data.shape[2]))
	# retrieve last observations for input data
	input_x = data[-n_input:, 0]
	# reshape into [1, n_input, 1]
	input_x = input_x.reshape((1, len(input_x), 1))
	# forecast the next week
	yhat = model.predict(input_x, verbose=0)
	# we only want the vector forecast
	yhat = yhat[0]
	return yhat

# evaluate a single model
def evaluate_model(train, test, n_input):
	# fit model
	model = build_model(train, n_input)
	# history is a list of weekly data
	history = [x for x in train]
	# walk-forward validation over each week
	predictions = list()
	for i in range(len(test)):
		# predict the week
		yhat_sequence = forecast(model, history, n_input)
		# store the predictions
		predictions.append(yhat_sequence)
		# get real observation and add to history for predicting the next week
		history.append(test[i, :])
	# evaluate predictions days for each week
	predictions = array(predictions)
	score, scores = evaluate_forecasts(test[:, :, 0], predictions)
	return score, scores

# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
# split into train and test
train, test = split_dataset(dataset.values)
# evaluate model and get scores
n_input = 14
score, scores = evaluate_model(train, test, n_input)
# summarize scores
summarize_scores('lstm', score, scores)
# plot scores
days = ['sun', 'mon', 'tue', 'wed', 'thr', 'fri', 'sat']
pyplot.plot(days, scores, marker='o', label='lstm')
pyplot.show()