使用PyTorch训练一个LSTM预测模型

使用PyTorch训练一个LSTM预测模型。

微信搜索关注《Python学研大本营》,加入读者群,分享更多精彩

图片

本文将参照如下notebook演示如何训练一个长短期记忆模型(LSTM),并且快速对比它与其他模型的性能。

【notebook】:https://github.com/microprediction/timeseries-notebooks/blob/main/pytorch_lstm.ipynb

图片

LSTM预测和实际波动率相关的时间序列

获取一些数据

选择一个数据数据流:

import matplotlib.pyplot as plt
from microprediction import MicroReader

# 初始化MicroReader
reader = MicroReader()

# 获取一个数据流名称的列表
stream_names = reader.get_stream_names()

# 从列表中选择第一个数据流
stream = stream_names[50]

# 或者硬连接它...
stream = 'yarx_vlty_2_mo.json'

# 获取历史数据(返回一个值的列表)
history = reader.get_lagged_values(name=stream)

# 绘制历史数据
plt.plot(history[:30])
plt.xlabel("Time")
plt.ylabel("Value")
plt.title(f"Historical Data for '{stream}'")
plt.show()

整理训练数据

现在我们需要将它变成PyTorch的回归数据格式。这是我们需要的模板代码,建议保存备用。

import numpy as np
import torch

def create_sequences(data, seq_length):
    xs, ys = [], []
    for i in range(len(data) - seq_length - 1):
        x = data[i:(i + seq_length)]
        y = data[i + seq_length]
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

这个函数的目的是为了生成用于训练和测试时间序列预测模型的输入-输出对。输入-输出对是通过在时间序列数据上滑动一个给定长度(seq_length)的窗口来创建的。对于每次迭代,它通过从索引i到索引i+seq_length切片数据来创建一个输入序列x。然后通过选择索引i+seq_length处的数据点来创建相应的目标值y

分离和转换

PyTorch需要张量格式,而不是numpy,因此:

# 将数据分成训练集和测试集
train_size = int(len(y) * 0.8)
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]

# 转换为PyTorch张量
X_train = torch.from_numpy(X_train).float()
y_train = torch.from_numpy(y_train).float()
X_test = torch.from_numpy(X_test).float()
y_test = torch.from_numpy(y_test).float()

定义模型

这段代码定义了一个名为LSTM的PyTorch自定义nn.Module类,它代表了时间序列预测的长短期记忆(LSTM)神经网络模型。

import torch.nn as nn

class LSTM(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, output_size):
        super(LSTM, self).__init__()
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size)
        c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size)
        
        out, _ = self.lstm(x, (h0, c0))
        out = self.fc(out[:, -1, :])
        return out

解释一下输入的情况:

  • input_size:每个时间步骤的输入特征数。

  • hidden_size:每个LSTM层中的隐藏单元数。

  • num_layers:堆叠的LSTM层数。

  • output_size:输出的大小(例如,预测值的数量)。

由于我们有单变量目标,我们将这样使用它:

# 初始化LSTM模型
input_size = 1
hidden_size = 50
num_layers = 1
output_size = 1
model = LSTM(input_size, hidden_size, num_layers, output_size)

你可能想要尝试一下这个笔记本,并改变隐藏层的数量或大小。另一件可以尝试的事情是学习率和训练周期数。

# 设置训练参数
learning_rate = 0.01
num_epochs = 100

但无论如何,跟随本文继续。将会选择ADAM,因为它大多数时候都很好用。

# 定义损失函数和优化器
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

# 训练模型
for epoch in range(num_epochs):
    outputs = model(X_train.unsqueeze(-1)).squeeze()  # Add .squeeze() here
    optimizer.zero_grad()
    loss = criterion(outputs, y_train)
    loss.backward()
    optimizer.step()

    if (epoch + 1) % 10 == 0:
        print(f"Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}")

仔细阅读。这里有一些陷阱和与维度相关的小麻烦。示例如下:

outputs = model(X_train.unsqueeze(-1)).squeeze()

X_train.unsqueeze(-1)将一个额外的维度添加到X_train张量中,使其具有形状(batchsequencefeature)。这是必要的,因为LSTM模型期望以这种格式输入张量。

model(X_train.unsqueeze(-1))将重塑后的X_train张量通过LSTM模型,生成输出预测。输出张量的形状为(batchoutput_size)。在这里squeeze()会从输出张量中删除任何大小为1的维度,从而简化后续计算。

其余部分更简单。optimizer.zero_grad()在开始新的优化迭代之前重置了模型参数的梯度。这很重要,因为在PyTorch中默认情况下梯度会累积,如果不将其清零,则会导致在后续迭代中出现不正确的梯度计算。

测试

由于我们不需要学习,只需要计算,所以不需要梯度:

with torch.no_grad():
    test_outputs = model(X_test.unsqueeze(-1)).squeeze()  # Add .squeeze() here
    test_loss = criterion(test_outputs, y_test)
    print(f"Test Loss: {test_loss.item():.4f}")

在本文的示例中,测试损失为0.0092。让我们将其与学习时的训练损失进行比较:

Epoch [10/100], Loss: 0.0283
Epoch [20/100], Loss: 0.0184
Epoch [30/100], Loss: 0.0163
Epoch [40/100], Loss: 0.0132
Epoch [50/100], Loss: 0.0104
Epoch [60/100], Loss: 0.0101
Epoch [70/100], Loss: 0.0097
Epoch [80/100], Loss: 0.0094
Epoch [90/100], Loss: 0.0090
Epoch [100/100], Loss: 0.0086

根据对比可以知道不应该再训练了,因为我们可能是在过度拟合。

基准测试

请参阅notebook中的绘图代码:

图片

好了,我们可以进行预测,让我们与timemachines包中的一些非常简单的模型进行比较。先来一个辅助函数:

图片

预测训练好的torch模型时关闭梯度。

def lstm_predict(model, input_data):
    input_data = torch.tensor(input_data).float().unsqueeze(0).unsqueeze(-1)
    with torch.no_grad():
        prediction = model(input_data).squeeze().item()
    return prediction

然后可以按以下方式进行基准测试:

from sklearn.metrics import mean_squared_error
from timemachines.skaters.simple.thinking import thinking_fast_and_slow as f 

lstm_predictions = []
fast_slow_predictions = []

s = {}
for i in range(seq_length, len(history)):
    input_data = history[i - seq_length:i]
    lstm_pred = lstm_predict(model, input_data)
    lstm_predictions.append(lstm_pred)
    fast_slow_pred, _, s = f(y=history[i - 1],s=s,k=1)
    fast_slow_predictions.append(fast_slow_pred)

lstm_mse = mean_squared_error(history[seq_length:], lstm_predictions)
fast_slow_mse = mean_squared_error(history[seq_length:], fast_slow_predictions)

print("LSTM Mean Squared Error:", lstm_mse)
print("Thinking Fast and Slow Mean Squared Error:", fast_slow_mse)

这段代码通过计算一组给定时间序列数据集(history)上它们的均方误差(MSE)来比较之前训练的LSTM模型与timemachines包中的thinking_fast_and_slow函数的性能。thinking_fast_and_slow函数是一个简单的时间序列预测模型,它结合了指数平滑法和残差追踪法。

比较结果:

thinking_fast_and_slow模型不如我们训练的LSTM。

图片

在提供的代码中,LSTM模型的性能仅与timemachines包中的thinking_fast_and_slow函数进行了比较。然而,为了更全面地了解LSTM模型的表现,测试其与timemachines包中的多个模型进行比较将是有益的。timemachines包提供了几个其他模型,每个模型实现了不同的时间序列预测方法。一些常见的模型包括:

  1. tbats(三角函数季节性,Box-Cox转换,ARIMA误差,趋势和季节分量)

  2. prophet(Facebook的时间序列预测模型)

要测试LSTM模型与这些模型的性能,你可以按照提供的代码类似的过程进行。导入所需的模型,初始化用于存储其预测结果的列表,并迭代时间序列数据以获取每个模型的预测。最后,计算每个模型的预测的均方误差(MSE)并将其与LSTM模型的MSE进行比较。

通过比较LSTM模型与多个模型中的表现,你可以更好地了解其在各种时间序列预测方法中的相对表现。如果想了解更多内容建议你参阅文档(https://microprediction.github.io/timemachines/)。

 推荐书单

【秋日阅读企划】点击下方链接立享优惠,叠加五折使用

秋日阅读企划icon-default.png?t=N7T8https://pro.m.jd.com/mall/active/2Z3HoZGKy5i9aEpmoTUZnmcoAhHg/index.html

《PyTorch深度学习简明实战》

本书针对深度学习及开源框架——PyTorch,采用简明的语言进行知识的讲解,注重实战。全书分为4篇,共19章。深度学习基础篇(第1章~第6章)包括PyTorch简介与安装、机器学习基础与线性回归、张量与数据类型、分类问题与多层感知器、多层感知器模型与模型训练、梯度下降法、反向传播算法与内置优化器。计算机视觉篇(第7章~第14章)包括计算机视觉与卷积神经网络、卷积入门实例、图像读取与模型保存、多分类问题与卷积模型的优化、迁移学习与数据增强、经典网络模型与特征提取、图像定位基础、图像语义分割。自然语言处理和序列篇(第15章~第17章)包括文本分类与词嵌入、循环神经网络与一维卷积神经网络、序列预测实例。生成对抗网络和目标检测篇(第18章~第19章)包括生成对抗网络、目标检测。

本书适合人工智能行业的软件工程师、对人工智能感兴趣的学生学习,同时也可作为深度学习的培训教程。
PyTorch深度学习简明实战icon-default.png?t=N7T8https://item.jd.com/13512395.html

精彩回顾

《手把手教你升级PyTorch 2.0和CUDA》

《使用TensorFlow训练深度学习模型实战指南(下)》

《使用TensorFlow训练深度学习模型实战指南(上)》

《10个Python代码分析工具,助力高效编程》

《使用Scikit-Learn,快速掌握机器学习预测方法》

《GPT4ALL:终极开源大语言模型解决方案》

微信搜索关注《Python学研大本营》,加入读者群

访问【IT今日热榜】,发现每日技术热点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值