From sztu 自动化专业的小菜鸡。
时间序列预测问题是一种困难类型的预测建模问题。
与回归预测建模不同,时间序列还增加了输入变量之间序列依赖性的复杂性。
一种用于处理序列依赖性的强大神经网络称为 循环神经网络 。 长短期记忆网络或 LSTM 网络是一种用于深度学习的循环神经网络,因为可以成功训练非常大的架构。
在这篇文章中,您将了解如何使用 Keras 深度学习库在 Python 中开发 LSTM 网络,以解决演示时间序列预测问题。
完成本教程后,您将了解如何为您自己的时间序列预测问题和其他更一般的序列问题实现和开发 LSTM 网络。 你会知道:
- 关于国际航空公司乘客时间序列预测问题。
- 如何为基于回归、窗口和时间步长的时间序列预测问题框架开发 LSTM 网络。
- 如何使用在很长的序列中保持状态(记忆)的 LSTM 网络进行开发和预测。
在本教程中,我们将为标准时间序列预测问题开发许多 LSTM。 LSTM 网络的问题和选择的配置仅用于演示目的,它们没有进行优化。
这些示例将准确地向您展示如何为时间序列预测建模问题开发自己的不同结构的 LSTM 网络。
获取数据集,这儿可以使用您想做lstm预测的数据集。
import pandas
import matplotlib.pyplot as plt
dataset = pandas.read_csv('airline-passengers.csv', usecols=[1], engine='python')
plt.plot(dataset)
plt.show()
随着时间的推移,您可以看到数据集中的上升趋势。
您还可以看到可能对应于北半球假期的数据集的一些周期性。
我们将保持简单并按原样处理数据。
通常,研究各种数据准备技术以重新调整数据并使其静止是一个好主意。
长短期记忆网络
Long Short-Term Memory 网络或 LSTM 网络是一种循环神经网络,它使用随时间的反向传播进行训练,并克服了梯度消失问题。
因此,它可用于创建大型循环网络,进而可用于解决机器学习中的困难序列问题并获得最先进的结果。
LSTM 网络没有神经元,而是具有通过层连接的内存块。
块具有使其比经典神经元更智能的组件和最近序列的记忆。 块包含管理块状态和输出的门。 块对输入序列进行操作,块内的每个门都使用 sigmoid 激活单元来控制它们是否被触发,从而使状态的改变和流经块的信息的添加成为有条件的。
一个单元内有三种类型的门:
- 忘记门 :有条件地决定从块中丢弃哪些信息。
- 输入门 :有条件地决定输入中的哪些值来更新内存状态。
- 输出门 :根据输入和块的内存有条件地决定输出什么。
每个单元就像一个迷你状态机,其中单元的门具有在训练过程中学习的权重。
您可以看到如何从 LSTM 层实现复杂的学习和记忆,并且不难想象高阶抽象如何与多个这样的层分层。
用于回归的 LSTM 网络
我们可以将问题表述为回归问题。
也就是说,给定本月的乘客数量(以千为单位),下个月的乘客数量是多少?
我们可以编写一个简单的函数将我们的单列数据转换为两列数据集:第一列包含本月 (t) 的乘客人数,第二列包含下个月 (t+1) 的乘客人数,以进行预测。
在开始之前,让我们首先导入我们打算使用的所有函数和类。 这假设安装了 Keras 深度学习库的工作 SciPy 环境。
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import LSTM
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
在我们做任何事情之前,最好先修复随机数种子,以确保我们的结果是可重现的。
# fix random seed for reproducibility
tf.random.set_seed(7)
我们还可以使用上一节中的代码将数据集加载为 Pandas 数据框。 然后我们可以从数据帧中提取 NumPy 数组并将整数值转换为浮点值,这更适合使用神经网络进行建模。
# load the dataset
dataframe = pd.read_csv('airline-passengers.csv', usecols=[1], engine='python')
dataset = dataframe.values
dataset = dataset.astype('float32')
LSTM 对输入数据的规模很敏感,特别是在使用 sigmoid(默认)或 tanh 激活函数时。 将数据重新缩放到 0 到 1 的范围是一个很好的做法,也称为规范化。 我们可以使用 MinMaxScaler scikit-learn 库
# 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))
现在我们可以定义一个函数来创建一个新的数据集,如上所述。
该函数有两个参数: dataset ,它是我们想要转换为数据集的 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 np.array(dataX), np.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) 以以下形式提供特定的数组结构: [样本、时间步长、特征] 。
目前,我们的数据采用以下形式:[ samples, features ],我们将问题构建为每个样本的一个时间步长。 将准备好的训练和测试输入数据转换为预期的结构 numpy.reshape() ,如下所示:
# reshape input to be [samples, time steps, features]
trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))
我们现在准备好为这个问题设计和拟合我们的 LSTM 网络。
该网络有一个带有 1 个输入的可见层,一个带有 4 个 LSTM 块或神经元的隐藏层,以及一个进行单值预测的输出层。 默认的 sigmoid 激活函数用于 LSTM 块。 网络训练了 100 个 epoch,使用了 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)
一旦模型拟合好,我们就可以估计模型在训练和测试数据集上的性能。 这将为我们提供新模型的比较点。