☰
LSTM股票预测实战:从数据清洗到模型调参的完整指南
2026/10/9 3:38:36 网站建设 项目流程

1. 毕设选型:为什么选LSTM做股价预测

毕设选题像开盲盒,选得好皆大欢喜,选不好天天返工。我最后敲定的是LSTM股价预测方向——从确定题目到系统上线,前后花了三个月。回头看,这个题目好在技术点集中、成果展示直观,缺点也非常明显:预测金融时间序列这件事本身,坑比想象的多得多。

数学系或计算机系的同学选这个题目很常见,但很多人忽略了一个关键事实:股价数据不满足平稳性假设,噪声占比极高,LSTM虽然在序列建模上很能打,但拿它直接预测股票价格,本质上是在和随机游走博弈。我在开题阶段就把目标做了收敛:不去追求预测未来某一天的精确价格,而是专注"用历史行情数据训练LSTM神经网络,评估模型在样本外数据上的跟踪能力与泛化表现"。这个定位让后续实验目标始终清晰,也方便在论文里围绕"LSTM预测能力边界"做深入讨论。

另一个考量是技术栈的成熟度。LSTM模型代码在PyTorch和TensorFlow里都有完善封装,公开教程一抓一大把,踩坑时有大量资料可查。我最终选了TensorFlow 2.x加Keras API,配合Pandas做数据清洗、Matplotlib做可视化,整套流程跑通以后,每个环节都能直观看到中间结果,非常适合毕设这种需要"过程可控"的项目。

这个方向适合谁来参考?如果你正在做金融时间序列相关的毕设或课设,或者想快速入门循环神经网络在真实数据上的应用,这篇分享能帮你少走很多弯路。我会把数据获取、清洗、模型搭建、训练调参、结果评估一条线全部讲透,包括那些论文里不会写的血泪坑。

2. 股价预测第一步:数据集的获取与清洗实操

2.1 数据源选择与下载

股价预测第一件事是搞到干净的数据。我对比过几个常见数据接口,A股行情用Tushare和AkShare比较方便,前者需要注册获取token,后者完全免费、接口封装更友好。考虑到毕设需要稳定复现,我选了Tushare的日线行情接口,数据质量高,字段也完整。

股票标的的选择也是有讲究的。我一开始选了波动剧烈的题材股,结果模型完全失控。后来换了流动性好的大盘蓝筹,比如贵州茅台、招商银行这类。用成熟大票的原因很简单:历史数据长、交易连续、停牌次数少,能减少数据清洗的工作量,把重心放到模型本身。如果你做毕设,强烈建议先选一只日线数据超过5年的大盘股,再用小盘股做补充对比实验。

下载的字段默认有开高低收、成交量、成交额。我这里额外注意了复权问题,因为股票除权除息会导致价格跳空,前后价格不可比。Tushare里可以拿前复权数据,让历史价格按当前价格做回溯调整。这个细节如果漏掉,模型会把分红送股当成了真实波动去学习。

注意:下载数据时顺手记录一下数据区间。后续做训练测试集切分时,起始日期和终止日期都会影响时间窗口的构造,别等做完了才发现数据少了一段。

2.2 数据清洗与特征工程

拿到原始数据后我先做基础清洗:检查缺失值、删除重复行、确认日期连续。A股数据会有停牌导致的空缺,Tushare返回的日线数据一般会把停牌日去掉,所以连续性检查要找那些"数量对不上"的情况。我的处理方式是按正常交易日历补齐日期,缺失字段用前向填充,因为股价通常被认为是"跳跃不太大"的序列,用前一天数值填充是行业常规做法。

特征工程这块,我把特征分成三组。第一组是基础行情特征,也就是OHLCV五元组;第二组是衍生统计特征,包括日涨跌幅、成交量变化率、5日均线、10日均线、20日均线;第三组是常用技术指标,比如RSI相对强弱指数和MACD的DIF、DEA值。这几组特征加在一起,输入维度大概在12到15之间。

这里有个经验:特征多了模型收敛慢,而且容易把噪声一起学进去。我做了一轮简单相关性分析,把和收盘价相关性过低、方差过大的几个指标踢掉了。最后保留的输入特征不要超过10个,否则在样本量有限的情况下,LSTM的优势反而会被高维特征稀释。毕设论文里写特征工程,这个筛选过程本身就是很好的工作量证明。

2.3 归一化与数据集划分

归一化是LSTM训练前必须做的一步,而且方法上有讲究。LSTM使用tanh和sigmoid作为激活函数,输入范围太大会直接把激活函数推向饱和区,导致梯度消失。我用的MinMaxScaler把所有特征缩放到[0,1]区间,公式是老一套:

from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler(feature_range=(0, 1)) scaled_data = scaler.fit_transform(features)

但这里有个大坑:测试集数据在训练阶段必须保持"不可见"。正确做法是用训练集fit后,再transform测试集,而不是对整个数据集一口气归一化。很多教程直接全量fit_transform,这会造成数据泄漏——模型在训练时"偷看"了测试数据的统计信息,测试评估结果虚高,答辩时被问住就尴尬了。

数据集划分也要注意时序性。我的做法是前80%做训练集,最后20%做测试集。因为股票数据是强时序依赖的,乱序随机切分会破坏时间上的先后关系,让训练集里的数据"预知"未来。训练集内部再切出一部分做验证集,用于早停和调参,测试集只用来做最终评估,全程不碰。

3. 核心环节:LSTM模型搭建、训练与调参全记录

3.1 理解LSTM的输入输出结构

开始写代码之前,先搞明白LSTM的数据形状,这是很多新手卡壳的地方。LSTM期望的输入是三维张量:(样本数, 时间步长, 特征维度)。时间步长就是我们常说的look_back,即用过去多少个交易日的特征来预测未来。我用的是60天,大致相当于三个月的交易日数量,既保留了足够的中短期行情信息,又不会因为窗口太长引入过多噪声。

特征维度对应上一步保留的特征数量,比如10个。每一个样本是由连续的60行特征矩阵构成,标签是对应第61天的收盘价。滑动窗口构造样本时,窗口每次向后滑动一天,这样5000条数据大概能生成4900多个样本。

LSTM的内部结构如果展开讲,核心是三个门:遗忘门决定丢弃多少历史信息,输入门决定写入多少新信息,输出门决定当前时刻输出什么。这三个门配合细胞状态,让信息可以在长序列中传递而不被过度稀释。理解到这一层就够用了,不用去手推反向传播公式,但答辩老师很爱问,提前准备好一两句通俗解释很有必要。

3.2 网络结构与关键参数

我的模型结构比较经典,三层构建:一个LSTM层加一个Dropout层,再接一个LSTM层,最后接两个全连接层输出单一数值。第一个LSTM层用64个单元,return_sequences设为True,让输出继续传递到下一层LSTM;第二个LSTM层用32个单元,只返回最后一个时间步的输出;全连接层先经过一个16维的隐藏层,激活函数用ReLU,最后的输出层用线性激活,因为这里是回归任务。

LSTM模型代码看起来不长,但里面门道不少:

import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau model = Sequential([ LSTM(64, return_sequences=True, input_shape=(look_back, n_features)), Dropout(0.2), LSTM(32, return_sequences=False), Dropout(0.2), Dense(16, activation='relu'), Dense(1) ]) model.compile(optimizer='adam', loss='mse', metrics=['mae']) model.summary()

Dropout比例我试过0.1、0.2、0.3,在0.2附近验证集误差最小。这个本质上是正则化强度和数据规模的平衡,毕设样本量通常不大,Dropout太高反而会让模型欠拟合。输出层不用激活函数这一点很多人忽略,回归预测的目标是任意实数,最后一层若套了sigmoid或tanh,预测范围就被限制死了。

3.3 训练流程与监控指标

训练配置上,batch_size设为32,epochs先跑100轮,配上早停回调。早停的监控指标是验证集损失,耐心值设10轮,也就是说验证损失连续10轮没有改善就停止训练,恢复到损失最小的权重。同时我还加了ReduceLROnPlateau回调,在损失停滞时自动把学习率降为原来的五分之一,帮助模型冲出平台期。

训练过程中的loss曲线需要全程记录。Keras自带History回调,训练结束后画出来看看。正常情况下训练loss和验证loss都应当缓慢下降;如果验证loss先降后升,说明过拟合了;如果两个loss都持平不降,可能是学习率偏大或特征质量问题。我在第一轮实验时就是验证loss一直震荡,后来把学习率从默认0.001调到0.0005才稳定下来。

训练完成后保存模型和Scaler对象。很多人只存模型不存Scaler,导致预测新数据时报维度错误或者归一化不一致。模型只有一个预测能力,但数据预处理和还原都依赖Scaler的参数,不保存的话还原预测价格时根本没有逆变换的基准。

4. 用指标和曲线说话:预测结果评估与可视化

4.1 评价指标怎么选

股价预测是回归任务,不能像分类任务那样用准确率。我用了三个指标:均方根误差RMSE、平均绝对误差MAE、平均绝对百分比误差MAPE。RMSE对大误差的惩罚更大,能反映出预测在某些极值点上的偏离程度;MAE直观反映平均偏差;MAPE能把误差变成百分比,便于横向对比不同股票。

计算这些指标的关键是还原真实量纲。模型输出的预测值是归一化后的数值,需要先逆变换回真实价格,再和真实收盘价做差。很多人直接在归一化域里比较,算出来的RMSE在0.02这种量级,看起来漂亮得不像话,实际上毫无意义。

4.2 训练集与测试集的曲线对比

可视化是整个毕设里展示效果最好的环节。我的做法是把训练集和测试集的预测曲线与真实收盘价曲线画在同一张图上,训练集部分展示模型对历史数据的拟合能力,测试集部分展示样本外泛化能力。测试集部分用更深的颜色区分,并给测试集的起始位置加一条竖线,让看图的人一眼就能分清哪里是"学过的数据",哪里是"没见过的新数据"。

第一次画出来的曲线让我既高兴又警惕。高兴的是训练集拟合得几乎完美,警惕的是测试集的预测曲线总是比真实曲线滞后一段时间。真实曲线开始下跌,预测曲线还要继续上涨一两天才掉头。这是LSTM预测金融序列的经典问题,原因很简单:训练时标签是下一个交易日的价格,对模型来说,最简单的"最优解"就是让预测值接近上一个已知价格,因为价格在短期内确实有强自相关性。模型本质上学会了"今天的价格大概就是昨天的价格"这个平凡的规律。

4.3 对比实验:LSTM vs 传统基线

为了证明LSTM的有效性,我又跑了两个基线模型:线性回归和ARIMA。线性回归直接把窗口内的60天价格展开成特征,同样预测第61天收盘价;ARIMA是经典时间序列模型,只拿收盘价做预测。三者在同一份数据、同样的训练测试划分下对比RMSE和MAPE。

结果不出意料:LSTM的测试集RMSE比线性回归低约18%,比ARIMA低约27%。但在不同股票上优势幅度不一样,波动大的股票上LSTM的优势会被削弱。所以论文里我没有笼统地说"LSTM优于其他模型",而是加了个限定语:在中低波动率的个股日线收盘价预测任务上,LSTM在样本外测试中相对线性基线和ARIMA有明显优势。这样表述既严谨又能体现分析能力。

我还额外做了小样本敏感性分析:把训练数据量从80%降到60%、40%,观察LSTM和ARIMA的性能变化。结论是数据量越小,LSTM的优势越弱,甚至可能被ARIMA反超。原因不难理解,深度模型是数据饥渴型选手,数据不够时,参数复杂的优势发挥不出来,反而是参数少的传统模型更稳。这个实验为毕设答辩增加了不少讨论空间。

5. 毕设血泪史:常见问题与避坑指南

5.1 数据泄漏与预测滞后

数据泄漏是时间序列预测最容易犯却最难发现的错误。除了前面说的归一化泄漏,还有一类泄漏来自时间窗口构造——如果滑动窗口生成样本时不小心shuffle了数据集,那么训练集里某个样本的"历史"可能包含测试集某天的数据,模型等于提前看到了未来。我建议在构造样本这一步就把数据按时间顺序切好,再生成特征序列,最后再划分训练测试。

预测滞后问题无法完全消除,但我做了两个改进:一是把标签从"下一交易日收盘价"改成"未来5日平均收盘价",让目标更平滑;二是在输入特征中增加差分项,把价格变化量而不是价格绝对值交给模型。这两个改动让测试集的MAPE降低了差不多4个百分点,尽管滞后现象仍然存在,但幅度缓解不少。如果你最终仍然看到曲线滞后,不要慌,这在金融预测论文里是公认现象,关键在于你能解释清楚原因并给出改进方案。

5.2 过拟合与训练稳定性问题

LSTM层数越多、单元数越多,过拟合来得越快。我的第一版模型用了三层LSTM、每层128个单元,训练集loss降到接近0,测试集却一路走差。后来砍到两层LSTM、单元数64和32,情况明显好转。经验是:毕设级别的数据量(几千到几万条),两层LSTM已经足够,单元数超过128基本是在浪费算力。

训练稳定性也值得单独说。LSTM对随机初始化比较敏感,同样的代码跑两遍结果可能差5个百分点。我一开始没有设置随机种子,导致一次实验里指标变好,另一次变差,根本没法判断是模型改进还是随机波动。后来在规定位置固定了随机种子,才让实验可复现。这里提醒一下,用Keras时要同时设置Python、NumPy和TensorFlow三个层面的随机种子,只设一个是不够的。

5.3 训练时间与资源管理

很多人担心LSTM训练慢。实际上以我的数据量(约4000个样本)在CPU上训练一轮大概十几秒,100轮下来半小时出头。如果你买了GPU服务器或者用Colab,一轮只要一两秒,完全不是瓶颈。真正耗时的是调参试错,一次早停十几轮,试几十组参数,时间成本就上去了。我的做法是把不同超参数的组合结果记录在表格里,包括units数量、Dropout比例、学习率、batch_size、早停轮数,对比之后再选最优组合,而不是盲目凭感觉改代码。

实操心得:训练时把日志输出打开,每轮打印loss和val_loss。我遇到过一次诡异情况——loss降了但val_loss不降,检查半天才发现是数据处理时把验证集顺序打乱了,泄漏了未来信息。这种问题如果不开日志盯着,很容易被忽略。

5.4 答辩准备与论文写作建议

毕设做到最后,工作能不能拿高分,很大程度上取决于呈现方式。我的论文框架是这样的:绪论部分讲研究背景和LSTM引入动机;相关技术部分讲RNN原理、LSTM三门结构和股价预测文献综述;数据部分写数据描述、清洗流程和特征工程细节;模型部分写网络结构设计、训练策略和超参数选择依据;实验部分放评估指标、对比实验和可视化图表;最后讨论模型的局限性和改进方向。

答辩PPT我做了两个强调点:一是模型的完整训练流程图,从原始数据到预测结果每一步都有图有真相;二是把我在第5章里写的"问题与改进"当作亮点来讲,因为评委老师最关心的不是"模型多厉害",而是"你清不清楚自己的模型在什么条件下会失效"。能把预测滞后和数据泄漏讲清楚,比单纯展示漂亮的RMSE更能加分。

我个人在实际操作中还有一个体会:不要把毕设当成深度学习竞赛,目标不是刷到最低的loss,而是把一个完整的研究流程走通,每一步都有清晰的决策依据和反思。我最后提交的所有代码都整理成了一个带注释的notebook,从数据下载到结果可视化一键运行,论文里的每一张图都能找到对应代码段,这对答辩和后续使用都非常重要。如果时间允许,你可以继续沿着多只股票、多步预测或者加入新闻情绪特征的方向扩展,这个题目后续还有很多值得挖的空间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询