PyTorch实现LSTM多变量多步预测:从数据到模型的完整实战

发布时间:2026/9/9 22:10:46
PyTorch实现LSTM多变量多步预测:从数据到模型的完整实战 简介PyTorch框架下基于LSTM的多变量多步股票预测项目面向深度学习和量化金融入门学习者帮助掌握完整的时间序列预测建模流程。项目围绕金融数据分析、LSTM门控机制、序列到序列预测展开涵盖数据标准化与缺失值处理、多因子特征构建、编码器-解码器结构、教师强制训练策略及MSE/MAE损失与Adam优化器配置等关键环节。训练数据采用贵州茅台历史行情600519.xlsx配套main.py可直接运行观察预测效果。压缩包共11个文件包括Python源码、Excel数据、结果可视化图res.png、项目配置与版本管理文件整体仅222KB轻量精简便于复现与二次开发。已有9228人学习下载。通过该项目不仅可理解LSTM如何捕捉股价长期依赖关系还能掌握多步预测中的数据划分、评估与可视化技巧为后续引入注意力机制、集成学习等进阶方法打下基础。 “多变量多步预测”这个词第一次接触时容易被它唬住但拆开看其实就是两件事输入里不止一个特征开盘价、收盘价、成交量、技术指标等输出也不再是单一的明日价格而是未来N天的预测序列。我最初用PyTorch实现这个需求时踩了一堆弯路最大的教训是“模型写对了数据处理错了预测结果照样是废的”。这篇文章会把从数据构造到LSTM模型实现、再到多步预测策略的完整链路写清楚适合已经跑通过PyTorch基础教程、想转向时序预测的读者也适合股票量化入门者参考整体建模思路。1. 方案选型为什么是LSTM以及“多变量多步”到底在预测什么1.1 单变量、多变量、单步、多步先把概念盘清楚很多教程把时间序列预测讲得很玄但本质上就四个组合单变量单步、单变量多步、多变量单步、多变量多步。单变量指的是只用一条序列预测自身未来值比如只拿收盘价预测明天的收盘价多变量则引入了多个相关特征比如把开盘价、最高价、最低价、成交量以及MACD、RSI等技术指标一起作为输入。多步则要求模型直接产出未来多个时间点的预测值而不是只预测一个点。股票数据的特殊性在于“因子之间互相影响”成交量的放大往往伴随价格异动技术指标本身又是价格和成交量的派生函数。如果只做单变量预测等于主动放弃这些信息。所以多变量多步预测在逻辑上更接近交易者对行情的真实认知——我们需要的不只是一个涨跌方向而是一条未来数日的价格路径哪怕它只是“参考路径”。1.2 LSTM凭什么适合金融时间序列LSTM是RNN的改进版核心贡献是引入了门控机制——遗忘门、输入门、输出门。这三个门分别决定“哪些历史信息需要丢弃”“哪些新信息需要存入”“哪些信息需要输出”。相比普通RNNLSTM能更有效地缓解梯度消失问题从而捕捉较长序列中的依赖关系。但这不意味着LSTM是股票预测的银弹。股票数据信噪比极低市场受政策、情绪、突发消息影响很多信息无法从历史价格中推导。用LSTM做股票预测更合理的定位是“挖掘历史行情中可复现的统计规律”而不是补全未知的未来。我在实际项目中把LSTM当作一个复杂的非线性特征提取器它能从过去60个交易日的多变量数据中学习到某种涨跌模式然后外推未来几天的走势。这个定位决定了后续的模型设计——我不会把预测结果直接当作交易信号而是作为辅助判断的一部分。2. 数据准备特征工程决定预测上限2.1 原始行情数据怎么拿、怎么清洗数据是时序预测的地基。一般可以从Tushare、AkShare、Yahoo Finance等渠道获取股票日线数据。建议至少取10年以上的日线数据原因很简单LSTM需要大量样本才能学到有意义的模式少于2000条样本训练出来的模型基本没有参考价值。拿到原始数据后第一件事不是建模型而是清洗。常遇到三个问题停牌导致的缺失值、复权价格的不一致、极端的异常值。缺失值最简单的处理方式是前向填充ffill但要注意停牌超过一定天数的股票建议直接剔除否则模型会学到“价格不变”这种错误规律。复权和未复权价格在长周期数据里差异很大分红送转会造成价格跳空如果用未复权数据模型会把除权跳空当成真实的下跌模式训练出的结果自然不可靠。2.2 特征构造与滑动窗口切分清洗完数据后开始构造特征。我个人常用的特征分三类原始量价数据开盘价、最高价、最低价、收盘价、成交量、成交额派生特征对数收益率、涨跌幅、量比、振幅技术指标类MA5、MA10、MA20、MACD的DIF/DEA、RSI、布林带位置技术指标类特征要注意一件事计算指标时只能使用截止到当前时刻的数据任何用到了“未来数据”的指标都会造成严重的未来函数泄漏。比如要计算t时刻的MA20必须用t-19到t这20天的数据而不能引入t1天的数据。这个是实战中最容易忽略但又最关键的点。数据窗口切分是建模的核心环节。通常设置lookback窗口为60个交易日约一个季度预测未来5个交易日。用Python生成样本时def create_sequences(data, lookback60, pred_len5): X, y [], [] for i in range(len(data) - lookback - pred_len 1): X.append(data[i:ilookback, :]) y.append(data[ilookback:ilookbackpred_len, [target_col_idx]]) return np.array(X), np.array(y)这里X的形状是(样本数, 60, 特征数)y的形状是(样本数, 5, 1)。训练集、验证集、测试集的划分必须严格按时间顺序不能随机打乱。如果像分类任务那样shuffle后再切分模型就等于“看到了”测试集时间段的数据分布得出的评估结果会虚高到完全失真。3. 模型实现PyTorch搭建多变量LSTM3.1 数据预处理与数据集类实现训练前必须做标准化这一步常常决定训练能否收敛。股票数据的量级差异很大收盘价可能是几十块成交量可能是几百万手直接把原始数值喂给LSTM梯度计算会非常不稳定。我习惯用StandardScaler按特征分别标准化。特别提醒Scaler必须只用训练集数据来fit然后用训练集的均值和方差去transform训练集、验证集和测试集。如果先对全量数据做标准化再切分验证集和测试集的信息就已经泄漏进训练过程了。from sklearn.preprocessing import StandardScaler scaler StandardScaler() # data 是形状为 (样本数, 特征数) 的二维数组按时间顺序排列 scaled_data scaler.fit_transform(data[:train_len]) # 验证集和测试集用相同的 scaler 做变换 scaled_val scaler.transform(data[train_len:val_len])然后用PyTorch的Dataset类封装import torch from torch.utils.data import Dataset, DataLoader class StockDataset(Dataset): def __init__(self, X, y): self.X torch.FloatTensor(X) self.y torch.FloatTensor(y) def __len__(self): return len(self.X) def __getitem__(self, idx): return self.X[idx], self.y[idx]3.2 LSTM网络结构与参数设计模型结构不复杂关键是理解张量形状的变化。输入形状是(batch_size, lookback, num_features)经过nn.LSTM后输出形状是(batch_size, lookback, hidden_size)我们取最后一个时间步的输出再通过全连接层映射到预测维度。import torch.nn as nn class StockLSTM(nn.Module): def __init__(self, num_features, hidden_size128, num_layers2, pred_len5): super().__init__() self.lstm nn.LSTM( input_sizenum_features, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropout0.2 if num_layers 1 else 0 ) self.fc nn.Sequential( nn.Linear(hidden_size, 64), nn.ReLU(), nn.Dropout(0.2), nn.Linear(64, pred_len) ) def forward(self, x): out, _ self.lstm(x) # out: (batch, lookback, hidden) out out[:, -1, :] # 取最后一个时间步 out self.fc(out) # (batch, pred_len) return outhidden_size我通常设为128或256太小拟合能力不足太大容易过拟合。num_layers用2层基本够用再深在股票这种低信噪比数据上并不会带来明显收益反而训练更容易震荡。dropout设在0.2到0.3之间可以抑制过拟合。3.3 多步预测的两种策略递归预测与多输出头多步预测的实现方式有两种主流思路。第一种是递归预测recursive预测出t1后把预测值作为输入的一部分送入模型预测t2如此循环。这种方法的优点是模型本身只做单步预测训练简单缺点是误差会逐步累积预测步数越长结果越不可靠甚至出现预测序列“冻住”不变的情况。第二种是直接多输出direct multi-output训练时让模型一次输出未来5个时间步的值。上面代码里的pred_len5就是这个思路。参数shared多步预测直接由一个全连接层映射出来。这种方式避免了误差累积但牺牲了LSTM在“逐步推理”上的时间依赖建模能力预测序列容易平滑过头。两种方式我都在项目中跑过结论是短期多步3~5天用直接多输出更稳长期多步10天以上递归策略也并没有明显优势。所以下面统一以直接多输出为主。4. 训练细节损失、优化器与评估指标4.1 训练循环与超参数设置训练循环本身中规中矩但有几个细节值得单独强调。损失函数用MSELoss即可它让模型优先拟合偏差大的样本适合价格类连续值预测。优化器优先选择Adam初始学习率1e-3如果训练中损失震荡剧烈降到3e-4甚至1e-4。device torch.device(cuda if torch.cuda.is_available() else cpu) model StockLSTM(num_featuresX.shape[2], hidden_size128, num_layers2, pred_len5).to(device) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, modemin, factor0.5, patience10) for epoch in range(100): model.train() train_loss 0.0 for xb, yb in train_loader: xb, yb xb.to(device), yb.to(device) optimizer.zero_grad() pred model(xb) loss criterion(pred, yb.squeeze(-1)) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() train_loss loss.item() * len(xb) # 验证 model.eval() val_loss 0.0 with torch.no_grad(): for xb, yb in val_loader: xb, yb xb.to(device), yb.to(device) pred model(xb) loss criterion(pred, yb.squeeze(-1)) val_loss loss.item() * len(xb) scheduler.step(val_loss) if (epoch 1) % 20 0: print(fEpoch {epoch1}: train_loss{train_loss/len(train_loader):.6f}, val_loss{val_loss/len(val_loader):.6f})梯度裁剪是我强烈建议加上的一个步骤。LSTM在长序列上很容易出现梯度爆炸一旦发生loss会直接变成NaN或者跳到天文数字。clip_grad_norm_设1.0就够了不加这个你可能会被莫名其妙的NaN毁掉一整晚。4.2 评估指标与回测陷阱评估模型时我同时使用MSE和MAPE平均绝对百分比误差。只看MSE很难直观判断预测效果是好是坏比如价格是50元的股票MSE是4残差标准差就是2元误差占比4%MAPE则更直白直接给出误差百分比。def mean_absolute_percentage_error(y_true, y_pred): return np.mean(np.abs((y_true - y_pred) / y_true)) * 100预测结果要做反标准化才能和真实价格对比。反标准化后计算MAPE才有实际意义。用标准化后的值计算MAPE会得到完全错误的结果因为分母变成了标准化后的接近0的值。另一个容易犯的错是“用预测曲线和真实曲线肉眼对比”。人的眼睛很容易被趋势相近的曲线骗过去觉得拟合得很好实际上可能只是模型学会了一两天前的价格复读。我习惯画出未来第1天、第3天、第5天的预测散点图分别计算对应时间步的MAPE这样能区分模型到底是“短期还行长期崩盘”还是各步表现稳定。5. 实战中的坑与排查清单5.1 数据泄漏预测结果虚高的头号原因数据泄漏在时序预测里最隐蔽也最致命。我在第一次实现时先对全量数据做了标准化再切分训练集和验证集结果验证集的MAPE只有0.8%看起来很完美实际上是因为验证集的信息已经参与了训练分布的计算。修正为先fit训练集的Scaler再transform其他部分后MAPE立刻变成了2.7%。这个教训让我养成了一个习惯所有预处理步骤里的统计量计算均值、方差只允许使用训练集。技术指标中的未来函数同理任何用到未来时刻数据算出来的指标都是数据泄漏。5.2 预测序列失效为什么模型输出近乎一条直线模型训练正常、loss也在下降但预测的未来5天序列几乎是一条直线这通常有三个原因。第一个是loss在MSE导向下会让模型倾向于输出条件均值解决办法是调整损失权重或改用Huber Loss它对异常值更鲁棒也能保留一点波动第二个是输入特征中技术指标类特征占主导导致LSTM学到的模式偏平滑第三是训练数据太少或特征窗口太长模型根本没有机会学到有效波动模式。遇到这种情况先检查测试集上第1步预测的MAPE如果第1步就烂说明建模方向需要调整如果只有第4、5步烂那是多步外推的正常衰减。5.3 过拟合验证集loss持续走高怎么处理股票数据的噪声本身就大过拟合几乎是一定会发生的事。验证集loss先降后升这是最经典的过拟合信号。我的处理顺序是优先增加dropout到0.3同时把hidden_size从128降到64观察验证集loss是否同步改善。如果还不行就加早停Early Stoppingpatience设置为15个epoch左右。数据增强类的技巧在时序数据里不太适用与其折腾数据增强不如老老实实加正则化和早停。5.4 训练不收敛loss为NaN的几个常见原因loss突然变成NaN排在第一位的原因是学习率过大特别是Adam在训练初期梯度较大时更容易触发。解决办法是降低学习率到1e-4并打开梯度裁剪。第二个原因是数据里有NaN或InfLSTM对输入中的数值异常非常敏感一个NaN会污染一整条序列甚至整个batch。训练前用np.isfinite(data).all()检查输入数据确定没有异常值。第三个原因是标准化后的数据仍然存在极端离群值可以考虑用中位数和四分位距IQR做更稳健的标准化。5.5 特征过多导致训练变慢但效果没提升多变量预测不等于特征越多越好。我试过把能想到的十几个技术指标全部作为输入特征训练时间明显变长预测精度却下降了。原因是指标之间存在高度相关性输入冗余加剧了过拟合。常用的解决办法是去除相关性高的特征。比如MACD的DIF和DEA与MA参数高度相关RSI和KDJ在某些行情下也高度同步保留其中一两个有代表性的即可。我最终稳定使用的特征组合是收盘价、成交量、5日收益率、10日收益率、MA20距离比、RSI一共6个特征。精简后训练速度快了验证集MAPE也下降了不少。6. 这个项目后续还能怎么扩展模型跑通后很多人会问“下一步能做什么”。最直接的扩展方向是引入更多维度的数据比如同行业其他股票的价格走势、大盘指数、成交量相关指标把LSTM的输入从单只股票的日线数据扩展为多标的的横截面数据。这种方法本质上是让模型自己学习个股与板块之间的联动关系理论上能提供更全面的决策参考。另一个方向是预测不确定性。单点预测只能告诉你“可能涨到多少”但没法告诉你“这个预测有多大把握”。可以在模型输出的基础上加上MC Dropout即在推理时保持dropout开启重复前向计算30次取结果的均值和标准差。标准差就能作为预测置信度的参考。这个方法不需要改动模型结构只需修改推理阶段的代码相比贝叶斯神经网络要简单得多适合作为下一步学习和优化的起点。最后说一点个人体会。用LSTM做股票预测最大的挑战不是技术本身而是对模型输出保持清醒。深度学习模型再复杂本质上仍是从历史数据中寻找统计规律市场环境一旦发生结构性变化模型的“经验”就会失效。所以我更倾向于用预测结果的置信区间和误差分布来辅助决策而不是直接根据模型给出的“未来5天价格”下单。如果你也是抱着“理解技术验证思路”的心态来复现这个项目它绝对值得花时间认真做一遍如果指望靠一个LSTM模型实现稳定盈利那我建议还是保持理性。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询