Machine Learning Mastery 深度学习时间序列教程(十五)
长短期记忆网络在时间序列预测中的适用性
原文:
machinelearningmastery.com/suitability-long-short-term-memory-networks-time-series-forecasting/
长短期记忆(LSTM)是一种循环神经网络,可以学习序列中项目之间的顺序依赖性。
LSTM 承诺能够学习在时间序列预测问题中做出预测所需的上下文,而不是预先指定和修复此上下文。
鉴于有希望,对于 LSTM 是否适合时间序列预测存在疑问。
在这篇文章中,我们将研究 LSTMs 在该技术的一些主要开发者的时间序列预测中的应用。
让我们开始吧。
关于长期短期记忆网络对时间序列预测的适用性 照片由 becosky ,保留一些权利。
LSTM 用于时间序列预测
我们将仔细研究一篇旨在探讨 LSTM 是否适合时间序列预测的论文。
该论文标题为“将 LSTM 应用于通过时间窗口方法预测的时间序列”(得到 PDF ,Gers,Eck 和 Schmidhuber,发表于 2001 年。
他们首先评论单变量时间序列预测问题实际上比传统上用于演示 LSTM 功能的问题类型更简单。
文献中发现的时间序列基准问题通常比 LSTM 已经解决的许多任务在概念上更简单。它们通常根本不需要 RNN,因为关于下一个事件的所有相关信息都是通过一个小时间窗内包含的一些最近事件来传达的。
本文重点研究了 LSTM 在两个复杂时间序列预测问题中的应用,并将 LSTM 的结果与其他类型的神经网络进行了对比。
该研究的重点是两个经典的时间序列问题:
麦基玻璃系列
这是根据微分方程计算的人为时间序列。
Mackey-Glass 系列的情节,取自 Schoarpedia
有关更多信息,请参阅:
混沌激光数据(A 组)
这是一系列来自圣达菲研究所的比赛。
集 A 定义为:
清洁的物理实验室实验。远红外激光器中的 1000 个波动点,大致由三个耦合非线性常微分方程描述。
混沌激光数据(A 组)的例子,取自时间序列的未来
有关更多信息,请参阅:
- 第二部分“竞争”时间序列的未来,1993。
自回归
使用自回归(AR)方法来模拟这些问题。
这意味着下一个时间步骤是作为一些过去(或滞后)观察的函数。
这是经典统计时间序列预测的常用方法。
LSTM 一次暴露于一个输入,没有固定的滞后变量集,如窗口多层感知机(MLP)。
有关时间序列 AR 的更多信息,请参阅帖子:
结果分析
一些更为突出的评论是为了回应 LSTM 对 Mackey-Glass 系列问题的糟糕结果。
首先,他们评论说增加网络的学习能力没有帮助:
增加内存块的数量并没有显着改善结果。
这可能需要进一步增加训练时期的数量。一堆 LSTM 也可能具有改进的结果。
他们评论说,为了在 Mackey-Glass 系列上做得好,LSTM 需要记住最近的过去观察结果,而 MLP 则明确地给出了这些数据。
AR-LSTM 方法的结果明显比时间窗方法的结果更差,例如使用 MLP。 AR-LSTM 网络无法访问过去作为其输入的一部分... [为了使 LSTM 做得好]需要记住过去的一个或两个事件,然后在覆盖相同的存储器单元之前使用该信息。
他们评论说,总的来说,这对 LSTM 和 RNN 构成了比 MLP 更大的挑战。
假设任何动态模型都需要来自 t-tau ...的所有输入,我们注意到 AR-RNN 必须存储从 t-tau 到 t 的所有输入并在适当的时间覆盖它们。这需要实现循环缓冲区,这是 RNN 难以模拟的结构。
同样,我不禁认为更大的隐藏层(更多的内存单元)和更深的网络(堆叠的 LSTM)更适合学习多个过去的观察。
他们后来总结了论文,并根据结果讨论了 LSTM 可能不适合时间序列预测的 AR 类型公式,至少当滞后观测值接近预测时间时。
鉴于 LSTM 在测试的单变量问题上的表现与 MLP 相比,这是一个公平的结论。
基于时间窗的 MLP 在某些时间序列预测基准上优于 LSTM 纯 AR 方法,仅通过查看一些最近的输入来解决。因此,LSTM 的特殊优势,即学习在很长的未知时间内记住单个事件,在这里是不必要的。
LSTM 学会了调整每个系列的基本振荡,但无法准确地跟踪信号。
它们确实强调了 LSTM 学习振荡行为的能力(例如周期或季节性)。
我们的结果建议仅在传统的基于时间窗口的方法必须失败的任务中使用 LSTM。
LSTM 跟踪混沌信号中的缓慢振荡的能力可适用于认知领域,例如语音和音乐中的节奏检测。
这很有趣,但可能没那么有用,因为这些模式通常会在预测之前尽可能明确地删除。然而,它可能突出了 LSTM 学习在非平稳序列背景下做出预测的可能性。
最后的话
那么,这一切意味着什么呢?
根据面值,我们可以得出结论,LSTM 不适用于基于 AR 的单变量时间序列预测。我们应首先转向具有固定窗口的 MLP,如果 MLP 无法获得良好结果,则仅转向 LSTM。
这听起来很公平。
在我们为时间序列预测注销 LSTM 之前,我会争论一些应该考虑的问题:
- 考虑更复杂的数据准备,例如至少缩放和平稳性。如果周期或趋势明显,则应将其移除,以便模型可以关注基础信号。话虽如此,LSTM 在非静止数据上表现良好或者比其他方法更好的能力是有趣的,但我希望与所需网络容量和训练的增加相称。
- 考虑使用较大的模型和分层模型(堆叠的 LSTM)来自动学习(或“记住”)更大的时间依赖性。较大的型号可以了解更多。
- 考虑将模型拟合更长时间,例如数千或数十万个时代,同时利用正规化技术。 LSTM 需要很长时间才能学习复杂的依赖关系。
我不会指出我们可以超越基于 AR 的模型;很明显,AR 模型是 LSTM 考虑和采用经典统计方法(如 ARIMA)和表现良好的神经网络(如窗口 MLP)的良好清洁试验场。
我相信 LSTM 应用于时间序列预测问题的巨大潜力和机会。
你同意吗? 请在下面的评论中告诉我。
时间序列预测问题的分类系统
原文:
machinelearningmastery.com/taxonomy-of-time-series-forecasting-problems/
当您遇到新的时间序列预测问题时,有许多事情需要考虑。
您所做的选择会直接影响项目的每个步骤,从测试工具的设计到评估预测模型,以及您正在处理的预测问题的基本难度。
通过处理有关时间序列预测问题的一系列问题,可以非常快速地缩小选项范围。通过考虑每个主题中的一些主题和问题,您可以缩小问题类型,测试工具,甚至为项目选择算法。
在这篇文章中,您将发现一个框架,您可以使用该框架快速了解和构建时间序列预测问题。
让我们开始吧。
时间序列预测问题的分类 摄影: Adam Meek ,保留一些权利。
框架概述
时间序列预测涉及在观测之间存在有序关系的数据上开发和使用预测模型。
您可以在此帖子中了解有关时间序列预测的更多信息:
在开始您的项目之前,您可以回答几个问题,并大大提高您对预测问题结构的理解,模型的结构要求以及如何评估它。
本文提出的框架分为七个部分;他们是:
- 输入与输出
- 内生与外生
- 非结构化与结构化
- 回归与分类
- 单变量与多变量
- 单步与多步
- 静态与动态
- 连续与不连续
我建议在开始任何时间序列预测项目之前完成此框架。
您的答案在第一时间可能不会很清晰,您可能需要在研究数据,领域以及与专家和利益相关方交谈时提出问题。
在了解更多信息时更新您的答案,因为它将帮助您保持正常运行,避免分心,并开发项目所需的实际模型。
1.输入与输出
通常,预测问题涉及使用过去的观察来预测或预测一个或多个可能的未来观察。
目标是猜测未来会发生什么。
当您需要做出预测时,考虑您可用于做出预测的数据以及您将来会猜测的内容至关重要。
我们可以总结一下,在进行单一预测时,模型的输入和输出是什么。
- 输入:提供给模型的历史数据,以便进行单一预测。
- 输出:预测或预测未来时间步长超出作为输入提供的数据。
输入数据不是用于训练模型的数据。我们还不是那个时候。它是用于进行一次预测的数据,例如销售数据的最后七天,用于预测下一天的销售数据。
定义模型的输入和输出会强制您考虑做出预测的确切或可能需要的内容。
在输入数据时,您可能无法具体说明。例如,您可能不知道是否需要一个或多个先前时间步骤来做出预测。但是,您将能够识别可用于做出预测的变量。
预测的输入和输出是什么?
2.内生与外生
输入数据可以进一步细分,以便更好地理解它与输出变量的关系。
如果输入变量受系统中其他变量的影响且输出变量依赖于它,则它是内生的。
在时间序列中,对输入变量的观察取决于彼此。例如,时间 t 的观察取决于 t-1 处的观察; t-1 可能取决于 t-2,依此类推。
如果输入变量独立于系统中的其他变量并且输出变量依赖于它,则它是外生变量。
简而言之,内生变量受系统中其他变量(包括它们自身)的影响,而外生变量则不被系统视为外部变量。
- 内生:受系统中其他变量影响且输出变量所依赖的输入变量。
- 外生:输入变量,不受系统中其他变量影响,输出变量所依赖的变量。
通常,时间序列预测问题具有内生变量(例如,输出是某些先前时间步长的函数)并且可能具有或可能不具有外生变量。
通常,考虑到对时间序列的强烈关注,外生变量会被忽略。明确地考虑两种变量类型可能有助于识别容易被忽视的外生数据,甚至可以改进模型的工程特征。
什么是内生和外生变量?
3.回归与分类
回归预测性建模问题是预测数量的问题。
数量是数值;例如价格,计数,数量等。您想要预测一个或多个未来数值的时间序列预测问题是回归类型预测性建模问题。
分类预测性建模问题是预测类别的问题。
类别是来自一组明确定义的标签的标签;例如{“hot”,“cold”},{“up”,“down”}和{“buy”,“sell”}是类别。
要对输入时间序列数据进行分类的时间序列预测问题是分类预测性建模问题。
- 回归:预测数量。
- 分类:分类为两个或多个标签之一。
您正在研究回归或分类预测性建模问题吗?
这些类型之间存在一定的灵活性。
例如,回归问题可以重新定义为分类,分类问题可以重新定义为回归。一些问题,如预测序数值,可以被定义为分类和回归。重构您的时间序列预测问题可能会简化它。
有哪些方法来构建时间序列预测问题?
4.非结构化与结构化
在时间序列中绘制每个变量并检查绘图以查找可能的模式非常有用。
单个变量的时间序列可能没有任何明显的模式。
我们可以把没有模式的系列想象成非结构化的,因为没有可辨别的时间依赖结构。
或者,时间序列可能具有明显的模式,例如趋势或季节性周期。
我们通常可以通过识别和删除数据中的明显结构来简化建模过程,例如增加趋势或重复循环。一些经典方法甚至允许您指定参数来直接处理这些系统结构。
- 非结构化:时间序列变量中没有明显的系统时间依赖模式。
- 结构化:时间序列变量中的系统时间依赖模式(例如趋势和/或季节性)。
时间序列变量是非结构化的还是结构化的?
5.单变量与多变量
随时间测量的单个变量被称为单变量时间序列。单变量意味着一个变量或一个变量。
随时间测量的多个变量被称为多变量时间序列:多个变量或多个变量。
- 单变量:随时间变化测量的一个变量。
- 多变量:随时间测量的多个变量。
您是在处理单变量或多变量时间序列问题吗?
考虑到关于投入和产出的这个问题可能会进一步加以区分。输入和输出之间的变量数可能不同,例如数据可能不对称。
例如,您可能有多个变量作为模型的输入,并且只对将其中一个变量预测为输出感兴趣。在这种情况下,模型中假设多个输入变量有助于预测单个输出变量。
- 单变量和多变量输入:随时间测量的一个或多个输入变量。
- 单变量和多变量输出:要预测的一个或多个输出变量。
6.单步与多步
需要预测下一个时间步长的预测问题称为一步预测模型。
而需要预测多个时间步长的预测问题称为多步预测模型。
预测到未来的时间步骤越多,问题就越具挑战性,因为每个预测时间步长的不确定性的复合性质。
- 一步:预测下一步。
- 多步:预测多个未来时间步骤。
您需要单步骤还是多步骤预测?
7.静态与动态
可以开发一次模型并重复使用它来做出预测。
鉴于模型未在预测之间更新或更改,我们可以将此模型视为静态模型。
相反,我们可能会在进行后续预测之前收到新的观察结果,这些预测可用于创建新模型或更新现有模型。我们可以考虑在每个预测之前开发一个新的或更新的模型作为动态问题。
例如,如果问题需要在未来一周的一周开始时做出预测,我们可能会收到本周末的真实观察结果,我们可以在下周预测之前更新模型。这将是一个动态模型。如果我们在本周末没有得到真实的观察结果,或者我们选择不重新拟合模型,那么这将是一个静态模型。
我们可能更喜欢动态模型,但是域的约束或所选算法的限制可能会产生使这种难以处理的约束。
- 静态。预测模型适合一次并用于做出预测。
- 动态。在每次预测之前,预测模型适合新的可用数据。
您需要静态或动态更新的模型吗?
8.连续与不连续
观察值随时间均匀的时间序列可以被描述为连续的。
许多时间序列问题都有连续的观察结果,例如每小时,每天,每月或每年观察一次。
观察结果随时间不均匀的时间序列可被描述为不连续的。
观察的不均匀性可能是由于缺失或损坏的值造成的。这也可能是问题的一个特征,其中观察仅偶尔可用或者以逐渐或逐渐间隔的时间间隔提供。
在非均匀观测的情况下,在拟合某些模型以使观察随时间均匀时可能需要特定的数据格式化。
- 连续。随着时间的推移,观察是统一的。
- 不连续。随着时间的推移,观察结果不一致。
您的观察结果是连续的还是不连续的?
进一步阅读
本节列出了一些可供进一步阅读的资源。
摘要
要查看,您可以询问有关您的问题的主题和问题如下:
- 输入与输出
- 预测的输入和输出是什么?
- 内生与外生
- 什么是内生和外生变量?
- 非结构化与结构化
- 时间序列变量是非结构化的还是结构化的?
- 回归与分类
- 您正在研究回归或分类预测性建模问题吗?
- 有哪些方法来构建时间序列预测问题?
- 单变量与多变量
- 您是在处理单变量或多变量时间序列问题吗?
- 单步与多步
- 您需要单步骤还是多步骤预测?
- 静态与动态
- 您需要静态或动态更新的模型吗?
- 连续与不连续
- 您的观察结果是连续的还是不连续的?
您是否发现此框架对您的时间序列预测问题有用? 请在下面的评论中告诉我。
你有任何问题吗? 在下面的评论中提出您的问题,我会尽力回答。
Python 中使用长短期记忆网络的时间序列预测
原文:
machinelearningmastery.com/time-series-forecasting-long-short-term-memory-network-python/
长期短期记忆复发神经网络有望学习长序列的观察。
它似乎是时间序列预测的完美匹配,事实上,它可能是。
在本教程中,您将了解如何为单步单变量时间序列预测问题开发 LSTM 预测模型。
完成本教程后,您将了解:
- 如何为预测问题制定绩效基准。
- 如何为一步时间序列预测设计一个强大的测试工具。
- 如何为时间序列预测准备数据,开发和评估 LSTM 循环神经网络。
让我们开始吧。
- 2017 年 5 月更新:修正了 invert_scale()函数中的错误,谢谢 Max。
使用 Python 中的长短期记忆网络进行时间序列预测 照片由 Matt MacGillivray 拍摄,保留一些权利。
教程概述
这是一个很大的话题,我们将涉及很多方面。带子。
本教程分为 9 个部分;他们是:
- 洗发水销售数据集
- 测试设置
- 持久性模型预测
- LSTM 数据准备
- LSTM 模型开发
- LSTM 预测
- 完整的 LSTM 示例
- 制定稳健的结果
- 教程扩展
Python 环境
本教程假定您已安装 Python SciPy 环境。您可以在本教程中使用 Python 2 或 3。
您必须安装带有 TensorFlow 或 Theano 后端的 Keras(2.0 或更高版本)。
本教程还假设您安装了 scikit-learn,Pandas,NumPy 和 Matplotlib。
如果您需要有关环境的帮助,请参阅此帖子:
洗发水销售数据集
该数据集描述了 3 年期间每月洗发水的销售数量。
单位是销售计数,有 36 个观察。原始数据集归功于 Makridakis,Wheelwright 和 Hyndman(1998)。
更新:这是数据集的直接链接,可以使用: shampoo.csv
将数据集下载到当前工作目录,名称为“ shampoo-sales.csv ”。请注意,您可能需要删除 DataMarket 添加的页脚信息。
下面的示例加载并创建已加载数据集的图。
# load and plot dataset
from pandas import read_csv
from pandas import datetime
from matplotlib import pyplot
# load dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# summarize first few rows
print(series.head())
# line plot
series.plot()
pyplot.show()
运行该示例将数据集作为 Pandas Series 加载并打印前 5 行。
Month
1901-01-01 266.0
1901-02-01 145.9
1901-03-01 183.1
1901-04-01 119.3
1901-05-01 180.3
Name: Sales, dtype: float64
然后创建该系列的线图,显示明显的增加趋势。
每月洗发水销售数据集的线图
实验测试设置
我们将 Shampoo Sales 数据集分为两部分:训练和测试集。
前两年的数据将用于训练数据集,剩余的一年数据将用于测试集。
例如:
# split data into train and test
X = series.values
train, test = X[0:-12], X[-12:]
将使用训练数据集开发模型,并对测试数据集做出预测。
将使用滚动预测场景,也称为前进模型验证。
测试数据集的每个时间步骤将一次一个地走。将使用模型对时间步长做出预测,然后将获取测试集的实际预期值,并使其可用于下一时间步的预测模型。
例如:
# walk-forward validation
history = [x for x in train]
predictions = list()
for i in range(len(test)):
# make prediction...
这模仿了一个真实世界的场景,每个月都会有新的洗发水销售观察结果,并用于下个月的预测。
最后,将收集测试数据集的所有预测,并计算错误分数以总结模型的技能。将使用均方根误差(RMSE),因为它会对大错误进行处罚,并产生与预测数据相同的分数,即每月洗发水销售额。
例如:
from sklearn.metrics import mean_squared_error
rmse = sqrt(mean_squared_error(test, predictions))
print('RMSE: %.3f' % rmse)
持久性模型预测
具有线性增长趋势的时间序列的良好基线预测是持久性预测。
持续性预测是使用先前时间步骤(t-1)的观察来预测当前时间步骤(t)的观察。
我们可以通过从训练数据和前进验证累积的历史记录中进行最后一次观察并使用它来预测当前时间步长来实现这一点。
例如:
# make prediction
yhat = history[-1]
我们将在数组中累积所有预测,以便可以直接将它们与测试数据集进行比较。
下面列出了 Shampoo Sales 数据集上的持久性预测模型的完整示例。
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from math import sqrt
from matplotlib import pyplot
# load dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# split data into train and test
X = series.values
train, test = X[0:-12], X[-12:]
# walk-forward validation
history = [x for x in train]
predictions = list()
for i in range(len(test)):
# make prediction
predictions.append(history[-1])
# observation
history.append(test[i])
# report performance
rmse = sqrt(mean_squared_error(test, predictions))
print('RMSE: %.3f' % rmse)
# line plot of observed vs predicted
pyplot.plot(test)
pyplot.plot(predictions)
pyplot.show()
运行该示例将打印出大约 136 个月洗发水销售额的 RMSE,用于测试数据集的预测。
RMSE: 136.761
还创建了测试数据集(蓝色)与预测值(橙色)的线图,显示了上下文中的持久性模型预测。
洗发水销售数据集的观察与预测持续性预测
有关时间序列预测的持久性模型的更多信息,请参阅此帖子:
现在我们已经在数据集上有了表现基准,我们可以开始为数据开发 LSTM 模型。
LSTM 数据准备
在我们将 LSTM 模型拟合到数据集之前,我们必须转换数据。
本节分为三个步骤:
- 将时间序列转换为监督学习问题
- 转换时间序列数据,使其静止不动。
- 将观察结果转换为具有特定比例。
将时间序列转换为监督学习
Keras 中的 LSTM 模型假设您的数据分为输入(X)和输出(y)组件。
对于时间序列问题,我们可以通过使用从最后时间步骤(t-1)作为输入的观察和在当前时间步骤(t)的观察作为输出来实现这一点。
我们可以使用 Pandas 中的 shift()函数来实现这一点,它会将一系列中的所有值按下指定的数字位置。我们需要移位 1 个位置,这将成为输入变量。现在的时间序列将是输出变量。
然后,我们可以将这两个系列连接在一起,为监督学习创建一个 DataFrame。推下的系列将在顶部有一个新的位置,没有任何价值。在此位置将使用 NaN(非数字)值。我们将这些 NaN 值替换为 0 值,LSTM 模型必须将其作为“系列的开头”或“我这里没有数据”来学习,因为没有观察到该数据集零销售的月份。
下面的代码定义了一个名为timeseries_to_supervised()的辅助函数。它需要一个原始时间序列数据的 NumPy 数组和一个滞后或移位序列的数量来创建和用作输入。
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
df.fillna(0, inplace=True)
return df
我们可以使用我们加载的 Shampoo Sales 数据集测试此功能,并将其转换为监督学习问题。
from pandas import read_csv
from pandas import datetime
from pandas import DataFrame
from pandas import concat
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
df.fillna(0, inplace=True)
return df
# load dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# transform to supervised learning
X = series.values
supervised = timeseries_to_supervised(X, 1)
print(supervised.head())
运行该示例将打印新监督学习问题的前 5 行。
0 0
0 0.000000 266.000000
1 266.000000 145.899994
2 145.899994 183.100006
3 183.100006 119.300003
4 119.300003 180.300003
有关将时间序列问题转换为监督学习问题的更多信息,请参阅帖子:
将时间序列转换为静止
Shampoo Sales 数据集不是固定的。
这意味着数据中的结构取决于时间。具体而言,数据呈上升趋势。
固定数据更易于建模,很可能会产生更加熟练的预测。
可以从观察中移除趋势,然后将其添加回预测以将预测返回到原始比例并计算可比较的误差分数。
消除趋势的标准方法是区分数据。即,从当前观察(t)中减去前一时间步骤(t-1)的观察结果。这消除了趋势,我们留下了差异序列,或者从一个时间步到下一个步骤的观察结果的变化。
我们可以使用 pandas 中的 diff()函数自动实现。或者,我们可以获得更精细的控制并编写我们自己的函数来执行此操作,这在这种情况下是灵活的首选。
下面是一个名为difference()的函数,用于计算差分序列。请注意,系列中的第一个观察值被跳过,因为没有先前的观察值来计算差值。
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
我们还需要反转此过程,以便将差异系列的预测恢复到原始比例。
以下函数称为 inverse_difference(),反转此操作。
# invert differenced value
def inverse_difference(history, yhat, interval=1):
return yhat + history[-interval]
我们可以通过对整个系列进行差分来测试这些函数,然后将其返回到原始比例,如下所示:
from pandas import read_csv
from pandas import datetime
from pandas import Series
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
# invert differenced value
def inverse_difference(history, yhat, interval=1):
return yhat + history[-interval]
# load dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
print(series.head())
# transform to be stationary
differenced = difference(series, 1)
print(differenced.head())
# invert transform
inverted = list()
for i in range(len(differenced)):
value = inverse_difference(series, differenced[i], len(series)-i)
inverted.append(value)
inverted = Series(inverted)
print(inverted.head())
运行该示例打印加载数据的前 5 行,然后打印差异系列的前 5 行,最后打印差异操作的前 5 行。
请注意,原始数据集中的第一个观察值已从反向差异数据中删除。除此之外,最后一组数据与第一组数据匹配。
Month
1901-01-01 266.0
1901-02-01 145.9
1901-03-01 183.1
1901-04-01 119.3
1901-05-01 180.3
Name: Sales, dtype: float64
0 -120.1
1 37.2
2 -63.8
3 61.0
4 -11.8
dtype: float64
0 145.9
1 183.1
2 119.3
3 180.3
4 168.5
dtype: float64
有关使时间序列固定和差分的更多信息,请参阅帖子:
将时间序列转换为比例
与其他神经网络一样,LSTM 期望数据在网络使用的激活函数的范围内。
LSTM 的默认激活函数是双曲正切(tanh),它输出介于-1 和 1 之间的值。这是时间序列数据的首选范围。
为了使实验公平,必须在训练数据集上计算缩放系数(最小和最大)值,并应用于缩放测试数据集和任何预测。这是为了避免使用测试数据集中的知识污染实验,这可能会给模型带来小的优势。
我们可以使用 MinMaxScaler 类将数据集转换为[-1,1]范围。与其他 scikit-learn 变换类一样,它需要以行和列的矩阵格式提供数据。因此,我们必须在转换之前重塑我们的 NumPy 数组。
例如:
# transform scale
X = series.values
X = X.reshape(len(X), 1)
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(X)
scaled_X = scaler.transform(X)
同样,我们必须反转预测的比例,以将值返回到原始比例,以便可以解释结果并计算可比较的误差分数。
# invert transform
inverted_X = scaler.inverse_transform(scaled_X)
将所有这些放在一起,下面的例子改变了 Shampoo Sales 数据的规模。
from pandas import read_csv
from pandas import datetime
from pandas import Series
from sklearn.preprocessing import MinMaxScaler
# load dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
print(series.head())
# transform scale
X = series.values
X = X.reshape(len(X), 1)
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(X)
scaled_X = scaler.transform(X)
scaled_series = Series(scaled_X[:, 0])
print(scaled_series.head())
# invert transform
inverted_X = scaler.inverse_transform(scaled_X)
inverted_series = Series(inverted_X[:, 0])
print(inverted_series.head())
运行该示例首先打印加载数据的前 5 行,然后打印缩放数据的前 5 行,然后是缩放比例变换的前 5 行,与原始数据匹配。
Month
1901-01-01 266.0
1901-02-01 145.9
1901-03-01 183.1
1901-04-01 119.3
1901-05-01 180.3
Name: Sales, dtype: float64
0 -0.478585
1 -0.905456
2 -0.773236
3 -1.000000
4 -0.783188
dtype: float64
0 266.0
1 145.9
2 183.1
3 119.3
4 180.3
dtype: float64
现在我们知道如何为 LSTM 网络准备数据,我们可以开始开发我们的模型了。
LSTM 模型开发
长短期记忆网络(LSTM)是一种循环神经网络(RNN)。
这种类型的网络的一个好处是它可以学习和记住长序列,并且不依赖于预先指定的窗口滞后观察作为输入。
在 Keras 中,这被称为有状态,并且涉及在定义 LSTM 层时将“_ 有状态 _”参数设置为“True”。
默认情况下,Keras 中的 LSTM 层维护一批数据之间的状态。一批数据是来自训练数据集的固定大小的行数,用于定义在更新网络权重之前要处理的模式数。默认情况下,批次之间的 LSTM 层中的状态被清除,因此我们必须使 LSTM 成为有状态。通过调用reset_states()函数,这可以精确控制 LSTM 层的状态何时被清除。
LSTM 层期望输入位于具有以下尺寸的矩阵中:[_ 样本,时间步长,特征 _]。
- 样本:这些是来自域的独立观察,通常是数据行。
- 时间步长:这些是给定观察的给定变量的单独时间步长。
- 特征:这些是在观察时观察到的单独测量。
我们在如何为网络构建 Shampoo Sales 数据集方面具有一定的灵活性。我们将保持简单并构建问题,因为原始序列中的每个步骤都是一个单独的样本,具有一个时间步长和一个特征。
鉴于训练数据集定义为 X 输入和 y 输出,必须将其重新整形为 Samples / TimeSteps / Features 格式,例如:
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
必须在 LSTM 层中使用“batch_input_shape”参数指定输入数据的形状作为元组,该元组指定读取每批的预期观察数,时间步数和数量特征。
批量大小通常远小于样本总数。它与时期的数量一起定义了网络学习数据的速度(权重更新的频率)。
定义 LSTM 层的最后一个导入参数是神经元的数量,也称为内存单元或块的数量。这是一个相当简单的问题,1 到 5 之间的数字就足够了。
下面的行创建了一个 LSTM 隐藏层,它还通过“batch_input_shape”参数指定输入层的期望值。
layer = LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True)
网络需要输出层中的单个神经元具有线性激活,以预测下一时间步骤的洗发水销售数量。
一旦指定了网络,就必须使用后端数学库(例如 TensorFlow 或 Theano)将其编译成有效的符号表示。
在编译网络时,我们必须指定一个损失函数和优化算法。我们将使用“mean_squared_error”作为损失函数,因为它与我们感兴趣的 RMSE 紧密匹配,以及有效的 ADAM 优化算法。
使用 Sequential Keras API 定义网络,下面的代码片段创建并编译网络。
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
编译后,它可以适合训练数据。由于网络是有状态的,我们必须控制何时重置内部状态。因此,我们必须在所需数量的时期内一次手动管理一个时期的训练过程。
默认情况下,一个迭代内的样本在暴露给网络之前进行混洗。同样,这对于 LSTM 来说是不合需要的,因为我们希望网络在整个观察序列中学习时建立状态。我们可以通过将“shuffle”设置为“False”来禁用样本的混洗。
此外,默认情况下,网络会在每个时代结束时报告有关模型学习进度和技能的大量调试信息。我们可以通过将“verbose”参数设置为“0”的级别来禁用它。
然后我们可以在训练时期结束时重置内部状态,为下一次训练迭代做好准备。
下面是一个手动使网络适合训练数据的循环。
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
综上所述,我们可以定义一个名为fit_lstm()的函数来训练并返回一个 LSTM 模型。作为参数,它将训练数据集置于监督学习格式,批量大小,多个时期和许多神经元中。
def fit_lstm(train, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
return model
batch_size 必须设置为 1.这是因为它必须是训练和测试数据集大小的因子。
模型上的predict()函数也受批量大小的限制;它必须设置为 1,因为我们有兴趣对测试数据进行一步预测。
我们不会在本教程中调整网络参数;相反,我们将使用以下配置,通过一些试验和错误找到:
- 批量大小:1
- 时代:3000
- 神经元:4
作为本教程的扩展,您可能希望探索不同的模型参数,看看是否可以提高表现。
- 更新:考虑尝试 1500 个迭代和 1 个神经元,表现可能更好!
接下来,我们将了解如何使用适合的 LSTM 模型进行一步预测。
LSTM 预测
一旦 LSTM 模型适合训练数据,它就可用于做出预测。
我们再次具有一定的灵活性。我们可以决定在所有训练数据上拟合一次模型,然后从测试数据中一次预测每个新时间步骤(我们称之为固定方法),或者我们可以重新拟合模型或更新模型的每一步作为测试数据的新观察结果的模型都可用(我们称之为动态方法)。
在本教程中,我们将采用固定方法来实现其简单性,但我们希望动态方法能够带来更好的模型技能。
为了做出预测,我们可以在模型上调用predict()函数。这需要 3D NumPy 数组输入作为参数。在这种情况下,它将是一个值的数组,即前一时间步的观察值。
predict()函数返回一个预测数组,每个输入行一个。因为我们提供单个输入,所以输出将是具有一个值的 2D NumPy 数组。
我们可以在下面列出的名为forecast()的函数中捕获此行为。给定拟合模型,在拟合模型时使用的批量大小(例如 1)和测试数据中的行,该函数将从测试行中分离输入数据,重新整形,并将预测作为单个返回浮点值。
def forecast(model, batch_size, row):
X = row[0:-1]
X = X.reshape(1, 1, len(X))
yhat = model.predict(X, batch_size=batch_size)
return yhat[0,0]
在训练期间,内部状态在每个时期之后被重置。在预测时,我们不希望重置预测之间的内部状态。事实上,我们希望模型在我们预测测试数据集中的每个时间步骤时建立状态。
这就提出了一个问题,即在预测测试数据集之前,网络的初始状态是什么。
在本教程中,我们将通过对训练数据集中的所有样本做出预测来播种状态。从理论上讲,应该建立内部状态,以便预测下一个时间步骤。
我们现在拥有适合 Shampoo Sales 数据集的 LSTM 网络模型并评估其表现的所有部分。
在下一节中,我们将把所有这些部分放在一起。
完整的 LSTM 示例
在本节中,我们将 LSTM 适用于 Shampoo Sales 数据集并评估模型。
这将涉及将前面部分中的所有元素汇总在一起。其中有很多,所以让我们回顾一下:
- 从 CSV 文件加载数据集。
- 转换数据集以使其适用于 LSTM 模型,包括:
- 将数据转换为监督学习问题。
- 将数据转换为静止。
- 转换数据使其具有-1 到 1 的比例。
- 将有状态 LSTM 网络模型拟合到训练数据。
- 评估测试数据上的静态 LSTM 模型。
- 报告预测的表现。
有关示例的一些注意事项:
- 为简洁起见,缩放和反缩放行为已移至函数
scale()和 invert_scale()。 - 使用缩放器在训练数据上的拟合来缩放测试数据,这是确保测试数据的最小值/最大值不影响模型所需的。
- 调整数据变换的顺序以便于首先使数据静止,然后是监督学习问题,然后进行缩放。
- 为了方便起见,在分成训练和测试集之前对整个数据集执行差分。我们可以在前进验证过程中轻松收集观察结果,并在我们前进时区分它们。为了便于阅读,我决定反对它。
下面列出了完整的示例。
from pandas import DataFrame
from pandas import Series
from pandas import concat
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sqrt
from matplotlib import pyplot
import numpy
# date-time parsing function for loading the dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
df.fillna(0, inplace=True)
return df
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
# invert differenced value
def inverse_difference(history, yhat, interval=1):
return yhat + history[-interval]
# scale train and test data to [-1, 1]
def scale(train, test):
# fit scaler
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(train)
# transform train
train = train.reshape(train.shape[0], train.shape[1])
train_scaled = scaler.transform(train)
# transform test
test = test.reshape(test.shape[0], test.shape[1])
test_scaled = scaler.transform(test)
return scaler, train_scaled, test_scaled
# inverse scaling for a forecasted value
def invert_scale(scaler, X, value):
new_row = [x for x in X] + [value]
array = numpy.array(new_row)
array = array.reshape(1, len(array))
inverted = scaler.inverse_transform(array)
return inverted[0, -1]
# fit an LSTM network to training data
def fit_lstm(train, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
return model
# make a one-step forecast
def forecast_lstm(model, batch_size, X):
X = X.reshape(1, 1, len(X))
yhat = model.predict(X, batch_size=batch_size)
return yhat[0,0]
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, 1)
supervised_values = supervised.values
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# fit the model
lstm_model = fit_lstm(train_scaled, 1, 3000, 4)
# forecast the entire training dataset to build up state for forecasting
train_reshaped = train_scaled[:, 0].reshape(len(train_scaled), 1, 1)
lstm_model.predict(train_reshaped, batch_size=1)
# walk-forward validation on the test data
predictions = list()
for i in range(len(test_scaled)):
# make one-step forecast
X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
yhat = forecast_lstm(lstm_model, 1, X)
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
expected = raw_values[len(train) + i + 1]
print('Month=%d, Predicted=%f, Expected=%f' % (i+1, yhat, expected))
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('Test RMSE: %.3f' % rmse)
# line plot of observed vs predicted
pyplot.plot(raw_values[-12:])
pyplot.plot(predictions)
pyplot.show()
运行该示例将在测试数据集中打印 12 个月中每个月的预期值和预测值。
该示例还打印所有预测的 RMSE。该模型显示每月洗发水销售量为 71.721 的 RMSE,优于达到 136.761 洗发水销售 RMSE 的持久性模型。
随机数用于播种 LSTM,因此,您可能会从模型的单次运行中获得不同的结果。我们将在下一节进一步讨论这个问题。
Month=1, Predicted=351.582196, Expected=339.700000
Month=2, Predicted=432.169667, Expected=440.400000
Month=3, Predicted=378.064505, Expected=315.900000
Month=4, Predicted=441.370077, Expected=439.300000
Month=5, Predicted=446.872627, Expected=401.300000
Month=6, Predicted=514.021244, Expected=437.400000
Month=7, Predicted=525.608903, Expected=575.500000
Month=8, Predicted=473.072365, Expected=407.600000
Month=9, Predicted=523.126979, Expected=682.000000
Month=10, Predicted=592.274106, Expected=475.300000
Month=11, Predicted=589.299863, Expected=581.300000
Month=12, Predicted=584.149152, Expected=646.900000
Test RMSE: 71.721
还创建了测试数据(蓝色)与预测值(橙色)的线图,为模型技能提供了上下文。
LSTM 预测和预期值的线图
作为 afternote,您可以进行快速实验,以建立您对测试工具以及所有变换和逆变换的信任。
在前进验证中注释掉适合 LSTM 模型的行:
yhat = forecast_lstm(lstm_model, 1, X)
并将其替换为以下内容:
yhat = y
这应该产生具有完美技能的模型(例如,将预期结果预测为模型输出的模型)。
结果应如下所示,表明如果 LSTM 模型能够完美地预测该系列,则逆变换和误差计算将正确显示。
Month=1, Predicted=339.700000, Expected=339.700000
Month=2, Predicted=440.400000, Expected=440.400000
Month=3, Predicted=315.900000, Expected=315.900000
Month=4, Predicted=439.300000, Expected=439.300000
Month=5, Predicted=401.300000, Expected=401.300000
Month=6, Predicted=437.400000, Expected=437.400000
Month=7, Predicted=575.500000, Expected=575.500000
Month=8, Predicted=407.600000, Expected=407.600000
Month=9, Predicted=682.000000, Expected=682.000000
Month=10, Predicted=475.300000, Expected=475.300000
Month=11, Predicted=581.300000, Expected=581.300000
Month=12, Predicted=646.900000, Expected=646.900000
Test RMSE: 0.000
制定稳健的结果
神经网络的一个难点是它们在不同的起始条件下给出不同的结果。
一种方法可能是修复 Keras 使用的随机数种子,以确保结果可重复。另一种方法是使用不同的实验设置来控制随机初始条件。
有关机器学习中随机性的更多信息,请参阅帖子:
我们可以多次重复上一节中的实验,然后将平均 RMSE 作为预期配置在平均看不见的数据上的表现的指示。
这通常称为多次重复或多次重启。
我们可以在固定数量的重复循环中包装模型拟合和前进验证。每次迭代都可以记录运行的 RMSE。然后我们可以总结 RMSE 分数的分布。
# repeat experiment
repeats = 30
error_scores = list()
for r in range(repeats):
# fit the model
lstm_model = fit_lstm(train_scaled, 1, 3000, 4)
# forecast the entire training dataset to build up state for forecasting
train_reshaped = train_scaled[:, 0].reshape(len(train_scaled), 1, 1)
lstm_model.predict(train_reshaped, batch_size=1)
# walk-forward validation on the test data
predictions = list()
for i in range(len(test_scaled)):
# make one-step forecast
X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
yhat = forecast_lstm(lstm_model, 1, X)
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
数据准备与以前相同。
我们将使用 30 次重复,因为这足以提供 RMSE 分数的良好分布。
下面列出了完整的示例。
from pandas import DataFrame
from pandas import Series
from pandas import concat
from pandas import read_csv
from pandas import datetime
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from math import sqrt
from matplotlib import pyplot
import numpy
# date-time parsing function for loading the dataset
def parser(x):
return datetime.strptime('190'+x, '%Y-%m')
# frame a sequence as a supervised learning problem
def timeseries_to_supervised(data, lag=1):
df = DataFrame(data)
columns = [df.shift(i) for i in range(1, lag+1)]
columns.append(df)
df = concat(columns, axis=1)
df.fillna(0, inplace=True)
return df
# create a differenced series
def difference(dataset, interval=1):
diff = list()
for i in range(interval, len(dataset)):
value = dataset[i] - dataset[i - interval]
diff.append(value)
return Series(diff)
# invert differenced value
def inverse_difference(history, yhat, interval=1):
return yhat + history[-interval]
# scale train and test data to [-1, 1]
def scale(train, test):
# fit scaler
scaler = MinMaxScaler(feature_range=(-1, 1))
scaler = scaler.fit(train)
# transform train
train = train.reshape(train.shape[0], train.shape[1])
train_scaled = scaler.transform(train)
# transform test
test = test.reshape(test.shape[0], test.shape[1])
test_scaled = scaler.transform(test)
return scaler, train_scaled, test_scaled
# inverse scaling for a forecasted value
def invert_scale(scaler, X, value):
new_row = [x for x in X] + [value]
array = numpy.array(new_row)
array = array.reshape(1, len(array))
inverted = scaler.inverse_transform(array)
return inverted[0, -1]
# fit an LSTM network to training data
def fit_lstm(train, batch_size, nb_epoch, neurons):
X, y = train[:, 0:-1], train[:, -1]
X = X.reshape(X.shape[0], 1, X.shape[1])
model = Sequential()
model.add(LSTM(neurons, batch_input_shape=(batch_size, X.shape[1], X.shape[2]), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(nb_epoch):
model.fit(X, y, epochs=1, batch_size=batch_size, verbose=0, shuffle=False)
model.reset_states()
return model
# make a one-step forecast
def forecast_lstm(model, batch_size, X):
X = X.reshape(1, 1, len(X))
yhat = model.predict(X, batch_size=batch_size)
return yhat[0,0]
# load dataset
series = read_csv('shampoo-sales.csv', header=0, parse_dates=[0], index_col=0, squeeze=True, date_parser=parser)
# transform data to be stationary
raw_values = series.values
diff_values = difference(raw_values, 1)
# transform data to be supervised learning
supervised = timeseries_to_supervised(diff_values, 1)
supervised_values = supervised.values
# split data into train and test-sets
train, test = supervised_values[0:-12], supervised_values[-12:]
# transform the scale of the data
scaler, train_scaled, test_scaled = scale(train, test)
# repeat experiment
repeats = 30
error_scores = list()
for r in range(repeats):
# fit the model
lstm_model = fit_lstm(train_scaled, 1, 3000, 4)
# forecast the entire training dataset to build up state for forecasting
train_reshaped = train_scaled[:, 0].reshape(len(train_scaled), 1, 1)
lstm_model.predict(train_reshaped, batch_size=1)
# walk-forward validation on the test data
predictions = list()
for i in range(len(test_scaled)):
# make one-step forecast
X, y = test_scaled[i, 0:-1], test_scaled[i, -1]
yhat = forecast_lstm(lstm_model, 1, X)
# invert scaling
yhat = invert_scale(scaler, X, yhat)
# invert differencing
yhat = inverse_difference(raw_values, yhat, len(test_scaled)+1-i)
# store forecast
predictions.append(yhat)
# report performance
rmse = sqrt(mean_squared_error(raw_values[-12:], predictions))
print('%d) Test RMSE: %.3f' % (r+1, rmse))
error_scores.append(rmse)
# summarize results
results = DataFrame()
results['rmse'] = error_scores
print(results.describe())
results.boxplot()
pyplot.show()
运行该示例每次重复打印 RMSE 分数。运行结束提供收集的 RMSE 分数的摘要统计。
我们可以看到平均值和标准差 RMSE 分数分别为 138.491905 和 46.313783 月洗发水销售额。
这是一个非常有用的结果,因为它表明上面报告的结果可能是一个统计上的侥幸。实验表明,该模型可能与持久性模型一样好(136.761),如果不是稍差的话。
这表明,至少需要进一步的模型调整。
1) Test RMSE: 136.191
2) Test RMSE: 169.693
3) Test RMSE: 176.553
4) Test RMSE: 198.954
5) Test RMSE: 148.960
6) Test RMSE: 103.744
7) Test RMSE: 164.344
8) Test RMSE: 108.829
9) Test RMSE: 232.282
10) Test RMSE: 110.824
11) Test RMSE: 163.741
12) Test RMSE: 111.535
13) Test RMSE: 118.324
14) Test RMSE: 107.486
15) Test RMSE: 97.719
16) Test RMSE: 87.817
17) Test RMSE: 92.920
18) Test RMSE: 112.528
19) Test RMSE: 131.687
20) Test RMSE: 92.343
21) Test RMSE: 173.249
22) Test RMSE: 182.336
23) Test RMSE: 101.477
24) Test RMSE: 108.171
25) Test RMSE: 135.880
26) Test RMSE: 254.507
27) Test RMSE: 87.198
28) Test RMSE: 122.588
29) Test RMSE: 228.449
30) Test RMSE: 94.427
rmse
count 30.000000
mean 138.491905
std 46.313783
min 87.198493
25% 104.679391
50% 120.456233
75% 168.356040
max 254.507272
根据下面显示的分布创建一个盒子和胡须图。这将捕获数据的中间以及范围和异常值结果。
LSTM 重复实验箱和晶须图
这是一个实验设置,可用于比较 LSTM 模型的一个配置或设置到另一个配置。
教程扩展
我们可能会考虑本教程的许多扩展。
也许您可以自己探索其中一些,并在下面的评论中发布您的发现。
- 多步预测。可以改变实验设置以预测下一个
n- 时间步骤而不是下一个单一时间步骤。这也将允许更大的批量和更快的训练。请注意,我们基本上在本教程中执行了一种 12 步一步预测,因为模型未更新,尽管有新的观察结果并且可用作输入变量。 - 调谐 LSTM 模型。该模型没有调整;相反,配置被发现有一些快速的试验和错误。我相信通过至少调整神经元的数量和训练时期的数量可以获得更好的结果。我还认为通过回调提前停止可能在训练期间很有用。
- 种子状态实验。目前尚不清楚在预测之前通过预测所有训练数据来播种系统是否有益。理论上这似乎是一个好主意,但这需要得到证明。此外,也许在预测之前播种模型的其他方法也是有益的。
- 更新模型。可以在前进验证的每个时间步骤中更新模型。需要进行实验以确定从零开始重新修改模型是否更好,或者使用包括新样本在内的更多训练时期更新权重。
- 输入时间步。 LSTM 输入支持样本的多个时间步长。需要进行实验以查看是否包括滞后观察作为时间步骤提供任何益处。
- 输入延迟功能。可以包括滞后观察作为输入特征。需要进行实验以查看包含滞后特征是否提供任何益处,与 AR(k)线性模型不同。
- 输入错误系列。可以构造误差序列(来自持久性模型的预测误差)并且用作附加输入特征,与 MA(k)线性模型不同。需要进行实验以确定这是否会带来任何好处。
- 学习非固定。 LSTM 网络可能能够了解数据中的趋势并做出合理的预测。需要进行实验以了解 LSTM 是否可以学习和有效预测数据中剩余的时间依赖结构,如趋势和季节性。
- 对比无状态。本教程中使用了有状态 LSTM。应将结果与无状态 LSTM 配置进行比较。
- 统计学意义。可以进一步扩展多重复实验方案以包括统计显着性检验,以证明具有不同构型的 RMSE 结果群体之间的差异是否具有统计学显着性。
摘要
在本教程中,您了解了如何为时间序列预测开发 LSTM 模型。
具体来说,你学到了:
- 如何准备用于开发 LSTM 模型的时间序列数据。
- 如何开发 LSTM 模型进行时间序列预测。
- 如何使用强大的测试工具评估 LSTM 模型。
你能得到更好的结果吗? 在下面的评论中分享您的发现。
基于 Python 和 Keras 的使用 LSTM 循环神经网络的时间序列预测
原文:
machinelearningmastery.com/time-series-prediction-lstm-recurrent-neural-networks-python-keras/
时间序列预测问题是一种难以预测的建模问题。
与回归预测性建模不同,时间序列还增加了输入变量之间序列依赖性的复杂性。
设计用于处理序列依赖性的强大类型的神经网络称为循环神经网络。长短期记忆网络或 LSTM 网络是一种用于深度学习的循环神经网络,因为可以成功训练非常大的架构。
在本文中,您将了解如何使用 Keras 深度学习库在 Python 中开发 LSTM 网络,以解决演示时间序列预测问题。
完成本教程后,您将了解如何为您自己的时间序列预测问题和其他更常见的序列问题实现和开发 LSTM 网络。你会知道:
- 关于国际航空公司乘客时间序列预测问题。
- 如何开发 LSTM 网络用于基于回归,窗口和时间步长的时间序列预测问题的框架。
- 如何使用 LSTM 网络开发和预测,这些网络在很长的序列中维护状态(内存)。
在本教程中,我们将针对标准时间序列预测问题开发许多 LSTM。 LSTM 网络的问题和所选配置仅用于演示目的,仅用于优化。
这些示例将向您展示如何为时间序列预测性建模问题开发自己的不同结构的 LSTM 网络。
让我们开始吧。
- 2016 年 10 月更新:在每个示例中计算 RMSE 的方式出错。报告的 RMSE 是完全错误的。现在,RMSE 直接根据预测计算,RMSE 和预测图都以原始数据集为单位。使用 Keras 1.1.0,TensorFlow 0.10.0 和 scikit-learn v0.18 评估模型。感谢所有指出这个问题的人,以及 Philip O'Brien 帮助指出修复的问题。
- 2017 年 3 月更新:更新了 Keras 2.0.2,TensorFlow 1.0.1 和 Theano 0.9.0 的示例。
- 2017 年 4 月更新:有关时间序列预测的更完整和更好解释的 LSTM 教程,请参阅 Python 中的长期短期记忆网络时间序列预测。
更新了 LSTM 时间序列预测帖子:
这篇文章中的例子非常陈旧,我有更好的例子可用于在时间序列中使用 LSTM,请参阅:
时间序列预测与 LSTM 循环神经网络在 Python 与 Keras 照片由 Margaux-Marguerite Duquesnoy ,保留一些权利。
问题描述
我们将在这篇文章中讨论的问题是国际航空公司乘客预测问题。
这是一个问题,在一年零一个月的情况下,任务是以 1,000 为单位预测国际航空公司乘客的数量。数据范围从 1949 年 1 月到 1960 年 12 月,或 12 年,有 144 个观测值。
该数据集可从 DataMarket 网页免费下载,作为 CSV 下载,文件名为“ international-airline-passengers.csv ”。
下面是该文件前几行的示例。
"Month","International airline passengers: monthly totals in thousands. Jan 49 ? Dec 60"
"1949-01",112
"1949-02",118
"1949-03",132
"1949-04",129
"1949-05",121
我们可以使用 Pandas 库轻松加载此数据集。鉴于每个观察以一个月的相同间隔分开,我们对日期不感兴趣。因此,当我们加载数据集时,我们可以排除第一列。
下载的数据集还有页脚信息,我们可以将 skipfooter 参数排除到 **pandas.read_csv()**为 3 页脚行设置为 3。加载后,我们可以轻松绘制整个数据集。下面列出了加载和绘制数据集的代码。
import pandas
import matplotlib.pyplot as plt
dataset = pandas.read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
plt.plot(dataset)
plt.show()
随着时间的推移,您可以看到数据集中的上升趋势。
您还可以看到数据集的某些周期性,可能与北半球度假期相对应。
航空公司乘客数据集的情节
我们将保持简单并按原样处理数据。
通常,研究各种数据准备技术以重缩放数据并使其静止是一个好主意。
长短期记忆网络
长期短期记忆网络或 LSTM 网络是一种循环神经网络,使用反向传播时间训练并克服消失的梯度问题。
因此,它可用于创建大型循环网络,这些网络又可用于解决机器学习中的困难序列问题并实现最先进的结果。
LSTM 网络具有通过层连接的存储块,而不是神经元。
块具有使其比经典神经元更聪明的组件和用于最近序列的存储器。块包含管理块状态和输出的门。块对输入序列进行操作,并且块内的每个门使用 S 形激活单元来控制它们是否被触发,使状态的改变和流过块的信息的添加成为条件。
一个单元内有三种类型的门:
- 忘记门:有条件地决定从块中丢弃的信息。
- 输入门:有条件地决定输入中的哪些值来更新存储器状态。
- 输出门:根据输入和块的存储器有条件地决定输出内容。
每个单元就像一个小型状态机,其中单元的门具有在训练过程中学习的权重。
您可以看到如何通过一层 LSTM 实现复杂的学习和记忆,并且不难想象高阶抽象如何与多个这样的层分层。
LSTM 回归网络
我们可以将问题表述为回归问题。
也就是说,考虑到本月乘客人数(以千人为单位),下个月的乘客人数是多少?
我们可以编写一个简单的函数将我们的单列数据转换为两列数据集:第一列包含本月的(t)乘客数,第二列包含下个月的(t + 1)乘客数,可以预测。
在我们开始之前,让我们首先导入我们打算使用的所有函数和类。这假设一个工作的 SciPy 环境安装了 Keras 深度学习库。
import numpy
import matplotlib.pyplot as plt
import pandas
import math
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
在我们做任何事情之前,最好修复随机数种子以确保我们的结果可重复。
# fix random seed for reproducibility
numpy.random.seed(7)
我们还可以使用上一节中的代码将数据集作为 Pandas 数据帧加载。然后,我们可以从数据帧中提取 NumPy 数组,并将整数值转换为浮点值,这些值更适合使用神经网络进行建模。
# load the dataset
dataframe = pandas.read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
dataset = dataframe.values
dataset = dataset.astype('float32')
LSTM 对输入数据的比例敏感,特别是在使用 sigmoid(默认)或 tanh 激活函数时。将数据重缩放到 0 到 1 的范围是一种很好的做法,也称为标准化。我们可以使用 scikit-learn 库中的 MinMaxScaler 预处理类轻松地规范化数据集。
# normalize the dataset
scaler = MinMaxScaler(feature_range=(0, 1))
dataset = scaler.fit_transform(dataset)
在我们对数据建模并估计模型在训练数据集上的技能之后,我们需要了解模型在新的看不见的数据上的技能。对于正常的分类或回归问题,我们将使用交叉验证来执行此操作。
对于时间序列数据,值的序列很重要。我们可以使用的一种简单方法是将有序数据集拆分为训练和测试数据集。下面的代码计算分裂点的索引,并将数据分成训练数据集,其中 67%的观测值可用于训练我们的模型,剩余的 33%用于测试模型。
# split into train and test sets
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
print(len(train), len(test))
现在我们可以定义一个函数来创建一个新的数据集,如上所述。
该函数有两个参数:数据集,它是我们想要转换为数据集的 NumPy 数组,以及 look_back ,它是之前使用的时间步数输入变量来预测下一个时间段 - 在这种情况下默认为 1。
此默认值将创建一个数据集,其中 X 是给定时间(t)的乘客数量,Y 是下次乘客的数量(t + 1)。
它可以配置,我们将在下一节中构建一个不同形状的数据集。
# convert an array of values into a dataset matrix
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return numpy.array(dataX), numpy.array(dataY)
让我们看一下这个函数对数据集第一行的影响(为了清晰起见,以非标准化形式显示)。
X Y
112 118
118 132
132 129
129 121
121 135
如果将前 5 行与上一节中列出的原始数据集样本进行比较,则可以在数字中看到 X = t 和 Y = t + 1 模式。
让我们使用这个函数来准备训练和测试数据集以进行建模。
# reshape into X=t and Y=t+1
look_back = 1
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
LSTM 网络期望输入数据(X)以以下形式提供特定的数组结构: [样本,时间步长,特征] 。
目前,我们的数据形式为:[_ 样本,特征 _],我们将问题定为每个样本的一个时间步长。我们可以使用 **numpy.reshape()**将准备好的训练和测试输入数据转换为预期结构,如下所示:
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
testX = numpy.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
我们现在准备设计并适应我们的 LSTM 网络来解决这个问题。
网络具有带有 1 个输入的可见层,带有 4 个 LSTM 块或神经元的隐藏层,以及进行单个值预测的输出层。默认的 sigmoid 激活函数用于 LSTM 块。对网络进行 100 个迭代的训练,并使用 1 的批量大小。
# create and fit the LSTM network
model = Sequential()
model.add(LSTM(4, input_shape=(1, look_back)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2)
一旦模型拟合,我们就可以估计模型在训练和测试数据集上的表现。这将为我们提供新模型的比较点。
请注意,我们在计算错误分数之前反转预测,以确保以与原始数据相同的单位报告表现(每月数千名乘客)。
# make predictions
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
# invert predictions
trainPredict = scaler.inverse_transform(trainPredict)
trainY = scaler.inverse_transform([trainY])
testPredict = scaler.inverse_transform(testPredict)
testY = scaler.inverse_transform([testY])
# calculate root mean squared error
trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0]))
print('Train Score: %.2f RMSE' % (trainScore))
testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0]))
print('Test Score: %.2f RMSE' % (testScore))
最后,我们可以使用模型为训练和测试数据集生成预测,以获得模型技能的直观指示。
由于数据集的准备方式,我们必须改变预测,使它们在 x 轴上与原始数据集对齐。准备好后,绘制数据,以蓝色显示原始数据集,以绿色显示训练数据集的预测,以及以红色显示未见测试数据集的预测。
# shift train predictions for plotting
trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
# shift test predictions for plotting
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
# plot baseline and predictions
plt.plot(scaler.inverse_transform(dataset))
plt.plot(trainPredictPlot)
plt.plot(testPredictPlot)
plt.show()
我们可以看到该模型在拟合训练和测试数据集方面做得非常出色。
LSTM 对乘客预测问题的回归制定进行了训练
为了完整起见,下面是整个代码示例。
# LSTM for international airline passengers problem with regression framing
import numpy
import matplotlib.pyplot as plt
from pandas import read_csv
import math
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
# convert an array of values into a dataset matrix
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return numpy.array(dataX), numpy.array(dataY)
# fix random seed for reproducibility
numpy.random.seed(7)
# load the dataset
dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
dataset = dataframe.values
dataset = dataset.astype('float32')
# normalize the dataset
scaler = MinMaxScaler(feature_range=(0, 1))
dataset = scaler.fit_transform(dataset)
# split into train and test sets
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
# reshape into X=t and Y=t+1
look_back = 1
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
testX = numpy.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
# create and fit the LSTM network
model = Sequential()
model.add(LSTM(4, input_shape=(1, look_back)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2)
# make predictions
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
# invert predictions
trainPredict = scaler.inverse_transform(trainPredict)
trainY = scaler.inverse_transform([trainY])
testPredict = scaler.inverse_transform(testPredict)
testY = scaler.inverse_transform([testY])
# calculate root mean squared error
trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0]))
print('Train Score: %.2f RMSE' % (trainScore))
testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0]))
print('Test Score: %.2f RMSE' % (testScore))
# shift train predictions for plotting
trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
# shift test predictions for plotting
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
# plot baseline and predictions
plt.plot(scaler.inverse_transform(dataset))
plt.plot(trainPredictPlot)
plt.plot(testPredictPlot)
plt.show()
运行该示例将生成以下输出。
...
Epoch 95/100
0s - loss: 0.0020
Epoch 96/100
0s - loss: 0.0020
Epoch 97/100
0s - loss: 0.0020
Epoch 98/100
0s - loss: 0.0020
Epoch 99/100
0s - loss: 0.0020
Epoch 100/100
0s - loss: 0.0020
Train Score: 22.93 RMSE
Test Score: 47.53 RMSE
我们可以看到该模型在训练数据集上的平均误差约为 23 名乘客(以千计),在测试数据集上的平均误差约为 52 名乘客(以千计)。没那么糟。
使用窗口方法进行回归的 LSTM
我们还可以对问题进行短语,以便可以使用多个最近时间步骤来对下一个时间步做出预测。
这称为窗口,窗口的大小是可以针对每个问题进行调整的参数。
例如,给定当前时间(t)我们想要预测序列中下一次的值(t + 1),我们可以使用当前时间(t),以及前两次(t-1)和 t-2)作为输入变量。
当表达为回归问题时,输入变量为 t-2,t-1,t,输出变量为 t + 1。
我们在上一节中创建的 **create_dataset()**函数允许我们通过将 look_back 参数从 1 增加到 3 来创建时间序列问题的这个公式。
具有此秘籍的数据集样本如下所示:
X1 X2 X3 Y
112 118 132 129
118 132 129 121
132 129 121 135
129 121 135 148
121 135 148 148
我们可以使用更大的窗口大小重新运行上一节中的示例。为了完整性,下面列出了仅包含窗口大小更改的整个代码清单。
# LSTM for international airline passengers problem with window regression framing
import numpy
import matplotlib.pyplot as plt
from pandas import read_csv
import math
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
# convert an array of values into a dataset matrix
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return numpy.array(dataX), numpy.array(dataY)
# fix random seed for reproducibility
numpy.random.seed(7)
# load the dataset
dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
dataset = dataframe.values
dataset = dataset.astype('float32')
# normalize the dataset
scaler = MinMaxScaler(feature_range=(0, 1))
dataset = scaler.fit_transform(dataset)
# split into train and test sets
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
# reshape into X=t and Y=t+1
look_back = 3
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
testX = numpy.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
# create and fit the LSTM network
model = Sequential()
model.add(LSTM(4, input_shape=(1, look_back)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2)
# make predictions
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
# invert predictions
trainPredict = scaler.inverse_transform(trainPredict)
trainY = scaler.inverse_transform([trainY])
testPredict = scaler.inverse_transform(testPredict)
testY = scaler.inverse_transform([testY])
# calculate root mean squared error
trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0]))
print('Train Score: %.2f RMSE' % (trainScore))
testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0]))
print('Test Score: %.2f RMSE' % (testScore))
# shift train predictions for plotting
trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
# shift test predictions for plotting
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
# plot baseline and predictions
plt.plot(scaler.inverse_transform(dataset))
plt.plot(trainPredictPlot)
plt.plot(testPredictPlot)
plt.show()
运行该示例提供以下输出:
...
Epoch 95/100
0s - loss: 0.0021
Epoch 96/100
0s - loss: 0.0021
Epoch 97/100
0s - loss: 0.0021
Epoch 98/100
0s - loss: 0.0021
Epoch 99/100
0s - loss: 0.0022
Epoch 100/100
0s - loss: 0.0020
Train Score: 24.19 RMSE
Test Score: 58.03 RMSE
我们可以看到,与上一节相比,误差略有增加。窗口大小和网络架构没有调整:这只是如何构建预测问题的演示。
LSTM 训练乘客预测问题的窗口方法
带时间步长的回归 LSTM
您可能已经注意到 LSTM 网络的数据准备包括时间步骤。
一些序列问题可能每个样本具有不同数量的时间步长。例如,您可能会测量导致故障点或喘振点的物理机器。每个事件都是一个样本,导致事件的观察将是时间步骤,观察到的变量将是特征。
时间步骤提供了另一种表达时间序列问题的方法。与上面的窗口示例一样,我们可以将时间序列中的先前时间步长作为输入来预测下一时间步的输出。
我们可以将它们用作一个输入要素的时间步长,而不是将过去的观察结果作为单独的输入要素,这确实是对问题的更准确的框架。
我们可以使用与上一个基于窗口的示例相同的数据表示来执行此操作,除非我们对数据进行整形,我们将列设置为时间步长维并将要素维更改为 1.例如:
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1))
testX = numpy.reshape(testX, (testX.shape[0], testX.shape[1], 1))
下面提供了整个代码清单,以确保完整性。
# LSTM for international airline passengers problem with time step regression framing
import numpy
import matplotlib.pyplot as plt
from pandas import read_csv
import math
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
# convert an array of values into a dataset matrix
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return numpy.array(dataX), numpy.array(dataY)
# fix random seed for reproducibility
numpy.random.seed(7)
# load the dataset
dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
dataset = dataframe.values
dataset = dataset.astype('float32')
# normalize the dataset
scaler = MinMaxScaler(feature_range=(0, 1))
dataset = scaler.fit_transform(dataset)
# split into train and test sets
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
# reshape into X=t and Y=t+1
look_back = 3
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1))
testX = numpy.reshape(testX, (testX.shape[0], testX.shape[1], 1))
# create and fit the LSTM network
model = Sequential()
model.add(LSTM(4, input_shape=(look_back, 1)))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=100, batch_size=1, verbose=2)
# make predictions
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
# invert predictions
trainPredict = scaler.inverse_transform(trainPredict)
trainY = scaler.inverse_transform([trainY])
testPredict = scaler.inverse_transform(testPredict)
testY = scaler.inverse_transform([testY])
# calculate root mean squared error
trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0]))
print('Train Score: %.2f RMSE' % (trainScore))
testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0]))
print('Test Score: %.2f RMSE' % (testScore))
# shift train predictions for plotting
trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
# shift test predictions for plotting
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
# plot baseline and predictions
plt.plot(scaler.inverse_transform(dataset))
plt.plot(trainPredictPlot)
plt.plot(testPredictPlot)
plt.show()
运行该示例提供以下输出:
...
Epoch 95/100
1s - loss: 0.0021
Epoch 96/100
1s - loss: 0.0021
Epoch 97/100
1s - loss: 0.0021
Epoch 98/100
1s - loss: 0.0020
Epoch 99/100
1s - loss: 0.0021
Epoch 100/100
1s - loss: 0.0020
Train Score: 23.69 RMSE
Test Score: 58.88 RMSE
我们可以看到结果略好于前面的例子,尽管输入数据的结构更有意义。
LSTM 训练乘客预测问题的时间步长形式
LSTM 与批次之间的内存
LSTM 网络具有内存,能够记住长序列。
通常,在拟合模型时,在每次训练批次之后重置网络内的状态,以及每次调用 **model.predict()**或 model.evaluate()。
通过使 LSTM 层“有状态”,我们可以更好地控制何时在 Keras 中清除 LSTM 网络的内部状态。这意味着它可以在整个训练序列中构建状态,甚至在需要做出预测时保持该状态。
它要求在安装网络时不要改组训练数据。它还要求通过调用 model.reset_states(),在每次暴露于训练数据(时期)后明确重置网络状态。这意味着我们必须在每个时代调用 **model.fit()**和 **model.reset_states()**内创建我们自己的时代外环。例如:
for i in range(100):
model.fit(trainX, trainY, epochs=1, batch_size=batch_size, verbose=2, shuffle=False)
model.reset_states()
最后,当构造 LSTM 层时,有状态参数必须设置为 True 而不是指定输入维度,我们必须硬编码批次中的样本数量,通过设置 batch_input_shape 参数,样本中的时间步长和时间步长中的要素数量。例如:
model.add(LSTM(4, batch_input_shape=(batch_size, time_steps, features), stateful=True))
然后,在评估模型和做出预测时,必须使用相同的批量大小。例如:
model.predict(trainX, batch_size=batch_size)
我们可以调整前一个时间步骤示例以使用有状态 LSTM。完整的代码清单如下。
# LSTM for international airline passengers problem with memory
import numpy
import matplotlib.pyplot as plt
from pandas import read_csv
import math
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
# convert an array of values into a dataset matrix
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return numpy.array(dataX), numpy.array(dataY)
# fix random seed for reproducibility
numpy.random.seed(7)
# load the dataset
dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
dataset = dataframe.values
dataset = dataset.astype('float32')
# normalize the dataset
scaler = MinMaxScaler(feature_range=(0, 1))
dataset = scaler.fit_transform(dataset)
# split into train and test sets
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
# reshape into X=t and Y=t+1
look_back = 3
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1))
testX = numpy.reshape(testX, (testX.shape[0], testX.shape[1], 1))
# create and fit the LSTM network
batch_size = 1
model = Sequential()
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(100):
model.fit(trainX, trainY, epochs=1, batch_size=batch_size, verbose=2, shuffle=False)
model.reset_states()
# make predictions
trainPredict = model.predict(trainX, batch_size=batch_size)
model.reset_states()
testPredict = model.predict(testX, batch_size=batch_size)
# invert predictions
trainPredict = scaler.inverse_transform(trainPredict)
trainY = scaler.inverse_transform([trainY])
testPredict = scaler.inverse_transform(testPredict)
testY = scaler.inverse_transform([testY])
# calculate root mean squared error
trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0]))
print('Train Score: %.2f RMSE' % (trainScore))
testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0]))
print('Test Score: %.2f RMSE' % (testScore))
# shift train predictions for plotting
trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
# shift test predictions for plotting
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
# plot baseline and predictions
plt.plot(scaler.inverse_transform(dataset))
plt.plot(trainPredictPlot)
plt.plot(testPredictPlot)
plt.show()
运行该示例提供以下输出:
...
Epoch 1/1
1s - loss: 0.0017
Epoch 1/1
1s - loss: 0.0017
Epoch 1/1
1s - loss: 0.0017
Epoch 1/1
1s - loss: 0.0017
Epoch 1/1
1s - loss: 0.0017
Epoch 1/1
1s - loss: 0.0016
Train Score: 20.74 RMSE
Test Score: 52.23 RMSE
我们确实看到结果更糟。该模型可能需要更多模块,并且可能需要针对更多时期进行训练以内化问题的结构。
有状态 LSTM 训练乘客预测问题的回归形式
栈式 LSTM 与批次之间的内存
最后,我们将了解 LSTM 的一大优势:它们可以在堆叠到深层网络架构中时成功进行训练。
LSTM 网络可以像其他层类型堆叠一样堆叠在 Keras 中。对配置的一个补充是每个后续 LSTM 层之前的 LSTM 层必须返回序列。这可以通过将层上的 return_sequences 参数设置为 True 来完成。
我们可以在上一节中扩展有状态 LSTM,使其具有两层,如下所示:
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True, return_sequences=True))
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True))
下面提供了整个代码清单,以确保完整性。
# Stacked LSTM for international airline passengers problem with memory
import numpy
import matplotlib.pyplot as plt
from pandas import read_csv
import math
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
# convert an array of values into a dataset matrix
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return numpy.array(dataX), numpy.array(dataY)
# fix random seed for reproducibility
numpy.random.seed(7)
# load the dataset
dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
dataset = dataframe.values
dataset = dataset.astype('float32')
# normalize the dataset
scaler = MinMaxScaler(feature_range=(0, 1))
dataset = scaler.fit_transform(dataset)
# split into train and test sets
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
# reshape into X=t and Y=t+1
look_back = 3
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# reshape input to be [samples, time steps, features]
trainX = numpy.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1))
testX = numpy.reshape(testX, (testX.shape[0], testX.shape[1], 1))
# create and fit the LSTM network
batch_size = 1
model = Sequential()
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True, return_sequences=True))
model.add(LSTM(4, batch_input_shape=(batch_size, look_back, 1), stateful=True))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
for i in range(100):
model.fit(trainX, trainY, epochs=1, batch_size=batch_size, verbose=2, shuffle=False)
model.reset_states()
# make predictions
trainPredict = model.predict(trainX, batch_size=batch_size)
model.reset_states()
testPredict = model.predict(testX, batch_size=batch_size)
# invert predictions
trainPredict = scaler.inverse_transform(trainPredict)
trainY = scaler.inverse_transform([trainY])
testPredict = scaler.inverse_transform(testPredict)
testY = scaler.inverse_transform([testY])
# calculate root mean squared error
trainScore = math.sqrt(mean_squared_error(trainY[0], trainPredict[:,0]))
print('Train Score: %.2f RMSE' % (trainScore))
testScore = math.sqrt(mean_squared_error(testY[0], testPredict[:,0]))
print('Test Score: %.2f RMSE' % (testScore))
# shift train predictions for plotting
trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
# shift test predictions for plotting
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
# plot baseline and predictions
plt.plot(scaler.inverse_transform(dataset))
plt.plot(trainPredictPlot)
plt.plot(testPredictPlot)
plt.show()
运行该示例将生成以下输出。
...
Epoch 1/1
1s - loss: 0.0017
Epoch 1/1
1s - loss: 0.0017
Epoch 1/1
1s - loss: 0.0017
Epoch 1/1
1s - loss: 0.0017
Epoch 1/1
1s - loss: 0.0016
Train Score: 20.49 RMSE
Test Score: 56.35 RMSE
对测试数据集的预测再次恶化。这是更多证据表明需要额外的训练时期。
堆积状态 LSTM 训练对乘客预测问题的回归表达式
摘要
在这篇文章中,您了解了如何使用 Keras 深度学习网络在 Python 中开发用于时间序列预测的 LSTM 循环神经网络。
具体来说,你学到了:
- 关于国际航空公司客运时间序列预测问题。
- 如何为回归创建 LSTM 以及时间序列问题的窗口公式。
- 如何使用时间序列问题的时间步长公式创建 LSTM。
- 如何使用状态和堆叠的 LSTM 创建具有状态的 LSTM 以学习长序列。
您对 LSTMs 有关时间序列预测或关于这篇文章的任何疑问吗? 在下面的评论中提出您的问题,我会尽力回答。
更新了 LSTM 时间序列预测帖子:
这篇文章中的例子非常陈旧,我有更好的例子可用于在时间序列中使用 LSTM,请参阅:
Keras 中使用深度学习的时间序列预测
原文:
machinelearningmastery.com/time-series-prediction-with-deep-learning-in-python-with-keras/
时间序列预测是框架和机器学习解决的难题。
在这篇文章中,您将了解如何使用 Keras 深度学习库在 Python 中开发用于时间序列预测的神经网络模型。
阅读这篇文章后你会知道:
- 关于航空公司乘客单变量时间序列预测问题。
- 如何将时间序列预测作为回归问题,并为其开发神经网络模型。
- 如何构建具有时滞的时间序列预测,并为其开发神经网络模型。
让我们开始吧。
- 2016 年 10 月更新:用更准确的版本替换图表,评论了第一种方法的有限表现。
- 2017 年 3 月更新:更新了 Keras 2.0.2,TensorFlow 1.0.1 和 Theano 0.9.0 的示例。
问题描述
我们将在这篇文章中看到的问题是国际航空公司乘客预测问题。
这是一个问题,给定一年零一个月,任务是以 1,000 为单位预测国际航空公司乘客的数量。数据范围从 1949 年 1 月到 1960 年 12 月或 12 年,共有 144 个观测值。
该数据集可从 DataMarket 网页免费下载,作为 CSV 下载,文件名为“ international-airline-passengers.csv ”。
下面是该文件前几行的示例。
"Month","International airline passengers: monthly totals in thousands. Jan 49 ? Dec 60"
"1949-01",112
"1949-02",118
"1949-03",132
"1949-04",129
"1949-05",121
我们可以使用 Pandas 库轻松加载此数据集。鉴于每个观察以一个月的相同间隔分开,我们对日期不感兴趣。因此,当我们加载数据集时,我们可以排除第一列。
下载的数据集还有页脚信息,我们可以将 skipfooter 参数排除到 **pandas.read_csv()**为 3 页脚行设置为 3。加载后,我们可以轻松绘制整个数据集。下面列出了加载和绘制数据集的代码。
import pandas
import matplotlib.pyplot as plt
dataset = pandas.read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
plt.plot(dataset)
plt.show()
您可以在情节中看到上升趋势。
您还可以看到数据集的某些周期性,这可能与北半球夏季假期相对应。
航空公司乘客数据集的情节
我们将保持简单并按原样处理数据。
通常,研究各种数据准备技术以重缩放数据并使其静止是一个好主意。
多层感知机回归
我们想将时间序列预测问题表述为回归问题。
也就是说,考虑到本月的乘客人数(以千人为单位),下个月的乘客数量是多少。
我们可以编写一个简单的函数将我们的单列数据转换为两列数据集。第一列包含本月的(t)乘客数,第二列包含下个月的(t + 1)乘客数,需要预测。
在我们开始之前,让我们首先导入我们打算使用的所有函数和类。这假设一个工作的 SciPy 环境安装了 Keras 深度学习库。
import numpy
import matplotlib.pyplot as plt
import pandas
from keras.models import Sequential
from keras.layers import Dense
在我们做任何事情之前,最好修复随机数种子以确保我们的结果可重复。
# fix random seed for reproducibility
numpy.random.seed(7)
我们还可以使用上一节中的代码将数据集作为 Pandas 数据帧加载。然后,我们可以从数据帧中提取 NumPy 数组,并将整数值转换为更适合使用神经网络建模的浮点值。
# load the dataset
dataframe = pandas.read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
dataset = dataframe.values
dataset = dataset.astype('float32')
在我们对数据建模并估计模型在训练数据集上的技能之后,我们需要了解模型在新的看不见的数据上的技能。对于正常的分类或回归问题,我们将使用交叉验证来完成此操作。
对于时间序列数据,值的序列很重要。我们可以使用的一种简单方法是将有序数据集拆分为训练和测试数据集。下面的代码计算分裂点的索引,并将数据分成训练数据集,其中 67%的观测值可用于训练我们的模型,剩余的 33%用于测试模型。
# split into train and test sets
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
print(len(train), len(test))
现在我们可以定义一个函数来创建一个新的数据集,如上所述。该函数有两个参数,数据集是我们想要转换成数据集的 NumPy 数组,而 look_back 是用作预测下一个时间段的输入变量的先前时间步数,在这种情况下,默认为 1。
此默认值将创建一个数据集,其中 X 是给定时间(t)的乘客数量,Y 是下次乘客的数量(t + 1)。
它可以配置,我们将在下一节中介绍构建不同形状的数据集。
# convert an array of values into a dataset matrix
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return numpy.array(dataX), numpy.array(dataY)
让我们看看这个函数对数据集的前几行的影响。
X Y
112 118
118 132
132 129
129 121
121 135
如果将前 5 行与上一节中列出的原始数据集样本进行比较,则可以在数字中看到 X = t 和 Y = t + 1 模式。
让我们使用这个函数来准备准备建模的训练和测试数据集。
# reshape into X=t and Y=t+1
look_back = 1
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
我们现在可以在训练数据中使用多层感知机模型。
我们使用一个简单的网络,1 个输入,1 个隐藏层,8 个神经元和一个输出层。使用均方误差拟合模型,如果我们取平方根,则以数据集为单位给出误差分数。
我尝试了一些粗略的参数并确定了下面的配置,但绝不是网络列出了优化。
# create and fit Multilayer Perceptron model
model = Sequential()
model.add(Dense(8, input_dim=look_back, activation='relu'))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=200, batch_size=2, verbose=2)
一旦模型拟合,我们就可以估计模型在训练和测试数据集上的表现。这将为我们提供新模型的比较点。
# Estimate model performance
trainScore = model.evaluate(trainX, trainY, verbose=0)
print('Train Score: %.2f MSE (%.2f RMSE)' % (trainScore, math.sqrt(trainScore)))
testScore = model.evaluate(testX, testY, verbose=0)
print('Test Score: %.2f MSE (%.2f RMSE)' % (testScore, math.sqrt(testScore)))
最后,我们可以使用模型为训练和测试数据集生成预测,以获得模型技能的直观指示。
由于数据集是如何准备的,我们必须改变预测,以便它们在 x 轴上与原始数据集一致。准备好后,绘制数据,以蓝色显示原始数据集,以绿色显示训练数据集的预测,以红色显示未见测试数据集的预测。
# generate predictions for training
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
# shift train predictions for plotting
trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
# shift test predictions for plotting
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
# plot baseline and predictions
plt.plot(dataset)
plt.plot(trainPredictPlot)
plt.plot(testPredictPlot)
plt.show()
我们可以看到该模型在拟合训练和测试数据集方面做得很差。它基本上预测了与输出相同的输入值。
神经网络的原始时间序列预测 蓝色=整个数据集,绿色=训练,红色=预测
为完整起见,以下是整个代码清单。
# Multilayer Perceptron to Predict International Airline Passengers (t+1, given t)
import numpy
import matplotlib.pyplot as plt
import pandas
import math
from keras.models import Sequential
from keras.layers import Dense
# fix random seed for reproducibility
numpy.random.seed(7)
# load the dataset
dataframe = pandas.read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
dataset = dataframe.values
dataset = dataset.astype('float32')
# split into train and test sets
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
print(len(train), len(test))
# convert an array of values into a dataset matrix
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return numpy.array(dataX), numpy.array(dataY)
# reshape into X=t and Y=t+1
look_back = 1
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# create and fit Multilayer Perceptron model
model = Sequential()
model.add(Dense(8, input_dim=look_back, activation='relu'))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=200, batch_size=2, verbose=2)
# Estimate model performance
trainScore = model.evaluate(trainX, trainY, verbose=0)
print('Train Score: %.2f MSE (%.2f RMSE)' % (trainScore, math.sqrt(trainScore)))
testScore = model.evaluate(testX, testY, verbose=0)
print('Test Score: %.2f MSE (%.2f RMSE)' % (testScore, math.sqrt(testScore)))
# generate predictions for training
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
# shift train predictions for plotting
trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
# shift test predictions for plotting
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
# plot baseline and predictions
plt.plot(dataset)
plt.plot(trainPredictPlot)
plt.plot(testPredictPlot)
plt.show()
运行模型会生成以下输出。
Epoch 195/200
0s - loss: 536.7014
Epoch 196/200
0s - loss: 555.4216
Epoch 197/200
0s - loss: 552.2841
Epoch 198/200
0s - loss: 541.2220
Epoch 199/200
0s - loss: 542.3288
Epoch 200/200
0s - loss: 534.2096
Train Score: 532.59 MSE (23.08 RMSE)
Test Score: 2358.07 MSE (48.56 RMSE)
根据表现估计的平方根,我们可以看到该模型在训练数据集上有 23 名乘客(以千计)的平均误差,在测试数据集上有 48 名乘客(以千计)。
多层感知机使用窗口方法
我们还可以对问题进行短语,以便可以使用多个最近的时间步骤来对下一个时间步做出预测。
这称为窗口方法,窗口的大小是可以针对每个问题调整的参数。
例如,给定当前时间(t)我们想要预测序列中下一次的值(t + 1),我们可以使用当前时间(t)以及前两次(t-1 和 T-2)。
当表达为回归问题时,输入变量为 t-2,t-1,t,输出变量为 t + 1。
我们在上一节中编写的 **create_dataset()**函数允许我们通过将 look_back 参数从 1 增加到 3 来创建时间序列问题的这个公式。
具有此秘籍的数据集样本如下所示:
X1 X2 X3 Y
112 118 132 129
118 132 129 121
132 129 121 135
129 121 135 148
121 135 148 148
我们可以使用更大的窗口大小重新运行上一节中的示例。我们将增加网络容量来处理附加信息。第一个隐藏层增加到 14 个神经元,第二个隐藏层增加了 8 个神经元。时代数也增加到 400。
为了完整性,下面列出了仅包含窗口大小更改的整个代码清单。
# Multilayer Perceptron to Predict International Airline Passengers (t+1, given t, t-1, t-2)
import numpy
import matplotlib.pyplot as plt
from pandas import read_csv
import math
from keras.models import Sequential
from keras.layers import Dense
# convert an array of values into a dataset matrix
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset)-look_back-1):
a = dataset[i:(i+look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return numpy.array(dataX), numpy.array(dataY)
# fix random seed for reproducibility
numpy.random.seed(7)
# load the dataset
dataframe = read_csv('international-airline-passengers.csv', usecols=[1], engine='python', skipfooter=3)
dataset = dataframe.values
dataset = dataset.astype('float32')
# split into train and test sets
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
# reshape dataset
look_back = 3
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
# create and fit Multilayer Perceptron model
model = Sequential()
model.add(Dense(12, input_dim=look_back, activation='relu'))
model.add(Dense(8, activation='relu'))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
model.fit(trainX, trainY, epochs=400, batch_size=2, verbose=2)
# Estimate model performance
trainScore = model.evaluate(trainX, trainY, verbose=0)
print('Train Score: %.2f MSE (%.2f RMSE)' % (trainScore, math.sqrt(trainScore)))
testScore = model.evaluate(testX, testY, verbose=0)
print('Test Score: %.2f MSE (%.2f RMSE)' % (testScore, math.sqrt(testScore)))
# generate predictions for training
trainPredict = model.predict(trainX)
testPredict = model.predict(testX)
# shift train predictions for plotting
trainPredictPlot = numpy.empty_like(dataset)
trainPredictPlot[:, :] = numpy.nan
trainPredictPlot[look_back:len(trainPredict)+look_back, :] = trainPredict
# shift test predictions for plotting
testPredictPlot = numpy.empty_like(dataset)
testPredictPlot[:, :] = numpy.nan
testPredictPlot[len(trainPredict)+(look_back*2)+1:len(dataset)-1, :] = testPredict
# plot baseline and predictions
plt.plot(dataset)
plt.plot(trainPredictPlot)
plt.plot(testPredictPlot)
plt.show()
运行该示例提供以下输出。
Epoch 395/400
0s - loss: 485.3482
Epoch 396/400
0s - loss: 479.9485
Epoch 397/400
0s - loss: 497.2707
Epoch 398/400
0s - loss: 489.5670
Epoch 399/400
0s - loss: 490.8099
Epoch 400/400
0s - loss: 493.6544
Train Score: 564.03 MSE (23.75 RMSE)
Test Score: 2244.82 MSE (47.38 RMSE)
我们可以看到,与上一节相比,误差没有明显减少。
查看图表,我们可以在预测中看到更多结构。
同样,窗口大小和网络架构没有调整,这只是如何构建预测问题的演示。
根据绩效分数的平方根,我们可以看到训练数据集的平均误差为 23 名乘客(每月数千人),未见测试集的平均误差为 47 名乘客(每月数千人)。
使用神经网络的时间序列预测的窗口方法 蓝色=整个数据集,绿色=训练,红色=预测
摘要
在这篇文章中,您了解了如何使用 Keras 深度学习库为时间序列预测问题开发神经网络模型。
完成本教程后,您现在知道:
- 关于国际航空公司旅客预测时间序列数据集。
- 如何将时间序列预测问题构建为回归问题并开发神经网络模型。
- 如何使用窗口方法构建时间序列预测问题并开发神经网络模型。
您对神经网络或此帖的时间序列预测有任何疑问吗? 在下面的评论中提出您的问题,我会尽力回答。