鉴于智能电表的兴起以及太阳能电池板等发电技术的广泛采用,有大量的用电数据可供选择。该数据代表了多变量时间序列的功率相关变量,这些变量又可用于建模甚至预测未来的电力消耗。
与其他机器学习算法不同,长期短期记忆递归神经网络能够自动学习序列数据的特征,支持多变量数据,并且可以输出可用于多步预测的可变长度序列。
总体概述
本教程分为九个部分; 他们是:
问题描述
加载并准备数据集
模型评估
用于多步预测的LSTM
具有单变量输入和矢量输出的LSTM模型
具有单变量输入的LSTM模型
具有多变量输入的编码器 - 解码器LSTM模型
具有单变量输入的CNN-LSTM编码器 - 解码器模型
具有单变量输入的ConvLSTM编码器 - 解码器模型
问题描述
“ 家庭用电量 ”数据集是一个多变量时间序列数据集,用于描述单个家庭四年的用电量。该数据是在2006年12月至2010年11月之间收集的,并且每分钟收集家庭内的能耗观察结果。
它是一个多变量系列,由七个变量组成(除日期和时间外); 他们是:
global_active_power:家庭消耗的总有功功率(千瓦)。
global_reactive_power:家庭消耗的总无功功率(千瓦)。
voltage:平均电压(伏特)。
global_intensity:平均电流强度(安培)。
sub_metering_1:厨房的有功电能(瓦特小时的有功电能)。
sub_metering_2:用于洗衣的有功能量(瓦特小时的有功电能)。
sub_metering_3:气候控制系统的有功电能(瓦特小时的有功电能)。
有功和无功能量是指交流电的技术细节。
可以通过从总活动能量中减去三个定义的子计量变量的总和来创建第四个子计量变量,如下所示:
sub_metering_remainder = (global_active_power * 1000 / 60) - (sub_metering_1 + sub_metering_2 + sub_metering_3)
加载并准备数据集
数据集可以找博主索取
下载数据集并将其解压缩到当前工作目录中。您现在将拥有大小为127兆字节的文件“ household_power_consumption.txt ”并包含所有观察结果。
我们可以使用read_csv()函数加载数据,并将前两列组合成一个日期时间列,我们可以将其用作索引。
1
2 | # load all data
dataset = read_csv('household_power_consumption.txt', sep=';', header=0, low_memory=False, infer_datetime_format=True, parse_dates={'datetime':[0,1]}, index_col=['datetime']) |
接下来,我们可以标记用' ? '表示的所有缺失值。'具有NaN值的字符,这是一个浮点数。
这将允许我们将数据作为一个浮点值数组而不是混合类型(效率较低)。
1
2
3
4 | # mark all missing values
dataset.replace('?', nan, inplace=True)
# make dataset numeric
dataset = dataset.astype('float32') |
我们还需要填写缺失值,因为它们已被标记。
一种非常简单的方法是从前一天的同一时间复制观察。我们可以在一个名为fill_missing()的函数中实现它,该函数将从24小时前获取数据的NumPy数组并复制值。
1
2
3
4
5
6
7 | # fill missing values with a value at the same time one day ago
def fill_missing(values):
one_day = 60 * 24
for row in range(values.shape[0]):
for col in range(values.shape[1]):
if isnan(values[row, col]):
values[row, col] = values[row - one_day, col] |
我们可以将此函数直接应用于DataFrame中的数据。
1
2 | # fill missing
fill_missing(dataset.values) |
现在,我们可以使用上一节中的计算创建一个包含剩余子计量的新列。
1
2
3 | # add a column for for the remainder of sub metering
values = dataset.values
dataset['sub_metering_4'] = (values[:,0] * 1000 / 60) - (values[:,4] + values[:,5] + values[:,6]) |
我们现在可以将清理后的数据集版本保存到新文件中; 在这种情况下,我们只需将文件扩展名更改为.csv,并将数据集保存为“ household_power_consumption.csv ”。
1
2 | # save updated dataset
dataset.to_csv('household_power_consumption.csv') |
将所有这些结合在一起,下面列出了加载,清理和保存数据集的完整示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | # load and clean-up data
from numpy import nan
from numpy import isnan
from pandas import read_csv
from pandas import to_numeric
# fill missing values with a value at the same time one day ago
def fill_missing(values):
one_day = 60 * 24
for row in range(values.shape[0]):
for col in range(values.shape[1]):
if isnan(values[row, col]):
values[row, col] = values[row - one_day, col]
# load all data
dataset = read_csv('household_power_consumption.txt', sep=';', header=0, low_memory=False, infer_datetime_format=True, parse_dates={'datetime':[0,1]}, index_col=['datetime'])
# mark all missing values
dataset.replace('?', nan, inplace=True)
# make dataset numeric
dataset = dataset.astype('float32')
# fill missing
fill_missing(dataset.values)
# add a column for for the remainder of sub metering
values = dataset.values
dataset['sub_metering_4'] = (values[:,0] * 1000 / 60) - (values[:,4] + values[:,5] + values[:,6])
# save updated dataset
dataset.to_csv('household_power_consumption.csv') |
运行该示例将创建新文件“ household_power_consumption.csv ”,我们可以将其用作建模项目的起点。
模型评估
在本节中,我们将考虑如何开发和评估家庭电力数据集的预测模型。
本节分为四个部分; 他们是:
问题框架
评估指标
训练和测试集
前瞻性验证
问题框架
有许多方法可以利用和探索家庭用电量数据集。
在本教程中,我们将使用数据来探索一个非常具体的问题; 那是:
鉴于最近的耗电量,未来一周的预期耗电量是多少?
这要求预测模型预测未来七天每天的总有功功率。
从技术上讲,考虑到多个预测步骤,这个问题的框架被称为多步骤时间序列预测问题。利用多个输入变量的模型可以称为多变量多步时间序列预测模型。
这种类型的模型在规划支出方面可能对家庭有所帮助。在供应方面,它也可能有助于规划特定家庭的电力需求。
数据集的这种框架还表明,将每分钟的功耗观测值下采样到每日总数是有用的。这不是必需的,但考虑到我们对每天的总功率感兴趣,这是有道理的。
我们可以使用pandas DataFrame上的resample()函数轻松实现这一点。使用参数“ D ” 调用此函数允许按日期时间索引的已加载数据按天分组(请参阅所有偏移别名)。然后,我们可以计算每天所有观测值的总和,并为八个变量中的每一个创建每日耗电量数据的新数据集。
下面列出了完整的示例。
1
2
3
4
5
6
7
8
9
10
11
12 | # resample minute data to total for each day
from pandas import read_csv
# load the new file
dataset = read_csv('household_power_consumption.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
# resample data to daily
daily_groups = dataset.resample('D')
daily_data = daily_groups.sum()
# summarize
print(daily_data.shape)
print(daily_data.head())
# save
daily_data.to_csv('household_power_consumption_days.csv') |
运行该示例将创建一个新的每日总功耗数据集,并将结果保存到名为“ household_power_consumption_days.csv ” 的单独文件中。
我们可以将其用作数据集,用于拟合和评估所选问题框架的预测模型。
评估指标
预测将包含七个值,一个用于一周中的每一天。
多步预测问题通常分别评估每个预测时间步长。这有助于以下几个原因:
评估特定提前期的技能(例如+1天vs +3天)。
在不同的交付周期内根据他们的技能对比模型(例如,在+1天的模型和在日期+5的模型良好的模型)。
总功率的单位是千瓦,并且具有也在相同单位的误差度量将是有用的。均方根误差(RMSE)和平均绝对误差(MAE)均符合此法案,尽管RMSE更常用,将在本教程中采用。与MAE不同,RMSE更能惩罚预测误差。
此问题的性能指标将是从第1天到第7天的每个提前期的RMSE。
作为捷径,使用单个分数总结模型的性能以帮助模型选择可能是有用的。
可以使用的一个可能的分数是所有预测天数的RMSE。
下面的函数evaluate_forecasts()将实现此行为并基于多个七天预测返回模型的性能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | # 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 |
运行该函数将首先返回整个RMSE而不管白天,然后每天返回一系列RMSE分数。
训练和测试集
我们将使用前三年的数据来训练预测模型和评估模型的最后一年。
给定数据集中的数据将分为标准周。这些是从周日开始到周六结束的周。
这是使用所选模型框架的现实且有用的方法,其中可以预测未来一周的功耗。它也有助于建模,其中模型可用于预测特定日期(例如星期三)或整个序列。
我们将数据拆分为标准周,从测试数据集向后工作。
数据的最后一年是2010年,2010年的第一个星期日是1月3日。数据于2010年11月中旬结束,数据中最接近的最后一个星期六是11月20日。这给出了46周的测试数据。
下面提供了测试数据集的第一行和最后一行日常数据以供确认。
1
2
3 | 2010-01-03,2083.4539999999984,191.61000000000055,350992.12000000034,8703.600000000033,3842.0,4920.0,10074.0,15888.233355799992
...
2010-11-20,2197.006000000004,153.76800000000028,346475.9999999998,9320.20000000002,4367.0,2947.0,11433.0,17869.76663959999 |
每日数据从2006年底开始。
数据集中的第一个星期日是12月17日,这是第二行数据。
将数据组织成标准周,为训练预测模型提供了159个标准周。
1
2
3 | 2006-12-17,3390.46,226.0059999999994,345725.32000000024,14398.59999999998,2033.0,4187.0,13341.0,36946.66673200004
...
2010-01-02,1309.2679999999998,199.54600000000016,352332.8399999997,5489.7999999999865,801.0,298.0,6425.0,14297.133406600002 |
下面的函数split_dataset()将每日数据拆分为训练集和测试集,并将每个数据组织成标准周。
使用特定行偏移来使用数据集的知识来分割数据。然后使用NumPy split()函数将拆分数据集组织成每周数据。
1
2
3
4
5
6
7
8 | # 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 |
我们可以通过加载每日数据集并打印列车和测试集的第一行和最后一行数据来测试此功能,以确认它们符合上述预期。
完整的代码示例如下所示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | # split into standard weeks
from numpy import split
from numpy import array
from pandas import read_csv
# 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
# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
train, test = split_dataset(dataset.values)
# validate train data
print(train.shape)
print(train[0, 0, 0], train[-1, -1, 0])
# validate test
print(test.shape)
print(test[0, 0, 0], test[-1, -1, 0]) |
运行该示例表明,列车数据集确实有159周的数据,而测试数据集有46周。
我们可以看到,第一行和最后一行的列车和测试数据集的总有效功率与我们定义为每组标准周数的边界的特定日期的数据相匹配。
1
2
3
4 | (159, 7, 8)
3390.46 1309.2679999999998
(46, 7, 8)
2083.4539999999984 2197.006000000004 |
前瞻性验证
将使用称为前向验证的方案来评估模型。
这是需要模型进行一周预测的地方,然后该模型可用于该周的实际数据,以便它可以用作在随后一周进行预测的基础。这对于如何在实践中使用模型以及对模型有益而使其能够利用最佳可用数据都是现实的。
我们可以通过分离输入数据和输出/预测数据来证明这一点。
1
2
3
4
5 | Input, Predict
[Week1] Week2
[Week1 + Week2] Week3
[Week1 + Week2 + Week3] Week4
... |
评估此数据集上的预测模型的前瞻性验证方法在下面提供,名为evaluate_model()。
标准周格式的训练和测试数据集作为参数提供给函数。提供了另一个参数n_input,用于定义模型将用作输入以进行预测的先前观察的数量。
调用两个新函数:一个用于根据名为build_model()的训练数据构建模型,另一个用于使用该模型对每个新标准周进行预测,称为forecast()。这些将在后续章节中介绍。
我们正在使用神经网络,因此,它们通常很难训练,但很快就能进行评估。这意味着模型的首选用法是在历史数据上构建一次,并使用它们来预测前进验证的每个步骤。模型在评估期间是静态的(即未更新)。
这与其他模型不同,这些模型可以更快地进行训练,在新数据可用时,可以重新拟合或更新模型的每个步骤。有了足够的资源,就可以通过这种方式使用神经网络,但在本教程中我们不会这样做。
完整的evaluate_model()函数如下所示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | # 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 |
一旦我们对模型进行评估,我们就可以总结性能。
以下名为summarize_scores()的函数将模型的性能显示为单行,以便与其他模型进行比较。
1
2
3
4 | # 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)) |
我们现在已经开始评估数据集上的预测模型的所有元素。
用于多步预测的LSTM
循环神经网络或RNN专门用于工作,学习和预测序列数据。
递归神经网络是神经网络,其中来自一个时间步骤的网络输出在随后的时间步骤中被提供作为输入。这允许模型基于当前时间步长的输入和对先前时间步骤中输出的直接知识来决定预测什么。
也许最成功和最广泛使用的RNN是长期短期记忆网络,简称LSTM。它之所以成功,是因为它克服了训练复现神经网络所带来的挑战,从而产生了稳定的模型。除了利用先前时间步的输出的循环连接之外,LSTM还具有内部存储器,其操作类似于局部变量,允许它们在输入序列上累积状态。
LSTM在多步时间序列预测方面具有许多优势; 他们是:
对序列的本机支持。LSTM是一种循环网络,因此设计用于将序列数据作为输入,不像其他模型,其中滞后观察必须作为输入特征呈现。
多变量输入。LSTM直接支持多变量输入的多个并行输入序列,不同于其中多变量输入以平面结构呈现的其他模型。
矢量输出。与其他神经网络一样,LSTM能够将输入数据直接映射到可表示多个输出时间步长的输出向量。
此外,已经开发了专门的架构,其专门设计用于进行多步骤序列预测,通常称为序列到序列预测,或简称为seq2seq。这很有用,因为多步时间序列预测是一种seq2seq预测。
针对seq2seq问题设计的递归神经网络架构的示例是编码器 - 解码器LSTM。
编码器 - 解码器LSTM是由两个子模型组成的模型:一个称为编码器,其读取输入序列并将其压缩为固定长度的内部表示;以及输出模型,称为解码器,其解释内部表示并使用它预测输出序列。
事实证明,序列预测的编码器 - 解码器方法比直接输出矢量更有效,并且是首选方法。
通常,已经发现LSTM在自回归类型问题上不是非常有效。这些是预测下一个时间步长是最近时间步长的函数。
一维卷积神经网络(CNN)已证明在动学习输入序列的特征方面是有效的。
一种流行的方法是将CNN与LSTM组合,其中CNN作为编码器来学习输入数据的子序列的特征,这些子序列作为LSTM的时间步长提供。该架构称为CNN-LSTM。
CNN LSTM架构的功率变化是ConvLSTM,它直接在LSTM的单元内使用输入子序列的卷积读取。事实证明,这种方法对于时间序列分类非常有效,并且可以适用于多步骤时间序列预测。
在本教程中,我们将探索一套用于多步时间序列预测的LSTM架构。具体来说,我们将看看如何开发以下模型:
具有矢量输出的LSTM模型,用于具有单变量输入数据的多步预测。
用于多步预测的编码器 - 解码器LSTM模型,具有单变量输入数据。
用于多变量输入数据的多步预测的编码器 - 解码器LSTM模型。
CNN-LSTM编码器 - 解码器模型,用于使用单变量输入数据进行多步预测。
ConvLSTM编码器 - 解码器模型,用于使用单变量输入数据进行多步预测。
将在家庭电力预测问题上开发和演示这些模型。如果一个模型比天真模型更好地实现性能,那么该模型被认为是技术性的,在7天的预测中,该模型的总体RMSE约为465千瓦。
我们不会专注于调整这些模型以实现最佳性能; 相反,与天真的预测相比,我们将停止熟练的模型。选择的结构和超参数通过一些试验和错误来选择。分数应仅作为示例,而不是研究问题的最佳模型或配置。
鉴于模型的随机性,优良作法是多次评估给定模型并报告测试数据集的平均性能。为了简洁起见并保持代码简单,我们将在本教程中介绍单行模型。
我们无法知道哪种方法对于给定的多步预测问题最有效。探索一套方法是个好主意,以便发现哪些方法最适合您的特定数据集。
具有单变量输入和矢量输出的LSTM模型
我们将从开发一个简单或香草LSTM模型开始,该模型读取每日总功耗的天数,并预测下一个标准周的每日功耗的矢量输出。
这将为后续章节中开发的更精细的模型奠定基础。
用作输入的前几天定义了LSTM将读取并学习提取特征的数据的一维(1D)子序列。关于此输入的大小和性质的一些想法包括:
所有前几天,最多数年的数据。
前7天。
前两周。
前一个月。
前一年。
前一周和一周从一年前预测。
没有正确的答案; 相反,可以测试每种方法和更多方法,并且可以使用模型的性能来选择导致最佳模型性能的输入的性质。
这些选择定义了一些东西:
如何准备培训数据以适应模型。
如何准备测试数据以评估模型。
如何使用该模型在未来使用最终模型进行预测。
一个好的起点是使用前七天。
LSTM模型期望数据具有以下形状:
1 | [samples, timesteps, features] |
一个样本将包含七个时间步骤,其中一个功能用于每日总耗电量的七天。
训练数据集有159周的数据,因此训练数据集的形状为:
这是一个好的开始。此格式的数据将使用先前的标准周来预测下一个标准周。一个问题是训练神经网络的159个实例并不是很多。
创建更多培训数据的方法是在培训期间更改问题,以预测前七天的下一个七天,无论标准周。
这仅影响训练数据,并且测试问题保持不变:预测给定前一标准周的下一个标准周的每日功耗。
这需要对训练数据做一点准备。
训练数据在标准周内提供八个变量,特别是形状[ 159,7,8 ]。第一步是展平数据,以便我们有八个时间序列序列。
1
2 | # flatten data
data = train.reshape((train.shape[0]*train.shape[1], train.shape[2])) |
然后,我们需要迭代时间步骤并将数据划分为重叠窗口; 每次迭代沿着一个时间步长移动并预测随后的七天。
例如:
1
2
3
4 | Input, Output
[d01, d02, d03, d04, d05, d06, d07], [d08, d09, d10, d11, d12, d13, d14]
[d02, d03, d04, d05, d06, d07, d08], [d09, d10, d11, d12, d13, d14, d15]
... |
我们可以通过跟踪输入和输出的起始和结束索引来实现这一点,因为我们在时间步长方面迭代展平数据的长度。
我们也可以通过参数化输入和输出的数量(例如n_input,n_out)来执行此操作,以便您可以尝试不同的值或根据自己的问题进行调整。
下面是一个名为to_supervised()的函数,它接受周(历史)列表以及用作输入和输出的时间步数,并以重叠移动窗口格式返回数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | # 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) |
当我们在整个训练数据集上运行此函数时,我们将159个样本转换为1,099个; 具体地,变换的数据集具有形状X = [1099,7,1]和y = [1099,7]。
接下来,我们可以在训练数据上定义和拟合LSTM模型。
这个多步骤时间序列预测问题是一个自回归。这意味着它可能最好地建模,其中接下来的七天是先前时间步骤的观察功能。这和相对少量的数据意味着需要一个小型号。
我们将开发一个具有200个单元的单个隐藏LSTM层的模型。隐藏层中的单元数与输入序列中的时间步数无关。LSTM层之后是具有200个节点的完全连接层,其将解释LSTM层学习的特征。最后,输出层将直接预测具有七个元素的向量,输出序列中每天一个元素。
我们将使用均方误差损失函数,因为它与我们选择的RMSE误差度量非常匹配。我们将使用随机梯度下降的高效Adam实现,并使模型适合70个时期,批量大小为16。
小批量大小和算法的随机性意味着相同的模型将在每次训练时学习输入到输出的略微不同的映射。这意味着评估模型时结果可能会有所不同。您可以尝试多次运行模型并计算模型性能的平均值。
下面的build_model()准备训练数据,定义模型,并使模型适合训练数据,返回拟合模型准备好进行预测。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | # 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, 70, 16
n_timesteps, n_features, n_outputs = train_x.shape[1], train_x.shape[2], train_y.shape[1]
# define model
model = Sequential()
model.add(LSTM(200, activation='relu', input_shape=(n_timesteps, n_features)))
model.add(Dense(100, activation='relu'))
model.add(Dense(n_outputs))
model.compile(loss='mse', optimizer='adam')
# fit network
model.fit(train_x, train_y, epochs=epochs, batch_size=batch_size, verbose=verbose)
return model |
现在我们知道如何拟合模型,我们可以看看如何使用模型进行预测。
通常,模型期望数据在进行预测时具有相同的三维形状。
在这种情况下,输入模式的预期形状是一个样本,每天消耗的一个功能的七天:
在为测试集进行预测时以及在将来使用最终模型进行预测时,数据必须具有此形状。如果在输入天数为14时更改数字,则必须相应更改训练数据的形状和进行预测时新样本的形状,以便有14个时间步长。在使用模型时,您必须继续使用它。
我们正在使用前向验证来评估模型,如上一节所述。
这意味着我们可以获得前一周的观察结果,以预测下周。这些被收集到一系列称为历史的标准周。
为了预测下一个标准周,我们需要检索观察的最后几天。与训练数据一样,我们必须首先展平历史数据以删除每周结构,以便最终得到八个并行时间序列。
1
2 | # flatten data
data = data.reshape((data.shape[0]*data.shape[1], data.shape[2])) |
接下来,我们需要检索每日总功耗的最后七天(特征索引0)。
我们将对训练数据进行参数化,以便将来可以修改模型输入的前几天的数量。
1
2 | # retrieve last observations for input data
input_x = data[-n_input:, 0] |
接下来,我们将输入重塑为预期的三维结构。
1
2 | # reshape into [1, n_input, 1]
input_x = input_x.reshape((1, len(input_x), 1)) |
然后,我们使用拟合模型和输入数据进行预测,并检索七天输出的向量。
1
2
3
4 | # forecast the next week
yhat = model.predict(input_x, verbose=0)
# we only want the vector forecast
yhat = yhat[0] |
下面的forecast()函数实现了这一点,并将模型拟合在训练数据集上的参数,到目前为止观察到的数据历史以及模型预期的输入时间步数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | # 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 |
而已; 我们现在拥有了所需的一切,我们需要通过LSTM模型对单日数据集的每日总功耗进行多步时间序列预测。
我们可以将所有这些结合在一起。下面列出了完整的示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 | # univariate multi-step 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
# 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, 70, 16
n_timesteps, n_features, n_outputs = train_x.shape[1], train_x.shape[2], train_y.shape[1]
# define model
model = Sequential()
model.add(LSTM(200, activation='relu', input_shape=(n_timesteps, n_features)))
model.add(Dense(100, activation='relu'))
model.add(Dense(n_outputs))
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 = 7
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() |
运行该示例适合并评估模型,在所有七天内打印整体RMSE,以及每个提前期的每日RMSE。
鉴于算法的随机性,您的具体结果可能会有所不同。您可能想尝试运行该示例几次。
我们可以看到,在这种情况下,与天真的预测相比,该模型是巧妙的,实现了大约399千瓦的总体RMSE,小于465千瓦的天真模型。
1 | lstm: [399.456] 419.4, 422.1, 384.5, 395.1, 403.9, 317.7, 441.5 |
还创建了每日RMSE的图。
该图显示,周二和周五可能比其他日子更容易预测,也许星期六在标准周结束时是最难预测的日子。
具有矢量输出和7天输入的单变量LSTM的每日RMSE线图
我们可以通过更改n_input变量来增加用作7到14之间输入的前几天的数量。
1
2 | # evaluate model and get scores
n_input = 14 |
使用此更改重新运行示例首先会打印模型性能的摘要。
具体结果可能有所不同; 尝试运行几次示例。
在这种情况下,我们可以看到整体RMSE进一步下降到大约370千瓦,这表明进一步调整输入大小以及模型中节点的数量可能会带来更好的性能。
1 | lstm: [370.028] 387.4, 377.9, 334.0, 371.2, 367.1, 330.4, 415.1 |
比较每日RMSE分数,我们看到一些更好,有些比使用七天输入更差。
这可以建议以某种方式使用两个不同大小的输入的益处,例如两种方法的集合或者可能是以不同方式读取训练数据的单个模型(例如,多头模型)。
具有矢量输出和14天输入的单变量LSTM每日RMSE的线图
具有单变量输入的编码器 - 解码器LSTM模型
在本节中,我们可以更新vanilla LSTM以使用编码器 - 解码器模型。
这意味着模型不会直接输出矢量序列。相反,该模型将包括两个子模型,即用于读取和编码输入序列的编码器,以及将读取编码输入序列并对输出序列中的每个元素进行一步预测的解码器。
差异是微妙的,因为实际上两种方法实际上都预测了序列输出。
重要的区别在于,在解码器中使用LSTM模型,允许它既知道序列中前一天的预测值,又在输出序列时累积内部状态。
让我们仔细看看这个模型是如何定义的。
和以前一样,我们定义了一个包含200个单位的LSTM隐藏层。这是解码器模型,它将读取输入序列并输出一个200元素向量(每个单元一个输出),用于捕获输入序列中的特征。我们将使用14天的总功耗作为输入。
1
2
3 | # define model
model = Sequential()
model.add(LSTM(200, activation='relu', input_shape=(n_timesteps, n_features))) |
我们将使用一种易于在Keras中实现的简单编码器 - 解码器架构,它与LSTM自动编码器的架构有很多相似之处。
首先,输入序列的内部表示重复多次,输出序列中的每个时间步长一次。该序列的矢量将被呈现给LSTM解码器。
1 | model.add(RepeatVector(7)) |
然后,我们将解码器定义为具有200个单位的LSTM隐藏层。重要的是,解码器将输出整个序列,而不仅仅是输出序列末尾的输出,就像我们对编码器一样。这意味着200个单位中的每一个都将为七天中的每一天输出一个值,表示输出序列中每天预测的基础。
1 | model.add(LSTM(200, activation='relu', return_sequences=True)) |
然后,我们将使用完全连接的层来解释最终输出层之前的输出序列中的每个时间步长。重要的是,输出层预测输出序列中的单个步骤,而不是一次七天,
这意味着我们将使用应用于输出序列中每个步骤的相同图层。这意味着将使用相同的完全连接的层和输出层来处理由解码器提供的每个时间步长。为实现此目的,我们将解释层和输出层包装在TimeDistributed包装器中,该包装器允许包装的层用于解码器的每个时间步。
1
2 | model.add(TimeDistributed(Dense(100, activation='relu')))
model.add(TimeDistributed(Dense(1))) |
这允许LSTM解码器找出输出序列中的每个步骤所需的上下文以及包裹的密集层以分别解释每个时间步骤,同时重用相同的权重来执行解释。另一种方法是展平LSTM解码器创建的所有结构并直接输出矢量。您可以尝试将其作为扩展程序来查看它的比较方式。
因此,网络输出具有与输入相同结构的三维矢量,其尺寸为[ 样本,时间步长,特征 ]。
有一个功能,每日消耗的总功率,总有七个功能。因此,单个一周的预测将具有大小:[ 1,7,1]。
因此,在训练模型时,我们必须重新构造输出数据(y)以具有三维结构,而不是前一节中使用的[ 样本,特征 ] 的二维结构。
1
2 | # reshape output into [samples, timesteps, features]
train_y = train_y.reshape((train_y.shape[0], train_y.shape[1], 1)) |
我们可以将所有这些绑定到下面列出的更新的build_model()函数中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | # 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 |
下面列出了编码器 - 解码器模型的完整示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 | # 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() |
运行该示例适合模型并总结测试数据集的性能。
鉴于算法的随机性,您的具体结果可能会有所不同。您可能想尝试运行该示例几次。
我们可以看到,在这种情况下,该模型非常巧妙,总体RMSE得分约为372千瓦。
1 | lstm: [372.595] 379.5, 399.8, 339.6, 372.2, 370.9, 309.9, 424.8 |
还创建了每日RMSE的线图,显示了与上一节中看到的类似的错误模式。
具有14天输入的单变量编码器 - 解码器LSTM每天RMSE的线图
具有多变量输入的编码器 - 解码器LSTM模型
在本节中,我们将更新上一节中开发的编码器 - 解码器LSTM,以使用八个时间序列变量中的每一个来预测下一个标准周的每日总功耗。
我们将通过将每个一维时间序列作为单独的输入序列提供给模型来完成此操作。
LSTM将依次创建每个输入序列的内部表示,其将由解码器一起解释。
使用多变量输入有助于那些输出序列是来自多个不同特征的先前时间步长的观察的某些功能的问题,而不仅仅是(或包括)预测的特征。目前还不清楚功耗问题是否属于这种情况,但我们仍可以探索它。
首先,我们必须更新培训数据的准备工作,以包括所有八项功能,而不仅仅是每日消耗的一项功能。它需要单行更改:
1 | X.append(data[in_start:in_end, :]) |
下面列出了具有此更改的完整to_supervised()函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | # 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.append(data[in_start:in_end, :])
y.append(data[in_end:out_end, 0])
# move along one time step
in_start += 1
return array(X), array(y) |
我们还必须使用拟合模型更新用于进行预测的函数,以使用先前时间步骤中的所有八个特征。
再次,另一个小变化:
1
2
3
4 | # retrieve last observations for input data
input_x = data[-n_input:, :]
# reshape into [1, n_input, n]
input_x = input_x.reshape((1, input_x.shape[0], input_x.shape[1])) |
下面列出了具有此更改的完整forecast()函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | # 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:, :]
# reshape into [1, n_input, n]
input_x = input_x.reshape((1, input_x.shape[0], input_x.shape[1]))
# forecast the next week
yhat = model.predict(input_x, verbose=0)
# we only want the vector forecast
yhat = yhat[0]
return yhat |
直接使用相同的模型架构和配置,尽管我们将训练时期的数量从20增加到50,因为输入数据量增加了8倍。
下面列出了完整的示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135 | # multivariate 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.append(data[in_start:in_end, :])
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, 50, 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:, :]
# reshape into [1, n_input, n]
input_x = input_x.reshape((1, input_x.shape[0], input_x.shape[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() |
运行该示例适合模型并总结测试数据集的性能。
实验发现该模型看起来不如单变量情况稳定,并且可能与输入的八个变量的不同尺度有关。
鉴于算法的随机性,您的具体结果可能会有所不同。您可能想尝试运行该示例几次。
我们可以看到,在这种情况下,该模型非常巧妙,总体RMSE得分约为376千瓦。
1 | lstm: [376.273] 378.5, 381.5, 328.4, 388.3, 361.2, 308.0, 467.2 |
还创建了每日RMSE的线图。
具有14天输入的多变量编码器 - 解码器LSTM每天RMSE的线图鉴于智能电表的兴起以及太阳能电池板等发电技术的广泛采用,有大量的用电数据可供选择。该数据代表了多变量时间序列的功率相关变量,这些变量又可用于建模甚至预测未来的电力消耗。
与其他机器学习算法不同,长期短期记忆递归神经网络能够自动学习序列数据的特征,支持多变量数据,并且可以输出可用于多步预测的可变长度序列。
总体概述
本教程分为九个部分; 他们是:
问题描述
加载并准备数据集
模型评估
用于多步预测的LSTM
具有单变量输入和矢量输出的LSTM模型
具有单变量输入的LSTM模型
具有多变量输入的编码器 - 解码器LSTM模型
具有单变量输入的CNN-LSTM编码器 - 解码器模型
具有单变量输入的ConvLSTM编码器 - 解码器模型
问题描述
“ 家庭用电量 ”数据集是一个多变量时间序列数据集,用于描述单个家庭四年的用电量。该数据是在2006年12月至2010年11月之间收集的,并且每分钟收集家庭内的能耗观察结果。
它是一个多变量系列,由七个变量组成(除日期和时间外); 他们是:
global_active_power:家庭消耗的总有功功率(千瓦)。
global_reactive_power:家庭消耗的总无功功率(千瓦)。
voltage:平均电压(伏特)。
global_intensity:平均电流强度(安培)。
sub_metering_1:厨房的有功电能(瓦特小时的有功电能)。
sub_metering_2:用于洗衣的有功能量(瓦特小时的有功电能)。
sub_metering_3:气候控制系统的有功电能(瓦特小时的有功电能)。
有功和无功能量是指交流电的技术细节。
可以通过从总活动能量中减去三个定义的子计量变量的总和来创建第四个子计量变量,如下所示:
sub_metering_remainder = (global_active_power * 1000 / 60) - (sub_metering_1 + sub_metering_2 + sub_metering_3)
加载并准备数据集
数据集可以找博主索取
下载数据集并将其解压缩到当前工作目录中。您现在将拥有大小为127兆字节的文件“ household_power_consumption.txt ”并包含所有观察结果。
我们可以使用read_csv()函数加载数据,并将前两列组合成一个日期时间列,我们可以将其用作索引。
1
2 | # load all data
dataset = read_csv('household_power_consumption.txt', sep=';', header=0, low_memory=False, infer_datetime_format=True, parse_dates={'datetime':[0,1]}, index_col=['datetime']) |
接下来,我们可以标记用' ? '表示的所有缺失值。'具有NaN值的字符,这是一个浮点数。
这将允许我们将数据作为一个浮点值数组而不是混合类型(效率较低)。
1
2
3
4 | # mark all missing values
dataset.replace('?', nan, inplace=True)
# make dataset numeric
dataset = dataset.astype('float32') |
我们还需要填写缺失值,因为它们已被标记。
一种非常简单的方法是从前一天的同一时间复制观察。我们可以在一个名为fill_missing()的函数中实现它,该函数将从24小时前获取数据的NumPy数组并复制值。
1
2
3
4
5
6
7 | # fill missing values with a value at the same time one day ago
def fill_missing(values):
one_day = 60 * 24
for row in range(values.shape[0]):
for col in range(values.shape[1]):
if isnan(values[row, col]):
values[row, col] = values[row - one_day, col] |
我们可以将此函数直接应用于DataFrame中的数据。
1
2 | # fill missing
fill_missing(dataset.values) |
现在,我们可以使用上一节中的计算创建一个包含剩余子计量的新列。
1
2
3 | # add a column for for the remainder of sub metering
values = dataset.values
dataset['sub_metering_4'] = (values[:,0] * 1000 / 60) - (values[:,4] + values[:,5] + values[:,6]) |
我们现在可以将清理后的数据集版本保存到新文件中; 在这种情况下,我们只需将文件扩展名更改为.csv,并将数据集保存为“ household_power_consumption.csv ”。
1
2 | # save updated dataset
dataset.to_csv('household_power_consumption.csv') |
将所有这些结合在一起,下面列出了加载,清理和保存数据集的完整示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | # load and clean-up data
from numpy import nan
from numpy import isnan
from pandas import read_csv
from pandas import to_numeric
# fill missing values with a value at the same time one day ago
def fill_missing(values):
one_day = 60 * 24
for row in range(values.shape[0]):
for col in range(values.shape[1]):
if isnan(values[row, col]):
values[row, col] = values[row - one_day, col]
# load all data
dataset = read_csv('household_power_consumption.txt', sep=';', header=0, low_memory=False, infer_datetime_format=True, parse_dates={'datetime':[0,1]}, index_col=['datetime'])
# mark all missing values
dataset.replace('?', nan, inplace=True)
# make dataset numeric
dataset = dataset.astype('float32')
# fill missing
fill_missing(dataset.values)
# add a column for for the remainder of sub metering
values = dataset.values
dataset['sub_metering_4'] = (values[:,0] * 1000 / 60) - (values[:,4] + values[:,5] + values[:,6])
# save updated dataset
dataset.to_csv('household_power_consumption.csv') |
运行该示例将创建新文件“ household_power_consumption.csv ”,我们可以将其用作建模项目的起点。
模型评估
在本节中,我们将考虑如何开发和评估家庭电力数据集的预测模型。
本节分为四个部分; 他们是:
问题框架
评估指标
训练和测试集
前瞻性验证
问题框架
有许多方法可以利用和探索家庭用电量数据集。
在本教程中,我们将使用数据来探索一个非常具体的问题; 那是:
鉴于最近的耗电量,未来一周的预期耗电量是多少?
这要求预测模型预测未来七天每天的总有功功率。
从技术上讲,考虑到多个预测步骤,这个问题的框架被称为多步骤时间序列预测问题。利用多个输入变量的模型可以称为多变量多步时间序列预测模型。
这种类型的模型在规划支出方面可能对家庭有所帮助。在供应方面,它也可能有助于规划特定家庭的电力需求。
数据集的这种框架还表明,将每分钟的功耗观测值下采样到每日总数是有用的。这不是必需的,但考虑到我们对每天的总功率感兴趣,这是有道理的。
我们可以使用pandas DataFrame上的resample()函数轻松实现这一点。使用参数“ D ” 调用此函数允许按日期时间索引的已加载数据按天分组(请参阅所有偏移别名)。然后,我们可以计算每天所有观测值的总和,并为八个变量中的每一个创建每日耗电量数据的新数据集。
下面列出了完整的示例。
1
2
3
4
5
6
7
8
9
10
11
12 | # resample minute data to total for each day
from pandas import read_csv
# load the new file
dataset = read_csv('household_power_consumption.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
# resample data to daily
daily_groups = dataset.resample('D')
daily_data = daily_groups.sum()
# summarize
print(daily_data.shape)
print(daily_data.head())
# save
daily_data.to_csv('household_power_consumption_days.csv') |
运行该示例将创建一个新的每日总功耗数据集,并将结果保存到名为“ household_power_consumption_days.csv ” 的单独文件中。
我们可以将其用作数据集,用于拟合和评估所选问题框架的预测模型。
评估指标
预测将包含七个值,一个用于一周中的每一天。
多步预测问题通常分别评估每个预测时间步长。这有助于以下几个原因:
评估特定提前期的技能(例如+1天vs +3天)。
在不同的交付周期内根据他们的技能对比模型(例如,在+1天的模型和在日期+5的模型良好的模型)。
总功率的单位是千瓦,并且具有也在相同单位的误差度量将是有用的。均方根误差(RMSE)和平均绝对误差(MAE)均符合此法案,尽管RMSE更常用,将在本教程中采用。与MAE不同,RMSE更能惩罚预测误差。
此问题的性能指标将是从第1天到第7天的每个提前期的RMSE。
作为捷径,使用单个分数总结模型的性能以帮助模型选择可能是有用的。
可以使用的一个可能的分数是所有预测天数的RMSE。
下面的函数evaluate_forecasts()将实现此行为并基于多个七天预测返回模型的性能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | # 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 |
运行该函数将首先返回整个RMSE而不管白天,然后每天返回一系列RMSE分数。
训练和测试集
我们将使用前三年的数据来训练预测模型和评估模型的最后一年。
给定数据集中的数据将分为标准周。这些是从周日开始到周六结束的周。
这是使用所选模型框架的现实且有用的方法,其中可以预测未来一周的功耗。它也有助于建模,其中模型可用于预测特定日期(例如星期三)或整个序列。
我们将数据拆分为标准周,从测试数据集向后工作。
数据的最后一年是2010年,2010年的第一个星期日是1月3日。数据于2010年11月中旬结束,数据中最接近的最后一个星期六是11月20日。这给出了46周的测试数据。
下面提供了测试数据集的第一行和最后一行日常数据以供确认。
1
2
3 | 2010-01-03,2083.4539999999984,191.61000000000055,350992.12000000034,8703.600000000033,3842.0,4920.0,10074.0,15888.233355799992
...
2010-11-20,2197.006000000004,153.76800000000028,346475.9999999998,9320.20000000002,4367.0,2947.0,11433.0,17869.76663959999 |
每日数据从2006年底开始。
数据集中的第一个星期日是12月17日,这是第二行数据。
将数据组织成标准周,为训练预测模型提供了159个标准周。
1
2
3 | 2006-12-17,3390.46,226.0059999999994,345725.32000000024,14398.59999999998,2033.0,4187.0,13341.0,36946.66673200004
...
2010-01-02,1309.2679999999998,199.54600000000016,352332.8399999997,5489.7999999999865,801.0,298.0,6425.0,14297.133406600002 |
下面的函数split_dataset()将每日数据拆分为训练集和测试集,并将每个数据组织成标准周。
使用特定行偏移来使用数据集的知识来分割数据。然后使用NumPy split()函数将拆分数据集组织成每周数据。
1
2
3
4
5
6
7
8 | # 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 |
我们可以通过加载每日数据集并打印列车和测试集的第一行和最后一行数据来测试此功能,以确认它们符合上述预期。
完整的代码示例如下所示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | # split into standard weeks
from numpy import split
from numpy import array
from pandas import read_csv
# 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
# load the new file
dataset = read_csv('household_power_consumption_days.csv', header=0, infer_datetime_format=True, parse_dates=['datetime'], index_col=['datetime'])
train, test = split_dataset(dataset.values)
# validate train data
print(train.shape)
print(train[0, 0, 0], train[-1, -1, 0])
# validate test
print(test.shape)
print(test[0, 0, 0], test[-1, -1, 0]) |
运行该示例表明,列车数据集确实有159周的数据,而测试数据集有46周。
我们可以看到,第一行和最后一行的列车和测试数据集的总有效功率与我们定义为每组标准周数的边界的特定日期的数据相匹配。
1
2
3
4 | (159, 7, 8)
3390.46 1309.2679999999998
(46, 7, 8)
2083.4539999999984 2197.006000000004 |
前瞻性验证
将使用称为前向验证的方案来评估模型。
这是需要模型进行一周预测的地方,然后该模型可用于该周的实际数据,以便它可以用作在随后一周进行预测的基础。这对于如何在实践中使用模型以及对模型有益而使其能够利用最佳可用数据都是现实的。
我们可以通过分离输入数据和输出/预测数据来证明这一点。
1
2
3
4
5 | Input, Predict
[Week1] Week2
[Week1 + Week2] Week3
[Week1 + Week2 + Week3] Week4
... |
评估此数据集上的预测模型的前瞻性验证方法在下面提供,名为evaluate_model()。
标准周格式的训练和测试数据集作为参数提供给函数。提供了另一个参数n_input,用于定义模型将用作输入以进行预测的先前观察的数量。
调用两个新函数:一个用于根据名为build_model()的训练数据构建模型,另一个用于使用该模型对每个新标准周进行预测,称为forecast()。这些将在后续章节中介绍。
我们正在使用神经网络,因此,它们通常很难训练,但很快就能进行评估。这意味着模型的首选用法是在历史数据上构建一次,并使用它们来预测前进验证的每个步骤。模型在评估期间是静态的(即未更新)。
这与其他模型不同,这些模型可以更快地进行训练,在新数据可用时,可以重新拟合或更新模型的每个步骤。有了足够的资源,就可以通过这种方式使用神经网络,但在本教程中我们不会这样做。
完整的evaluate_model()函数如下所示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | # 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 |
一旦我们对模型进行评估,我们就可以总结性能。
以下名为summarize_scores()的函数将模型的性能显示为单行,以便与其他模型进行比较。
1
2
3
4 | # 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)) |
我们现在已经开始评估数据集上的预测模型的所有元素。
用于多步预测的LSTM
循环神经网络或RNN专门用于工作,学习和预测序列数据。
递归神经网络是神经网络,其中来自一个时间步骤的网络输出在随后的时间步骤中被提供作为输入。这允许模型基于当前时间步长的输入和对先前时间步骤中输出的直接知识来决定预测什么。
也许最成功和最广泛使用的RNN是长期短期记忆网络,简称LSTM。它之所以成功,是因为它克服了训练复现神经网络所带来的挑战,从而产生了稳定的模型。除了利用先前时间步的输出的循环连接之外,LSTM还具有内部存储器,其操作类似于局部变量,允许它们在输入序列上累积状态。
LSTM在多步时间序列预测方面具有许多优势; 他们是:
对序列的本机支持。LSTM是一种循环网络,因此设计用于将序列数据作为输入,不像其他模型,其中滞后观察必须作为输入特征呈现。
多变量输入。LSTM直接支持多变量输入的多个并行输入序列,不同于其中多变量输入以平面结构呈现的其他模型。
矢量输出。与其他神经网络一样,LSTM能够将输入数据直接映射到可表示多个输出时间步长的输出向量。
此外,已经开发了专门的架构,其专门设计用于进行多步骤序列预测,通常称为序列到序列预测,或简称为seq2seq。这很有用,因为多步时间序列预测是一种seq2seq预测。
针对seq2seq问题设计的递归神经网络架构的示例是编码器 - 解码器LSTM。
编码器 - 解码器LSTM是由两个子模型组成的模型:一个称为编码器,其读取输入序列并将其压缩为固定长度的内部表示;以及输出模型,称为解码器,其解释内部表示并使用它预测输出序列。
事实证明,序列预测的编码器 - 解码器方法比直接输出矢量更有效,并且是首选方法。
通常,已经发现LSTM在自回归类型问题上不是非常有效。这些是预测下一个时间步长是最近时间步长的函数。
一维卷积神经网络(CNN)已证明在动学习输入序列的特征方面是有效的。
一种流行的方法是将CNN与LSTM组合,其中CNN作为编码器来学习输入数据的子序列的特征,这些子序列作为LSTM的时间步长提供。该架构称为CNN-LSTM。
CNN LSTM架构的功率变化是ConvLSTM,它直接在LSTM的单元内使用输入子序列的卷积读取。事实证明,这种方法对于时间序列分类非常有效,并且可以适用于多步骤时间序列预测。
在本教程中,我们将探索一套用于多步时间序列预测的LSTM架构。具体来说,我们将看看如何开发以下模型:
具有矢量输出的LSTM模型,用于具有单变量输入数据的多步预测。
用于多步预测的编码器 - 解码器LSTM模型,具有单变量输入数据。
用于多变量输入数据的多步预测的编码器 - 解码器LSTM模型。
CNN-LSTM编码器 - 解码器模型,用于使用单变量输入数据进行多步预测。
ConvLSTM编码器 - 解码器模型,用于使用单变量输入数据进行多步预测。
将在家庭电力预测问题上开发和演示这些模型。如果一个模型比天真模型更好地实现性能,那么该模型被认为是技术性的,在7天的预测中,该模型的总体RMSE约为465千瓦。
我们不会专注于调整这些模型以实现最佳性能; 相反,与天真的预测相比,我们将停止熟练的模型。选择的结构和超参数通过一些试验和错误来选择。分数应仅作为示例,而不是研究问题的最佳模型或配置。
鉴于模型的随机性,优良作法是多次评估给定模型并报告测试数据集的平均性能。为了简洁起见并保持代码简单,我们将在本教程中介绍单行模型。
我们无法知道哪种方法对于给定的多步预测问题最有效。探索一套方法是个好主意,以便发现哪些方法最适合您的特定数据集。
具有单变量输入和矢量输出的LSTM模型
我们将从开发一个简单或香草LSTM模型开始,该模型读取每日总功耗的天数,并预测下一个标准周的每日功耗的矢量输出。
这将为后续章节中开发的更精细的模型奠定基础。
用作输入的前几天定义了LSTM将读取并学习提取特征的数据的一维(1D)子序列。关于此输入的大小和性质的一些想法包括:
所有前几天,最多数年的数据。
前7天。
前两周。
前一个月。
前一年。
前一周和一周从一年前预测。
没有正确的答案; 相反,可以测试每种方法和更多方法,并且可以使用模型的性能来选择导致最佳模型性能的输入的性质。
这些选择定义了一些东西:
如何准备培训数据以适应模型。
如何准备测试数据以评估模型。
如何使用该模型在未来使用最终模型进行预测。
一个好的起点是使用前七天。
LSTM模型期望数据具有以下形状:
1 | [samples, timesteps, features] |
一个样本将包含七个时间步骤,其中一个功能用于每日总耗电量的七天。
训练数据集有159周的数据,因此训练数据集的形状为:
这是一个好的开始。此格式的数据将使用先前的标准周来预测下一个标准周。一个问题是训练神经网络的159个实例并不是很多。
创建更多培训数据的方法是在培训期间更改问题,以预测前七天的下一个七天,无论标准周。
这仅影响训练数据,并且测试问题保持不变:预测给定前一标准周的下一个标准周的每日功耗。
这需要对训练数据做一点准备。
训练数据在标准周内提供八个变量,特别是形状[ 159,7,8 ]。第一步是展平数据,以便我们有八个时间序列序列。
1
2 | # flatten data
data = train.reshape((train.shape[0]*train.shape[1], train.shape[2])) |
然后,我们需要迭代时间步骤并将数据划分为重叠窗口; 每次迭代沿着一个时间步长移动并预测随后的七天。
例如:
1
2
3
4 | Input, Output
[d01, d02, d03, d04, d05, d06, d07], [d08, d09, d10, d11, d12, d13, d14]
[d02, d03, d04, d05, d06, d07, d08], [d09, d10, d11, d12, d13, d14, d15]
... |
我们可以通过跟踪输入和输出的起始和结束索引来实现这一点,因为我们在时间步长方面迭代展平数据的长度。
我们也可以通过参数化输入和输出的数量(例如n_input,n_out)来执行此操作,以便您可以尝试不同的值或根据自己的问题进行调整。
下面是一个名为to_supervised()的函数,它接受周(历史)列表以及用作输入和输出的时间步数,并以重叠移动窗口格式返回数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | # 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) |
当我们在整个训练数据集上运行此函数时,我们将159个样本转换为1,099个; 具体地,变换的数据集具有形状X = [1099,7,1]和y = [1099,7]。
接下来,我们可以在训练数据上定义和拟合LSTM模型。
这个多步骤时间序列预测问题是一个自回归。这意味着它可能最好地建模,其中接下来的七天是先前时间步骤的观察功能。这和相对少量的数据意味着需要一个小型号。
我们将开发一个具有200个单元的单个隐藏LSTM层的模型。隐藏层中的单元数与输入序列中的时间步数无关。LSTM层之后是具有200个节点的完全连接层,其将解释LSTM层学习的特征。最后,输出层将直接预测具有七个元素的向量,输出序列中每天一个元素。
我们将使用均方误差损失函数,因为它与我们选择的RMSE误差度量非常匹配。我们将使用随机梯度下降的高效Adam实现,并使模型适合70个时期,批量大小为16。
小批量大小和算法的随机性意味着相同的模型将在每次训练时学习输入到输出的略微不同的映射。这意味着评估模型时结果可能会有所不同。您可以尝试多次运行模型并计算模型性能的平均值。
下面的build_model()准备训练数据,定义模型,并使模型适合训练数据,返回拟合模型准备好进行预测。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | # 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, 70, 16
n_timesteps, n_features, n_outputs = train_x.shape[1], train_x.shape[2], train_y.shape[1]
# define model
model = Sequential()
model.add(LSTM(200, activation='relu', input_shape=(n_timesteps, n_features)))
model.add(Dense(100, activation='relu'))
model.add(Dense(n_outputs))
model.compile(loss='mse', optimizer='adam')
# fit network
model.fit(train_x, train_y, epochs=epochs, batch_size=batch_size, verbose=verbose)
return model |
现在我们知道如何拟合模型,我们可以看看如何使用模型进行预测。
通常,模型期望数据在进行预测时具有相同的三维形状。
在这种情况下,输入模式的预期形状是一个样本,每天消耗的一个功能的七天:
在为测试集进行预测时以及在将来使用最终模型进行预测时,数据必须具有此形状。如果在输入天数为14时更改数字,则必须相应更改训练数据的形状和进行预测时新样本的形状,以便有14个时间步长。在使用模型时,您必须继续使用它。
我们正在使用前向验证来评估模型,如上一节所述。
这意味着我们可以获得前一周的观察结果,以预测下周。这些被收集到一系列称为历史的标准周。
为了预测下一个标准周,我们需要检索观察的最后几天。与训练数据一样,我们必须首先展平历史数据以删除每周结构,以便最终得到八个并行时间序列。
1
2 | # flatten data
data = data.reshape((data.shape[0]*data.shape[1], data.shape[2])) |
接下来,我们需要检索每日总功耗的最后七天(特征索引0)。
我们将对训练数据进行参数化,以便将来可以修改模型输入的前几天的数量。
1
2 | # retrieve last observations for input data
input_x = data[-n_input:, 0] |
接下来,我们将输入重塑为预期的三维结构。
1
2 | # reshape into [1, n_input, 1]
input_x = input_x.reshape((1, len(input_x), 1)) |
然后,我们使用拟合模型和输入数据进行预测,并检索七天输出的向量。
1
2
3
4 | # forecast the next week
yhat = model.predict(input_x, verbose=0)
# we only want the vector forecast
yhat = yhat[0] |
下面的forecast()函数实现了这一点,并将模型拟合在训练数据集上的参数,到目前为止观察到的数据历史以及模型预期的输入时间步数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | # 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 |
而已; 我们现在拥有了所需的一切,我们需要通过LSTM模型对单日数据集的每日总功耗进行多步时间序列预测。
我们可以将所有这些结合在一起。下面列出了完整的示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 | # univariate multi-step 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
# 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, 70, 16
n_timesteps, n_features, n_outputs = train_x.shape[1], train_x.shape[2], train_y.shape[1]
# define model
model = Sequential()
model.add(LSTM(200, activation='relu', input_shape=(n_timesteps, n_features)))
model.add(Dense(100, activation='relu'))
model.add(Dense(n_outputs))
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 = 7
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() |
运行该示例适合并评估模型,在所有七天内打印整体RMSE,以及每个提前期的每日RMSE。
鉴于算法的随机性,您的具体结果可能会有所不同。您可能想尝试运行该示例几次。
我们可以看到,在这种情况下,与天真的预测相比,该模型是巧妙的,实现了大约399千瓦的总体RMSE,小于465千瓦的天真模型。
1 | lstm: [399.456] 419.4, 422.1, 384.5, 395.1, 403.9, 317.7, 441.5 |
还创建了每日RMSE的图。
该图显示,周二和周五可能比其他日子更容易预测,也许星期六在标准周结束时是最难预测的日子。
具有矢量输出和7天输入的单变量LSTM的每日RMSE线图
我们可以通过更改n_input变量来增加用作7到14之间输入的前几天的数量。
1
2 | # evaluate model and get scores
n_input = 14 |
使用此更改重新运行示例首先会打印模型性能的摘要。
具体结果可能有所不同; 尝试运行几次示例。
在这种情况下,我们可以看到整体RMSE进一步下降到大约370千瓦,这表明进一步调整输入大小以及模型中节点的数量可能会带来更好的性能。
1 | lstm: [370.028] 387.4, 377.9, 334.0, 371.2, 367.1, 330.4, 415.1 |
比较每日RMSE分数,我们看到一些更好,有些比使用七天输入更差。
这可以建议以某种方式使用两个不同大小的输入的益处,例如两种方法的集合或者可能是以不同方式读取训练数据的单个模型(例如,多头模型)。
具有矢量输出和14天输入的单变量LSTM每日RMSE的线图
具有单变量输入的编码器 - 解码器LSTM模型
在本节中,我们可以更新vanilla LSTM以使用编码器 - 解码器模型。
这意味着模型不会直接输出矢量序列。相反,该模型将包括两个子模型,即用于读取和编码输入序列的编码器,以及将读取编码输入序列并对输出序列中的每个元素进行一步预测的解码器。
差异是微妙的,因为实际上两种方法实际上都预测了序列输出。
重要的区别在于,在解码器中使用LSTM模型,允许它既知道序列中前一天的预测值,又在输出序列时累积内部状态。
让我们仔细看看这个模型是如何定义的。
和以前一样,我们定义了一个包含200个单位的LSTM隐藏层。这是解码器模型,它将读取输入序列并输出一个200元素向量(每个单元一个输出),用于捕获输入序列中的特征。我们将使用14天的总功耗作为输入。
1
2
3 | # define model
model = Sequential()
model.add(LSTM(200, activation='relu', input_shape=(n_timesteps, n_features))) |
我们将使用一种易于在Keras中实现的简单编码器 - 解码器架构,它与LSTM自动编码器的架构有很多相似之处。
首先,输入序列的内部表示重复多次,输出序列中的每个时间步长一次。该序列的矢量将被呈现给LSTM解码器。
1 | model.add(RepeatVector(7)) |
然后,我们将解码器定义为具有200个单位的LSTM隐藏层。重要的是,解码器将输出整个序列,而不仅仅是输出序列末尾的输出,就像我们对编码器一样。这意味着200个单位中的每一个都将为七天中的每一天输出一个值,表示输出序列中每天预测的基础。
1 | model.add(LSTM(200, activation='relu', return_sequences=True)) |
然后,我们将使用完全连接的层来解释最终输出层之前的输出序列中的每个时间步长。重要的是,输出层预测输出序列中的单个步骤,而不是一次七天,
这意味着我们将使用应用于输出序列中每个步骤的相同图层。这意味着将使用相同的完全连接的层和输出层来处理由解码器提供的每个时间步长。为实现此目的,我们将解释层和输出层包装在TimeDistributed包装器中,该包装器允许包装的层用于解码器的每个时间步。
1
2 | model.add(TimeDistributed(Dense(100, activation='relu')))
model.add(TimeDistributed(Dense(1))) |
这允许LSTM解码器找出输出序列中的每个步骤所需的上下文以及包裹的密集层以分别解释每个时间步骤,同时重用相同的权重来执行解释。另一种方法是展平LSTM解码器创建的所有结构并直接输出矢量。您可以尝试将其作为扩展程序来查看它的比较方式。
因此,网络输出具有与输入相同结构的三维矢量,其尺寸为[ 样本,时间步长,特征 ]。
有一个功能,每日消耗的总功率,总有七个功能。因此,单个一周的预测将具有大小:[ 1,7,1]。
因此,在训练模型时,我们必须重新构造输出数据(y)以具有三维结构,而不是前一节中使用的[ 样本,特征 ] 的二维结构。
1
2 | # reshape output into [samples, timesteps, features]
train_y = train_y.reshape((train_y.shape[0], train_y.shape[1], 1)) |
我们可以将所有这些绑定到下面列出的更新的build_model()函数中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | # 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 |
下面列出了编码器 - 解码器模型的完整示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137 | # 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() |
运行该示例适合模型并总结测试数据集的性能。
鉴于算法的随机性,您的具体结果可能会有所不同。您可能想尝试运行该示例几次。
我们可以看到,在这种情况下,该模型非常巧妙,总体RMSE得分约为372千瓦。
1 | lstm: [372.595] 379.5, 399.8, 339.6, 372.2, 370.9, 309.9, 424.8 |
还创建了每日RMSE的线图,显示了与上一节中看到的类似的错误模式。
具有14天输入的单变量编码器 - 解码器LSTM每天RMSE的线图
具有多变量输入的编码器 - 解码器LSTM模型
在本节中,我们将更新上一节中开发的编码器 - 解码器LSTM,以使用八个时间序列变量中的每一个来预测下一个标准周的每日总功耗。
我们将通过将每个一维时间序列作为单独的输入序列提供给模型来完成此操作。
LSTM将依次创建每个输入序列的内部表示,其将由解码器一起解释。
使用多变量输入有助于那些输出序列是来自多个不同特征的先前时间步长的观察的某些功能的问题,而不仅仅是(或包括)预测的特征。目前还不清楚功耗问题是否属于这种情况,但我们仍可以探索它。
首先,我们必须更新培训数据的准备工作,以包括所有八项功能,而不仅仅是每日消耗的一项功能。它需要单行更改:
1 | X.append(data[in_start:in_end, :]) |
下面列出了具有此更改的完整to_supervised()函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | # 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.append(data[in_start:in_end, :])
y.append(data[in_end:out_end, 0])
# move along one time step
in_start += 1
return array(X), array(y) |
我们还必须使用拟合模型更新用于进行预测的函数,以使用先前时间步骤中的所有八个特征。
再次,另一个小变化:
1
2
3
4 | # retrieve last observations for input data
input_x = data[-n_input:, :]
# reshape into [1, n_input, n]
input_x = input_x.reshape((1, input_x.shape[0], input_x.shape[1])) |
下面列出了具有此更改的完整forecast()函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | # 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:, :]
# reshape into [1, n_input, n]
input_x = input_x.reshape((1, input_x.shape[0], input_x.shape[1]))
# forecast the next week
yhat = model.predict(input_x, verbose=0)
# we only want the vector forecast
yhat = yhat[0]
return yhat |
直接使用相同的模型架构和配置,尽管我们将训练时期的数量从20增加到50,因为输入数据量增加了8倍。
下面列出了完整的示例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135 | # multivariate 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.append(data[in_start:in_end, :])
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, 50, 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:, :]
# reshape into [1, n_input, n]
input_x = input_x.reshape((1, input_x.shape[0], input_x.shape[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() |
运行该示例适合模型并总结测试数据集的性能。
实验发现该模型看起来不如单变量情况稳定,并且可能与输入的八个变量的不同尺度有关。
鉴于算法的随机性,您的具体结果可能会有所不同。您可能想尝试运行该示例几次。
我们可以看到,在这种情况下,该模型非常巧妙,总体RMSE得分约为376千瓦。
1 | lstm: [376.273] 378.5, 381.5, 328.4, 388.3, 361.2, 308.0, 467.2 |
还创建了每日RMSE的线图。
具有14天输入的多变量编码器 - 解码器LSTM每天RMSE的线图
|
|
2021-7-22 14:03:02
评论
举报
|
|
|