翻译自外网,这是我觉得少数在做预测的实验
原文链接
时间序列数据,顾名思义,是一种随时间变化的数据类型。例如,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类的构造函数接受三个形参:
- input_size:对应于输入中特性的数量。虽然我们的序列长度是12,但对于每个月我们只有1个值,即乘客总数,因此输入大小将是1。
- hidden_layer_size:指定隐藏层的数量以及每层神经元的数量。我们有一层有100个神经元。
- 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,以及如何根据实际值绘制预测结果,以查看训练过的算法的执行情况。
本文介绍了使用LSTM和PyTorch对时间序列数据进行预测的方法,以航班乘客数量为例。通过数据预处理、创建LSTM模型、训练和预测,展示了如何利用深度学习预测未来趋势。虽然预测结果不完全准确,但模型能捕捉到总体趋势和波动。
4426

被折叠的 条评论
为什么被折叠?



