2026/10/18 9:51:05

Django+TensorFlow股票走势预测系统:从LSTM训练到Web部署全攻略

Django+TensorFlow股票走势预测系统:从LSTM训练到Web部署全攻略 最近好几个准备做毕设的同学都在问同一个问题股票预测相关的项目网上单讲LSTM训练的教程一抓一大把单讲Django做网站的教程也多得是可一旦要把“深度学习模型跑在 Django 网站里”这条链路整体串起来能直接参考的资料就零星了。我这次做的“基于深度学习的 DjangoTensorFlow 股票走势预测系统”正好就是这样一个把数据采集、特征工程、LSTM 模型训练、Web 系统集成、预测结果可视化串成完整闭环的项目。这篇文章就把这个项目的设计思路、核心代码逻辑、训练细节、以及我在实际运维中踩过的坑全部摊开讲。想拿来做毕业设计、或者想通过一个完整全栈项目把深度学习落地到 Web 系统的同学可以直接照着这套方案走。先声明一句这套系统的定位是学术研究与技术学习不是炒股工具。市场预测本身具有高度的不确定性任何模型都不能作为真实投资决策依据。把预期摆正了后面每一步都会做得更踏实。1. 为什么选这个题目深度学习加 Web 全栈的技术选型逻辑1.1 这个选题在毕业设计里的“性价比”在哪每年都有大量同学做“管理系统”或“算法实验”前者容易做成重复劳动的增删改查后者容易被老师质疑“只是跑了个 notebook”。股票走势预测系统恰好把两边都占了算法层面有深度学习的训练与评估工程层面有数据落库、Web 展示、接口交互、用户管理。无论老师更看重算法创新还是系统完整度你都有东西可以拿出来讲。从工作量分配看数据清洗和特征工程占三成模型训练与调参占三成Django 集成和前端展示占三成论文与测试占一成。这个比例很健康不会出现“算法写完只剩一个光杆模型”或者“网页做完了但和深度学习毫无关系”的局面。更重要的是这类选题在网上有大量公开参考遇到报错基本都能搜到解决方案对本科生来说非常友好。1.2 Django 而不是 Flask系统完整度优先我见过不少同学为了图轻量选 Flask结果做着做着就开始自己实现登录、后台管理、分页这些 DJango 自带的功能。Flask 本身没有错但毕业设计要呈现的是“一个完整的系统”而不是“一组接口”。Django 自带 ORM、Admin 后台、用户认证、CSRF 防护这些不是摆设它们可以直接写进论文的功能模块里还能少写几百行代码。另一个非常实际的好处是Django 的 ORM 让数据库切换几乎没有成本。项目阶段我用 SQLite 就够了如果答辩环境要求 MySQL改一下配置文件里的数据库连接即可。数据表结构在 models.py 里定义好后迁移命令会帮你把表建好不需要手写 SQL。对于最终交付要包含“完整源码”的项目来说SQLite 也能保证评审老师拿到代码后直接跑起来省去配置数据库的麻烦。1.3 TensorFlow 选型的真实理由选 TensorFlow 而不是 PyTorch我得先说明PyTorch 在科研和动态图调试上确实很香但这个项目的核心诉求是“快速写出可部署的模型并集成到服务器”TensorFlow 的 Keras 高层 API 在这种场景下更顺手。Keras 的 Sequential 模型接口让 LSTM 原型的搭建非常短model.save() 导出的模型文件也可以被 Django 端直接加载。从资料获取角度看股票时序预测相关的开源项目里TensorFlow Keras 的参考代码占比非常高这意味着你在训练报错、维度不匹配、版本兼容这类问题时能搜到大量现成问答。唯一要提醒的是 TensorFlow 2.x 各小版本之间的 API 变动比较大比如 2.9 之后模型的保存格式从 .h5 逐步转向 .keras一不留神就会在加载时报协议错误。解决办法就是锁定版本后面我会专门写环境配置。2. 系统整体架构与数据流设计2.1 模块划分与各自职责整个系统我拆成了五个模块数据采集模块、数据处理模块、模型训练模块、预测服务模块、Web 展示模块。模块之间通过数据库和模型文件衔接耦合度很低任何一块单独抽出来都可以写进论文做详细设计。模块主要职责关键技术点数据采集从公开数据接口获取日K数据并落库增量更新、去重、行情完整性校验数据处理清洗、复权、特征衍生、归一化、生成样本pandas、MinMaxScaler、滑动窗口模型训练构建LSTM、训练、评估、导出模型文件TensorFlow Keras、EarlyStopping预测服务接收前端请求返回预测结果Django视图、模型常驻内存、线程加锁Web展示行情K线、预测曲线、股票切换、历史结果ECharts、Ajax、Bootstrap这五个模块不一定全要自己写但每一步的输入输出都要清晰。比如数据采集模块输出的必须是“已经去重、按日期排序”的行情表数据处理模块输出的必须是“已经归一化并切片好的数组”。能明确说出模块间接口的人答辩时给人的感觉完全不一样。2.2 数据流与关键路径整套系统的数据流可以概括成一条单行道外部数据源把日K数据写入数据库数据处理模块从库里读数据并加工成模型可用的张量模型训练模块产出模型文件Django 在启动时加载模型并对外提供预测接口浏览器端通过 ECharts 把真实走势和预测走势画在同一张图上。其中最容易出问题的是“数据处理逻辑不一致”。训练的时候你可能写了一套脚本做归一化和窗口切分到了 Django 端重新实现一遍时缩放器的参数、窗口长度、特征顺序只要有一处对不上预测结果就会完全错误。我的做法是把数据处理函数独立成一个公共模块训练脚本和 Django 视图都从同一个模块导入确保训练和预测走完全相同的代码路径。这个设计建议每个做类似项目的同学从一开始就采用。2.3 数据库设计细节数据库不需要复杂的表结构核心是两张表。一张是 stock_daily字段包括 stock_code、trade_date、open、high、low、close、volume另一张是 prediction_record字段包括 stock_code、model_version、pred_date、pred_value。用户功能直接用 Django 内置的 User 表不需要自己建。stock_daily 表的唯一索引要设置在 (stock_code, trade_date) 上避免重复导入。预测记录表其实是可选的但加上它之后能多做一个“历史预测对比”的功能论文里也能多放一张功能截图属于低成本高收益的设计。3. 数据获取与预处理决定模型上限的隐形战场3.1 数据源选择与完整性校验很多教程会直接给你一段“下载数据”的代码好像数据是免费的空气一样。实际做的时候你会发现不同数据源的字段命名、复权规则、停牌处理都不一样。我用的方案是直接调用国内免费开源数据接口它不需要复杂的权限申请拿某只股票近五年日K线就够用。数据拿回来之后先别急着训练先做三件事检查是否有空值、检查日期是否连续、检查是否存在价格异常跳变。停牌导致的成交量骤减和价格缺失在行情数据里非常常见如果不处理LSTM 会把这些异常当成正常规律去学习。我的处理策略是把停牌日的行直接用前一日数据补齐并在特征里增加一列“是否停牌次日”的标记。这一列能显著降低模型在某些跳水行情上的偏差。3.2 前复权处理最容易被忽略的关键细节股票除权除息后价格会出现人为的跳空缺口这一天前后 K 线的“连续感”被破坏了。LSTM 的核心假设就是历史模式会延续跳空缺口会让模型误以为发生了剧烈的真实波动。解决方式是使用前复权价格训练。前复权可以理解为以当前价格为准把历史价格按分红送股比例做统一换算让整条价格曲线保持连续。代码上常见的数据接口一般提供复权因子或者直接提供复权后的价格字段。要注意的是复权后的历史价格会随最新行情动态变化所以“固定某一天的复权数据”是更稳妥的做法否则同一只股票不同日期下载的数据特征会不一致。3.3 特征工程与归一化的正确姿势我的输入特征并不复杂开盘价、收盘价、最高价、最低价、成交量再加上几个常见技术指标比如 MA5、MA10、MA20、RSI。这些特征在 pandas 里几十行就能算完但有两个原则必须遵守其一所有特征序列必须对齐不能有的按自然日、有的按交易日其二技术指标计算只用历史数据不能把未来数据揉进指标里。归一化这里有一个很多教程都处理错的点直接对整个数据集做 fit_transform。这个操作看着没问题实际上是数据泄漏等于是让缩放器提前看到了测试集的统计信息。正确做法是先对训练集调用 fit 生成缩放器再用同一个缩放器分别 transform 训练集和测试集。用一个类比解释训练集是考前刷题测试集是正式考试你肯定不能在刷题时就拿到考卷答案去调整自己的答题尺度。3.4 滑动窗口样本构建模型输入需要固定长度的连续时间窗口。我选定的窗口是 60 个交易日也就是大约一个季度。每生成一个样本X 是连续 60 天的全部特征构成的矩阵形状是 (60, feature_count)y 是第 61 天的收盘价。窗口滑动一步就生成一个新样本一只股票五年数据大约能生成一千多个样本。import numpy as np def create_samples(features, target, window60): X, y [], [] for i in range(len(features) - window): X.append(features[i:i window]) y.append(target[i window]) return np.array(X), np.array(y)窗口选 60 的理由很简单太短看不出短期趋势太长训练样本数量会明显下降而且更早的数据对“明天”的预测价值不大。我也对比过 20 天和 120 天的效果60 天在多数股票上略好但这本身就是一个可以写进论文的对比实验。4. LSTM 模型设计与训练把预测问题变成可学习的模式4.1 为什么是 LSTM序列建模的基本逻辑股票行情是一组有序的时间序列今天的收盘价受过去几十个交易日影响这种时间依赖关系是本质特征。如果用普通全连接网络输入只被当成一组独立的数字时间顺序被完全丢弃。RNN 虽然能处理序列但梯度消失严重很难学到长期依赖。LSTM 通过输入门、遗忘门、输出门三个门控机制有选择地保留和遗忘历史信息从而在较长序列上保持稳定的梯度传递。通俗一点说LSTM 就像一个懂得记笔记的员工每天上班都会看一眼之前的记录但对哪些信息值得保留、哪些可以丢弃有自己的判断。股票数据噪声极大这种“选择性记忆”能力非常重要。GRU 也能达到类似效果且参数更少但 LSTM 在公开资料、模型解释性、论文写作素材方面更丰富所以我最终选了 LSTM。4.2 模型结构与参数设置我采用的是两层 LSTM 加 Dropout 加全连接输出层的结构from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam model Sequential([ LSTM(64, return_sequencesTrue, input_shape(60, feature_count)), Dropout(0.2), LSTM(32), Dropout(0.2), Dense(1) ]) model.compile(optimizerAdam(learning_rate0.001), lossmse)第一层 LSTM 返回完整序列给第二层提供更丰富的中间表示第二层只返回最后一个时间步的输出再接一个 Dense 层输出预测的收盘价。中间夹 Dropout 是为了抑制过拟合。只输出一个神经元是因为我把任务定义成了回归任务输出的就是一个连续价格如果你想预测涨跌方向可以把 Dense 改成两个神经元加 softmax那就是一个分类任务评价指标也要相应改成准确率。4.3 训练策略与评价指标训练时我会设置 EarlyStopping监控验证集损失连续 10 个 epoch 不下降就停止并且恢复最优权重。同时配合 ReduceLROnPlateau当验证损失进入平台期时自动把学习率降低一半这样既不用手动调参又能让收敛过程更平滑。评价指标我同时看三组MAE、RMSE、MAPE。其中 MAPE 是百分比误差报告给非专业听众时更直观。但是这里有个大坑当真实价格非常接近 0 时MAPE 会爆炸。股票价格一般不容易碰到这种情况但如果你的特征里放了成交量等指标要小心归一化后的数据范围问题。还有一个必须做的对照实验用“今天的收盘价预测明天的收盘价”作为朴素基线。如果 LSTM 在测试集上的误差连这个基线都跑不赢说明模型没有学到有效信息。这不是丢人的事金融时序本身的信噪比极低很多学术研究也承认纯价格序列的预测上限就在基线附近。论文里如实呈现这个对比反而能让老师相信你的结果是严谨的。5. Django 集成 TensorFlow 模型从训练脚本到 Web 服务的完整链路5.1 模型保存与加载别在请求里反复读模型训练完成后把模型导出成 SavedModel 格式这是 TensorFlow 官方推荐的生产部署格式。相比 .h5SavedModel 是一整个目录包含了模型结构、权重和推理所需的其他资源跨环境加载的容错性更好。如果你用的是 Keras 自带的 .h5 或 .keras在加载时务必保证训练环境和 Django 环境的 TensorFlow 版本一致否则很容易出现自定义层无法反序列化的报错。加载时机非常关键。直接把 load_model 写在视图函数里会带来两个问题每次请求都重新加载模型响应速度慢得让人无法接受多次加载同一模型还会造成内存浪费。我的做法是在 Django 的 AppConfig.ready() 方法里加载模型并把它挂到应用实例上这样服务启动时只加载一次。predict_service.py 里可以写一个简单的单例封装。# predictor.py import threading import tensorflow as tf _model None _lock threading.Lock() def load_model_once(model_path): global _model with _lock: if _model is None: _model tf.keras.models.load_model(model_path) return _model def predict(input_array): model load_model_once(models/stock_model) return model.predict(input_array, verbose0)5.2 预测接口设计与并发处理Django 端我设计了一个简单的 REST 风格接口。前端请求 /api/predict/?code600519视图函数拿到股票代码后查询最近 60 个交易日的数据调用公共数据处理模块做归一化和窗口拼装再交给模型推理最终返回 JSON 格式的预测结果。真实行情曲线也可以由同一个接口返回或者单独做一个行情查询接口。这里要特别提醒并发问题。TensorFlow 模型在多线程环境下调用 predict 并不是绝对线程安全的同一个 Session 或 Graph 被多个线程同时使用可能抛出莫名其妙的问题。我的做法是为推理加了一个线程锁。由于单次推理只有毫秒级加锁的吞吐损失在毕设演示场景下完全感受不到。如果你的系统要支撑更高并发更合理的方案是把模型推理拆成一个独立服务Django 通过 HTTP 或消息队列发起调用。毕设阶段不需要过度设计但论文里可以提一句未来优化方向这属于加分项。5.3 前端可视化ECharts 叠加真实曲线与预测曲线前端我用 Bootstrap 搭页面用 ECharts 画图。ECharts 的 k线图组件对股票场景支持非常好关键是把真实收盘价曲线和预测曲线画在一起做一个直观的视觉对比。预测曲线通常从最后一个真实数据点开始往后延伸未来 5 到 10 个交易日。$.getJSON(/api/predict/, {code: stockCode}, function (res) { myChart.setOption({ xAxis: { type: category, data: res.date_list }, series: [ { type: line, name: 真实收盘价, data: res.real_list }, { type: line, name: 预测收盘价, data: res.pred_list } ] }); });页面交互逻辑很简单一个下拉框切换股票代码点击预测按钮后 Ajax 请求接口拿到数据后刷新图表。整个过程中用户无感知的地方是后端要完成查询数据库、数据对齐、归一化、窗口拼接这一整套动作。所以这个接口在编写时一定要把“代码换成输入”的逻辑和训练脚本保持一致我建议直接把训练脚本里的数据处理函数做成公共模块 import 进来不要复制粘贴代码否则改了一处忘了另一处结果就会非常难排查。6. 实测效果复盘那些教程里不会写的真实问题6.1 预测曲线为什么总是“慢半拍”第一次在测试集上画图时我明显看到预测曲线比真实曲线在时间上“滞后”了一截看起来就像把真实曲线整体向右平移了一天。这不是过拟合而是 MSE 损失下的典型现象在随机游走型时间序列上对明天最安全的预测就是今天的价格。模型只要学会“输出接近最后一个输入值”MSE 就能降得比较低。这个现象我当时很不甘心甚至试过加更深的网络、更多特征结果都没有本质改善。后来在整理论文时我把它作为一个“模型局限性分析”章节好好写了出来反而成了答辩时老师最认可的分析片段。做时序预测项目一定要提前做好“纯价格序列预测难以大幅超越基线”的心理准备这并不代表项目失败而是对问题本质的准确认识。你可以尝试加入新闻情绪、宏观因子等外部数据来提升上限这是论文展望部分的天然素材。6.2 训练与测试的样本重叠问题滑动窗口生成的样本之间存在高度重叠相邻样本的时间点几乎一样。如果在构建数据集时不小心做了随机打乱再切分训练集和测试集那么测试集里的很多样本和训练集样本只差一两天模型相当于提前见过了答案测试指标看起来会很好但完全没有泛化意义。正确做法是按时间顺序硬切分先取前面 70% 的交易日生成样本作为训练集后面 15% 作为验证集最后 15% 作为测试集整个过程中不 shuffle。我在代码里严格按日期排序后再切分训练时只在训练集内部做 batch 的 shuffle验证集和测试集原样传入模型。这个细节我建议每个同学都写进论文的数据处理章节能显著提升评审老师对你专业度的评价。6.3 环境依赖与版本锁定这个项目最烦人的坑集中在环境依赖上。TensorFlow 2.10 之后在 Windows 上的 GPU 支持策略有调整如果没有 NVIDIA 显卡或 CUDA 环境直接用 CPU 版即可预测任务数据量小CPU 推理完全够用。Django 建议用 4.2 的稳定版本不要追最新主版本某些第三方插件可能还没有跟上新版本。我将 requirements.txt 贴出来供参考tensorflow2.10.0 Django4.2 pandas2.0.3 numpy1.24.3 scikit-learn1.3.0 akshare1.10.0这一组版本在我反复搭建和测试过程中没有出现兼容问题。特别提醒 numpyTensorFlow 较旧版本对 numpy 2.x 的兼容性很差如果直接 pip install 安装了最新 numpy启动时经常会报找不到某个内部符号的错误。先把 numpy 版本锁住能省掉大量排查时间。6.4 性能优化与体验打磨模型推理本身毫秒级真正拖慢响应的是数据查询和预处理。我做了两个优化第一给 stock_daily 表的 stock_code 和 trade_date 加上联合索引历史数据查询从几百毫秒降到几十毫秒第二把最近一次预测结果按股票代码缓存到内存里相同股票短时间内重复请求直接返回缓存。这两个优化都很简单但能让演示现场流畅很多体验感差距非常明显。7. 论文写作与答辩准备让项目“讲得出、问不倒”7.1 论文结构怎么安排论文的章节安排我建议遵循经典的“背景—技术—设计—实现—测试—总结”六段式绪论部分交代研究背景、意义、国内外研究现状相关技术部分介绍深度学习、LSTM、Django、前后端技术栈需求分析部分从功能和非功能两个维度展开系统设计部分画架构图、功能模块图、数据库 ER 图、时序图系统实现部分按模块展示核心代码和运行截图测试部分同时包含功能测试用例和模型实验结果对比表。一个非常有效的写作技巧把每个模块的“输入—处理—输出”用一个小节描述清楚配合一张截图这段基本不会缺字数。模型实验最好放一张对比表包括窗口长度、层数、学习率、MAE、RMSE、MAPE 这些指标表中呈现 2 到 3 组对比实验即可。论文不需要写出惊天动地的创新点但需要让老师看到你做了完整的实验闭环。7.2 答辩高频问题与应对思路答辩时老师大概率会问几个方向你为什么要选 LSTM数据泄漏怎么避免MAPE 和 MSE 的区别模型预测不准怎么办系统如何更新数据和模型这些问题其实都在前面的内容里覆盖了。我的建议是准备一张 A4 纸写下“技术难点—解决方案—实验结果”的对照提纲每一个问题都用“问题现象—原因分析—我的处理方式—效果如何”的结构回答。演示环节要提前准备一只走势有代表性的股票既有明显的上升和下跌段预测曲线也和真实曲线有可讲述的差异点。不要在答辩现场临时换股票或重新训练模型所有演示数据要提前跑一遍并截图保存。如果老师问到模型上线后的数据更新问题可以如实回答当前是离线训练、定期更新这属于合理的阶段性结论。7.3 给后面做同类项目的同学三条建议第一优先保证链路通畅再去调效果。先把“数据接入—模型训练—Django 加载—前端展示”跑通哪怕模型效果一般至少是一个完整系统先花两周调参而不搭系统最后很容易崩溃。第二所有预处理逻辑尽量收拢到一个模块保证训练和预测共用一套代码这是减少“跑通训练却集不成”的最有效手段。第三论文配图不要用手机拍照统一用截图工具导出高清图字体大小保持一致这属于第一印象分。最后再分享一个实际操作中的体会做完这个项目后我最大的收获不是“会了 LSTM”或者“会了 Django”而是建立了一个完整的产品思维闭环——从数据到模型再到用户交互每一层都有取舍每一层的结果都会传导到最终体验上。股票预测系统的精度上限摆在那里但系统设计与实现的工程价值不会因为模型效果打折。希望这篇拆解能帮你少走几个大弯把时间花在真正有产出的事情上。