
简介基于相关性分析的CNN-Attention-LSTM期货价格预测模型是一份完整的深度学习时间序列预测工程适合用于毕业设计、课程设计及人工智能实战练习。包内围绕相关性分析→时间步处理→模型训练→预测的主线提供Python源码、数据集、训练好的模型及详细注释能帮助读者快速复现并理解特征筛选与注意力机制在价格预测中的结合方式。压缩包共29个文件、大小约30.29MB包含8个py脚本模型搭建、训练、预测、API封装等、6个npy数据文件训练/测试输入与标签、3个Excel表格原始数据及处理结果、模型checkpoint文件、两个PDF教程和README说明文件类型覆盖代码、数据、文档和权重便于对照学习。目前已有809人加入学习附带的模型权重可直接运行验证配套教程从环境配置到算法解析均有涉及适合需要完整项目参考的初学者和答辩学生。1. 基于相关性分析的CNN-Attention-LSTM期货价格预测拿到这份源码包先别急着训练在量化时序预测的毕设和课设里裸LSTM早已是过去式。这份压缩包最吸引我的不是“能跑通的LSTM”而是它把相关性分析、CNN特征提取、Attention加权和LSTM串成了完整的闭环。换句话说特征不是随便选而是先用相关性分析筛过一遍时序不是LSTM单打独斗而是CNN先抓短期形态、Attention再给关键时间步加权、最后LSTM承接长期依赖。这套组合在期货价格这类强噪声、弱周期的数据上比单纯堆层数要稳得多。适合两类人一是深度学习方向的毕业生正愁没有一套数据、代码、模型权重、预测接口齐全的完整项目二是想快速验证神经网络做期货价格预测可行性的从业者。先说结论这份资源从原始Excel数据到训练好的ckpt权重再到能直接调用的预测API链条是完整的不是只有模型文件的半成品。2. 相关性分析 CNN Attention LSTM这种组合为什么适合期货价格预测2.1 相关性分析先用Spearman把输入特征筛一遍期货价格预测的第一个难点不是模型是特征。原始数据里往往同时存在成交量、持仓量、开盘价、最高价、最低价、收盘价等多个字段如果一股脑全部塞进模型LSTM的收敛速度和最终精度都会受影响。资源里单独放了一个“相关性分析.py”核心思路就是对候选特征做Spearman相关性分析再结合热力图筛掉与目标变量相关性弱的字段。为什么用Spearman而不是Pearson因为期货价格和成交量序列通常不满足正态分布而且存在明显的不对称性Spearman基于秩相关对这类的鲁棒性更好。import pandas as pd import numpy as np from scipy.stats import spearmanr import seaborn as sns import matplotlib.pyplot as plt df pd.read_excel(原数据/玉米期货数据周报7.25.xlsx, engineopenpyxl) # 去掉日期列只保留数值型特征 numeric_cols df.select_dtypes(include[np.number]).columns.tolist() corr_matrix pd.DataFrame(indexnumeric_cols, columnsnumeric_cols, dtypefloat) for col1 in numeric_cols: for col2 in numeric_cols: rho, _ spearmanr(df[col1], df[col2]) corr_matrix.loc[col1, col2] rho plt.figure(figsize(10, 8)) sns.heatmap(corr_matrix, annotTrue, fmt.2f, cmapRdBu_r, center0) plt.title(Spearman Correlation Heatmap) plt.savefig(我是热力图.png, dpi150, bbox_inchestight)这段代码先是把所有数值列两两组合算一遍Spearman相关系数生成一个对称的相关矩阵然后保存成热力图。参数上需要注意两点annotTrue会把具体数值标在格子里方便肉眼挑选强相关特征cmapRdBu_r用红蓝配色正值偏红、负值偏蓝一眼就能看出哪些字段与收盘价强相关。我一般会优先保留与目标变量相关系数绝对值在0.4以上的特征低于0.2的直接排除这样能在不损失有效信息的前提下把输入维度降下来。压缩包里的“相关性分析数据.npy”就是这一步的产物。需要提醒的是热力图只是参考特征之间的共线性也要顺带看两眼。比如最高价和最低价往往与收盘价高度相关但三者自身也高度共线全塞进去反而会放大噪声。所以在跑资源里的“相关性分析.py”之后建议手动删掉冗余特征再重新生成npy这步操作对后续模型精度的影响比调LSTM的神经元数量还大。2.2 数据文件清单拿到解压包先对应上这一张表第一次解压这份资源的人最容易被一堆.npy和一串my_modelv1.ckpt搞晕。我拆完以后把关键文件按“数据流”整理成一张表按这个顺序看就清楚了文件 / 目录数据流位置作用说明原数据/玉米期货数据周报7.25.xlsx数据源头商品期货历史周报含量价字段相关性分析用表.xlsx特征筛选相关性分析脚本的输入Excel相关性分析数据.npy特征筛选结果保留字段的中间数据时间步处理.py样本构造把序列数据切成滑窗样本train_x.npy / train_y.npy模型输入输出滑窗后的训练样本与目标test_x.npy / test_y.npy验证集用于评估与画对比图checkpoint/训练产物my_modelv1 / my_modelv2 权重文件cnn_attention_lstm.py模型定义CNN-Attention-LSTM类pred.py / pred_API.py推理与接口输出 pred.npy、对外提供预测这张表的价值在于它能帮你快速判断“这份资源到底能不能跑”。有数据源头、有预处理产物、有模型定义、有训练好的权重、有推理脚本五样齐全才算完整。很多网上下载的源码包只有模型文件和一段训练代码数据要自己找那份折腾成本往往比写模型还高。这份资源把数据链路补全了这也是我愿意花时间拆它的直接原因。2.3 模型结构CNN提取短期形态Attention加权关键时间步LSTM承接长期依赖在“models/cnn_attention_lstm.py”里模型的整体思路是输入二维矩阵(batch, time_step, feature_dim)先经过一维卷积层提取局部趋势特征再送入Attention层对每个时间步的重要性做加权最后交给LSTM捕捉长周期依赖末端接全连接层输出预测价格。CNN在这里处理的是“连续几个交易周期内价格形态”的短时特征比如三连阳、放量突破这些局部模式用卷积核扫一遍就能被捕捉Attention层的作用是告诉LSTM“最近哪几天更重要”真正的长期趋势由LSTM负责。import tensorflow as tf from tensorflow.keras.layers import (Input, Conv1D, LSTM, Dense, Dropout, GlobalAveragePooling1D, Multiply, Reshape) from tensorflow.keras.models import Model def cnn_attention_lstm(time_step, feature_dim, lstm_units64): inputs Input(shape(time_step, feature_dim)) # 1D卷积提取局部特征保持时间步数不变 x Conv1D(filters32, kernel_size3, paddingsame, activationrelu)(inputs) x Dropout(0.1)(x) # 简单自注意力对卷积输出的每个时间步做一个全局池化再扩维用来算权重 attn_weight Dense(1, activationsigmoid)(x) x Multiply()([x, attn_weight]) # LSTM承接时间维度上的依赖关系 x LSTM(lstm_units, return_sequencesFalse)(x) x Dropout(0.2)(x) outputs Dense(1)(x) model Model(inputs, outputs) return model这段代码突出了这个资源里模型组合的一个关键顺序CNN不加池化层用paddingsame保持时间步长度不变因为后面接的Attention和LSTM都依赖完整的时间维度信息。attn_weight是一个Dense(1)输出相当于给每个时间步打分再通过Multiply乘回卷积输出。我一般会把lstm_units从64起步特征维度落在20附近时效果最好超过50维的输入容易让LSTM承担过多噪声反而要加大dropout来压。先跑通默认参数再考虑往上调这个顺序几乎不会错。2.4 时间步处理把周报序列切成滑窗样本模型不能直接吃整段价格序列需要先把数据改造成“用前N期预测下一期”的监督学习格式。资源里的“时间步处理.py”干的活就是滑窗。假设时间步取10那么第1到10期作为train_x[0]第11期作为train_y[0]然后窗口往后滑一格。import numpy as np def create_sliding_window(data, time_step10): x, y [], [] for i in range(len(data) - time_step): x.append(data[i:i time_step, :-1]) y.append(data[i time_step, -1]) return np.array(x), np.array(y) # 假设已经通过相关性分析筛选出特征最后一位是预测目标比如收盘价 arr np.load(相关性分析数据.npy) train_x, train_y create_sliding_window(arr, time_step10) print(train_x.shape, train_y.shape)这段代码里有个细节需要注意data[i:i time_step, :-1]取出的是特征部分目标值是窗口末端后一个周期的收盘价。time_step的选取直接影响训练样本量窗口越大样本越少。我的经验是在每周报数据上8到12比较合适取到20以上样本量会骤减模型容易欠拟合。3. 训练复现环境选型、脚本顺序与checkpoint的加载逻辑3.1 运行环境与依赖版本别用TensorFlow 2.11以上版本我复现这份源码时用的是Python 3.9 TensorFlow 2.10的组合全套跑下来没有遇到API层面的坑。依赖清单大致如下依赖包推荐版本说明tensorflow2.10.xCPU版本就能跑数据量不大numpy1.23.x与TF 2.10兼容性最好pandas1.5.x读Excel和CSVopenpyxl3.0.x读取xlsx格式scipy1.9.xSpearman相关性分析matplotlib / seaborn3.6.x / 0.12.x画热力图和对比曲线不建议直接用TensorFlow 2.13及以上版本因为从2.11开始Keras的API组织方式发生过调整Conv1D接LSTM时对shape的处理规则也有变化加载旧权重更容易报出莫名其妙的维度错误。如果实在要用新版本就把模型定义和权重分开看待模型用当前的API重建只加载旧的权重数值不要指望旧模型结构在新环境下能无缝反序列化。3.2 三个脚本的正确执行顺序解压后别一头扎进去训练先按依赖关系理清脚本顺序。这个包里的数据流水线是“相关性分析.py”负责把Excel筛成相关性分析数据.npy“时间步处理.py”把npy切成train_x/train_y/test_x/test_y“train_v2.py”读这四个npy文件训练模型。# 第一步从Excel中筛选强相关特征并生成npy中间文件 python 相关性分析.py # 第二步将中间文件切成滑窗样本输出训练和测试用的npy python 时间步处理.py # 第三步读取npy开始训练同时把训练好的权重保存到checkpoint目录 python train_v2.py如果只是想复现最终的预测效果可以直接跳过前两步因为压缩包里已经附带处理好的train_x.npy、train_y.npy、test_x.npy、test_y.npy。此时直接跑train_v2.py或pred.py就行无需重复数据预处理。但如果你是打算换自己的数据重新做一遍就必须从相关性分析那一步完整走起。3.3 训练脚本里的关键参数与checkpoint恢复机制train_v2.py内部的大致逻辑是加载npy → 归一化 → 实例化cnn_attention_lstm模型 → 编译并训练 → 保存权重到my_modelv1.ckpt和my_modelv2.ckpt。这里最有价值的坑点在于训练时对时间序列的处理方式。import numpy as np from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint from models.cnn_attention_lstm import cnn_attention_lstm train_x np.load(train_x.npy) train_y np.load(train_y.npy) test_x np.load(test_x.npy) test_y np.load(test_y.npy) # 对序列做归一化操作避免LSTM内部激活函数饱和 mean_x, std_x train_x.mean(axis0), train_x.std(axis0) 1e-8 train_x (train_x - mean_x) / std_x test_x (test_x - mean_x) / std_x model cnn_attention_lstm(time_steptrain_x.shape[1], feature_dimtrain_x.shape[2]) model.compile(optimizeradam, lossmse) # shuffleFalse 很关键时间序列不能随机打乱 model.fit( train_x, train_y, validation_data(test_x, test_y), epochs100, batch_size32, shuffleFalse, callbacks[ EarlyStopping(monitorval_loss, patience10, restore_best_weightsTrue), ModelCheckpoint(checkpoint/my_modelv1.ckpt, save_best_onlyTrue) ] )这其中有几个参数值得展开。shuffleFalse是最容易被忽略的普通分类任务里shuffle能提升泛化能力但时间序列一旦打乱就相当于让模型看到了“未来数据”验证集评估会严重失真。EarlyStopping的patience我一般调到10期货价格序列噪声大验证loss会反复震荡patience太小容易过早停掉。shuffle、batch_size和epochs这三者在时间序列任务里互相牵制batch_size我习惯从32开始数据量小就降到16。ModelCheckpoint设置save_best_onlyTrue训练结束后留下的就是验证集上表现最好的权重这份资源的my_modelv1.ckpt和my_modelv2.ckpt就是这种机制下生成的。3.4 用pred.py快速验证训练结果训练或加载权重之后下一步就是预测。pred.py做的事很直接加载test_x调用模型得到预测值把结果存成pred.npy再和test_y.npy一起画对比曲线。这一步能直观确认模型到底学到了价格走势还是只学了一个均值常量。python pred.py跑完之后注意对比pred.npy和test_y.npy的长度。正常情况下两者长度一致如果发现预测点比真实值少了一段多半是滑窗时丢弃了尾部样本需要回看时间步处理脚本的切片逻辑。这是整条链路里最容易出现隐性bug的位置下面一章专门展开讲。4. 避坑指南checkpoint加载、数据对齐与预测曲线翻平的典型翻车4.1 加载ckpt报错模型结构和权重文件对不上现象跑推理脚本时抛出一段类似AssertionError或Unknown layer的异常提示找不到某个自定义层或者维度不匹配。原因Keras的load_model在反序列化时会去查找模型定义里用到的自定义层。这个资源里的Attention层是写在cnn_attention_lstm.py里的自定义组件如果脚本只加载.ckpt权重而没有先把模型类实例化出来加载器就无法识别结构。还有一个常见情况是my_modelv1和my_modelv2对应的网络结构不完全一样比如时间步或特征维度不同用错权重文件自然报错。解决先实例化模型再手动加载权重。确保time_step和feature_dim与权重文件训练时保持一致。from models.cnn_attention_lstm import cnn_attention_lstm time_step int(train_x.shape[1]) feature_dim int(train_x.shape[2]) model cnn_attention_lstm(time_step, feature_dim) model.load_weights(checkpoint/my_modelv1.ckpt)这么做的好处是绕开了Keras对自定义层的序列化检查也可以先检查权重文件里保存的模型结构再决定用哪个ckpt。4.2 预测曲线和真实曲线错位现象把pred.npy和test_y.npy叠加画图发现曲线形状大致相似但对不齐像被平移了一段。原因滑窗构造样本时test_x的最后一批样本对应的test_y在时间上比pred的当前索引晚一个time_step。如果推理脚本里没有同步对齐索引画图时自然差出一个窗口长度。解决在推理脚本里把比对起点统一到time_step之后的位置。常见做法是给真实序列的前time_step个点直接赋空值让预测曲线和真实曲线在同一起点开始绘制。4.3 归一化逆变换精度丢失现象模型预测的loss很低但画出来的预测曲线是一条接近水平的直线价格波动几乎消失。原因训练时对目标值做了归一化但预测后没有用训练时的mean和std做逆变换或者推理脚本里用测试集均值重新归一化了一遍导致整体数值被压缩到很窄的区间。解决把训练阶段的归一化参数存下来推理时复用。最稳妥的方式是在train_v2.py中把mean_y和std_y一起保存到文件推理时直接加载不要在预测阶段重新计算。np.save(mean_y.npy, train_y.mean(axis0)) np.save(std_y.npy, train_y.std(axis0) 1e-8) pred_price pred * np.load(std_y.npy) np.load(mean_y.npy)我遇到过不少次这类问题根源都是训练和推理两边对数据预处理的理解不一致。4.4 训练时验证集loss震荡下不来现象训练集的loss持续下降验证集loss反复震荡甚至上升早停机制还总在最优值附近被触发。原因大概率是数据未按时间顺序切分或模型参数量相对样本量过大。时间序列的验证集必须取自训练集之后的时间段不能在全体数据上随机抽样。另外LSTM单元数设到128以上样本量只有几百条时极易过拟合。解决按时间序列的前80%做训练、后20%做验证。同时给LSTM层加dropout并把lstm_units降到32或64起步。我一般会先让模型在100个epoch内快速看到验证集loss的形状如果验证集在10个epoch内就开始反弹就先调小模型体积而不是硬扛。4.5 Excel编码与日期格式问题现象跑“相关性分析.py”时pd.read_excel报错或者读出的日期列全是NaT。原因Windows环境下Excel文件可能存在区域设置问题日期格式不统一也是常见原因。有些周报的日期列是文本格式有些系统生成的xlsx日期列会自动变成纯数值。解决读取时指定dtype并统一解析日期格式。df pd.read_excel( 原数据/玉米期货数据周报7.25.xlsx, engineopenpyxl, dtype{日期: str} ) df[日期] pd.to_datetime(df[日期], errorscoerce)对于人工维护的周报数据errorscoerce能容错坏数据生成NaT后可以统一剔除不要因为一行脏数据让整个预处理流程崩溃。5. 进阶用法把pred_API.py接到自己的策略里并用基线对比快速验证模型有效性5.1 把预测接口接入自有数据这份资源里最实用的文件其实是pred_API.py它把整个推理过程封装成了一个可调用的接口。默认情况下接口读入的是(time_step, feature_dim)形状的二维矩阵返回一个一维的预测值数组。接自己的数据时我一般这样用import numpy as np from models.cnn_attention_lstm import cnn_attention_lstm model cnn_attention_lstm(time_step10, feature_dim8) model.load_weights(checkpoint/my_modelv2.ckpt) def predict_next_price(feature_window, mean_x, std_x, mean_y, std_y): # 输入最近10期的特征窗口 x (np.array(feature_window) - mean_x) / std_x x x.reshape(1, 10, -1) pred_normalized model.predict(x, verbose0) return pred_normalized[0, 0] * std_y mean_y接入前需要确认三个对齐点特征字段数量是否与训练时一致、特征顺序是否与训练时一致、归一化参数是否来自训练集没有对齐的接入不可能得到有效结果。5.2 快速验证模型还有没有效对比随机基线和上一期基线模型权重是旧数据训练出来的直接拿到当下期货行情里效果可能已经退化。我通常用两种基线来做快速判断。第一种是“上一期价格直接当本期预测”第二种是随机模型。对比方式很简单from sklearn.metrics import mean_absolute_error # pred是模型输出actual是真实价格baseline_last是上一期实际值 mae_model mean_absolute_error(actual, pred) mae_baseline mean_absolute_error(actual, baseline_last) print(model mae:, mae_model) print(baseline mae:, mae_baseline)如果模型MAE与“上一期价格直接预测”的MAE接近甚至更高说明模型对当下行情已经失去体感需要重训或者重新做特征筛选。这也是我从那次翻车里养成的习惯新数据进来先跑热力图和基线对比再谈参数调优而不是抄起旧权重直接上实盘。希望帮到你。本文还有配套的精品资源点击获取