☰
LSTM股市预测实战:从数据预处理到模型评估的完整源码解析
2026/10/1 19:14:24 网站建设 项目流程

简介:这份资源面向金融量化初学者与深度学习实践者,提供一套基于LSTM的股票价格预测完整实现,帮助理解循环神经网络在时间序列预测中的应用。压缩包共19个文件,约3.92MB,包含9个Python脚本、2个CSV与1个Excel数据集、2个H5训练模型、2张预测对比图及说明文档,覆盖数据加载、预处理、模型构建、训练、评估与可视化全流程。代码以Keras搭建LSTM,对沪深300及基金历史行情进行归一化与序列化,输出MSE、MAE等指标并绘制真实值与预测值曲线,还涉及参数选择与基金筛选脚本。已有517人学习下载,适合想跑通金融预测项目、复现模型并迁移到自有数据的读者参考,同时需注意股市预测本身存在不确定性。

1. 从一份能跑通的 LSTM 股市预测源码说起

很多人第一次接触 LSTM 时间序列预测,都是被"用深度学习预测股票"这个念头勾进来的。真到动手时才发现,网上大部分示例要么只有一段模型定义,要么数据、训练、评估、画图散落在不同博客里,拼起来根本跑不通。这份lstm_stock-master资源包的价值就在这——它把数据、模型、训练脚本、评估脚本、可视化脚本和一份参数表打包成了一个完整闭环,拿到手就能顺着run.py走一遍全流程。

它解决的不是"预测准不准"的问题,而是"我能不能先把一条完整的 LSTM 预测流水线跑起来"的问题。包里有沪深 300 的hs300.csv、基金001632.csv,还有已经训练好的hs300.h5、001632.h5权重文件,连parameter.xlsx这种调参记录都留了。适合两类人:一类是想学 LSTM 时间序列预测但缺一个能跑通的工程模板的新手,另一类是手里有金融数据、想快速验证 LSTM 在自己标的上表现的从业者。下面按"资源结构 → 数据预处理 → 模型训练 → 评估可视化 → 避坑 → 进阶"的顺序拆开讲。

2. 资源包结构与运行环境:先搞清楚每个文件干什么

2.1 目录拆解与模块职责

拿到压缩包先别急着python run.py,花五分钟把目录结构过一遍,后面排错能省一半时间。这个包的结构大致是这样:

文件/目录作用
run.py主入口,串起数据加载、训练、评估、画图
get_data/数据获取相关脚本目录
get_jz.py净值/基金数据获取脚本
choose_fund.py基金筛选脚本
model/模型定义与训练相关脚本目录
lstm_train.pyLSTM 模型构建与训练
lstm_predict.py加载权重做预测
lstm_choose_parameter.py参数搜索/调参脚本
model_evalute.py模型评估,算 MSE、MAE 等指标
data_preprocess.py数据清洗、归一化、序列化
plot_graph.py实际值 vs 预测值对比图
data/hs300.csv沪深 300 历史数据
data/001632.csv单只基金历史数据
hs300.h5/001632.h5训练好的模型权重
parameter.xlsx参数记录表
requirements.txt依赖清单

这个划分是典型的"数据层 / 模型层 / 评估层 / 展示层"四段式,好处是每个环节可以单独替换。比如你想换成自己的股票数据,只要保证 CSV 列名和data_preprocess.py里读的字段一致,其余脚本基本不用动。

2.2 环境搭建与依赖安装

先确认 Python 版本。这类基于 Keras/TensorFlow 的 LSTM 项目,Python 3.7~3.9 兼容性最稳,3.10 以上有时会在 TensorFlow 版本上卡住。装环境建议用虚拟环境隔离,别直接怼进系统 Python:

# 创建并激活虚拟环境 python -m venv lstm_env # Windows lstm_env\Scripts\activate # macOS / Linux source lstm_env/bin/activate # 安装依赖 pip install -r requirements.txt

requirements.txt里通常锁定了tensorflow、keras、pandas、numpy、matplotlib、scikit-learn、openpyxl这几个。如果安装时 TensorFlow 报版本冲突,常见做法是先单独装tensorflow==2.x对应版本,再装其余包。装完用下面这段验证环境是否就绪:

import tensorflow as tf import pandas as pd import numpy as np import matplotlib.pyplot as plt print("TensorFlow:", tf.__version__) print("Pandas:", pd.__version__) # 检查 GPU 是否可用(没有也不影响,CPU 能跑) print("GPU:", tf.config.list_physical_devices('GPU'))

逻辑说明:这段只做环境自检,不涉及业务。参数上重点看 TensorFlow 版本号,如果低于 2.0,keras的导入路径会不一样(老版本是from keras.models import Sequential,新版本推荐from tensorflow.keras.models import Sequential)。这一步过了,再往下走。

提示:如果pip install卡在下载 TensorFlow,可以换国内镜像源,命令后面加-i https://pypi.tuna.tsinghua.edu.cn/simple,这是常规操作,不涉及任何网络工具。

3. 数据预处理:把 CSV 变成 LSTM 能吃的序列

3.1 数据加载与字段确认

LSTM 吃的是序列,不是原始表格。所以第一步是把hs300.csv读进来,确认有哪些列。金融数据一般包含日期、开盘、收盘、最高、最低、成交量。先跑一段探查代码:

import pandas as pd # 读取沪深300数据 df = pd.read_csv('data/hs300.csv') print(df.head()) print(df.columns.tolist()) print(df.shape) print(df.isnull().sum()) # 看缺失值分布

逻辑说明:head()看前几行长什么样,columns.tolist()确认列名,isnull().sum()统计每列缺失值。参数上要注意两点:一是日期列如果是字符串,后面要么转成datetime要么直接丢弃;二是如果收盘价列名是中文(比如"收盘价"),后续代码引用时要么改列名要么改代码,别硬对。这一步的产出是"我知道我手里有什么数据"。

3.2 归一化与滑动窗口序列化

原始股价动辄几千点,直接喂给 LSTM 会让梯度爆炸,所以必须归一化。常见做法是用MinMaxScaler把价格压到 0~1 区间。归一化之后还要做滑动窗口——LSTM 需要"用前 N 天预测第 N+1 天"这种样本对:

import numpy as np from sklearn.preprocessing import MinMaxScaler # 取收盘价一列 close = df['close'].values.reshape(-1, 1) # 归一化到 0-1 scaler = MinMaxScaler(feature_range=(0, 1)) scaled = scaler.fit_transform(close) # 滑动窗口:用前 60 天预测下一天 window = 60 X, y = [], [] for i in range(window, len(scaled)): X.append(scaled[i-window:i, 0]) y.append(scaled[i, 0]) X, y = np.array(X), np.array(y) # LSTM 输入要求三维:[样本数, 时间步, 特征数] X = np.reshape(X, (X.shape[0], X.shape[1], 1)) print("X shape:", X.shape, "y shape:", y.shape)

逻辑说明:window=60是时间步,意思是"看过去 60 个交易日"。这个值不是随便定的——太小(比如 5)模型学不到趋势,太大(比如 250)样本数骤减且训练变慢,60 是金融时间序列里比较常用的经验值。reshape那一步是新手最容易翻车的地方:LSTM 层要求输入是三维张量(samples, timesteps, features),少一维直接报错。归一化后的scaler一定要留着,因为预测出来的结果还是 0~1,最后要用scaler.inverse_transform()还原成真实价格,否则你画出来的图全是 0 到 1 之间的线,看着像模型废了。

注意:归一化必须只用训练集拟合 scaler,再用同一个 scaler 变换测试集。如果拿全量数据拟合,等于把未来信息泄露给了训练,评估指标会虚高,这是时间序列里最隐蔽的坑之一。

4. 模型构建与训练:LSTM 层怎么堆、参数怎么设

4.1 LSTM 网络结构设计

这个包里lstm_train.py用的是 Keras 的Sequential堆叠。典型结构是:一层或两层 LSTM + 一层 Dropout + 一层 Dense 输出。为什么这么搭?LSTM 层负责提取时序依赖,Dropout 防过拟合,Dense 把隐藏状态映射成一个标量预测值。代码骨架大致如下:

from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout model = Sequential() # 第一层 LSTM,return_sequences=True 才能接第二层 LSTM model.add(LSTM(units=50, return_sequences=True, input_shape=(60, 1))) model.add(Dropout(0.2)) # 第二层 LSTM,输出二维给 Dense model.add(LSTM(units=50, return_sequences=False)) model.add(Dropout(0.2)) # 输出层,预测一个价格 model.add(Dense(units=1)) model.compile(optimizer='adam', loss='mean_squared_error') model.summary()

逻辑说明:units=50是 LSTM 隐藏单元数,太小欠拟合,太大过拟合且慢,50 是常见起点。return_sequences这个参数是分水岭——第一层设True是为了把每个时间步的输出都传给下一层,第二层设False是因为后面接 Dense,只需要最后一个时间步的输出。Dropout(0.2)表示随机丢弃 20% 神经元,金融数据噪声大,这个值能明显压住过拟合。loss用 MSE 是因为这是回归任务,预测的是连续价格。

4.2 训练参数与早停策略

模型搭好之后,fit的参数直接决定训练能不能收敛。批大小、轮数、验证集比例、早停,这几个要一起看:

from tensorflow.keras.callbacks import EarlyStopping # 划分训练集/测试集,时间序列不能打乱 split = int(len(X) * 0.8) X_train, X_test = X[:split], X[split:] y_train, y_test = y[:split], y[split:] # 早停:验证损失 10 轮不降就停,并恢复最优权重 early_stop = EarlyStopping(monitor='val_loss', patience=10, restore_best_weights=True) history = model.fit( X_train, y_train, batch_size=32, epochs=100, validation_split=0.1, callbacks=[early_stop], verbose=1 )

逻辑说明:batch_size=32是显存和收敛速度的折中,数据量小可以降到 16。epochs=100配合EarlyStopping用,patience=10表示验证损失连续 10 轮不下降就停,restore_best_weights=True保证留下的是最优那一轮的权重而不是最后一轮。时间序列划分绝对不能shuffle,因为打乱后"用未来预测过去",指标好看但毫无意义。validation_split=0.1是从训练集里再切 10% 做验证,不碰测试集。

训练完记得保存权重,包里hs300.h5就是这么来的:

model.save('hs300.h5')

4.3 参数搜索脚本怎么用

包里有个lstm_choose_parameter.py,这是调参用的。它一般会遍历不同的window、units、batch_size组合,跑完记录每组在验证集上的 loss,最后写进parameter.xlsx。用法上,先打开这个脚本看它遍历的范围,如果范围太大(比如 units 从 10 到 200 步长 10),跑一轮可能要几小时。我的习惯是先用粗粒度(units 取 32/64/128,window 取 30/60/90)跑一遍,锁定大致区间,再细调。parameter.xlsx就是记录这些实验结果的表,别删,它是你复现最优配置的唯一依据。

5. 评估与可视化:MSE、MAE 和那张对比图怎么看

5.1 评估指标计算

model_evalute.py负责算指标。回归任务常用 MSE、RMSE、MAE 三个。MSE 对异常值敏感,MAE 更稳健,RMSE 和原数据同量纲好解释。核心代码:

from sklearn.metrics import mean_squared_error, mean_absolute_error import numpy as np # 预测 pred = model.predict(X_test) # 反归一化,还原成真实价格 pred_price = scaler.inverse_transform(pred) true_price = scaler.inverse_transform(y_test.reshape(-1, 1)) mse = mean_squared_error(true_price, pred_price) rmse = np.sqrt(mse) mae = mean_absolute_error(true_price, pred_price) print(f"MSE: {mse:.2f}, RMSE: {rmse:.2f}, MAE: {mae:.2f}")

逻辑说明:关键在inverse_transform这一步。如果忘了反归一化,算出来的 MSE 是 0.00x 级别,看着特别漂亮,但那是 0~1 区间的误差,没有实际意义。反归一化之后,RMSE 的单位和股价一致,比如 RMSE=45 就表示平均预测偏差约 45 个点,这样才有参考价值。评估时一定要用测试集,别用训练集自欺欺人。

5.2 预测对比图与结果解读

plot_graph.py画的是实际值 vs 预测值曲线。这张图是判断模型好坏的直观依据,但也是最容易被误读的地方:

import matplotlib.pyplot as plt plt.figure(figsize=(12, 6)) plt.plot(true_price, label='Actual', color='blue') plt.plot(pred_price, label='Predicted', color='red') plt.title('Stock Price Prediction') plt.xlabel('Time') plt.ylabel('Price') plt.legend() plt.savefig('hs300.png') plt.show()

逻辑说明:figsize调大是为了看清细节,label和legend让两条线可区分。看图时注意三点:一是预测线是否整体滞后于实际线——LSTM 预测股价最常见的现象就是"慢半拍",因为它本质是在拟合趋势;二是拐点处预测是否平滑过度,说明模型没抓住突变;三是如果预测线几乎是一条直线,那模型根本没学到东西,回去检查归一化和 window 设置。包里hs300.png、001632.png就是这两张图,可以直接打开对照。

提示:股价预测里,模型能把趋势方向猜对已经算不错,指望它精确到每个点位是不现实的。评估时多看方向准确率,别只盯 MSE。

6. 避坑与常见问题排查

6.1 报错 "Input 0 is incompatible with layer lstm"

现象:运行训练脚本时 Keras 抛出维度不匹配错误,提示期望 3 维输入但收到 2 维。 原因:X没有 reshape 成(samples, timesteps, features),或者window和input_shape对不上。 解决:在喂给模型前打印X.shape,确认是三维;同时检查input_shape=(window, 1)里的window和生成序列时用的window是不是同一个值。这两个地方不一致是高频翻车点。

6.2 预测结果全是同一条水平线

现象:画出来的预测曲线几乎是一条直线,不随实际价格波动。 原因:通常是归一化没做,或者学习率过大导致模型直接收敛到均值;也可能是window太小,模型看不到趋势。 解决:先确认MinMaxScaler有没有生效,打印scaled[:5]看值是否在 0~1;再把window调到 60 以上;如果还不行,把优化器学习率从默认 0.001 降到 0.0001 试。

6.3 评估指标好得离谱

现象:MSE 低到 0.0001,RMSE 只有零点几,看着像完美模型。 原因:忘了反归一化,指标是在 0~1 区间算的;或者划分数据时打乱了顺序,造成未来信息泄露。 解决:检查评估代码里有没有scaler.inverse_transform;确认划分训练/测试集时没有shuffle。时间序列的评估必须严格按时间先后切分。

6.4 训练 loss 不下降或震荡

现象:fit跑了几十轮,loss 一直在高位震荡或几乎不动。 原因:学习率不合适、batch_size 太小、或者数据本身噪声太大没有可学模式。 解决:先把batch_size提到 32 或 64;优化器换成adam并显式设learning_rate=0.001;如果还不行,检查输入数据是不是有大量缺失值或异常值没处理。金融数据里停牌、除权造成的跳变要提前清洗。

6.5 换自己的数据后脚本报 KeyError

现象:把hs300.csv换成自己的股票数据,运行时报列名找不到。 原因:data_preprocess.py里写死了列名(比如df['close']),而你的 CSV 列名不一样。 解决:先print(df.columns.tolist())看实际列名,然后在预处理脚本开头统一重命名,比如df.rename(columns={'收盘价': 'close'}, inplace=True)。别去改后面所有引用,改一处映射最省事。

7. 进阶玩法:把单标的预测扩成多标的批量验证

跑通单只股票之后,真正有价值的是拿这套流程去批量验证多个标的,看 LSTM 在不同波动率资产上的表现差异。包里已经有choose_fund.py和get_jz.py,说明作者本来就考虑了多标的场景。我的做法是写一个外层循环,把data/目录下所有 CSV 依次喂进同一套预处理和训练流程,每个标的记录一组指标,最后汇总成一张对比表:

import os import pandas as pd results = [] for fname in os.listdir('data'): if not fname.endswith('.csv'): continue code = fname.replace('.csv', '') # 复用前面的预处理 + 训练 + 评估流程 mse, rmse, mae = run_pipeline(os.path.join('data', fname)) results.append({'code': code, 'MSE': mse, 'RMSE': rmse, 'MAE': mae}) df_result = pd.DataFrame(results).sort_values('RMSE') df_result.to_excel('batch_result.xlsx', index=False) print(df_result)

逻辑说明:run_pipeline是你把第 3、4、5 章的流程封装成的函数,输入 CSV 路径,输出三个指标。sort_values('RMSE')让误差最小的标的排前面,方便一眼看出 LSTM 在哪些标的上更靠谱。参数上要注意,不同标的的价格量级不同,RMSE 不能直接横向比,最好再算一个归一化后的指标,比如 RMSE 除以均价,这样才有可比性。

批量跑完你会发现一个规律:波动率低、趋势性强的标的(比如宽基指数)预测误差明显小于个股,个股里题材股、小盘股的误差往往大得没法看。这不是模型的问题,是标的本身的可预测性差异。所以拿到结果别急着调模型,先看数据本身值不值得预测。

还有个技巧是拿parameter.xlsx里的历史实验记录做二次分析。把不同window、units组合对应的验证 loss 拉出来画个热力图,能直观看出哪个参数区间是"甜点区"。我一般会固定units扫window,再固定window扫units,两轮下来基本能锁定最优组合,比盲目网格搜索省一半时间。

从那以后我每次拿到新的时间序列数据,都强制先跑一遍"数据探查 → 归一化验证 → 小样本过拟合测试"这三步,确认数据本身没问题再上模型。很多所谓的"模型不收敛",根子都在数据那一步。希望这套流程帮你少走点弯路。

本文还有配套的精品资源,点击获取

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

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

立即咨询