译自How to Develop LSTM Models for Multi-Step Time Series Forecasting of Household Power Consumption~
随着智能电表的兴起和太阳能电池板等发电技术的广泛应用,有大量可用的用电数据。这些数据代表了一系列与电力相关的多元时间序列,进而可以用来建模甚至预测未来的用电量。
与其他机器学习算法不同,长短时记忆循环神经网络能够从序列数据中自动学习特征,支持多变量数据,并能输出可用于多步预测的变长序列。
在本教程中,您将了解如何开发长期短期记忆循环神经网络,用于多步骤时间序列预测家庭用电量。
完成本教程后,您将知道:
- 如何开发和评估用于多步时间序列预测的单变量和多变量编解码器LSTMs。
- 如何建立和评价一个用于多步时间序列预测的CNN-LSTM编解码器模型。
- 如何建立和评价用于多步时间序列预测的ConvLSTM编解码器模型。
让我们开始吧。
0 教程概述
本教程分为九个部分;它们是:
- 问题描述
- 加载和准备数据集
- 模型评价
- 用于多步预测的LSTMs
- 具有单变量输入和向量输出的LSTM模型
- 单变量输入的编解码器LSTM模型
- 多变量输入的LSTM模型
- 具有单变量输入的CNN-LSTM编解码器模型
- 单变量输入的ConvLSTM编解码器模型
1 问题描述
“家庭用电量”数据集是一个多元时间序列数据集,描述一个家庭四年以上的用电量。这些数据是在2006年12月至2010年11月期间收集的,每分钟收集一次对家庭用电情况的观察。
它是由7个变量(除日期和时间外)组成的多元系列;它们是:
- global_active_power:家庭消耗的总有功功率(千瓦)。
- global_reactive_power:家庭消耗的总无功功率(千瓦)。
- voltage:平均电压(伏特)。
- global_intensity:平均电流强度(amps)。
- 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)
2 加载和准备数据集
2.1 数据加载
数据集可以从UCI机器学习存储库下载为一个20 mb .zip文件:
下载数据集并将其解压缩到当前工作目录中。现在您将拥有文件“household_power_consumption”。它的大小约为127mb,包含所有的观测数据。
我们可以使用read_csv()函数加载数据,并将前两列合并为一个日期-时间列,作为索引使用。
# 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'])
2.2 数据清预处理
接下来,我们可以用'标记所有缺失的值?'字符,带有NaN值,这是一个浮点数。
这将允许我们将数据作为一个浮点值数组来处理,而不是混合类型(效率较低)。
# load and clean-up data
from numpy import nan
from numpy import isnan
from pandas import read_csv
from pandas import to_numeric
# mark all missing values
dataset.replace('?', nan, inplace=True)
# make dataset numeric
dataset = dataset.astype('float32')
既然已经标记了缺失的值,我们还需要填充它们。
一个非常简单的方法是复制前一天同一时间的观察结果。我们可以在一个名为fill_missing()的函数中实现这一点,该函数将获取数据的NumPy数组并复制24小时前的值。
# 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中的数据。
# 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])
现在,我们可以将数据集的清理版本保存到一个新文件中;在本例中,我们将文件扩展名更改为.csv,并将数据集保存为' household_power_consumption.csv '。
# save updated dataset
dataset.to_csv('household_power_consumption.csv')
将所有这些结合在一起,下面列出了加载、清理和保存数据集的完整示例。
# 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')
3 模型评价
在本节中,我们将考虑如何为家用电力数据集开发和评估预测模型。
本节共分为四个部分;它们是:
- 问题的框架
- 评价指标
- 训练及测试设备
- Walk-Forward验证
3.1 问题的框架
有许多方法可以利用和探索家庭能耗数据集。
在本教程中,我们将使用这些数据来探索一个非常具体的问题;那就是:
考虑到最近的用电量,未来一周的预期用电量是多少?
这需要一个预测模型预测未来七天每天的总有功功率。
从技术上讲,这个问题的框架被称为一个多步骤时间序列预测问题,给出了多个预测步骤。利用多个输入变量的模型可称为多元多步时间序列预测模型。
这种模式可以帮助家庭内部规划支出。它还可以在供应方面帮助规划特定家庭的电力需求。
数据集的这一框架还表明,将每分钟的耗电量观测数据降至每日总耗电量是有用的。这不是必需的,但是有意义,因为我们对每天的总功率感兴趣。
我们可以很容易地在pandas DataFrame上使用resample()函数来实现这一点。使用参数“D”调用此函数允许按日期-时间索引的加载数据按天分组(请参阅所有偏移别名)。然后,我们可以计算每天所有观察值的总和,并为这八个变量中的每一个创建一个新的日能耗数据集。
完整的示例如下所示。
# 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 ption_days.csv”的单独文件中。
我们可以使用这个数据集来拟合和评估问题所选框架的预测模型。
3.2 评价指标
预测将由7个值组成,每个值代表未来一周的每一天。
在多步预测问题中,通常对每个预测的时间步分别进行评估。这样做有几个好处:
- 在特定的准备时间(例如+1天vs +3天)对技能进行评价。
- 根据不同的交付时间(例如,擅长+1天的模型与擅长+5天的模型),对模型进行比较。
总功率的单位是千瓦,有一个同样单位的误差度量是很有用的。均方根误差(RMSE)和平均绝对误差(MAE)都符合这一要求,但是RMSE更常用,并将在本教程中采用。与MAE不同,RMSE对预测错误的惩罚更大。
这个问题的性能指标将是从第1天到第7天的每个交付时间的RMSE。
作为一种捷径,使用单个分数来总结模型的性能,以辅助模型选择,这可能是有用的。
可以使用的一个可能的评分是所有预测日的RMSE。
下面的evaluate_forecasts()函数将实现此行为,并返回基于多个七天预测的模型的性能。
# 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得分数组。
3.3 训练及测试集
我们将使用前三年的数据来训练预测模型,并使用最后一年的数据来评估模型。
给定数据集中的数据将被划分为标准周。这些星期从星期天开始,到星期六结束。
这是使用所选择的模型框架的一种现实而有用的方法,可以预测未来一周的电力消耗。它还有助于建模,在建模中,模型可以用来预测特定的一天(例如周三)或整个序列。
我们将把数据分成标准周,从测试数据集中向后工作。
数据的最后一年是2010年,2010年的第一个周日是1月3日。该数据将于2010年11月中旬结束,其中最近的一个周六是11月20日。这给出了46周的测试数据。
下面提供测试数据集的每日数据的第一行和最后一行,以供确认。
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
下面的函数split_dataset()将日常数据分解为训练和测试集,并将每个数据组织为标准周。
特定的行偏移量用于使用数据集的知识分割数据。然后,使用NumPy split()函数将分割的数据集组织为每周的数据。
# 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
我们可以通过加载日常数据集并打印来自火车和测试集的第一行和最后一行数据来测试这个函数,以确认它们符合上面的期望。完整的代码示例如下所示。
# 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周。
我们可以看到,训练和测试数据集的第一行和最后一行的总有功功率与我们为每个集合定义的标准周的界限指定的特定日期的数据相匹配。
(159, 7, 8)
3390.46 1309.2679999999998
(46, 7, 8)
2083.4539999999984 2197.006000000004
3.4 Walk-Forward验证
模型将使用一种称为Walk-Forward验证的方案进行评估。
这是需要模型进行一周预测的地方,然后将该周的实际数据提供给模型,以便可以将其用作对下一周进行预测的基础。这对于在实践中如何使用模型是现实的,并且对于允许模型使用最佳可用数据是有益的。
我们可以通过分离输入数据和输出/预测数据来说明这一点。
Input, Predict
[Week1] Week2
[Week1 + Week2] Week3
[Week1 + Week2 + Week3] Week4
...
下面提供了评估此数据集上的预测模型的前向验证方法evaluate_model()。
标准周格式的训练和测试数据集作为参数提供给函数。提供了一个额外的参数n_input,用于定义模型将用作输入的先前观察值的数量,以便进行预测。
调用了两个新函数:一个函数从名为build_model()的培训数据构建模型,另一个函数使用该模型为每个新标准周进行预测,称为forecast()。这些将在后面的小节中讨论。
我们正在研究神经网络,因此,它们通常训练速度较慢,但评估速度较快。这意味着模型的首选用法是在历史数据上构建它们一次,并使用它们来预测前进验证的每个步骤。模型在评估期间是静态的(即没有更新)。
这与其他训练更快的模型不同,在这些模型中,当新数据可用时,模型可以重新适应或更新前向验证的每个步骤。有了足够的资源,就可以用这种方式使用神经网络,但在本教程中我们不会这样做。
完整的evaluate_model()函数如下所示。
# 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