简介这份Python期末大型作业资源聚焦深度学习在股票分析预测中的实战应用面向计算机相关专业学生及希望积累项目经验的自学者难度适中适合作为课程设计或简历项目参考。资源包共20个文件包含6个py源码、6张jpg预测结果图、3个csv股票数据集以及txt、md说明文档和license等配置项压缩包约4.24MB结构清晰便于按模块阅读与复现。项目覆盖数据下载与预处理、股票指标计算、LSTM等模型训练、回测与策略评估等完整流程代码经导师指导并通过评审本地编译调试可运行。目前已有76人学习下载。读者可从中获得一套可运行的深度学习选股预测方案理解时间序列建模、过拟合抑制与MSE、RMSE等评估指标的实际用法并参考回测脚本与结果图验证模型效果适合作为金融科技方向的入门实战练习。1. 一份能跑通的股票预测作业到底长什么样每年期末季GitHub 和各类资源站上都会冒出一大批「深度学习股票预测」的作业代码但真正能让人从头跑通、看懂每一行在干什么的并不多。这份 Python 期末大型作业的定位很明确它是一套完整的、面向课程答辩场景的深度学习股票分析预测项目代码覆盖数据获取、特征工程、模型搭建、训练评估到可视化输出的全链路。适合谁正在赶 Python 期末大作业的学生、想找一个能直接改参数复现的入门级量化 demo 的开发者以及需要一份结构清晰的深度学习实战项目案例来对照学习的自学者。它不承诺帮你赚钱但能让你在答辩时讲清楚 LSTM 到底学了什么、为什么用收盘价做归一化、损失曲线震荡时该看哪个参数。下面我从代码结构、环境配置、核心实现到踩坑记录把这套东西拆开讲一遍。2. 代码结构与数据流先搞清楚文件谁调谁拿到一个压缩包最忌讳的就是双击 main.py 直接跑。这套项目的目录组织是典型的课程作业风格根目录下分 data、models、utils、results 四个文件夹外加一个 config.py 和 train.py。先花五分钟把调用关系理清楚后面调参和排错能省一半时间。2.1 目录树与模块职责我一般拿到这类项目会先跑一遍tree -L 2或者直接在编辑器里看结构。这套代码的典型布局如下stock_prediction/ ├── config.py # 全局超参数、路径、股票代码 ├── data/ │ ├── raw/ # 原始 CSV从 akshare 或 tushare 拉取 │ └── processed/ # 归一化后的 npy 文件 ├── models/ │ ├── lstm_model.py # LSTM 网络定义 │ └── train_utils.py # 训练循环、早停、学习率调度 ├── utils/ │ ├── data_loader.py # 数据读取与滑动窗口切分 │ └── metrics.py # RMSE、MAE、方向准确率 ├── results/ │ ├── figures/ # 预测对比图、损失曲线 │ └── checkpoints/ # 保存的 .pth 权重 └── train.py # 入口脚本config.py是整个项目的控制面板所有路径、窗口长度、batch size、学习率都从这里读。常见做法是把SEQ_LEN时间窗口、PRED_LEN预测步长、TRAIN_RATIO放在最上面改参数不用翻遍所有文件。data_loader.py负责把原始价格序列切成(样本数, SEQ_LEN, 特征数)的三维张量这一步的边界处理最容易出问题后面避坑章节会细说。2.2 数据获取与特征工程的实际选择这套代码默认用 akshare 拉 A 股日线数据接口是ak.stock_zh_a_hist。如果你本地没装 akshare也可以换成 tushare 或者直接读自带的 CSV 样例。关键不在于用哪个库而在于特征怎么选。课程作业里最常见的做法是只用收盘价Close做单变量预测但这份代码额外加入了成交量Volume和最高最低价差High-Low形成三通道输入。# utils/data_loader.py 核心片段 import numpy as np import pandas as pd from sklearn.preprocessing import MinMaxScaler def load_and_preprocess(csv_path, seq_len60, pred_len1, train_ratio0.8): df pd.read_csv(csv_path, parse_dates[日期]) df df.sort_values(日期).reset_index(dropTrue) # 选取三个特征列缺失值前向填充 features df[[收盘, 成交量, 最高]].copy() features[最高] features[最高] - df[最低] # 价差特征 features features.ffill().bfill() scaler MinMaxScaler(feature_range(0, 1)) scaled scaler.fit_transform(features.values) X, y [], [] for i in range(len(scaled) - seq_len - pred_len 1): X.append(scaled[i:iseq_len]) y.append(scaled[iseq_len:iseq_lenpred_len, 0]) # 只预测收盘价 X, y np.array(X), np.array(y) split int(len(X) * train_ratio) return X[:split], y[:split], X[split:], y[split:], scaler这段逻辑有三个参数需要你根据数据量调整seq_len60表示用过去 60 个交易日预测下一天A 股一年约 240 个交易日60 天大约是一个季度的窗口pred_len1是单步预测改成 5 就是预测一周train_ratio0.8是训练集比例时间序列不能随机打乱必须按时间顺序切分。MinMaxScaler的fit_transform只能在训练集上做验证集和测试集要用训练集的 scaler 做transform否则就是典型的数据泄露。代码里为了简洁把 scaler 返回出去你在评估阶段记得用它做逆变换还原价格。注意akshare 接口偶尔会因为网络或数据源调整报错建议第一次运行时先把数据存成本地 CSV后续调试直接读文件别每次都重新拉。3. LSTM 模型搭建与训练从张量形状到早停策略数据管道通了之后核心就是模型。这份代码用的是两层 LSTM 加全连接输出结构不复杂但每一层的输入输出形状、dropout 位置、损失函数选择都有讲究。我带过的几个做类似作业的同学翻车最多的地方不是网络写错而是张量维度对不上和训练策略没设好。3.1 网络定义与前向传播的形状推导先看模型代码再逐行拆形状变化# models/lstm_model.py import torch import torch.nn as nn class StockLSTM(nn.Module): def __init__(self, input_size3, hidden_size64, num_layers2, dropout0.2): super().__init__() self.lstm nn.LSTM( input_sizeinput_size, hidden_sizehidden_size, num_layersnum_layers, batch_firstTrue, dropoutdropout if num_layers 1 else 0 ) self.fc nn.Sequential( nn.Linear(hidden_size, 32), nn.ReLU(), nn.Dropout(dropout), nn.Linear(32, 1) ) def forward(self, x): # x: (batch, seq_len, input_size) lstm_out, (h_n, c_n) self.lstm(x) # 取最后一个时间步的输出 last_step lstm_out[:, -1, :] # (batch, hidden_size) out self.fc(last_step) # (batch, 1) return outinput_size3对应收盘、成交量、价差三个特征。hidden_size64是 LSTM 内部状态的维度课程作业里 32 到 128 都算合理太小欠拟合太大容易过拟合且训练慢。num_layers2表示堆叠两层 LSTM注意 PyTorch 的nn.LSTM在num_layers1时才会启用 dropout 参数单层加 dropout 是无效的。batch_firstTrue让输入形状是(batch, seq, feature)如果你从 Keras 转过来习惯(batch, feature, seq)这里不改会直接报维度错误。前向传播里lstm_out[:, -1, :]取的是最后一个时间步的隐藏状态这是做单步预测最常见的做法。另一种做法是对所有时间步做注意力池化但课程作业用最后一步就够了答辩时也好解释模型认为最近一天的信息最重要。3.2 训练循环、损失函数与早停训练脚本的核心逻辑如下# train.py 核心训练片段 import torch from torch.utils.data import DataLoader, TensorDataset def train_model(model, X_train, y_train, X_val, y_val, config): device torch.device(cuda if torch.cuda.is_available() else cpu) model model.to(device) train_ds TensorDataset(torch.FloatTensor(X_train), torch.FloatTensor(y_train)) train_loader DataLoader(train_ds, batch_sizeconfig.batch_size, shuffleTrue) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lrconfig.lr) scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemin, patience10, factor0.5 ) best_val_loss float(inf) patience_counter 0 for epoch in range(config.epochs): model.train() for batch_x, batch_y in train_loader: batch_x, batch_y batch_x.to(device), batch_y.to(device) optimizer.zero_grad() pred model(batch_x) loss criterion(pred, batch_y) loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm1.0) optimizer.step() # 验证 model.eval() with torch.no_grad(): val_pred model(torch.FloatTensor(X_val).to(device)) val_loss criterion(val_pred, torch.FloatTensor(y_val).to(device)).item() scheduler.step(val_loss) if val_loss best_val_loss: best_val_loss val_loss torch.save(model.state_dict(), results/checkpoints/best.pth) patience_counter 0 else: patience_counter 1 if patience_counter config.early_stop_patience: print(fEarly stop at epoch {epoch}) break几个关键参数batch_size一般设 32 或 64股票数据量不大太小梯度震荡太大收敛慢。lr0.001是 Adam 的常用起点配合ReduceLROnPlateau在验证损失不降时自动减半。clip_grad_norm_是防梯度爆炸的后悔药LSTM 在序列较长时容易出现梯度爆炸加上这行能稳很多。早停的patience设 15 到 20 比较合理设太小容易在损失还在下降时提前停掉。提示如果你用 CPU 训练把seq_len从 60 降到 30hidden_size从 64 降到 32否则一个 epoch 可能要等好几分钟调试效率极低。4. 避坑与排查那些答辩前夜才发现的翻车点这一章是我自己跑这类代码时踩过的坑也是帮别人看作业时最常被问到的几个问题。每条按现象、原因、解决来写你对着排查就行。4.1 损失曲线震荡剧烈验证集 RMSE 比训练集还低现象训练 loss 上下跳验证 loss 反而更低且平稳。原因通常有两个一是数据泄露归一化时用了全量数据 fit导致验证集信息泄露到训练过程二是训练集和验证集切分时随机打乱了时间顺序。解决检查MinMaxScaler是否只在训练集上 fit检查切分是否用了train_test_split(shuffleFalse)或手动按索引切。时间序列的验证集必须晚于训练集。4.2 预测结果是一条直线完全不跟随价格波动现象画出来的预测曲线几乎水平和真实价格走势无关。原因模型输出被归一化到 [0,1] 后没有做逆变换或者逆变换时只对预测列做了 scaler 但 scaler 是多列拟合的。解决如果 scaler 是用三列 fit 的逆变换时要构造一个同样形状的数组把预测值放在收盘价那一列其余列填 0再inverse_transform后取第一列。另一种可能是学习率太大导致模型直接收敛到均值把 lr 降到 1e-4 试试。4.3 运行时报错 RuntimeError: expected scalar type Double but found Float现象PyTorch 报类型不匹配。原因numpy 默认 float64转 tensor 时用了torch.DoubleTensor或没显式转 float32。解决统一用torch.FloatTensor(X)或者在DataLoader里加torch.tensor(X, dtypetorch.float32)。这个错误在从 sklearn 转过来的代码里特别常见。4.4 akshare 拉数据返回空 DataFrame 或列名对不上现象ak.stock_zh_a_hist返回空或者列名从「收盘」变成了「close」。原因akshare 版本更新后接口参数或返回列名有变化不同版本行为不一致。解决先打印df.columns确认列名用df.rename(columns{...})统一成代码里用的名字。如果接口一直不稳定直接去网易财经或新浪财经下载 CSV手动放到data/raw/下改config.py里的路径即可。4.5 GPU 显存够但训练速度没提升现象torch.cuda.is_available()返回 True但训练速度和 CPU 差不多。原因数据量太小GPU 的 kernel 启动开销占了大头或者batch_size太小导致 GPU 利用率低。解决把batch_size提到 128 或 256或者干脆用 CPU 跑这套模型的参数量在 CPU 上完全能承受。别为了用 GPU 而用 GPU。5. 评估与可视化怎么把结果讲成一个完整故事训练跑通只是及格线答辩时老师最想看的是你怎么评估、怎么解释结果。这份代码在utils/metrics.py里提供了 RMSE、MAE 和方向准确率三个指标但光有数字不够你得把预测曲线和真实曲线画在一起再补一张损失下降曲线故事才完整。5.1 三个评估指标的实际含义RMSE 和 MAE 衡量的是价格预测的绝对误差但股票预测里更有意义的是方向准确率——预测涨跌方向对了没有。计算方式很简单# utils/metrics.py import numpy as np def direction_accuracy(y_true, y_pred): # y_true, y_pred 是逆变换后的真实价格序列 true_diff np.diff(y_true.flatten()) pred_diff np.diff(y_pred.flatten()) correct np.sum((true_diff 0) (pred_diff 0)) return correct / len(true_diff) def rmse(y_true, y_pred): return np.sqrt(np.mean((y_true - y_pred) ** 2))direction_accuracy先做一阶差分得到涨跌方向再比较预测方向和真实方向是否一致。注意这个指标在震荡行情里可能只有 50% 左右别期望太高答辩时能解释清楚「方向准确率接近随机说明模型没有学到有效信号」反而是加分项。5.2 可视化脚本与结果解读画图部分用 matplotlib 就行关键是两张图一张是测试集上的预测 vs 真实价格曲线另一张是训练和验证损失随 epoch 的变化。代码不复杂import matplotlib.pyplot as plt def plot_results(y_true, y_pred, train_losses, val_losses, save_path): fig, axes plt.subplots(1, 2, figsize(14, 5)) axes[0].plot(y_true, labelReal Price, linewidth1.5) axes[0].plot(y_pred, labelPredicted, linewidth1.5, linestyle--) axes[0].set_title(Test Set: Prediction vs Real) axes[0].legend() axes[1].plot(train_losses, labelTrain Loss) axes[1].plot(val_losses, labelVal Loss) axes[1].set_title(Loss Curve) axes[1].legend() plt.tight_layout() plt.savefig(save_path, dpi150)解读时注意如果预测曲线整体滞后于真实曲线一天说明模型学到的是「昨天价格约等于今天价格」这个平凡解这时候需要检查特征里是否包含了未来信息或者尝试改成多步预测。损失曲线如果验证 loss 先降后升说明过拟合了早停点应该取验证 loss 最低的那个 epoch 的权重而不是最后一个 epoch。5.3 答辩时怎么讲这份作业我一般建议按这个顺序讲先讲数据来源和特征选择理由再讲为什么选 LSTM 而不是 ARIMA 或 Transformer然后展示损失曲线说明训练过程可控最后放预测对比图并主动指出模型的局限性。主动说「这个模型在震荡市里方向准确率只有 52%说明它主要学到了趋势惯性」比硬吹「预测很准」要可信得多。老师问「为什么不用注意力机制」时回答「课程作业范围内 LSTM 已经能说明序列建模的基本流程注意力机制留作后续改进方向」就够了。从那以后我每次带人跑这类项目都强制先跑一遍数据管道的形状检查再开训练。希望帮到你。本文还有配套的精品资源点击获取