在金融领域,预测股票价格走势一直是众多投资者和研究者关注的焦点。今天,我们将利用深度学习中的循环神经网络(RNN)来构建一个简单的股票价格预测模型,并详细介绍从数据加载、预处理、模型搭建、训练到最终结果可视化的全过程。
一、项目概述
本项目旨在通过历史股票价格数据,训练一个 RNN 模型,使其能够对未来股票价格进行一定程度的预测。我们将使用 Python 作为主要编程语言,结合 NumPy、PyTorch 以及 Scikit-learn 等强大的库来实现这一目标。
二、数据准备
- 加载数据:首先,我们使用
np.loadtxt
函数从 CSV 文件(假设名为data-02-stock_daily.csv
)中读取股票价格数据。这里需要注意指定正确的分隔符,通常股票数据 CSV 文件是以逗号分隔的,所以我们传入delimiter=','
。读取到的数据是一个二维数组,每一行代表一天的股票相关信息,如开盘价、收盘价、最高价、最低价等。为了让数据按照时间顺序排列,方便后续处理,我们使用切片操作data = data[::-1]
将数据反转。 - 归一化处理:不同特征的数值范围可能差异很大,这会影响模型训练的效率和效果。因此,我们引入
MinMaxScaler
类进行归一化处理。它会将数据的每一个特征都映射到 0 到 1 的区间内,具体操作是通过data = MinMaxScaler().fit_transform(data)
实现。经过这一步,数据的分布更加规整,有助于模型更快更好地收敛。 - 构建输入输出序列:为了让 RNN 模型能够学习到股票价格的时间序列特征,我们需要设置一个时间步长
c
(这里设为 7)。通过循环遍历归一化后的数据,构建输入序列x
和对应的输出序列y
。对于输入序列,我们将连续c
天的数据作为一个样本,即x.append(data[i:i + c])
;而输出序列则是第c + 1
天的股票价格,也就是y.append(data[i + c][-1])
。最后,将x
和y
转换为 PyTorch 张量,方便后续在深度学习框架中使用,使用x = torch.tensor(x, dtype=torch.float)
和y = torch.tensor(y, dtype=torch.float)
完成转换。 - 划分数据集:使用
sklearn
的train_test_split
函数将数据集划分为训练集和测试集。为了保证实验的可重复性,我们指定test_size=0.2
,表示测试集占总数据集的 20%,以及random_state=42
作为随机种子。通过x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
得到划分后的数据集,并打印出训练集的形状,以便了解数据的维度信息,用于后续模型参数的设置。
三、模型搭建
我们定义了一个自定义的 RNN 模型类,继承自 torch.nn.Module
。在 __init__
方法中:
- 首先调用父类的初始化方法
super().__init__()
,确保模型的基础结构正确初始化。 - 接着创建两个 RNN 层,第一个
self.rnn1
的输入大小根据训练数据的特征维度确定,即input_size=x_train.shape[2]
,这里x_train.shape[2]
表示输入数据的特征数量,隐藏层大小设为 128,并且设置batch_first=True
,使输入张量的批次维度在第一维,方便与后续的数据加载器等组件配合;第二个self.rnn2
的输入大小为第一个 RNN 层的隐藏层大小 128,隐藏层大小设为 256,同样设置batch_first=True
。 - 最后定义一个线性层
self.linear
,将第二个 RNN 层的输出映射到预测的股票价格维度,其输入特征数量为 256,输出特征数量为 1。
在 forward
方法中:
- 输入数据
x
首先经过第一个 RNN 层self.rnn1
,得到输出x
和隐藏状态y
,由于在这个预测场景中我们不需要关注隐藏状态,所以直接忽略y
,即x, _ = self.rnn1(x)
。 - 接着
x
再经过第二个 RNN 层self.rnn2
,同样忽略隐藏状态,x, _ = self.rnn2(x)
。 - 最后将经过两层 RNN 处理后的
x
的最后一个时间步的输出(也就是x[:, -1, :]
)传入线性层self.linear
,得到最终的预测结果并返回。
四、模型训练
- 实例化模型:创建
RNN
模型的实例,即model = RNN()
。 - 定义损失函数:选用均方误差损失函数(MSELoss)来衡量模型预测值与真实值之间的差异,
loss_fn = torch.nn.MSELoss()
。这是因为在预测股票价格这种连续值的任务中,均方误差能够很好地反映预测的准确性。 - 定义优化器:使用 Adam 优化器来更新模型的参数,指定学习率为 0.01,通过
optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
完成定义。Adam 优化器在实际应用中表现出良好的收敛性能,能够自适应地调整学习率,使得模型训练更加高效。 - 训练循环:设置训练的轮数为 1000,在每一轮训练中:
- 首先使用
optimizer.zero_grad()
清空上一轮训练的梯度信息,确保每一轮的梯度计算都是基于当前轮次的输入数据。 - 然后将训练数据
x_train
传入模型,得到预测输出h = model(x_train)
,并使用loss_fn
计算预测值与真实值y_train
之间的损失。 - 接着调用
loss.backward()
进行反向传播,计算模型参数的梯度。 - 最后使用
optimizer.step()
根据计算得到的梯度更新模型参数,并将当前轮次的损失值添加到损失列表loss_list
中。为了便于观察训练过程,每 100 个轮次打印一次损失值,如if (epoch + 1) % 100 == 0: print(f'Epoch [{epoch + 1}/{num_epoch00}, Loss: {loss.item():.4f}')
。
- 首先使用
五、模型预测与可视化
- 预测测试集:训练完成后,将测试集数据
x_test
传入模型,得到预测结果predictions = model(x_test).squeeze()
,这里的squeeze
操作是为了去除可能存在的多余维度,使预测结果的维度与真实值y_test
相匹配。 - 绘制预测结果:使用
matplotlib
库绘制预测结果和真实结果的对比图。首先创建一个新的绘图窗口,设置合适的图幅大小,如plt.figure(figsize=(10, 6))
。然后分别绘制预测值和真实值的折线图,用红色表示预测值plt.plot(predictions.detach().numpy(), c='r', label='Prediction')
,绿色表示真实值plt.plot(y_test.detach().numpy(), c='g', label='Actual')
,并添加标题、坐标轴标签以及图例,最后通过plt.show()
展示绘图结果。这使得我们能够直观地看到模型预测的股票价格与实际价格的接近程度,评估模型的性能。 - 绘制损失曲线:为了进一步了解模型训练过程中的收敛情况,我们还绘制了训练损失随轮次变化的曲线。同样创建一个新的绘图窗口,绘制损失列表
loss_list
中的值,用蓝色表示训练损失plt.plot(loss_list, c='b', label='Training Loss')
,添加相应的标题、坐标轴标签和图例,最后展示绘图结果。通过观察损失曲线,我们可以判断模型是否收敛,以及收敛的速度如何,为后续模型的优化提供参考。
通过以上完整的步骤,我们成功地利用 RNN 模型对股票价格进行了预测,并通过可视化手段直观地展示了预测结果和训练过程。当然,这只是一个简单的示例,在实际应用中,还可以进一步优化模型结构、调整参数、增加更多的数据特征等,以提高预测的准确性。希望这个项目能够为你在深度学习应用于金融领域的探索中提供一些帮助!