时间序列预测-Python中使用LSTM和PyTorch预测航班数据集

本文介绍了使用LSTM和PyTorch对时间序列数据进行预测的方法,以航班乘客数量为例。通过数据预处理、创建LSTM模型、训练和预测,展示了如何利用深度学习预测未来趋势。虽然预测结果不完全准确,但模型能捕捉到总体趋势和波动。

翻译自外网,这是我觉得少数在做预测的实验
原文链接
时间序列数据,顾名思义,是一种随时间变化的数据类型。例如,24小时时间段内的温度,一个月内各种产品的价格,某一特定公司一年内的股票价格。先进的深度学习模型,如Long Short Term Memory Networks (LSTM),能够捕捉时间序列数据中的模型,因此可以用来预测数据的未来趋势。在本文中,您将看到如何使用LSTM算法使用时间序列数据进行未来预测。

在本文中,我们将使用PyTorch库,这是深度学习中最常用的Python库之一。在继续学习之前,假设您对Python编程语言具有中级水平的熟练程度,并且已经安装了PyTorch库。此外,了解基本机器学习概念和深度学习概念也会有所帮助。如果你还没有安装PyTorch,你可以使用下面的pip命令:

pip install pytorch

数据集和问题定义

导入航班数据集 flights.csv(数据集已上传)。让我们加载数据集到我们的程序,看看它是什么样子:

import torch
import torch.nn as nn
from pandas import read_csv
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

flight_data = read_csv("flights.csv")
print(flight_data.head())

Output:
在这里插入图片描述
数据集有三列:年、月和乘客。“passengers”列包含指定月份中旅行的乘客总数。让我们绘制数据集的形状:

print(flight_data.shape)

Output:
在这里插入图片描述
可以看到数据集中有144行3列,这意味着数据集中包含了乘客12年的出行记录。这项任务是根据前132个月的数据,预测过去12个月出行的乘客数量。请记住,我们有144个月的记录,这意味着前132个月的数据将用于训练我们的LSTM模型,而模型性能将使用最后12个月的值进行评估。

让我们将数据可视化:

fig_size = plt.rcParams["figure.figsize"]
fig_size[0] = 15
fig_size[1] = 5
plt.rcParams["figure.figsize"] = fig_size
plt.title('Month vs Passenger')
plt.ylabel('Total Passengers')
plt.xlabel('Months')
plt.grid(True)
plt.autoscale(axis='x',tight=True)
plt.plot(flight_data['passengers'])
plt.show()

Output:
在这里插入图片描述
输出显示,多年来乘飞机旅行的平均人数增加了。一年内旅行的乘客数量是波动的,这是有道理的,因为在寒暑假期间,旅行的乘客数量比一年中其他时间增加。

数据预处理

第一个预处理步骤是将passengers列的类型更改为float:

all_data = flight_data['passengers'].values.astype(float)

现在,如果您打印all_data numpy数组,您应该看到以下浮动类型值:

print(all_data)

Output:
在这里插入图片描述
接下来,我们将数据集划分为训练集和测试集。LSTM算法在训练集上进行训练,然后用模型对测试集进行预测,并将预测结果与测试集中的实际值进行比较,以评估训练后的模型的性能。

前132条记录将用于训练模型,后12条记录将用作测试集。下面的脚本将数据分为训练集和测试集。

test_data_size = 12

train_data = all_data[:-test_data_size]
test_data = all_data[-test_data_size:]

现在,让我们打印测试和列车集的长度:

print(len(train_data))
print(len(test_data))

Output:
在这里插入图片描述
如果您现在打印测试数据,您将看到它包含all_data numpy数组中的最后12条记录:

print(test_data)

Output:
在这里插入图片描述
我们的数据集目前还没有规范化。最初几年的乘客总数远远少于以后几年的乘客总数。数据归一化对于时间序列预测是非常重要的。我们将对数据集执行最小/最大缩放,将数据规格化在一定的最小和最大值范围内。我们将使用sklearn中的MinMaxScaler类。

下面的代码使用最小/最大标量(最小值和最大值分别为-1和1)对数据进行规范化。

from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler(feature_range=(-1, 1))
train_data_normalized = scaler.fit_transform(train_data .reshape(-1, 1))

现在,让我们打印规范化列车数据的前5条记录和后5条记录。

print(train_data_normalized[:5])
print(train_data_normalized[-5:])

Output:
在这里插入图片描述
您可以看到数据集的值现在介于-1和1之间。

这里需要强调的是,数据归一化只适用于训练数据,而不适用于测试数据。如果对测试数据进行归一化处理,就有可能会有一些信息从训练集泄露到测试集。

下一步是将数据集转换为张量,因为PyTorch模型是使用张量进行训练的。要将数据集转换为张量,只需将数据集传递给FloatTensor对象的构造函数即可,如下所示:

train_data_normalized = torch.FloatTensor(train_data_normalized).view(-1)

最后的预处理步骤是将我们的训练数据转换成序列和相应的标签。

您可以使用任何序列长度,这取决于领域知识。然而,在我们的数据集中,使用序列长度为12是很方便的,因为我们有月度数据,一年有12个月。如果我们有每天的数据,更好的序列长度应该是365天,即一年中的天数。因此,我们将训练的输入序列长度设置为12。

train_window = 12

接下来,我们将定义一个名为create_inout_sequences的函数。该函数将接受原始输入数据,并返回一列元组。在每个元组中,第一个元素将包含12项的列表,这些项对应于12个月内旅行的乘客数量,第二个元组元素将包含一项,即12+第一个月的乘客数量。

def create_inout_sequences(input_data, tw):
    inout_seq = []
    L = len(input_data)
    for i in range(L-tw):
        train_seq = input_data[i:i+tw]
        train_label = input_data[i+tw:i+tw+1]
        inout_seq.append((train_seq ,train_label))
    return inout_seq

执行以下脚本,创建用于训练的序列和相应的标签:

train_inout_seq = create_inout_sequences(train_data_normalized, train_window)

如果您打印train_inout_seq列表的长度,您将看到它包含120个项目。这是因为虽然训练集包含132个元素,但序列长度是12,这意味着第一个序列包含前12个元素,而第13个元素是第一个序列的标签。类似地,第二个序列从第二个项开始,在第13项结束,而第14项是第二个序列的标签,依此类推。

现在,让我们输出train_inout_seq列表的前5项:
Output:
在这里插入图片描述
您可以看到,每个项都是一个元组,其中第一个元素由序列的12个项组成,第二个元组元素包含相应的标签。

创建LSTM模型

我们已经对数据进行了预处理,现在是时候训练我们的模型了。我们将定义一个继承自nn的类LSTM。PyTorch库的模块类。请参阅我的上一篇文章,了解如何使用PyTorch创建分类模型。

class LSTM(nn.Module):
    def __init__(self, input_size=1, hidden_layer_size=200, output_size=1):
        super().__init__()
        self.hidden_layer_size = hidden_layer_size

        self.lstm = nn.LSTM(input_size, hidden_layer_size)

        self.linear = nn.Linear(hidden_layer_size, output_size)

        self.hidden_cell = (torch.zeros(1,1,self.hidden_layer_size),
                            torch.zeros(1,1,self.hidden_layer_size))

    def forward(self, input_seq):
        lstm_out, self.hidden_cell = self.lstm(input_seq.view(len(input_seq) ,1, -1), self.hidden_cell)
        predictions = self.linear(lstm_out.view(len(input_seq), -1))
        return predictions[-1]

让我总结一下上面代码中发生的事情。LSTM类的构造函数接受三个形参:

  1. input_size:对应于输入中特性的数量。虽然我们的序列长度是12,但对于每个月我们只有1个值,即乘客总数,因此输入大小将是1。
  2. hidden_layer_size:指定隐藏层的数量以及每层神经元的数量。我们有一层有100个神经元。
  3. output_size:输出中的数量,因为我们要预测未来1个月的乘客数量,所以输出大小为1。

接下来,在构造函数中创建变量hidden_layer_size、lstm、linear和hidden_cell。LSTM算法接受三个输入:前一个隐藏状态、前一个单元状态和当前输入。hidden_cell变量包含以前的隐藏状态和单元格状态。lstm和linear layer变量用于创建lstm和linear layer。

在forward方法内部,input_seq作为参数传递,首先通过lstm层传递。lstm层的输出是当前时间步长的隐藏状态和单元状态,以及输出。lstm层的输出被传递到线性层。预测的乘客数量存储在预测列表的最后一项中,该列表返回给调用函数。

下一步是创建LSTM()类的一个对象,定义丢失函数和优化器。因为,我们在解决分类问题,我们将使用交叉熵损失。对于优化器函数,我们将使用adam优化器。

model = LSTM()
loss_function = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

打印我们的模型:

print(model)

Output:
在这里插入图片描述

训练模型

我们将用150个epochs训练我们的模型。如果你愿意,你可以尝试使用更多的迭代。每隔25个迭代,损失将被打印。

epochs = 150

for i in range(epochs):
    for seq, labels in train_inout_seq:
        optimizer.zero_grad()
        model.hidden_cell = (torch.zeros(1, 1, model.hidden_layer_size),
                        torch.zeros(1, 1, model.hidden_layer_size))

        y_pred = model(seq)

        single_loss = loss_function(y_pred, labels)
        single_loss.backward()
        optimizer.step()

    if i%25 == 1:
        print(f'epoch: {i:3} loss: {single_loss.item():10.8f}')

print(f'epoch: {i:3} loss: {single_loss.item():10.10f}')

Output:
在这里插入图片描述

你可能会得到不同的值,因为默认的权重是在PyTorch神经网络中随机初始化的。

做预测

既然我们的模型已经训练好了,我们就可以开始进行预测了。因为我们的测试集包含过去12个月的乘客数据,并且我们的模型被训练为使用序列长度为12进行预测。我们将首先从训练集中过滤最后12个值:

fut_pred = 12

test_inputs = train_data_normalized[-train_window:].tolist()
print(test_inputs)

Output:
在这里插入图片描述

您可以将上面的值与train_data_normalized数据列表的最后12个值进行比较。

最初test_inputs项将包含12个项。在for循环中,这12个项目将用于对测试集中的第一个项目进行预测,即项目编号133。然后,预测值将被添加到test_inputs列表中。在第二次迭代期间,最后的12个条目将再次被用作输入,并将做出新的预测,然后将再次添加到test_inputs列表中。for循环将执行12次,因为测试集中有12个元素。在循环的末尾,test_inputs列表将包含24个元素。最后12项将是测试集的预测值。

下面的脚本是用来预测的:

model.eval()

for i in range(fut_pred):
    seq = torch.FloatTensor(test_inputs[-train_window:])
    with torch.no_grad():
        model.hidden = (torch.zeros(1, 1, model.hidden_layer_size),
                        torch.zeros(1, 1, model.hidden_layer_size))
        test_inputs.append(model(seq).item())

如果您打印test_inputs列表的长度,您将看到它包含24个条目。最后12个预测项目可打印如下:

print(test_inputs[fut_pred:])

Output:
在这里插入图片描述
需要再次提到的是,根据用于训练LSTM的权重的不同,您可能得到不同的值。

由于我们对训练数据集进行了规范化,所以预测值也进行了规范化。我们需要将标准化预测值转换为实际预测值。我们可以通过将归一化值传递给min/max scaler对象的inverse_transform方法来实现这一点,该对象用于对数据集进行归一化。

actual_predictions = scaler.inverse_transform(np.array(test_inputs[train_window:] ).reshape(-1, 1))
print(actual_predictions)

Output:
在这里插入图片描述

现在让我们将预测值与实际值作图。看看下面的代码:

x = np.arange(132, 144, 1)
print(x)
plt.title('Month vs Passenger')
plt.ylabel('Total Passengers')
plt.grid(True)
plt.autoscale(axis='x', tight=True)
plt.plot(flight_data['passengers'])
plt.plot(x,actual_predictions)
plt.show()

Output:

我们的LSTM的预测用橙色线表示。你可以看到,我们的算法不是很准确,但它仍然能够捕捉到过去12个月的旅客总人数上升的趋势,以及偶尔的波动。您可以尝试使用更大数量的epoch和LSTM层中更多数量的神经元,看看是否可以获得更好的性能。

在这里插入图片描述
为了更清楚地了解客运量,我们可以将过去12个月的实际客运量和预测客运量作图如下:

plt.title('Month vs Passenger')
plt.ylabel('Total Passengers')
plt.grid(True)
plt.autoscale(axis='x', tight=True)

plt.plot(flight_data['passengers'][-train_window:])
plt.plot(x,actual_predictions)
plt.show()

Output:
在这里插入图片描述

预测不是很准确(这已经是我十次训练中最好的结果),但算法能够捕捉到未来几个月的乘客数量应该高于前几个月的趋势,偶尔会有波动。

总结

LSTM算法是求解序列问题中应用最广泛的算法之一。在本文中,我们了解了如何使用LSTM使用时间序列数据进行未来预测。您还了解了如何使用PyTorch库实现LSTM,以及如何根据实际值绘制预测结果,以查看训练过的算法的执行情况。

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值