概述

任何时态数据都可以框定为时间序列任务。心率、股票市场价格、传感器日志等数据都属于时间序列数据的范畴。许多深度学习架构用于对此类数据进行建模,LSTM 就是其中之一。本文重点介绍如何构建 LSTM 时间序列模型。

我们在建设什么?

在本文中,我们将创建一个 LSTM 时间序列模型。我们将使用我们生成的数据并创建一个简单的 LSTM 来准确地对其进行建模。为了执行这项任务,我们将编写可以生成数据、建模并对未来点进行预测的函数。 我们将使用 Tensorflow 实现此模型,以下部分将说明如何执行此操作。

先决条件

在创建 LSTM 时序模型之前,我们必须了解一些先决条件信息。

  • 什么是时间序列?时间序列数据是具有离散时间间隔和几乎等距时间步长的任何时态数据。一般任务是估计用于生成此类时间序列的函数。如果我们能够正确地估计函数,我们就可以预测模型尚未遇到的未来点。 时间序列的示例包括心率数据、股票市场数据等。

  • 神经网络 (RNN)RNN 是一系列模型,将整个系列作为输入,将返回系列作为输出。与使用反向传播的简单卷积网络不同,RNN 使用一种称为时间反向传播 (BPTT) 的修改变体来嵌入时态数据。此算法是一个顺序过程,包含对基础数据进行建模的隐藏状态。RNN 被认为是图灵完备的,用于自然语言处理、计算机视觉、机器人等领域。 RNN 架构由门组成。

  • 经典 RNN 的问题

    RNN 由于其顺序性而存在各种问题。

    • RNN 无法对更长的序列进行建模。此属性使其很难用于具有较长时间跨度的数据。
    • 由于底层架构的工作方式,经典的 RNN 也存在梯度爆炸和消失的问题。这些问题使得 RNN 非常不稳定。
  • 什么是 LSTM?

    LSTM 是具有不同门的 RNN 的修改版本,使该架构能够对更长的序列进行建模。LSTM 使用门控连接来学习要忘记哪些功能以及要记住哪些功能。选择忘记什么的能力使它们比经典的 RNN 好得多。LSTM 也更加稳定,不太可能爆炸或消失梯度。

我们将如何构建它?

我们将编写生成时间序列数据的函数,以构建 LSTM 时间序列模型。一旦我们有了数据,我们将对其进行预处理,使其适合模型使用。我们还将编写一个可以显示模型结果的函数。创建这些辅助函数后,我们将创建一个简单的 LSTM 模型,并使用我们之前生成的数据对其进行训练。 我们将在本文中使用的 LSTM 时间序列模型由一个 LSTM 模块和一个 FC 层组成,并且非常容易实现。在实现所有必需的函数后,我们将训练模型并使用它来预测未来的点。 以下各节详细介绍了实现。

最终输出

LSTM 时间序列模型的最终输出是对模型尚未遇到的未来点的预测。在训练模型 ~50 个 epoch 后,输出如下所示。

要求

我们需要许多库来实现“LSTM 时间序列模型”。下面列出了这些。

  • TensorFlow 用于构建 LSTM 时间序列模型架构。
  • 用于数值处理的 NumPy。
  • 用于处理表格数据的 Pandas。
  • Matplotlib 和 seaborn 用于绘图。
  • Matplotlib 中的 RC 模块,用于配置绘图。

使用 LSTM 构建时间序列预测器

时序预测器模型是使用简单的 LSTM 体系结构构建的。要创建它,我们必须首先设置必要的库,生成和加载数据,并对其进行预处理。 我们将在本文中使用的模型是一个顺序模型,由一个 LSTM 模块和一个全连接层组成。然后,我们将使用生成的数据和此模型来训练 LSTM 时间序列预测模型。我们将使用经过训练的模型来预测模型以前从未见过的未来点。 以下各节详细介绍了所有这些要点。

导入和设置

为了设置我们的模块,我们设置了 RANDOM_SEED 变量。这个变量为随机数生成器设置种子,并确保我们每次都得到相同的“随机”数。种子在实践中没有用,而仅用于演示。我们还将绘图修改为带有柔和调色板的白色网格,以便更好地显示。

import numpy as np
import tensorflow as tf
from tensorflow import keras
import pandas as pd
import seaborn as sns
from pylab import rcParams
import matplotlib.pyplot as plt
from matplotlib import RC

sns.set(style='whitegrid', palette='muted', font_scale=1.5)
rcParams['figure.filesize] = 16, 10
RANDOM_SEED = 42
np.random.seed(RANDOM_SEED)
tf.random.set_seed(RANDOM_SEED)

数据

为了生成数据,我们创建了一个结合了正弦波和小高斯噪声的自定义函数。这些值在 (0,200) 范围内生成,步长为 0.1。 要查看这些数据的样子,我们可以使用 matplotlib 绘制它。

data_time = np.arange(0, 200, 0.1)
sin_values = np.sin(data_time) + np.random.normal(scale=0.5, size=len(data_time))
plt.plot(data_time, sin_values, label='sine (with noise)');

数据预处理

在将其传递给模型之前,我们必须将此数据转换为 DataFrame。这样做可以使未来的流程变得更加容易。我们还将数据拆分为训练和测试组件。 此处显示了 DataFrame 的前几行。

正弦
0.00.248357
0.10.030701
0.20.522514
0.31.057035
0.40.272342
data_full = pd.DataFrame(dict(sine=sin_values), index=data_time, columns=['sine'])
data_full.head()

len_train = int(len(data_full) * 0.8)
len_test = len(data_full) - len_train
train, test = data_full.iloc[0:len_train], data_full.iloc[len_train:len(data_full)]

现在我们已经创建了一个数据框,我们将使用它来生成批量数据。我们使用以下函数执行此操作,并创建用于训练和测试的输入和标签。

def gen_data(X, y, num_steps=1):
    Xs, ys = [], []
    for i in range(len(X) - num_steps):
        Xs.append(X.iloc[i:(i + num_steps)].values)       
        ys.append(y.iloc[i + num_steps])
    return np.array(Xs), np.array(ys)
    
num_steps = 10
trainX, trainY = gen_data(train, train.sine, num_steps)
testX, testY = gen_data(test, test.sine, num_steps)

实现顺序模型

我们终于可以实现 LSTM 时间序列模型了。这个简单的模型有一个 LSTM 层,后跟一个 FC 层。 我们使用均方误差损失函数和亚当优化器编译模型。现在,我们可以在生成的数据上训练这个编译后的模型。

lstm_model = keras.Sequential()
lstm_model.add(keras.layers.LSTM(128, input_shape=(trainX.shape[1], trainX.shape[2])))
lstm_model.add(keras.layers.Dense(1))
lstm_model.compile(loss='mean_squared_error', optimizer=keras.optimizers.Adam(0.001))

提前停止回调

时间序列模型往往容易过度拟合。为了降低过拟合的概率,使用了 Early Stopping 回调。此回调使用纪元数作为超参数。如果验证精度在几个时期内没有增加,并且训练停止,则会保存模型。此方法在模型过多关注训练数据之前停止训练。

callbacks=[tf.keras.callbacks.EarlyStopping(monitor='loss', patience=3)]

模型训练

一旦我们定义了所有必需的函数,我们就可以训练模型了。在本文中,我们将训练 LSTM 时间序列模型 30 个 epoch,批大小为 16。我们使用 0.1% 的验证拆分,并提供我们之前定义的 Early Stopping 回调。

history = lstm_model.fit(
    trainX, trainY, 
    epochs=30, 
    batch_size=16, 
    validation_split=0.1,
    shuffle=False,
    callbacks=[tf.keras.callbacks.EarlyStopping(monitor='loss', patience=3)],
)
Train on 711 samples, validate on 79 samples
Epoch 1/30
711/711 [==============================] - 7s 10ms/sample - loss: 0.4394 - val_loss: 0.3740
Epoch 2/30
711/711 [==============================] - 0s 235us/sample - loss: 0.3324 - val_loss: 0.3310
Epoch 3/30
711/711 [==============================] - 0s 228us/sample - loss: 0.3219 - val_loss: 0.3254
Epoch 4/30
711/711 [==============================] - 0s 366us/sample - loss: 0.3199 - val_loss: 0.3217
Epoch 5/30
711/711 [==============================] - 0s 230us/sample - loss: 0.3184 - val_loss: 0.3191
Epoch 6/30
711/711 [==============================] - 0s 273us/sample - loss: 0.3173 - val_loss: 0.3171
Epoch 7/30
711/711 [==============================] - 0s 283us/sample - loss: 0.3165 - val_loss: 0.3154
Epoch 8/30
711/711 [==============================] - 0s 387us/sample - loss: 0.3157 - val_loss: 0.3140
Epoch 9/30
711/711 [==============================] - 0s 344us/sample - loss: 0.3150 - val_loss: 0.3126
Epoch 10/30
711/711 [==============================] - 0s 264us/sample - loss: 0.3144 - val_loss: 0.3113
Epoch 11/30
711/711 [==============================] - 0s 252us/sample - loss: 0.3137 - val_loss: 0.3100
Epoch 12/30
711/711 [==============================] - 0s 225us/sample - loss: 0.3130 - val_loss: 0.3088
Epoch 13/30
711/711 [==============================] - 0s 231us/sample - loss: 0.3123 - val_loss: 0.3078
Epoch 14/30
711/711 [==============================] - 0s 228us/sample - loss: 0.3117 - val_loss: 0.3071
...
Epoch 30/30
711/711 [==============================] - 0s 230us/sample - loss: 0.3075 - val_loss: 0.3075

评估

训练模型后,我们可以使用 evaluate 函数对测试数据集进行批量评估。该模型的表现相当不错。

lstm_model.evaluate(testX)

我们绘制了整个历史中的训练和验证损失,以可视化训练性能。我们可以看到,模型正在稳定地学习,既不是过拟合也不是欠拟合数据。

预测未来的新点

LSTM 时间序列模型仅用于预测未来点。我们可以对未来点使用预测函数来查看模型对结果的预测能力。执行推理后,我们将结果与实际数据作对比。

y_pred = lstm_model.predict(testX)
plt.plot(testY, marker='.', label="true")
plt.plot(y_pred, 'r', label="prediction")
plt.ylabel('Value')
plt.xlabel('Time Step')
plt.legend()
plt.show();

该模型确实表现得相当不错。通过更长时间的训练、使用更多数据以及本文范围之外的许多其他方法,可以获得进一步的性能改进。

结论

  • 这篇文章教会了我们什么是 LSTM 模型以及创建它的原因。
  • 我们学习了如何在 Tensorflow 中创建 LSTM 模型。
  • 我们还学习了如何使用罪恶曲线生成时间序列数据。
  • 最后,我们在生成的数据上训练了一个 LSTM 时间序列模型。
  • 我们还学习了如何使用经过训练的模型来预测未来的点并显示其预测。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐