基于 PyTorch + LSTM 进行时间序列预测(附完整源码)

时间序列数据,顾名思义是一种随时间变化的数据类型。

例如,24小时内的温度、一个月内各种产品的价格、某家公司一年内的股票价格等。深度学习模型如长短期记忆网络(LSTM)能够捕捉时间序列数据中的模式,因此可以用于预测未来趋势。

文章目录

    • 技术提升
    • 数据集和问题定义
    • 数据预处理
    • 创建LSTM模型
    • 训练模型
    • 进行预测
    • 结论

在本文中,您将看到如何使用 LSTM 算法利用时间序列数据进行未来预测,使用的是 PyTorch 库,这是最常用于深度学习的Python库之一。

在继续之前,确保已安装了 PyTorch 库。同时掌握基本机器学习和深度学习概念会有所帮助。如果尚未安装PyTorch,则可以使用以下pip命令进行安装:

$ pip install pytorch

技术提升

技术要学会分享、交流,不建议闭门造车。一个人走的很快、一堆人可以走的更远。

完整代码、数据、技术交流提升, 均可加知识星球交流群获取,群友已超过2000人,添加时切记的备注方式为:来源+兴趣方向,方便找到志同道合的朋友。

方式①、添加微信号:pythoner666,备注:来自 CSDN + python
方式②、微信搜索公众号:Python学习与数据挖掘,后台回复:加群

数据集和问题定义

我们将使用的数据集是Python Seaborn库中内置的。让我们首先导入所需的库,然后再导入数据集:

import torch
import torch.nn as nn

import seaborn as sns
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

让我们打印出Seaborn库中内置的所有数据集列表:

sns.get_dataset_names()

输出

['anscombe',
 'attention',
 'brain_networks',
 'car_crashes',
 'diamonds',
 'dots',
 'exercise',
 'flights',
 'fmri',
 'gammas',
 'iris',
 'mpg',
 'planets',
 'tips',
 'titanic']

我们将使用的数据集是航班数据集。让我们将数据集加载到我们的应用程序中,看看它的样子:

flight_data = sns.load_dataset("flights")
flight_data.head()

数据集有三列:年份、月份和乘客数。乘客列包含指定月份旅行的总人数。让我们绘制数据集的形状:

flight_data.shape

## (144, 3)

您可以看到数据集中有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'])

输出结果显示,多年来乘坐飞机旅行的平均乘客人数增加了。一年内旅行的乘客数量波动,这是有道理的,因为在暑假或寒假期间,与其他时间相比旅行乘客数量会增加。

数据预处理

我们数据集中的列类型是对象,如下代码所示:

flight_data.columns
## output
Index(['year', 'month', 'passengers'], dtype='object')

第一个预处理步骤是将乘客列的类型更改为浮点数。

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

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

print(all_data)
[112. 118. 132. 129. 121. 135. 148. 148. 136. 119. 104. 118. 115. 126.
 141. 135. 125. 149. 170. 170. 158. 133. 114. 140. 145. 150. 178. 163.
 172. 178. 199. 199. 184. 162. 146. 166. 171. 180. 193. 181. 183. 218.
 230. 242. 209. 191. 172. 194. 196. 196. 236. 235. 229. 243. 264. 272.
 237. 211. 180. 201. 204. 188. 235. 227. 234. 264. 302. 293. 259. 229.
 203. 229. 242. 233. 267. 269. 270. 315. 364. 347. 312. 274. 237. 278.
 284. 277. 317. 313. 318. 374. 413. 405. 355. 306. 271. 306. 315. 301.
 356. 348. 355. 422. 465. 467. 404. 347. 305. 336. 340. 318. 362. 348.
 363. 435. 491. 505. 404. 359. 310. 337. 360. 342. 406. 396. 420. 472.
 548. 559. 463. 407. 362. 405. 417. 391. 419. 461. 472. 535. 622. 606.
 508. 461. 390. 432.]

接下来,我们将把数据集分成训练集和测试集。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))
##
132
12

如果您现在打印测试数据,您将看到它包含了all_data numpy数组中的最后12条记录:

我们的数据集目前尚未标准化。最初几年的乘客总数远远少于后来几年的乘客总数。对于时间序列预测,将数据进行标准化非常重要。我们将在数据集上执行 min/max 缩放,该方法可以使数据在一定范围内归一化到最小值和最大值之间。

我们将使用 sklearn.preprocessing 模块中的 MinMaxScaler 类来缩放我们的数据。

以下代码使用最小值为-1,最大值为1 的 min/max 缩放器对我们的数据进行了标准化处理:

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
[[-0.96483516]
 [-0.93846154]
 [-0.87692308]
 [-0.89010989]
 [-0.92527473]]
[[1.        ]
 [0.57802198]
 [0.33186813]
 [0.13406593]
 [0.32307692]]

你可以看到数据集的值现在在-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个月内旅行的乘客数量,第二个元素将包含一项即第13个月的乘客数量。

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]
        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项:

train_inout_seq[:5]
## output
[(tensor([-0.9648, -0.9385, -0.8769, -0.8901, -0.9253, -0.8637, -0.8066, -0.8066,
          -0.8593, -0.9341, -1.0000, -0.9385]), tensor([-0.9516])),
 (tensor([-0.9385, -0.8769, -0.8901, -0.9253, -0.8637, -0.8066, -0.8066, -0.8593,
          -0.9341, -1.0000, -0.9385, -0.9516]),
  tensor([-0.9033])),
 (tensor([-0.8769, -0.8901, -0.9253, -0.8637, -0.8066, -0.8066, -0.8593, -0.9341,
          -1.0000, -0.9385, -0.9516, -0.9033]), tensor([-0.8374])),
 (tensor([-0.8901, -0.9253, -0.8637, -0.8066, -0.8066, -0.8593, -0.9341, -1.0000,
          -0.9385, -0.9516, -0.9033, -0.8374]), tensor([-0.8637])),
 (tensor([-0.9253, -0.8637, -0.8066, -0.8066, -0.8593, -0.9341, -1.0000, -0.9385,
          -0.9516, -0.9033, -0.8374, -0.8637]), tensor([-0.9077]))]

你可以看到每个项目都是一个元组,其中第一个元素包含序列的12个项目,第二个元组元素包含相应的标签。

创建LSTM模型

我们已经预处理了数据,现在是训练模型的时候了。我们将定义一个类LSTM,它继承自PyTorch库的nn.Module类。

class LSTM(nn.Module):
    def __init__(self, input_size=1, hidden_layer_size=100, 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)

    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层变量用于创建LSTM和线性层。

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

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

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

让我们打印我们的模型:

print(model)

## output
LSTM(
  (lstm): LSTM(1, 100)
  (linear): Linear(in_features=100, out_features=1, bias=True)
)

训练模型

我们将训练我们的模型150个epochs。如果您愿意,可以尝试更多的epochs。每25个epochs后会打印损失值。

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}')

输出

epoch:   1 loss: 0.00517058
epoch:  26 loss: 0.00390285
epoch:  51 loss: 0.00473305
epoch:  76 loss: 0.00187001
epoch: 101 loss: 0.00000075
epoch: 126 loss: 0.00608046
epoch: 149 loss: 0.0004329932

由于 PyTorch 神经网络默认情况下会随机初始化权重,因此您可能会得到不同的值。

进行预测

现在我们的模型已经训练好了,我们可以开始进行预测。由于测试集包含过去 12 个月的乘客数据,并且我们的模型是使用长度为 12 的序列来进行预测训练的。因此,我们将首先从训练集中筛选出最后 12 个值:

fut_pred = 12

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

output
[0.12527473270893097, 0.04615384712815285, 0.3274725377559662, 0.2835164964199066, 0.3890109956264496, 0.6175824403762817, 0.9516483545303345, 1.0, 0.5780220031738281, 0.33186814188957214, 0.13406594097614288, 0.32307693362236023]

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

最初,test_inputs 项目将包含12个项目。在for循环内部,这些12个项目将用于对测试集中的第一个项目(即第133项)进行预测。然后,预测值将附加到test_inputs列表中。在第二次迭代中,再次使用最后12个项目作为输入,并进行新的预测,然后再次将其附加到test_inputs列表中。由于测试集中有12个元素,因此for循环将执行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个预测项可以按如下方式打印:

test_inputs[fut_pred:]
[0.4574652910232544,
 0.9810629487037659,
 1.279405951499939,
 1.0621851682662964,
 1.5830546617507935,
 1.8899496793746948,
 1.323508620262146,
 1.8764172792434692,
 2.1249167919158936,
 1.7745600938796997,
 1.7952896356582642,
 1.977765679359436]

需要再次提到的是,根据用于训练 LSTM 的权重不同,您可能会得到不同的值。

由于我们对数据集进行了归一化处理以进行训练,因此预测值也被归一化。我们需要将归一化后的预测值转换为实际预测值。我们可以通过将归一化后的值传递给最小/最大缩放器对象的inverse_transform方法来实现这一点。

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

## output
[[435.57335371]
 [554.69182083]
 [622.56485397]
 [573.14712578]
 [691.64493555]
 [761.46355206]
 [632.59821111]
 [758.38493103]
 [814.91857016]
 [735.21242136]
 [739.92839211]
 [781.44169205]]

现在让我们将预测值与实际值绘制出来。请看以下代码:

x = np.arange(132, 144, 1)
print(x)

## output 
[132 133 134 135 136 137 138 139 140 141 142 143]

在上面的脚本中,我们创建了一个包含过去12个月数值的列表。第一个月的索引值为0,因此最后一个月将在索引143处。

在下面的脚本中,我们将绘制144个月的乘客总数以及最后12个月预测的乘客数量。

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()

我们的LSTM所做出的预测由橙色线条表示。您可以看到,我们的算法并不是非常准确,但仍然能够捕捉到过去12个月旅客总数上升趋势以及偶发波动。您可以尝试增加训练轮数和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()

结论

LSTM 是解决序列问题最广泛使用的算法之一。在本文中,我们看到了如何使用 LSTM 对时间序列数据进行未来预测。

您还学会了如何使用 PyTorch 库实现 LSTM,并将预测结果与实际值绘制在一起以查看训练好的算法表现如何。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/3520.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C/C++】程序的内存开辟

在C/C语言中,不同的类型开辟的空间区域都是不一样的. 这节我们就简单了解下开辟不同的类型内存所存放的区域在哪里. 文章目录栈区(stack)堆区(heap)数据段(静态区)常量存储区内存开辟布局图栈区…

批量保存网页为单个网页文件

有时候,总有会遇到一些奇怪的需求,各种搜索都找不到答案,本次记录批量保存网页到单个网页文件。使用背景:只想简单的解决问题,不涉及编程网页带格式,将网页存为PDF格式会变量太大,一个个的处理太累涉及技术…

《毫无意义的工作》读书思考——互联网中,技术管理岗的价值或作用是什么?

目录 一、背景 二、书中咋说的 三、价值或作用可以从哪些方面思考? 四、写在最后 一、背景 日常工作中,自己经历的、身边人的吐槽,常常会有对纯技术管理者意义的怀疑。工作若干年,遇到各种各样的管理者,但让人吐槽…

【2023年第十一届泰迪杯数据挖掘挑战赛】B题:产品订单的数据分析与需求预测 建模及python代码详解 问题一

相关链接 【2023年第十一届泰迪杯数据挖掘挑战赛】B题:产品订单的数据分析与需求预测 建模及python代码详解 问题一 【2023年第十一届泰迪杯数据挖掘挑战赛】B题:产品订单的数据分析与需求预测 建模及python代码详解 问题二 1 题目 一.问题…

【Linux】进程理解与学习Ⅲ-环境变量

环境:centos7.6,腾讯云服务器Linux文章都放在了专栏:【Linux】欢迎支持订阅🌹相关文章推荐:【Linux】冯.诺依曼体系结构与操作系统【Linux】进程理解与学习Ⅰ-进程概念浅谈Linux下的shell--BASH【Linux】进程理解与学习…

学习系统编程No.10【文件描述符】

引言: 北京时间:2023/3/25,昨天摆烂一天,今天再次坐牢7小时,难受尽在不言中,并且对于笔试题,还是非常的困难,可能是我做题不够多,也可能是没有好好的总结之前做过的一些…

UE4/5 C++网络服务器编程纪录【零】--准备篇

前言之前利用业余时间重新复习UE4/5的C开发,闲来无事做了个基于独立服务器的多人在线(目前限定客户数量是20人以内)DEMO,核心功能在我之前发的B站视频里面有,战斗、动作、交互以及场景演示都有了,有朋友看了…

Spring容器实现原理-Spring的结构组成与核心类

Spring容器基本用法 bean是Spring中最核心的东西,因为Spring就像是个大水桶,而bean就像是容器中的水,水桶脱离了水便没有什么用处了,让我们先看看bean的定义: /*** ClassName MyTestBean* Author jiaxinxiao* Date 2…

2021全球开放数据应用创新大赛-法律咨询问答亚军方案

赛题分析 任务:给定用户问题,根据多个候选答案生成回复,属于文本生成任务。 问题信用逾期了,银行打电话骚扰我父母,改如何处理候选答案1. 按照约定还款 2.报警标准回复你好,这种情况只能按照约定还款&…

Python 练习 六

1、(最大数的出现)编写程序读取整数,找出它们中的最大值,然后计算它的出现次数。假设输入以数字0结束。假设你输入的是“352555 0";程序找出的最大数是5,而5的出现次数是4。(提示:维护两个变量max和 count。变量max存储的是当前最大数,而…

CentOS 7安装redis6.2.6(包括服务开机自启和开放端口)

CentOS 7安装redis6.2.61. 官网下载redis文件2. 校验安装依赖2.1 安装系统默认版本gcc2.2 升级gcc版本3. 解压编译安装4. 修改配置redis.conf4.2 设置密码4.3 绑定ip(可选)5. 启动redis服务并测试5.2 测试安装是否成功5.3 redis开机自启配置6.开放防火墙…

QT表格控件实例(Table Widget 、Table View)

欢迎小伙伴的点评✨✨,相互学习🚀🚀🚀 博主🧑🧑 本着开源的精神交流Qt开发的经验、将持续更新续章,为社区贡献博主自身的开源精神👩‍🚀 文章目录前言一、图示实例二、列…

art 虚拟机相关

​​​​​​​虚拟机中对象锁实现分析 Android 库加载命名空间 https://source.android.google.cn/docs/core/architecture/vndk/linker-namespace Android共享库命名空间-CSDN博客 编译 prebuilts/clang/host/linux-x86/clang-r450784d/bin/llvm-addr2line -f -C -e…

项目文章 | 缓解高胆固醇血症 ,浒苔多糖如何相助?

文章标题:Polysaccharides from Enteromorpha prolifera alleviate hypercholesterolemia via modulating the gut microbiota and bile acid metabolism 发表期刊:Food & Function 影响因子:6.317 作者单位:福建医科大…

关于Docker逃逸

关于Docker逃逸 文章目录关于Docker逃逸前言一、判断是否为docker容器?二、privileged特权模式启动容器逃逸三、 Docker Remote API未授权访问逃逸四、危险挂载导致Docker逃逸五、危险挂载Docker Socket逃逸六、 挂载宿主机procfs逃逸七、脏牛漏洞来进行docker逃逸八…

蓝桥杯C/C++VIP试题每日一练之矩形面积交

💛作者主页:静Yu 🧡简介:CSDN全栈优质创作者、华为云享专家、阿里云社区博客专家,前端知识交流社区创建者 💛社区地址:前端知识交流社区 🧡博主的个人博客:静Yu的个人博客 🧡博主的个人笔记本:前端面试题 个人笔记本只记录前端领域的面试题目,项目总结,面试技…

Qt容器学习

Qt容器Qt容器主要优点就是在所有的平台上的运行都表现的一致,并且它们都是隐含共享的.Qt容器的另外一个主要特征就是易于使用的迭代器类,它们可以利用QDataStream变成数据流,而且他们通常可以使用执行文件中的代码量比相应的STL类中的要少.最后Qt/Embedd…

动态规划---线性dp和区间dp

动态规划(三) 目录动态规划(三)一:线性DP1.数字三角形1.1数字三角形题目1.2代码思路1.3代码实现(正序and倒序)2.最长上升子序列2.1最长上升子序列题目2.2代码思路2.3代码实现3.最长公共子序列3.1最长公共子序列题目3.2代码思路3.3代码实现4.石子合并4.1题目如下4.2代…

论文解读:Less is More: Learning Highlight Detection from Video Duration

引言 高亮检测有可能极大地简化视频浏览,但现有的方法往往受到昂贵的监督要求的影响,人类观众必须手动识别训练视频中的高亮部分。我们提出了一种可扩展的无监督解决方案,利用视频时长作为隐式监督信号。我们的关键见解是,来自较…

【lwIP(第三章)】内存管理

目录一、内存管理简介二、lwIP内存堆和内存池应用三、lwIP内存堆简介1. First Fit算法2. lwIP内存堆原理解析2.1 mem_init程序解析2.2 mem_malloc程序解析2.3 mem_free程序解析四、lwIP内存池简介1. 实现lwIP内存池的文件2. lwIP内存池函数2.1 memp_init()2.2 memp_malloc()2.3…
最新文章