BiLSTM 实现股票多变量时间序列预测(PyTorch版)

羊市
前言

系列专栏:【深度学习:算法项目实战】✨︎
涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域,讨论了各种复杂的深度神经网络思想,如卷积神经网络、循环神经网络、生成对抗网络、门控循环单元、长短期记忆、自然语言处理、深度强化学习、大型语言模型和迁移学习。

在数据科学领域中,时间序列预测一直是一个重要且挑战性的任务。随着深度学习技术的兴起,特别是循环神经网络(RNN)及其变种如长短期记忆网络(LSTM)和门控循环单元(GRU)的引入,时间序列预测的性能得到了显著提升。在这些模型中,双向长短期记忆网络(BiLSTM)因其能够同时捕获序列中的前向和后向信息,而在许多应用中表现出色。

在本文中,我们将探讨如何使用PyTorch框架实现基于双向长短期记忆网络(BiLSTM)的多变量时间序列预测模型。多变量时间序列预测涉及多个输入变量和一个或多个输出变量,这些变量随时间变化并可能相互影响。通过利用BiLSTM的双向特性,我们的模型将能够同时考虑历史数据和未来趋势,从而更准确地预测时间序列的未来值。

BiLSTM 实现股票多变量时间序列预测

  • 1. 股票时间序列数据
    • 1.1 数据预处理
    • 1.2 数据可视化
  • 2. 时间数据特征工程(APPL)
    • 2.1 特征缩放(归一化)
    • 2.2 数据集划分(TimeSeriesSplit)
    • 2.3 数据集张量(TensorDataset)
  • 3. 构建时间序列模型(BiLSTM)
    • 3.1 构建Bi-LSTM 模型
    • 3.2 定义模型、损失函数与优化器
  • 4. 模型训练与可视化
    • 4.1 模型训练与权重更新(保存最佳模型)
    • 4.2 可视化训练过程(Loss损失)
  • 5. 模型评估与可视化
    • 5.1 评估指标(MAE、RMSE、MAPE)
    • 5.2 反归一化
    • 5.3 结果可视化
  • 6. 模型预测
    • 6.1 转换最新时间步收盘价的数组为张量
    • 6.2 预测下一个时间点的收盘价格

1. 股票时间序列数据

股票时间序列数据是指按照时间顺序排列的关于股票市场的历史数据。这些数据记录了股票市场在不同时间点上的各种信息,如股票的开盘价、最高价、最低价、收盘价、成交量等。时间序列数据是金融市场分析的重要基础,因为它们反映了市场参与者的行为、市场供求关系以及宏观经济和政策的影响等因素。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as snsfrom sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import TimeSeriesSplitimport torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
from torchinfo import summary
from tqdm import tqdm
np.random.seed(0)
torch.manual_seed(0)

1.1 数据预处理

pandas.to_datetime 函数将标量、数组、Series 或 DataFrame/dict-like 转换为 pandas datetime 对象。

AAPL = pd.read_csv('AAPL.csv')
print(type(AAPL['Close'].iloc[0]),type(AAPL['Date'].iloc[0]))
# Let's convert the data type of timestamp column to datatime format
AAPL['Date'] = pd.to_datetime(AAPL['Date'])
print(type(AAPL['Close'].iloc[0]),type(AAPL['Date'].iloc[0]))# Selecting subset
cond_1 = AAPL['Date'] >= '2021-04-23 00:00:00'
cond_2 = AAPL['Date'] <= '2024-04-23 00:00:00'
AAPL = AAPL[cond_1 & cond_2].set_index('Date')
print(AAPL.shape)
<class 'numpy.float64'> <class 'str'>
<class 'numpy.float64'> <class 'pandas._libs.tslibs.timestamps.Timestamp'>
(755, 6)

1.2 数据可视化

收盘价是股票在正常交易日的最后交易价格。股票的收盘价是投资者跟踪其长期表现的标准基准。

# plt.style.available
plt.style.use('_mpl-gallery')
plt.figure(figsize=(18,6))
plt.title('Close Price History')
plt.plot(AAPL['Close'],label='AAPL')
plt.ylabel('Close Price USD ($)', fontsize=18)
plt.legend()
plt.show()

收盘价

2. 时间数据特征工程(APPL)

在时间序列分析中,时间窗口通常用于描述在训练模型时考虑的连续时间步 time steps 的数量。这个时间窗口的大小,即 window_size,对于模型预测的准确性至关重要。

具体来说,window_size 决定了模型在做出预测时所使用的历史数据的长度。例如,如果我们想要用前60天的股票数据来预测未来7天的收盘价,那么window_size 就是60。

# 设置时间窗口大小
window_size = 60

除此之外还需构建序列函数,该函数需要两个参数:datasetlookback,前者是要转换成数据集的 NumPy 数组,后者是用作预测下一个时间段的输入变量的前一时间步数,默认设为 1。

# 构造序列数据函数,若target为 Close,即索引为 3
def create_dataset(dataset, lookback= 1):X, y = [], []for i in range(len(dataset)-lookback): feature = dataset[i:(i+lookback), :]target = dataset[i + lookback, 3]X.append(feature)y.append(target)return np.array(X), np.array(y)

2.1 特征缩放(归一化)

MinMaxScaler() 函数主要用于将特征数据按比例缩放到指定的范围。默认情况下,它将数据缩放到[0, 1]区间内,但也可以通过参数设置将数据缩放到其他范围。在机器学习中,MinMaxScaler()函数常用于不同尺度特征数据的标准化,以提高模型的泛化能力。

# 选取 AAPL[['Open', 'High', 'Low', 'Close']]作为特征, 归一化数据
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(AAPL[['Open', 'High', 'Low', 'Close']].values)# 获取反归一化参数(即原始数据的最小值和最大值)
original_min = scaler.data_min_
original_max = scaler.data_max_# scale_params是一个包含所有特征反归一化参数的列表或数组,
# 其中第四个元素是Close价格的反归一化参数
scale_params = original_max - original_min

2.2 数据集划分(TimeSeriesSplit)

TimeSeriesSplit() 函数与传统的交叉验证方法不同,TimeSeriesSplit 特别适用于需要考虑时间顺序的数据集,因为它确保测试集中的所有数据点都在训练集数据点之后,并且可以分割多个训练集和测试集。

# 创建数据集,数据形状为 [samples, time steps, features]
X, y = create_dataset(scaled_data, lookback = window_size)
print(X.shape, y.shape)
(695, 60, 4) (695,)
# 使用TimeSeriesSplit划分数据集,根据需要调整n_splits
tscv = TimeSeriesSplit(n_splits=3, test_size=90)
# 遍历所有划分进行交叉验证
for i, (train_index, test_index) in enumerate(tscv.split(X)):X_train, X_test = X[train_index], X[test_index]y_train, y_test = y[train_index], y[test_index]# print(f"Fold {i}:")# print(f"  Train: index={train_index}")# print(f"  Test:  index={test_index}")# 查看最后一个 fold 数据帧的维度
print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)
(605, 60, 4) (90, 60, 4) (605,) (90,)

2.3 数据集张量(TensorDataset)

张量是一个多维数组或矩阵的数学对象,可以看作是向量和矩阵的推广。在深度学习中,张量通常用于表示输入数据、模型参数以及输出数据

# 将 NumPy数组转换为 tensor张量
X_train_tensor = torch.from_numpy(X_train).type(torch.Tensor)
X_test_tensor = torch.from_numpy(X_test).type(torch.Tensor)
y_train_tensor = torch.from_numpy(y_train).type(torch.Tensor).view(-1, 1)
y_test_tensor = torch.from_numpy(y_test).type(torch.Tensor).view(-1, 1)print(X_train_tensor.shape, X_test_tensor.shape, y_train_tensor.shape, y_test_tensor.shape)

view() 函数用于重塑张量对象,它等同于 NumPy 中的 reshape() 函数,允许我们重组数据,以匹配 BiLSTM 模型所需的输入形状。以这种方式重塑数据可确保 BiLSTM 模型以预期格式接收数据。

torch.Size([605, 60, 4]) torch.Size([90, 60, 4]) torch.Size([605, 1]) torch.Size([90, 1])

接下来,我们将使用 TensorDatasetDataLoader创建数据集和数据加载器

train_dataset = TensorDataset(X_train_tensor, y_train_tensor)
train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_dataset = TensorDataset(X_test_tensor, y_test_tensor)
test_loader = DataLoader(test_dataset, batch_size=32, shuffle=False)

shuffle=True 表示在每个epoch开始时,数据集将被随机打乱,这有助于防止模型在训练时过拟合。与训练数据加载器类似,shuffle=False 表示在测试时不需要打乱数据集。因为测试集通常用于评估模型的性能,而不是用于训练,所以不需要打乱。

3. 构建时间序列模型(BiLSTM)

双向LSTM(Bi-directional Long Short-Term Memory)网络可以通过使用 nn.LSTM 类并设置 bidirectional=True 参数来完成

PyTorch所提供的数学公式及解释如下:🔗


Apply a multi-layer long short-term memory (LSTM) RNN to an input sequence. For each element in the input sequence, each layer computes the following function:
i t = σ ( W i i x t + b i i + W h i h t − 1 + b h i ) f t = σ ( W i f x t + b i f + W h f h t − 1 + b h f ) g t = tanh ⁡ ( W i g x t + b i g + W h g h t − 1 + b h g ) o t = σ ( W i o x t + b i o + W h o h t − 1 + b h o ) c t = f t ⊙ c t − 1 + i t ⊙ g t h t = o t ⊙ tanh ⁡ ( c t ) \begin{array}{ll} \\ i_t = \sigma(W_{ii} x_t + b_{ii} + W_{hi} h_{t-1} + b_{hi}) \\ f_t = \sigma(W_{if} x_t + b_{if} + W_{hf} h_{t-1} + b_{hf}) \\ g_t = \tanh(W_{ig} x_t + b_{ig} + W_{hg} h_{t-1} + b_{hg}) \\ o_t = \sigma(W_{io} x_t + b_{io} + W_{ho} h_{t-1} + b_{ho}) \\ c_t = f_t \odot c_{t-1} + i_t \odot g_t \\ h_t = o_t \odot \tanh(c_t) \\ \end{array} it=σ(Wiixt+bii+Whiht1+bhi)ft=σ(Wifxt+bif+Whfht1+bhf)gt=tanh(Wigxt+big+Whght1+bhg)ot=σ(Wioxt+bio+Whoht1+bho)ct=ftct1+itgtht=ottanh(ct)
where h t h_t ht is the hidden state at time t t t, c t c_t ct is the cell state at time t t t, x t x_t xt is the input at time t t t, h t − 1 h_{t-1} ht1 is the hidden state of the layer at time t − 1 t-1 t1 or the initial hidden state at time 0 0 0, and i t i_t it, f t f_t ft, g t g_t gt, o t o_t ot are the input, forget, cell, and output gates, respectively. σ \sigma σ is the sigmoid function, and ⊙ \odot is the Hadamard product.

In a multilayer LSTM, the input x t ( l ) x^{(l)}_t xt(l) of the l l l -th layer ( l ≥ 2 l \ge 2 l2) is the hidden state h t ( l − 1 ) h^{(l-1)}_t ht(l1) of the previous layer multiplied by dropout δ t ( l − 1 ) \delta^{(l-1)}_t δt(l1) where each δ t ( l − 1 ) \delta^{(l-1)}_t δt(l1) is a Bernoulli random variable which is 0 0 0 with probability d r o p o u t dropout dropout.

If proj_size > 0 is specified, LSTM with projections will be used. This changes the LSTM cell in the following way. First, the dimension of h t h_t ht will be changed from hidden_size to proj_size (dimensions of W h i W_{hi} Whi will be changed accordingly). Second, the output hidden state of each layer will be multiplied by a learnable projection matrix: h t = W h r h t h_t = W_{hr}h_t ht=Whrht. Note that as a consequence of this, the output of LSTM network will be of different shape as well. See Inputs/Outputs sections below for exact dimensions of all variables. You can find more details in https://arxiv.org/abs/1402.1128.


3.1 构建Bi-LSTM 模型

class BiLSTM(nn.Module):def __init__(self, input_size, hidden_size, num_layers, output_size):# input_size 是输入特征的维度,hidden_size 是LSTM隐藏层神经单元维度(或称为隐藏状态的大小),# num_layers 是LSTM网络层数,output_size 是输出维度super(BiLSTM, self).__init__()# 通过调用 super(BiLSTM, self).__init__() 初始化父类 nn.Moduleself.hidden_size = hidden_sizeself.num_layers = num_layersself.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, bidirectional=True)# 定义 LSTM 层,使用 batch_first=True 表示输入数据的形状是 [batch_size, seq_len(time_steps), input_size]# 使用 bidirectional=True 表示的是 bidirectional LSTM,即双向 LSTMself.fc = nn.Linear(hidden_size * 2, output_size)  # 定义全连接层,将 LSTM 的最后一个隐藏状态映射到输出维度 output_size,双向LSTM的输出维度是隐藏层维度的两倍def forward(self, x):# 初始化隐藏状态(hidden state)和单元状态(cell state)为全零张量h0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size).to(x.device)  # 双向LSTM,所以是2倍c0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size).to(x.device)  # LSTM输出, (output, (hn, cn))out, _ = self.lstm(x, (h0, c0))  # out: tensor of shape (batch_size, seq_length(time_steps), hidden_size*2)# 取最后一个时间步的输出(或者对输出进行平均/最大池化等操作)# 这里我们选择最后一个时间步的输出out = self.fc(out[:, -1, :])return out

在时间序列模型中 seq_len 代表的就是时间步 time_steps
batch_first=True 表示输入数据的形状是 [batch_size, seq_len, input_size],输出数据形状是 [batch_size, seq_len, num_directions * hidden_size]
batch_first=False 表示输入数据的形状是 [seq_len, batch_size, input_size],输出数据形状是 [seq_len, batch_size, num_directions * hidden_size]

3.2 定义模型、损失函数与优化器

在神经网络中,模型通常指的是神经网络的架构,它定义了输入数据如何通过网络进行转换和计算,从而得到输出。在本案例中我们可以通过调用 BiLSTM 类来定义 Bi-directional Long Short-Term Memory

损失函数(又称代价函数)是衡量模型预测值与真实值之间不一致性的指标。在训练神经网络时,我们的目标是找到一组网络参数,它使得损失函数的值最小化。损失函数的选择取决于问题的类型,例如,对于回归问题,常用的损失函数包括均方误差(MSE);对于分类问题,则常使用交叉熵损失等。在PyTorch中,可以使用torch.nn模块中的损失函数类,如nn.MSELoss用于回归问题,nn.CrossEntropyLoss用于分类问题

优化器的任务是通过更新网络的权重和偏置来最小化损失函数。这通常是通过反向传播算法完成的,该算法计算损失相对于每个参数的梯度,并使用梯度下降或其变体来更新参数。在PyTorch中,torch.optim模块提供了多种优化器实现,如随机梯度下降(SGD)、带动量的SGD(Momentum)、RMSProp、Adam等。这些优化器都是基于梯度下降算法进行改进的,旨在更有效地更新模型参数。

model = BiLSTM(input_size = X_train_tensor.size(2), # 输入数据的特征数量 X_train.shape[2]hidden_size = 64,num_layers = 3,output_size = 1)
criterion = torch.nn.MSELoss() # 定义均方误差损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.01) # 定义优化器
summary(model, (32, 60, 4)) # batch_size, seq_len(time_steps), input_size
==========================================================================================
Layer (type:depth-idx)                   Output Shape              Param #
==========================================================================================
BiLSTM                                   [32, 1]                   --
├─LSTM: 1-1                              [32, 60, 128]             234,496
├─Linear: 1-2                            [32, 1]                   129
==========================================================================================
Total params: 234,625
Trainable params: 234,625
Non-trainable params: 0
Total mult-adds (Units.MEGABYTES): 450.24
==========================================================================================
Input size (MB): 0.03
Forward/backward pass size (MB): 1.97
Params size (MB): 0.94
Estimated Total Size (MB): 2.94
==========================================================================================

4. 模型训练与可视化

4.1 模型训练与权重更新(保存最佳模型)

train_losses = [] # 初始化列表来存储损失值
test_losses = [] # 初始化列表来存储损失值
best_loss = float('100') # 初始化为一个很大的值
best_model = None # 初始化最佳模型为None# 训练循环
num_epochs = 20  # 假设我们要训练 20个 epoch
for epoch in range(num_epochs):model.train()  # 初始化训练进程train_loss = 0.0  # 初始化每个epoch的损失总和pbar = tqdm(train_loader, desc=f"Epoch {epoch+1}/{num_epochs}")for batch_idx, (data, target) in enumerate(pbar):optimizer.zero_grad()  # 将优化器中所有参数的梯度归零output = model(data)  # 每个批次的预测值loss = criterion(output, target)  # 计算模型损失值(y_train_pred 与 y_train)# 累加每个批次的损失train_loss += loss.item() * data.size(0)# 反向传播和优化loss.backward()optimizer.step()# 更新进度条(显示当前批次的损失)pbar.update()# 计算当前epoch的平均损失average_loss = train_loss / len(train_loader.dataset)train_losses.append(average_loss)model.eval()  # 将模型设置为评估模式with torch.no_grad():  # 不计算梯度以节省内存和计算资源test_loss = 0.0for data, target in test_loader:output = model(data) # 每个批次的预测值loss = criterion(output, target)test_loss += loss.item() * data.size(0)# 计算测试集的平均损失test_loss = test_loss / len(test_loader.dataset)test_losses.append(test_loss)# 如果测试损失比之前的最小值小,则更新最佳模型权重if test_loss < best_loss:best_loss = test_lossbest_model = model.state_dict() # 训练循环结束后保存最佳模型
if best_model is not None:torch.save(best_model, 'model_best.pth')print(f'Best model saved with test loss {best_loss:.4f}')
else:print('No model saved as no improvement was made.')

loss.item():loss是一个PyTorch的Tensor对象,而.item()方法用于从只包含一个元素的Tensor中提取Python数值(通常是浮点数)。由于损失通常是一个标量(scalar),因此我们可以安全地使用.item()来获取其数值。
data.size(0):这返回data这个Tensor在第一维度(通常是批次维度)的大小。在深度学习中,我们经常使用mini-batch梯度下降,这意味着我们将整个数据集分成较小的批次,并在每个批次上计算梯度并更新模型。data.size(0)就告诉我们当前批次中有多少个数据样本。
loss.item() * data.size(0):这实际上是在对损失进行“加权”。我们乘以批次大小是为了稍后能够计算平均损失(average loss)。这是因为在累积损失时,我们不仅要考虑损失的大小,还要考虑该损失是基于多少数据样本计算出来的。
train_loss += ...:最后,我们将加权后的损失累加到train_loss变量中。这样做是为了在整个训练循环结束时计算平均损失。

Epoch 1/20: 100%|███████████████████████████████| 19/19 [00:01<00:00, 13.24it/s]
Epoch 2/20: 100%|███████████████████████████████| 19/19 [00:01<00:00, 14.14it/s]
Epoch 3/20: 100%|███████████████████████████████| 19/19 [00:01<00:00, 13.10it/s]
Epoch 4/20: 100%|███████████████████████████████| 19/19 [00:01<00:00, 14.17it/s]
Epoch 5/20: 100%|███████████████████████████████| 19/19 [00:01<00:00, 14.00it/s]
Epoch 6/20: 100%|███████████████████████████████| 19/19 [00:01<00:00, 14.26it/s]
Epoch 7/20: 100%|███████████████████████████████| 19/19 [00:01<00:00, 14.48it/s]
Epoch 8/20: 100%|███████████████████████████████| 19/19 [00:01<00:00, 13.82it/s]
Epoch 9/20: 100%|███████████████████████████████| 19/19 [00:01<00:00, 13.80it/s]
Epoch 10/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 14.18it/s]
Epoch 11/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 14.16it/s]
Epoch 12/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 14.30it/s]
Epoch 13/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 13.36it/s]
Epoch 14/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 12.76it/s]
Epoch 15/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 15.39it/s]
Epoch 16/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 14.80it/s]
Epoch 17/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 13.82it/s]
Epoch 18/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 12.27it/s]
Epoch 19/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 14.48it/s]
Epoch 20/20: 100%|██████████████████████████████| 19/19 [00:01<00:00, 13.97it/s]
Best model saved with test loss 0.0010
best_epoch_idx = test_losses.index(min(test_losses))
print(f"Best epoch: {best_epoch_idx+1}, with test loss: {min(test_losses):.4f}")
Best epoch: 20, with test loss: 0.0010

4.2 可视化训练过程(Loss损失)

# 绘制损失图
plt.figure(figsize=(18, 6))
plt.plot(train_losses, label='Train Loss')
plt.plot(test_losses, label='Test Loss')
plt.title('Training and Test Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.show()

请添加图片描述

5. 模型评估与可视化

5.1 评估指标(MAE、RMSE、MAPE)

RMSE:该指标计算预测值与实际值之间平均平方差的平方根。它对较大误差给予较高的惩罚。
MAPE:该指标计算实际值与预测值之间绝对百分比差异的平均值。它用百分比来表示平均绝对误差,这对了解相对预测误差非常有用。

# 加载最佳模型
model.load_state_dict(torch.load('model_best.pth'))
def mean_absolute_percentage_error(y_true, y_pred):y_true, y_pred = np.array(y_true), np.array(y_pred)mask = y_true != 0return np.mean(np.abs((y_true[mask] - y_pred[mask]) / y_true[mask])) * 100 if mask.any() else 0# 将模型设置为评估模式
model.eval()
y_pred_all = []
y_true_all = []with torch.no_grad():pbar = tqdm(test_loader, desc='Evaluating')for data, target in pbar:y_pred = model(data).detach().cpu().numpy()  # 确保张量在CPU上y_true = target.detach().cpu().numpy()  # 确保张量在CPU上y_pred_all.append(y_pred)y_true_all.append(y_true)pbar.update()# 合并所有批次的预测和真实值
y_pred_all = np.concatenate(y_pred_all)
y_true_all = np.concatenate(y_true_all)mae = np.mean(np.abs(y_pred_all - y_true_all))
print(f"MAE: {mae:.4f}")rmse = np.sqrt(np.mean((y_pred_all - y_true_all) ** 2))
print(f"RMSE: {rmse:.4f}")mape = mean_absolute_percentage_error(y_true_all, y_pred_all)
print(f"MAPE: {mape:.4f}%")
Evaluating: 100%|████████████████████████████| 3/3 [00:00<00:00, 63.98it/s]
MAE: 0.0241
RMSE: 0.0321
MAPE: 3.2193%

5.2 反归一化

.inverse_transform 函数将经过转换或缩放的数据转换回其原始形式或接近原始形式,若在这里使用会发生错误,是因为用一个形状为(605,4)的数组去广播(broadcast)到一个形状为(605, 1)的数组上,这两个形状不兼容,它们的第二个维度不匹配,这里我们将使用下面的函数进行反归一化

# 提取Close价格的反归一化参数
close_denorm_param = scale_params[3]  # 假设Close是第四个特征# 如果反归一化包括一个偏移量shift,需要再加上 shift_param
# denormalized_predictions = (predictions[:, 0] * scale_param) + shift_param
# 将训练预测 Close的价格反归一化
y_train_pred = model(X_train_tensor).detach().numpy()
y_train_denormalized_predictions = (y_train_pred * scale_params[3]) + original_min[3]# 将测试预测 Close的价格反归一化
y_test_pred = model(X_test_tensor).detach().numpy()
y_test_denormalized_predictions = (y_test_pred * scale_params[3]) + original_min[3]

5.3 结果可视化

计算训练预测与测试预测的绘图数据

# shift train predictions for plotting
trainPredict = AAPL[window_size:X_train.shape[0]+X_train.shape[1]]
trainPredictPlot = trainPredict.assign(TrainPrediction= y_train_denormalized_predictions)testPredict = AAPL[X_train.shape[0]+X_train.shape[1]:]
testPredictPlot = testPredict.assign(TestPrediction= y_test_denormalized_predictions)

绘制模型收盘价格的原始数据与预测数据

# Visualize the data
plt.figure(figsize=(18,6))
plt.title('BiLSTM Close Price Validation')
plt.plot(AAPL['Close'], color='blue', label='original')
plt.plot(trainPredictPlot['TrainPrediction'], color='orange',label='Train Prediction')
plt.plot(testPredictPlot['TestPrediction'], color='red', label='Test Prediction')
plt.legend()
plt.show()

请添加图片描述

6. 模型预测

6.1 转换最新时间步收盘价的数组为张量

# 假设latest_closes是一个包含最新window_size个收盘价的列表或数组
latest_closes = AAPL[['Open', 'High', 'Low', 'Close']][-window_size:].values
scaled_latest_closes = scaler.transform(latest_closes)
tensor_latest_closes = torch.from_numpy(scaled_latest_closes).type(torch.Tensor).view(1, window_size, 4) 
print(tensor_latest_closes.shape)
torch.Size([1, 60, 4])

6.2 预测下一个时间点的收盘价格

# 使用模型预测下一个时间点的收盘价
next_close_pred = model(tensor_latest_closes).detach().numpy()
next_close_denormalized_pred = (next_close_pred * scale_params[3]) + original_min[3]
next_close_denormalized_pred
array([[165.9167]], dtype=float32)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MATLAB第106期】#源码分享 | 基于MATLAB的有限差分算法的全局敏感性分析模型

【MATLAB第106期】#源码分享 | 基于MATLAB的有限差分法的全局敏感性分析模型 一、原理 有限差分法是一种数值方法&#xff0c;用于估计函数对输入参数的敏感性。在全局敏感性分析中&#xff0c;这种方法特别有用&#xff0c;因为它可以评估模型输出对所有输入参数变化的整体响…

vscode+SSH连接Ubuntu

目录 问题引入 基本思路 Permission denied, please try again 修改用户名与密码 新建用户 最终成功方案 问题引入 ssh 用户名ip地址。用户名是远端服务器的用户名&#xff0c;ip地址也是远端服务器的地址。linux虚拟机的ip地址与windous主体不一样&#xff0c;所以还需要…

【BUG】已解决:AttributeError: ‘NoneType‘ object has no attribute ‘split‘

已解决&#xff1a;AttributeError: ‘NoneType‘ object has no attribute ‘split‘ 英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;…

基于Web的特产美食销售系统的设计与实现

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

HardeningMeter:一款针对二进制文件和系统安全强度的开源工具

关于HardeningMeter HardeningMeter是一款针对二进制文件和系统安全强度的开源工具&#xff0c;该工具基于纯Python开发&#xff0c;经过了开发人员的精心设计&#xff0c;可以帮助广大研究人员全面评估二进制文件和系统的安全强化程度。 功能特性 其强大的功能包括全面检查各…

抖音seo短视频账号矩阵系统源码-SaaS开源部署流程开发者技术分享

抖音seo账号矩阵系统&#xff0c;短视频矩阵系统源码&#xff0c; 短视频矩阵是一种常见的视频编码标准&#xff0c;通过多账号一键授权管理的方式&#xff0c;为运营人员打造功能强大及全面的“矩阵式“管理平台。使用矩阵系统也能保证账号的稳定性&#xff0c;降低账号的风险…

客户端与服务器通讯详解(3):如何选择合适的通讯方式

上篇文章中&#xff0c;我们讲解了客户端与服务器通讯详解&#xff08;2&#xff09;&#xff1a;12种常见通讯方式&#xff0c;重点讲解了http、websocket和RESTful API三种&#xff0c;本文我们继续讲解如何依据场景选择最合适的通讯方式。欢迎友友们点赞评论。 一、客户端服…

基于 asp.net家庭财务管理系统设计与实现

博主介绍&#xff1a;专注于Java .net php phython 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找不到哟 我的博客空间发布了1000毕设题目 方便大家学习使用感兴趣的可以先…

Linux 下 ElasticSearch 集群部署

目录 1. ElasticSearch下载 2. 环境准备 3. ElasticSearch部署 3.1 修改系统配置 3.2 开放端口 3.3 安装 ElasticSearch 4. 验证 本文将以三台服务器为例&#xff0c;介绍在 linux 系统下ElasticSearch的部署方式。 1. ElasticSearch下载 下载地址&#xff1a;Past Rel…

vue3+ts+vite项目中使用vite-plugin-pwa搭建 PWA 项目

参考官方github地址&#xff1a; https://github.com/vite-pwa/vite-plugin-pwa 官方文档地址&#xff1a; https://vite-pwa-org.netlify.app/guide MDN地址&#xff1a; https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps 前提概要 最近项目更新需求中&am…

【C++】类和对象·this指针

C中的类与C语言中的结构体有很多的相似的地方&#xff0c;可以说本质上除了结构体只能定义成员变量&#xff0c;以及结构体默认的访问控制权限是public之外与class没啥区别。但是结构体变量每次调用函数的时候需要指针&#xff0c;而类中的成员函数明明被保存在公共代码段&…

30秒学会UML-功能类图

目录 1、类图本体 三部分 修饰符 2、类与类直接关系 泛化关系 实现关系 简单关联关系 依赖关系 组合关系 聚合关系 1、类图本体 三部分 第一层&#xff1a;类名第二层&#xff1a;成员变量&#xff08;类的属性&#xff09;第三层&#xff1a;函数方法&#xff08;类…

水利行业的智慧革命:深度剖析智慧水利解决方案,看其如何以科技力量提升水资源管理效率,保障水生态安全

目录 一、智慧水利的概念与内涵 二、智慧水利解决方案的核心要素 1. 感知层&#xff1a;全面监测&#xff0c;精准感知 2. 网络层&#xff1a;互联互通&#xff0c;信息共享 3. 平台层&#xff1a;数据分析&#xff0c;智能决策 4. 应用层&#xff1a;精准施策&#xff0…

【线程系列之五】线程池介绍C语言

一、基本概念 1.1 概念 线程池&#xff08;Thread Pool&#xff09;是一种基于池化技术管理线程的机制&#xff0c;旨在减少线程创建和销毁的开销&#xff0c;提高系统资源的利用率&#xff0c;以及更好地控制系统中同时运行的线程数量。线程池通过预先创建一定数量的线程&am…

3 C 语言运算符深度解析:从基础到实战

目录 1 运算符分类 2 算术运算符与算术表达式 2.1 算术运算符的用法 2.2 左操作数和右操作数 3 关系运算符与关系表达式 3.1 关系运算符的用法 3.2 常量左置防错 3.3 三数相等判断误区 4 逻辑运算符与逻辑表达式 4.1 逻辑运算符的用法 4.2 闰年的判断 4.3 短路运算…

golang单元测试性能测试常见用法

关于go test的一些说明 golang安装后可以使用go test工具进行单元测试 代码片段对比的性能测试,使用起来还是比较方便,下面是一些应用场景 平时自己想做一些简单函数的单元测试&#xff0c;不用每次都新建一个main.go 然后go run main.go相对某个功能做下性能测试 看下cpu/内存…

k8s集群 安装配置 Prometheus+grafana+alertmanager

k8s集群 安装配置 Prometheusgrafanaalertmanager k8s环境如下&#xff1a;机器规划&#xff1a; node-exporter组件安装和配置安装node-exporter通过node-exporter采集数据显示192.168.40.180主机cpu的使用情况显示192.168.40.180主机负载使用情况 Prometheus server安装和配置…

自动驾驶AVM环视算法–全景和标定全功能算法实现和exe测试demo

参考&#xff1a;全景和标定全功能算法实现和exe测试demo-金书世界 1、测试环境 opencv310vs2022 2、使用的编程语言 c和c 3、测试的demo的获取 更新&#xff1a;测试的exe程序&#xff0c;无需解压码就可以体验算法测试效果 百度网盘&#xff1a; 链接&#xff1a;http…

代理IP服务中的代理池大小有何影响?

在当今数字化时代&#xff0c;网络爬虫已经成为获取各类信息必不可少的工具。在大规模数据抓取中&#xff0c;使用单一 IP 地址或同一 IP 代理往往会面临抓取可靠性降低、地理位置受限、请求次数受限等一系列问题。为了克服这些问题&#xff0c;构建代理池成为一种有效的解决方…

基于若依的ruoyi-nbcio流程管理系统修正自定义业务表单的回写bug

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…