资讯动态

LSTM股票预测源码实战:从数据抓取到模型部署的完整解析

发布时间:2026/9/28 5:38:06 来源:尧图企业网站定制
简介这份资源是一套基于深度学习神经网络实现的通用股票预测模型Python源码面向计算机、人工智能、金融商贸等方向的在校学生、教师及企业员工也适合作为毕设、课程设计或项目立项演示。项目以LSTM、RNN、Transformer及BERT等模型为核心配套数据下载、预处理、训练、预测与评估的完整流程帮助读者理解时序预测在金融场景中的落地方式。压缩包共33个文件约5MB包含13个py源码、13张png结果图、3个md说明文档及txt、gz等辅助文件源码与图表对应清晰便于对照实验效果。目前已有104人学习下载。读者可获得可直接运行的训练与预测脚本、损失与精度曲线、预测对比图及数据爬取工具并能在现有代码基础上修改模型或替换数据源用于二次开发与论文写作。1. 从一份能跑通的 LSTM 股票预测源码说起很多人第一次接触深度学习都是从「用 LSTM 预测股票」这个经典命题入门的。但真正动手时才发现网上大部分教程要么只给一段残缺的模型定义要么数据接口早就失效跑起来不是报错就是结果毫无意义。这份stock_prediction-master源码包恰好是一个能完整跑通的参考实现它用 Python 把数据抓取、预处理、LSTM/RNN/Transformer 建模、训练、预测、指标可视化串成了一条闭环链路还附带了 BERT 做文本情绪特征的尝试。对于计算机、人工智能、金融商贸方向的在校学生或者想快速搭一个量化预测原型的工程师来说它的价值不在于预测准不准而在于给你一套结构清晰、模块解耦、能直接替换数据源的工程骨架。下面我按实际拆包和复现的顺序把这份资源讲透。2. 拆开源码包模块划分与数据流走向2.1 目录结构与各文件职责拿到stock_prediction-master.zip解压后根目录下大致是这么几类文件。先看清楚谁依赖谁后面改代码才不会牵一发动全身。文件/目录作用是否必须getdata.py从公开数据源拉取日线行情落地到stock_daily必须stock_data_spider.py另一套抓取逻辑通常作为备用数据源可选data_preprocess.py缺失值处理、归一化、滑窗构造必须common.py路径、超参、随机种子等公共配置必须target.py标签构造比如把收盘价位移成预测目标必须utils.py绘图、指标计算、模型保存加载必须train.py/test.py训练入口与评估入口必须predict.py加载权重做单次或批量预测必须bert_train.py/bert_data_preprocess.py/get_bert_data.py文本情绪分支独立于价量分支可选fig_lstm/fig_transformer/训练曲线和预测对比图输出目录自动生成install_files/yfinance-0.1.59.tar.gz离线依赖包视网络而定这里有个容易忽略的点common.py里的路径配置决定了数据从哪读、图往哪写。很多人解压后直接python train.py结果报FileNotFoundError就是因为没先跑getdata.pystock_daily目录压根不存在。2.2 数据从抓取到入模的完整链路整条链路可以概括为getdata.py拉取原始 OHLCV →data_preprocess.py清洗并归一化 →target.py生成监督学习的 X/y →train.py按时间顺序切分训练集验证集 → 模型训练 →predict.py反归一化输出真实价格。先看数据抓取这一环。源码里用的是yfinance拉取 A 股数据代码结构大致如下# getdata.py 核心逻辑示意 import yfinance as yf import os def download_stock(code, start, end, save_dirstock_daily): # code 形如 000001.SZyfinance 对 A 股需要加后缀 df yf.download(code, startstart, endend) if df.empty: raise ValueError(f{code} 返回空数据检查代码格式或网络) os.makedirs(save_dir, exist_okTrue) df.to_csv(os.path.join(save_dir, f{code}.csv)) return df if __name__ __main__: download_stock(000001.SZ, 2015-01-01, 2023-12-31)逻辑说明yf.download返回的是 DataFrame索引是日期列包含 Open/High/Low/Close/Volume。参数上code必须带交易所后缀000001.SZ是深市600000.SS是沪市写错就返回空。start和end决定样本区间太短模型学不到周期太长又容易混入不同市场阶段常见做法是取 5 到 8 年。抓完数据后进入预处理。这一步的坑最多因为金融时间序列不能像图像那样随便 shuffle。data_preprocess.py里通常会做三件事前向填充缺失值、用训练集统计量做归一化、用滑动窗口把一维序列切成(样本数, 时间步, 特征数)的三维张量。# data_preprocess.py 滑窗构造示意 import numpy as np import pandas as pd def make_windows(series, window30, horizon1): X, y [], [] for i in range(len(series) - window - horizon 1): X.append(series[i:iwindow]) y.append(series[iwindowhorizon-1]) return np.array(X), np.array(y) df pd.read_csv(stock_daily/000001.SZ.csv, index_col0) close df[Close].ffill().values.reshape(-1, 1) # 归一化只用训练段统计量避免未来信息泄漏 train_len int(len(close) * 0.8) mu, sigma close[:train_len].mean(), close[:train_len].std() close_norm (close - mu) / sigma X, y make_windows(close_norm, window30, horizon1)参数说明window30表示用过去 30 个交易日预测下一天这个值在源码里是可调的改大能捕捉更长趋势但样本数会减少horizon1是预测步长改成 5 就是预测一周后。归一化这里我特意强调只用训练段统计量因为源码如果偷懒用全量均值方差验证集指标会虚高实盘直接翻车。2.3 模型定义LSTM、RNN 与 Transformer 的取舍源码里同时给了 LSTM、RNN 和 Transformer 三套结构这不是为了堆料而是让你对比不同归纳偏置在金融序列上的表现。RNN 最简单但梯度容易消失LSTM 靠门控缓解了长依赖问题Transformer 用自注意力捕捉全局关系但对数据量要求更高。以 LSTM 为例PyTorch 下的定义通常长这样# 模型定义示意对应源码中的 LSTM 分支 import torch import torch.nn as nn class StockLSTM(nn.Module): def __init__(self, input_size1, hidden_size64, num_layers2, dropout0.2): super().__init__() self.lstm nn.LSTM(input_size, hidden_size, num_layers, batch_firstTrue, dropoutdropout) self.fc nn.Linear(hidden_size, 1) def forward(self, x): # x: (batch, seq_len, input_size) out, _ self.lstm(x) # 只取最后一个时间步的输出做回归 return self.fc(out[:, -1, :])逻辑说明batch_firstTrue让输入维度是(batch, seq, feature)跟前面滑窗构造的格式对齐。hidden_size64和num_layers2是源码默认值前者太小欠拟合太大在几千条样本上直接过拟合。dropout0.2只在多层 LSTM 之间生效单层时这个参数会被忽略这是 PyTorch 的一个静默行为很多人以为设了没用其实是层数不够。选型上我的建议是样本少于 3000 条优先用 LSTM别碰 Transformer想验证注意力机制是否有效至少准备 5 年以上日线数据并且把d_model和nhead调小否则训练损失震荡得让你怀疑人生。3. 跑通训练与预测环境、命令与参数调优3.1 环境准备与依赖安装源码根目录有requirements.txt但金融数据接口的版本兼容性是个老大难。install_files里放了yfinance-0.1.59.tar.gz说明作者也遇到过新版接口变动的问题。我一般会先建虚拟环境再按下面顺序装# 建议 Python 3.8 到 3.10太新的版本部分科学计算包轮子不全 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 先装离线包避免新版 yfinance 接口不兼容 pip install install_files/yfinance-0.1.59.tar.gz # 再装其余依赖 pip install -r requirements.txt逻辑说明先装固定版本的yfinance是关键因为新版把download的返回列名和参数改过源码里的列名引用会直接 KeyError。requirements.txt里通常包含torch、pandas、numpy、matplotlib、scikit-learn如果机器没有 GPUtorch装 CPU 版即可这份模型的参数量 CPU 也能跑。3.2 训练命令与关键超参环境就绪后标准流程是三步抓数据、预处理、训练。python getdata.py # 生成 stock_daily/000001.SZ.csv python data_preprocess.py # 生成训练用的 npz 或 pkl python train.py --model lstm --epochs 100 --lr 0.001 --window 30参数说明--model在源码里通常支持lstm、rnn、transformer三选一--epochs设 100 是保守值配合早停机制实际可能 40 轮就收敛--lr 0.001是 Adam 的常用起点如果损失前几轮就爆成 NaN先降到 0.0001--window要和预处理阶段保持一致两边对不上会报维度错误。训练过程中fig_lstm/下会生成train_loss.png和loss.png前者是训练损失曲线后者通常是训练加验证的对比。判断是否过拟合就看验证损失在第几轮开始抬头抬头之后继续跑就是在背训练集。3.3 预测与指标解读训练完用predict.py加载权重做推理输出会经过反归一化还原成真实价格并和真实值画在同一张图上对应prediction.png。评估指标方面源码给出了MAPE.png和MSE.png这两个要结合看。# 指标计算示意对应 utils.py 中的评估逻辑 import numpy as np def mape(y_true, y_pred): # 避免除零真实值加一个极小量 return np.mean(np.abs((y_true - y_pred) / (y_true 1e-8))) * 100 def mse(y_true, y_pred): return np.mean((y_true - y_pred) ** 2)逻辑说明MAPE 是百分比误差直观但股价接近零时会被放大MSE 对异常值敏感能反映大偏差。源码里如果 MAPE 在 2% 以内说明模型至少学到了趋势如果超过 10%大概率是归一化或滑窗对齐出了问题而不是模型本身不行。提示预测图好看不代表能实盘。股价的随机游走特性决定了任何模型都只能拟合历史统计规律源码的定位是学习工程流程不是交易信号。4. 避坑与排查那些让源码跑不起来的常见问题4.1 数据抓取返回空或列名报错现象运行getdata.py后stock_daily目录为空或者读 CSV 时提示KeyError: Close。 原因yfinance版本差异导致返回列名从Close变成Adj Close或者 A 股代码后缀写错导致请求被拒。 解决确认代码格式为000001.SZ或600000.SS锁定yfinance0.1.59读列时用df.columns先打印确认再决定取哪一列。4.2 归一化用了全量数据导致指标虚高现象验证集 MAPE 低到 1% 以下但预测图明显滞后于真实价格。 原因预处理时对整段序列做了归一化验证集的均值和方差信息泄漏进了训练过程。 解决严格按时间顺序切分只用训练段计算mu和sigma再应用到验证段和测试段。这个坑我见过太多人踩指标漂亮但毫无意义。4.3 滑窗与 batch 维度对不上现象训练时报Expected input batch_size (...) to match target batch_size (...)。 原因DataLoader的batch_size和模型输入维度理解错位或者window在预处理和训练脚本里设了不同值。 解决在common.py里把window定义成全局常量两边引用同一个变量打印一个 batch 的 shape 确认是(batch, seq, feature)。4.4 GPU 显存不足或 CPU 训练过慢现象CUDA out of memory或者 CPU 上跑 100 轮要几个小时。 原因hidden_size或num_layers设得过大或者batch_size没根据硬件调整。 解决把hidden_size从 128 降到 64batch_size从 64 降到 32CPU 训练时把num_layers降到 1epochs配合早停通常 30 轮内就能看出趋势。4.5 BERT 分支依赖缺失导致整体报错现象只想跑 LSTM但import阶段就提示缺少transformers或torchtext。 原因bert_train.py和主训练脚本共享了部分导入或者__init__.py里做了统一入口。 解决要么补装transformers要么把 BERT 相关文件暂时移出目录只保留价量分支。源码把可选模块和核心模块放在同一层这是结构上的小瑕疵动手前先理清依赖。5. 进阶玩法换数据源、加特征与滚动验证5.1 替换成自己的数据源这份源码最大的价值是数据接口和模型解耦。你完全可以把getdata.py换成从本地数据库或 CSV 读取只要保证输出是带日期索引的 OHLCV 表后面整条链路不用动。# 自定义数据源接入示意 import pandas as pd def load_local(code, pathmy_data): df pd.read_csv(f{path}/{code}.csv, parse_dates[date], index_coldate) # 统一列名和源码预期对齐 df df.rename(columns{open: Open, high: High, low: Low, close: Close, volume: Volume}) return df[[Open, High, Low, Close, Volume]].ffill()逻辑说明关键是列名和索引类型对齐源码后续按df[Close]取值列名不一致就会 KeyError。日期列必须转成 DatetimeIndex否则滑窗顺序会乱。5.2 加入技术指标作为额外特征单靠收盘价预测模型能学到的信息有限。常见做法是把均线、RSI、MACD 作为额外特征拼进输入张量input_size从 1 变成 N。特征计算方式加入后的影响MA5/MA20收盘价滚动均值帮助模型识别趋势方向RSI相对强弱指标补充超买超卖信息成交量变化率Volume 的 pct_change反映资金活跃度波动率收益率滚动标准差让模型感知风险状态加入后记得同步修改模型第一层的input_size并且所有特征都要用训练段统计量做标准化否则量纲差异会让 LSTM 门控失效。5.3 用滚动验证替代单次切分单次 8:2 切分的结果偶然性太大换一段区间可能完全变样。更稳妥的做法是滚动验证每次用前 N 年训练、后 1 年验证然后窗口向前滑动。# 滚动验证框架示意 def walk_forward(df, train_years3, test_months6): results [] start df.index.min() while start pd.DateOffset(yearstrain_years1) df.index.max(): train_end start pd.DateOffset(yearstrain_years) test_end train_end pd.DateOffset(monthstest_months) train df[start:train_end] test df[train_end:test_end] # 这里调用训练和评估函数 results.append(evaluate(train, test)) start start pd.DateOffset(monthstest_months) return results逻辑说明train_years3保证模型见过一个完整市场周期test_months6让每次评估有足够样本。滚动验证的指标均值比单次切分可信得多也能暴露模型在不同市场阶段的稳定性差异。5.4 一个容易被忽视的技巧预测差分而非价格直接预测价格模型很容易退化成「输出约等于昨天收盘价」的懒惰解。改成预测收益率或价格差分再累加还原往往能让模型真正去学波动规律。# 差分预测示意 close df[Close].values diff np.diff(close, prependclose[0]) # 一阶差分 # 用 diff 做训练目标预测后再 cumsum 还原 pred_price np.cumsum(pred_diff) close[0]这个改动不大但效果立竿见影。我第一次跑源码时预测图几乎是一条平移的直线换成差分目标后曲线才跟真实价格有了互动。从那以后我每次做时序回归都强制先问一句目标变量是不是该做差分。希望这份拆解能帮你少走几个弯路把这份源码真正跑成自己的东西。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑