简介这份资源是面向计算机、人工智能、自动化等专业学生与教师的期货价格预测毕业设计项目核心为基于相关性分析的CNN-Attention-LSTM组合模型实现可用于课程设计、毕设、作业或项目初期立项演示。压缩包共29个文件约30.28MB包含8个Python源码文件、6个npy数据文件、3个xlsx表格、2个模型checkpoint文件及2份PDF教程覆盖数据预处理、相关性分析、时间步处理、模型训练与预测推理等完整流程并附有配置教程与详细注释。项目代码经过测试运行成功读者可据此掌握从特征筛选到深度学习建模的完整链路理解CNN提取局部特征、Attention加权与LSTM时序建模的融合思路也可在现有代码基础上修改以扩展其他功能。目前已有677人学习适合需要完整赛题方案与排错参考的学习者。1. 期货价格预测毕设项目相关性分析加 CNN-Attention-LSTM 到底能跑出什么做期货价格预测的毕设最怕两件事一是数据拿不到二是模型跑出来一条直线。这个标题里的方案核心思路是用 Spearman 相关性分析先筛因子再把筛选后的多变量序列喂给 CNN-Attention-LSTM 做预测。它解决的不是预测明天涨跌这种玄学问题而是给定一段历史量价数据能不能比单变量 LSTM 更稳地拟合下一根 K 线的收盘价。适合谁适合正在做量化方向毕设、手里有 Python 基础、但不知道从哪套模型下手的同学。整套流程在普通笔记本上就能跑不需要 GPU 集群。下面我把从数据到模型到评估的完整路径拆开讲包括我踩过的坑和参数怎么调。2. 相关性分析筛因子Spearman 为什么比 Pearson 更适合期货数据2.1 期货收益率序列的非正态性决定了相关系数的选择期货价格序列有两个显著特征一是非平稳二是收益率分布尖峰厚尾。Pearson 相关系数假设变量线性相关且服从正态分布直接套在期货收益率上会严重低估尾部关联。Spearman 秩相关系数不要求正态假设只关心排序一致性对异常值也更稳健。我一般会先对原始价格做一阶差分得到收益率再算 Spearman 矩阵。实际操作中把主力连续合约的收盘价、成交量、持仓量、以及几个宏观因子比如螺纹钢对应的铁矿石价格一起纳入候选池算完 Spearman 矩阵后按绝对值排序取前 60% 作为入模特征。阈值不要卡太死0.3 以上就可以保留因为后续 CNN 层本身有特征提取能力。import pandas as pd import numpy as np from scipy.stats import spearmanr # 假设 df 的列是各因子行是时间序列 # 先做一阶差分转收益率避免伪相关 returns df.pct_change().dropna() # 计算 Spearman 相关系数矩阵 corr_matrix, p_values spearmanr(returns) # 转成 DataFrame 方便筛选 corr_df pd.DataFrame(corr_matrix, indexreturns.columns, columnsreturns.columns) # 假设目标变量是 close取与它相关性绝对值大于 0.3 的因子 target_corr corr_df[close].abs().sort_values(ascendingFalse) selected_features target_corr[target_corr 0.3].index.tolist() print(入选因子, selected_features)这段代码的关键点pct_change()做差分是必须的否则价格序列之间的高相关性只是共同趋势造成的伪相关。spearmanr返回的是矩阵和 p 值矩阵p 值可以用来做显著性过滤但毕设场景下样本量通常够大p 值普遍很小重点看相关系数绝对值。selected_features里会包含 close 自身后续建模时要把它从特征列里去掉只保留其他因子作为输入。2.2 滚动窗口相关性静态矩阵会骗你静态 Spearman 矩阵算的是全样本相关性但期货市场结构会变。2020 年之前螺纹钢和铁矿石的联动关系和现在完全不同。我建议用滚动窗口算时变相关性窗口长度取 60 或 120 个交易日。具体做法是每个窗口算一次 Spearman然后看目标因子相关性的稳定性——如果某个因子在多个窗口里相关性忽高忽低直接剔除。window_size 60 rolling_corr [] for i in range(window_size, len(returns)): window returns.iloc[i-window_size:i] corr, _ spearmanr(window) corr_df pd.DataFrame(corr, indexwindow.columns, columnswindow.columns) rolling_corr.append(corr_df[close]) rolling_corr_df pd.concat(rolling_corr, axis1) # 计算每个因子相关性的均值和标准差 stability rolling_corr_df.T.describe().T[[mean, std]] # 保留均值绝对值大于 0.25 且标准差小于 0.15 的因子 stable_features stability[(stability[mean].abs() 0.25) (stability[std] 0.15)].index.tolist()参数说明window_size取 60 是因为期货大约三个月换一次主力合约60 个交易日覆盖了一个完整的主力周期。std阈值 0.15 是我试出来的经验值太严会筛掉所有因子太松等于没筛。这一步做完入模特征通常从十几个降到五六个模型训练速度明显提升。3. CNN-Attention-LSTM 模型搭建从输入形状到注意力权重的完整实现3.1 为什么是 CNN 在前、LSTM 在后、Attention 夹中间这个架构的顺序不是随便排的。CNN 做一维卷积负责从原始序列里提取局部模式比如连续三天的放量上涨。LSTM 处理时序依赖记住更长周期的趋势。Attention 放在 LSTM 之后对 LSTM 每个时间步的输出加权让模型自己决定哪些历史时刻对当前预测更重要。如果反过来先 LSTM 再 CNN卷积会破坏时序结构效果通常更差。输入数据的形状是(样本数, 时间步长, 特征数)。时间步长我一般取 20也就是用过去 20 天的数据预测第 21 天。特征数就是上一步筛选后的因子数量。标签是下一日的收盘价收益率注意是收益率不是价格这样模型学的是变化量泛化更好。import numpy as np import tensorflow as tf from tensorflow.keras import layers, Model def build_cnn_attention_lstm(input_shape, cnn_filters64, kernel_size3, lstm_units64): inputs layers.Input(shapeinput_shape) # CNN 层提取局部特征 x layers.Conv1D(filterscnn_filters, kernel_sizekernel_size, paddingsame, activationrelu)(inputs) x layers.BatchNormalization()(x) x layers.MaxPooling1D(pool_size2)(x) # LSTM 层捕捉时序依赖return_sequencesTrue 保留每个时间步输出 x layers.LSTM(lstm_units, return_sequencesTrue)(x) x layers.LSTM(lstm_units // 2, return_sequencesTrue)(x) # Attention 层对 LSTM 输出加权 attention layers.Dense(1, activationtanh)(x) attention layers.Flatten()(attention) attention layers.Activation(softmax)(attention) attention layers.RepeatVector(lstm_units // 2)(attention) attention layers.Permute([2, 1])(attention) # 加权求和 x layers.Multiply()([x, attention]) x layers.Lambda(lambda z: tf.reduce_sum(z, axis1))(x) # 输出层 x layers.Dense(32, activationrelu)(x) x layers.Dropout(0.2)(x) outputs layers.Dense(1)(x) model Model(inputsinputs, outputsoutputs) model.compile(optimizertf.keras.optimizers.Adam(learning_rate0.001), lossmse, metrics[mae]) return model model build_cnn_attention_lstm(input_shape(20, 6)) model.summary()逻辑说明Conv1D的paddingsame保证输出长度不变MaxPooling1D把时间步从 20 降到 10减少 LSTM 的计算量。两层 LSTM 的return_sequencesTrue是必须的否则 Attention 拿不到每个时间步的输出。Attention 的实现方式是先用一个 Dense 层算每个时间步的分数softmax 归一化成权重再和 LSTM 输出逐元素相乘后求和。RepeatVector和Permute是为了把权重维度对齐到 LSTM 输出。参数说明cnn_filters64是起点数据量大可以加到 128。kernel_size3对应三天的局部模式改成 5 会捕捉更长的模式但参数更多。lstm_units64配合两层结构第二层减半是为了压缩表示。学习率 0.001 是 Adam 的默认值如果 loss 震荡明显降到 0.0005。3.2 训练集划分与早停策略别让模型在验证集上过拟合期货数据不能随机打乱划分必须按时间顺序切。我一般用 70% 训练、15% 验证、15% 测试。验证集用来做早停测试集只在最后评估一次。早停的patience设 10意思是验证集 loss 连续 10 个 epoch 不下降就停。from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau # 假设 X_train, y_train, X_val, y_val 已经准备好 early_stop EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue) reduce_lr ReduceLROnPlateau(monitorval_loss, factor0.5, patience5, min_lr1e-6) history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs100, batch_size32, callbacks[early_stop, reduce_lr], verbose1 )restore_best_weightsTrue是关键它保证训练结束后模型权重是验证集 loss 最低的那个 epoch而不是最后一个 epoch。ReduceLROnPlateau在 loss 停滞时自动降学习率比手动调省事。batch_size32是常规选择数据量少于 5000 条可以降到 16。4. 避坑与排查相关性分析和模型训练里最容易翻车的五个地方4.1 现象Spearman 矩阵全是 0.9 以上模型却完全不收敛原因没有做差分直接用价格算相关性。所有期货价格都有共同趋势算出来当然高度相关。模型输入的是价格绝对值而不是收益率量纲差异巨大梯度爆炸。解决建模前强制做pct_change()并且检查returns.describe()确认均值在 0 附近。如果某个因子差分后全是 NaN说明原始数据有缺失先做前向填充。4.2 现象训练 loss 降到 0.001测试集 loss 是 0.5原因时间序列泄露。划分训练集和测试集时随机打乱了或者用未来数据做了标准化。期货预测里这是最致命的错误。解决用train_test_split(shuffleFalse)或者手动按索引切。标准化参数只能用训练集的均值和方差然后应用到验证集和测试集。我一般写一个StandardScaler只在训练集上fit再transform其他集。4.3 现象Attention 权重全是均匀的 1/20原因Attention 的 Dense 层初始化太小或者 LSTM 输出本身没有区分度。常见于 LSTM 层数太少或return_sequences没开。解决检查 LSTM 是否设置了return_sequencesTrue。把 Attention 的 Dense 初始化改成glorot_uniform或者加一个BatchNormalization在 Attention 之前。如果还是均匀说明数据本身没有时序模式考虑换因子。4.4 现象验证集 loss 比训练集低原因Dropout 在验证集不生效训练集有 Dropout 导致 loss 偏高。这是正常的不是 bug。但如果验证集 loss 持续低于训练集很多说明验证集太简单或者太小。解决把验证集比例提高到 20%或者用 K 折交叉验证时间序列要用TimeSeriesSplit。不用纠结这个现象重点看测试集表现。4.5 现象预测曲线比真实曲线滞后一天原因模型学到了y[t] y[t-1]这个恒等映射。因为期货收益率自相关性很弱模型找不到其他模式只能复制前一天。解决在损失函数里加一个惩罚项或者把标签改成y[t1] - y[t]的差分。更直接的办法是检查输入特征里是否包含了当天的收盘价——如果包含了模型直接抄就行必须把当天收盘价从特征里去掉。5. 评估与调参用 IC 和方向准确率替代 MSEMSE 只能告诉你预测值离真实值多远但期货交易更关心方向对不对。我习惯加两个指标ICInformation Coefficient和方向准确率。IC 是预测收益率和真实收益率的 Spearman 相关系数方向准确率是预测涨跌符号和真实符号一致的比例。from scipy.stats import spearmanr y_pred model.predict(X_test).flatten() y_true y_test.flatten() # IC ic, _ spearmanr(y_pred, y_true) print(fIC: {ic:.4f}) # 方向准确率 direction_acc np.mean(np.sign(y_pred) np.sign(y_true)) print(f方向准确率: {direction_acc:.4f})IC 在 0.05 以上就算有效因子方向准确率超过 0.52 就有实用价值。如果 MSE 很小但 IC 接近 0说明模型只是拟合了均值没有预测能力。调参顺序我一般这样先调 LSTM 层数和 units再调 CNN 的 filters 和 kernel_size最后调学习率和 dropout。每次只动一个参数记录验证集 IC。Attention 层本身没有太多超参Dense 的 units 固定为 1 就行。最后一个技巧把训练好的模型在测试集上跑 10 次每次用不同的随机种子取 IC 的均值和标准差。如果标准差大于均值说明模型不稳定需要简化结构或增加数据量。这个习惯帮我省了很多后悔药希望帮到你。本文还有配套的精品资源点击获取