获课地址:666it.top/4842/
时序预测——基于LSTM的股票价格趋势预测
引言
从自然语言处理领域,我们转向另一个重要的方向:时间序列预测。无论是金融领域的股票价格、商品销量预测,还是工业领域的设备故障预警,时序预测都有着巨大的应用价值。对于求职者来说,拥有一个时序预测项目,能证明你处理结构化数据、理解数据动态变化的能力。本篇将以经典的股票价格预测为例,手把手教你如何使用PyTorch构建一个LSTM模型来预测未来的股价趋势。
项目拆解:核心流程
- 数据理解与准备:时序数据的核心特征是时间依赖性。我们需要将原始的时间序列数据(如每日收盘价)转换为监督学习问题。常用的方法是创建“滑动窗口”,即用过去N天的数据作为特征(X),来预测第N+1天的数据作为标签(y)。
- 数据标准化:神经网络对输入数据的尺度非常敏感。对于股价这种数值范围较大的数据,使用
MinMaxScaler将其缩放到[0, 1]或[-1, 1]区间是至关重要的预处理步骤。 - 模型构建:我们将再次使用LSTM,因为其记忆单元非常适合捕捉时间序列中的长期依赖关系。模型结构相对简单:一个LSTM层后接一个全连接层输出预测值。
- 训练与评估:在时序预测中,数据集的划分必须保持时间顺序,绝不能随机打乱。我们通常用较早的数据作为训练集,较晚的数据作为测试集。评估指标常用均方根误差或平均绝对误差。
PyTorch实战代码
import torch
import torch.nn as nn
import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_squared_error
import math
# --- 1. 数据准备与预处理 ---
# 创建一个模拟的股票价格数据
np.random.seed(42)
days = 200
prices = np.cumsum(np.random.randn(days) * 2 + 100) + 5000
data = pd.DataFrame(prices, columns=['Close'])
# 数据标准化
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data)
# 创建滑动窗口数据集
def create_dataset(dataset, look_back=1):
dataX, dataY = [], []
for i in range(len(dataset) - look_back - 1):
a = dataset[i:(i + look_back), 0]
dataX.append(a)
dataY.append(dataset[i + look_back, 0])
return np.array(dataX), np.array(dataY)
look_back = 10 # 用过去10天的数据预测第11天
X, y = create_dataset(scaled_data, look_back)
# 划分训练集和测试集 (按时间顺序)
train_size = int(len(X) * 0.8)
test_size = len(X) - train_size
trainX, testX = X[0:train_size,:], X[train_size:len(X),:]
trainY, testY = y[0:train_size], y[train_size:len(y)]
# 转换为PyTorch Tensors
trainX = torch.from_numpy(trainX).float()
trainY = torch.from_numpy(trainY).float()
testX = torch.from_numpy(testX).float()
testY = torch.from_numpy(testY).float()
# 为LSTM调整输入形状 [samples, time_steps, features]
trainX = trainX.view(trainX.shape[0], trainX.shape[1], 1)
testX = testX.view(testX.shape[0], testX.shape[1], 1)
# --- 2. 模型构建 ---
class StockPriceLSTM(nn.Module):
def __init__(self, input_size, hidden_size, num_layers, output_size):
super(StockPriceLSTM, 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).to(x.device)
c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)
# LSTM前向传播
out, _ = self.lstm(x, (h0, c0))
# 只取最后一个时间步的输出
out = self.fc(out[:, -1, :])
return out
# --- 3. 训练循环 ---
# 超参数
input_size = 1
hidden_size = 50
num_layers = 2
output_size = 1
learning_rate = 0.01
num_epochs = 100
# 实例化模型、损失函数和优化器
model = StockPriceLSTM(input_size, hidden_size, num_layers, output_size)
criterion = nn.MSELoss()
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
# 训练模型
for epoch in range(num_epochs):
model.train()
outputs = model(trainX)
optimizer.zero_grad()
loss = criterion(outputs, trainY.view(-1, 1))
loss.backward()
optimizer.step()
if (epoch+1) % 10 == 0:
print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.6f}')
# --- 4. 评估与预测 ---
model.eval()
with torch.no_grad():
train_predict = model(trainX)
test_predict = model(testX)
# 反标准化,将预测值转换回原始尺度
train_predict = scaler.inverse_transform(train_predict.numpy())
trainY_original = scaler.inverse_transform(trainY.view(-1, 1).numpy())
test_predict = scaler.inverse_transform(test_predict.numpy())
testY_original = scaler.inverse_transform(testY.view(-1, 1).numpy())
# 计算RMSE
trainScore = math.sqrt(mean_squared_error(trainY_original, train_predict))
print(f'Train Score: {trainScore:.2f} RMSE')
testScore = math.sqrt(mean_squared_error(testY_original, test_predict))
print(f'Test Score: {testScore:.2f} RMSE')
# --- 5. 结果可视化 (需要matplotlib) ---
# import matplotlib.pyplot as plt
# train_predict_plot = np.empty_like(scaled_data)
# train_predict_plot[:, :] = np.nan
# train_predict_plot[look_back:len(train_predict)+look_back, :] = train_predict
# test_predict_plot = np.empty_like(scaled_data)
# test_predict_plot[:, :] = np.nan
# test_predict_plot[len(train_predict)+(look_back*2)+1:len(scaled_data)-1, :] = test_predict
# plt.plot(scaler.inverse_transform(scaled_data), label='Original Data')
# plt.plot(train_predict_plot, label='Train Predict')
# plt.plot(test_predict_plot, label='Test Predict')
# plt.legend()
# plt.show()
总结与就业对标
本篇文章带你完成了第一个时序预测项目。在面试中,你需要重点突出以下几点:
- 数据转换:清晰地解释为什么以及如何将时间序列数据通过“滑动窗口”转换为监督学习格式。这是时序建模的基石。
- 数据划分:强调时序数据不能随机划分,必须按时间顺序切分,以避免“未来信息泄露”到训练中。
- 模型选择:阐述为什么LSTM比传统的ARIMA或简单的MLP更适合处理具有长期依赖性的时序数据。
- 评估指标:解释为什么在回归问题中,RMSE比准确率更合适。
这个项目展示了你处理非文本、非图像的结构化数据的能力,拓宽了你的技术栈,使你在求职市场上更具竞争力。