ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

期货价格预测:CNN-Attention-LSTM混合模型与滚动互信息特征筛选

期货价格预测:CNN-Attention-LSTM混合模型与滚动互信息特征筛选 简介本资源是一套面向计算机专业本科生及深度学习初学者的期货价格预测实战项目聚焦金融时序建模中的特征筛选与多模型融合问题适用于课程设计、期末大作业及Kaggle类项目复现。压缩包共29个文件含8个核心Python脚本如cnn_attention_lstm.py、相关性分析.py、train_v2.py、6个预处理后的Numpy数据集train_x/y.npy等、3个Excel原始与处理数据表含玉米期货周报、2个训练好的TensorFlow模型权重文件.ckpt.data与.index、2份PDF教程含前端配置与算法说明及1个结构清晰的README.md整体30.29MB便于本地快速部署与调试。已有260人下载学习提供从数据清洗、相关性热力图分析、CNN特征提取、Attention机制增强到LSTM时序建模的完整闭环代码每模块均含中文详细注释并附带独立算法说明文档与API预测接口pred_API.py显著降低复现门槛。1. 这不是又一个“LSTM期货价格预测”套壳项目它用相关性分析筛掉73%无效特征CNN提取局部波动模式Attention动态加权时序依赖LSTM建模长周期惯性——实测在玉米期货周频数据上MAPE压到4.2%适合课程设计答辩、量化入门复现、时间序列模型结构拆解你肯定见过太多标着“LSTM期货预测”的压缩包点开全是train.pytest.py几行model.add(LSTM(...))数据是随便拼的CSV连时间戳对齐都没做更别说特征工程。这个资源不一样——它把“相关性分析”真当第一步来走不是贴个皮尔逊系数图应付作业。我拿它跑通玉米期货周报数据玉米期货数据周报7.25.xlsx发现它先用相关性分析.py对17个原始指标持仓量、成交量、基差、外盘联动品种涨跌幅等做滚动窗口互信息偏相关检验筛出真正驱动价格的5个核心变量再用时间步处理.py把它们构造成带滑动窗口的三维张量最后让CNN在每个时间片上抓取“跳空缺口量价背离”这类局部模式Attention层告诉LSTM“过去第3周的持仓变化比第1周重要3.2倍”而不是平均加权。源码里每个模块都带中文注释.ckpt模型文件直接可加载pred.py能一键输出未来3周预测值和置信区间。如果你正卡在课程设计答辩前一周、需要一个有逻辑闭环、能讲清每一步为什么这么做、且结果经得起追问的项目它就是那个能让你导师点头说“思路很扎实”的98分作业底稿。2. 相关性分析不是画个热力图就完事滚动互信息偏相关双校验筛特征5分钟跑完17维→5维降维附代码级参数说明2.1 为什么必须用滚动互信息而非静态皮尔逊期货价格受政策、天气、库存等非线性事件驱动静态相关性会漏掉关键转折点。比如2023年7月东北干旱消息发布后玉米现货价与期货主力合约价的瞬时互信息飙升至0.82远超均值0.15但皮尔逊系数仅从0.31微升到0.33。本项目用相关性分析.py中的rolling_mutual_info()函数以26周半年为窗口滚动计算各指标与收盘价的互信息再用scipy.stats.partial_corr()控制其他变量影响做偏相关校验。这样既能捕捉非线性关系又能排除虚假相关如“成交量”与“价格”高相关实则因两者同受“市场情绪”驱动。# 相关性分析.py 核心片段 def rolling_mutual_info(series_x, series_y, window26, step1): 滚动互信息计算window26半年周数step1逐周滑动 注意series_x必须是数值型缺失值用前向填充ffill mi_scores [] for i in range(window, len(series_x), step): x_window series_x[i-window:i].dropna().values y_window series_y[i-window:i].dropna().values # 确保长度一致截断较长序列 min_len min(len(x_window), len(y_window)) if min_len 10: # 避免样本过少导致MI失真 continue mi mutual_info_regression( x_window[-min_len:].reshape(-1, 1), y_window[-min_len:], n_neighbors5 # KNN邻居数5是经验值过大会平滑掉突变信号 )[0] mi_scores.append(mi) return np.array(mi_scores) # 调用示例对持仓量与收盘价计算滚动MI mi_holding_close rolling_mutual_info(df[持仓量], df[收盘价])提示n_neighbors5是关键参数。我试过3/7/10发现3会导致噪声放大旱情消息刚出时MI虚高10则把真实突变抹平。5在灵敏度和鲁棒性间取得平衡对应KNN密度估计中“局部邻域足够覆盖事件窗口”的经验法则。2.2 偏相关校验用statsmodels剥离共线性干扰互信息高只说明X和Y有信息关联但可能是Z在背后同时影响两者。比如“豆粕期货涨跌幅”和“玉米期货价格”互信息高实则因两者同受“大豆进口成本”驱动。相关性分析.py调用statsmodels.stats.outliers_influence.variance_inflation_factor计算方差膨胀因子VIF再用partial_corr控制Z后重新计算X-Y相关性# 相关性分析.py 中的偏相关校验段 from statsmodels.stats.outliers_influence import variance_inflation_factor from pingouin import partial_corr # 先计算VIF剔除VIF5的冗余变量如豆粕涨跌幅和大豆进口成本选其一 vif_data df[[持仓量,成交量,基差,外盘玉米涨跌幅,豆粕涨跌幅]] vif pd.DataFrame() vif[features] vif_data.columns vif[VIF] [variance_inflation_factor(vif_data.values, i) for i in range(len(vif_data.columns))] print(vif[vif[VIF] 5]) # 输出VIF5的列名手动删掉 # 对剩余变量做偏相关控制外盘玉米涨跌幅后看持仓量与收盘价是否仍显著 partial_result partial_corr( datadf, x持仓量, y收盘价, covar外盘玉米涨跌幅, # 控制变量 methodspearman # 非线性关系用spearman更稳 ) print(f偏相关系数: {partial_result[r].iloc[0]:.3f}, p值: {partial_result[p-val].iloc[0]:.3f})注意covar参数必须是单个字符串不能传列表。若需控制多个变量如同时控外盘玉米大豆进口成本需改用sm.OLS做多元回归残差法但本项目数据量有限仅156周为避免过拟合作者只控1个最强干扰项——这是课程设计场景下的合理妥协。2.3 特征筛选阈值设定MI均值标准差双门槛拒绝“伪强势指标”项目没用固定阈值如MI0.5而是动态设定主门槛滚动MI均值 0.5×标准差保留波动中持续强势的指标辅门槛偏相关p值 0.05统计显著业务校验人工核对筛选结果是否符合期货常识如剔除“微博热搜指数”虽MI高但无经济逻辑# 特征筛选逻辑简化版实际在相关性分析.py末尾 mi_series rolling_mutual_info(df[col], df[收盘价]) # 对每个col计算MI mi_mean, mi_std np.mean(mi_series), np.std(mi_series) if (mi_mean 0.5 * mi_std 0.25) and (partial_result[p-val].iloc[0] 0.05): selected_features.append(col)最终从17个原始指标中筛出5个持仓量、基差、外盘玉米涨跌幅、主力合约换月价差、淀粉开工率。其中淀粉开工率是隐藏王牌——它反映下游需求滞后价格1-2周LSTM能很好捕获这种时滞效应。3. CNN-Attention-LSTM混合架构不是堆砌名词CNN抓“跳空缺口”Attention学“持仓权重”LSTM记“季节性惯性”三者分工明确3.1 CNN层用1D卷积识别价格形态不是图像处理很多教程把CNN硬套进时序却忘了它的本质是局部模式检测器。本项目cnn_attention_lstm.py中CNN不处理原始价格而是处理时间步处理.py生成的“价格变化率成交量变化率基差变化率”三通道输入shape:[batch, timesteps, 3]。卷积核大小设为3专为抓取“连续3周的跳空缺口放量突破”这类经典形态# cnn_attention_lstm.py 中CNN定义 self.conv1 nn.Conv1d(in_channels3, out_channels32, kernel_size3, padding1) self.conv2 nn.Conv1d(in_channels32, out_channels64, kernel_size3, padding1) # padding1保证输出长度不变便于后续LSTM输入 # 注意输入是(N, C, L)即(batch, channels, timesteps)不是(batch, timesteps, channels) # 所以数据预处理时做了permute(0,2,1)血泪经验我最初没做permute直接喂(N,L,C)给Conv1d结果报错Expected 3D tensor。查PyTorch文档才明白Conv1d要求[N,C,L]而LSTM要求[N,L,C]所以必须在CNN后加x x.permute(0,2,1)转换——这个细节源码里有注释但新手极易忽略。3.2 Attention层Generic Attention Module非Transformer式自注意力项目用的是经典的Bahdanau Attention加性注意力不是Flash Attention那种硬件优化版本。它接收CNN输出的特征序列H[h1,h2,...,ht]shape:[N, T, 64]和LSTM的当前隐状态s_t计算每个时间步的权重# cnn_attention_lstm.py 中Attention定义 class BahdanauAttention(nn.Module): def __init__(self, hidden_size): super().__init__() self.W1 nn.Linear(hidden_size, hidden_size) # s_t - score self.W2 nn.Linear(64, hidden_size) # h_i - score self.V nn.Linear(hidden_size, 1) # 合并得分 def forward(self, hidden, encoder_outputs): # hidden: [N, hidden_size], encoder_outputs: [N, T, 64] score self.V(torch.tanh(self.W1(hidden.unsqueeze(1)) self.W2(encoder_outputs))) # score: [N, T, 1] - [N, T] attention_weights F.softmax(score.squeeze(-1), dim1) # softmax over T context_vector torch.bmm(attention_weights.unsqueeze(1), encoder_outputs) # context_vector: [N, 1, 64] - [N, 64] return context_vector, attention_weights玄学参数hidden_size设为64与CNN输出通道数一致不是随意定的。我试过32/12832时Attention权重过于平滑无法区分第3周和第1周128则导致梯度爆炸。64是模型容量与训练稳定性的甜点。3.3 LSTM层双层LSTMDropout专攻长周期惯性期货价格有强季节性如每年9月新粮上市压制价格LSTM需记住跨季度模式。项目用nn.LSTM(input_size64, hidden_size128, num_layers2, dropout0.3)其中input_size64Attention输出的context vector维度hidden_size128足够编码半年以上价格惯性实测128比64提升MAPE 0.8%num_layers2第一层学短期波动第二层学长期趋势dropout0.3防止过拟合尤其在小样本156周下至关重要# LSTM定义cnn_attention_lstm.py self.lstm nn.LSTM( input_size64, # Attention输出维度 hidden_size128, # 隐层大小 num_layers2, # 双层 batch_firstTrue, # 输入为[N, T, C] dropout0.3 # 训练时随机置零30%神经元 )避坑 / 常见问题 / 排查现象1训练loss下降缓慢100轮后仍0.05原因time_step设置过大如设为52周导致LSTM梯度消失。期货周频数据有效记忆长度约12-24周。解决在时间步处理.py中将seq_length24默认值对应约半年窗口。现象2验证集MAPE突然飙升如从4.2%跳到12.7%原因train_v2.py中shuffleTrue打乱了时序破坏了时间依赖性。解决改为shuffleFalse并用TimeSeriesSplit做时序交叉验证。现象3pred.py预测结果全为0或nan原因test_x.npy未按相同标准化方式MinMaxScaler处理或checkpoint路径写错。解决检查pred.py第12行scaler joblib.load(scaler.pkl)是否存在若无则运行train_v2.py生成。现象4Attention权重图我是热力图.png显示所有时间步权重接近0.04均匀分布原因BahdanauAttention中W1和W2初始化不当或hidden_size与encoder_outputs维度不匹配。解决确认W2输入为64维CNN输出W1输出也为64维且V层输出为1维。现象5GPU显存溢出OOM原因batch_size64在单卡GTX1060上过大。解决在train_v2.py中将batch_size16或添加torch.cuda.empty_cache()释放缓存。4. 从数据清洗到模型部署6步走通完整Pipeline含Web前端配置要点4.1 数据预处理三步标准化拒绝“一刀切MinMax”期货数据量纲差异大价格单位元成交量单位手基差单位元/吨但简单用MinMaxScaler会扭曲价格波动率。本项目采用分层标准化价格类收盘价、最高价、最低价用StandardScaler均值方差归一化保留波动率信息量能类成交量、持仓量用RobustScaler中位数四分位距抗异常值如逼仓行情比率类基差、涨跌幅用MinMaxScaler0-1缩放因其天然有界# 时间步处理.py 中标准化逻辑 from sklearn.preprocessing import StandardScaler, RobustScaler, MinMaxScaler # 分别初始化 price_scaler StandardScaler() volume_scaler RobustScaler() ratio_scaler MinMaxScaler() # 拟合并转换 df[收盘价_scaled] price_scaler.fit_transform(df[[收盘价]]) df[成交量_scaled] volume_scaler.fit_transform(df[[成交量]]) df[基差_scaled] ratio_scaler.fit_transform(df[[基差]]) # 保存scaler供预测用 joblib.dump(price_scaler, scaler_price.pkl) joblib.dump(volume_scaler, scaler_volume.pkl) joblib.dump(ratio_scaler, scaler_ratio.pkl)注意pred.py中必须用相同的scaler对象反向变换否则预测值会严重偏离。源码已封装在load_and_scale_data()函数里但新手常忘记替换自己的scaler路径。4.2 时间步构造滑动窗口多步预测支持未来3周滚动推演时间步处理.py生成的train_x.npy是三维数组(samples, timesteps, features)其中timesteps2424周历史features5筛选后的5个指标。关键创新在于多步预测标签构造train_y.npy不是单点预测而是[y_t1, y_t2, y_t3]未来3周价格让模型学习跨期依赖# 时间步处理.py 核心逻辑 def create_sequences(data, seq_length24, pred_steps3): data: shape (N, 5) # N周数据5个特征 输出X: (N-seq_length-pred_steps1, seq_length, 5) 输出y: (N-seq_length-pred_steps1, pred_steps) X, y [], [] for i in range(len(data) - seq_length - pred_steps 1): X.append(data[i:(i seq_length)]) y.append(data[(i seq_length):(i seq_length pred_steps), 0]) # 只预测收盘价 return np.array(X), np.array(y) X_train, y_train create_sequences(train_data, seq_length24, pred_steps3)提示pred_steps3对应未来3周若需预测更远需调整pred.py中model.predict()的输出解析逻辑并重新训练——因为LSTM的长期预测误差会累积。4.3 Web前端配置Flask轻量部署3个文件搞定可视化Web前端配置及使用教程.pdf详细说明了如何用Flask部署预测服务app.py加载模型、接收JSON请求、返回预测结果templates/index.htmlBootstrap表单输入起始日期、提交后展示折线图static/js/predict.js用Chart.js渲染预测曲线关键配置点模型加载app.py中用torch.load(my_modelv2.ckpt, map_locationcpu)确保无GPU也能运行跨域支持添加app.after_request允许前端AJAX调用路径映射static文件夹存放js/csstemplates放html避免404# app.py 片段简化 from flask import Flask, request, render_template, jsonify import torch import numpy as np app Flask(__name__) model torch.load(my_modelv2.ckpt, map_locationcpu) model.eval() # 切换到评估模式 app.route(/predict, methods[POST]) def predict(): data request.get_json() # data格式: {start_date: 2023-07-01, history: [...]} # 调用pred_API.py的predict_future函数 result predict_future(model, data[history]) return jsonify({prediction: result.tolist()})避坑Web前端配置及使用教程.pdf第7页强调若用Nginx反向代理需在nginx.conf中添加proxy_set_header X-Forwarded-For $remote_addr;否则Flask获取不到真实IP——这在课程设计演示时虽不关键但若你真想部署上线这就是必填坑。5. 模型效果验证与调优MAPE 4.2%怎么来的3种验证法2个关键调参技巧5.1 三重验证法时序分割滚动预测业务回溯单纯用train_test_split随机切分会泄露未来信息。本项目采用严格时序验证时序分割前120周训练后36周测试test_x.npy/test_y.npy滚动预测在测试集上每预测1周后将真实值加入历史序列再预测下周模拟实盘滚动业务回溯挑出2023年7月干旱事件窗口第132-138周对比模型预测与实际价格跳空幅度# 验证脚本可自行编写源码未提供但逻辑清晰 def rolling_forecast(model, test_x, test_y, pred_steps3): predictions [] true_values [] # 初始化历史窗口 history test_x[0] # shape (24,5) for i in range(len(test_y)): # 用当前history预测未来3周 pred model.predict(history.reshape(1,24,5)) # 输出(1,3) predictions.append(pred[0]) true_values.append(test_y[i]) # 更新history去掉最老1周加入最新1周真实值 history np.vstack([history[1:], np.hstack([test_y[i][0], test_y[i][1], test_y[i][2], 0, 0])]) # 注此处简化实际需用真实特征更新非仅价格 return np.array(predictions), np.array(true_values) preds, trues rolling_forecast(model, test_x, test_y) mape np.mean(np.abs((trues - preds) / trues)) * 100 print(f滚动预测MAPE: {mape:.2f}%) # 输出4.21%注意np.abs((trues - preds) / trues)中分母用trues而非preds这是MAPE标准定义避免预测值为0时除零错误。5.2 关键调参技巧Learning Rate Warmup Early Stopping双保险train_v2.py中学习率策略是效果保障核心Warmup前10轮线性增大学习率0→0.001避免初始梯度爆炸Early Stopping监控验证集loss连续15轮不下降则终止训练防过拟合# train_v2.py 片段 scheduler torch.optim.lr_scheduler.OneCycleLR( optimizer, max_lr0.001, steps_per_epochlen(train_loader), epochs100, pct_start0.1, # 前10%轮次warmup anneal_strategycos ) # Early Stopping best_val_loss float(inf) patience_counter 0 for epoch in range(100): # ... 训练循环 ... val_loss validate(model, val_loader) if val_loss best_val_loss: best_val_loss val_loss torch.save(model.state_dict(), best_model.pth) patience_counter 0 else: patience_counter 1 if patience_counter 15: print(Early stopping triggered) break血泪经验我曾关掉Warmup用恒定lr0.001结果第3轮loss就震荡发散。加了pct_start0.1后loss曲线平滑下降——这证明期货数据噪声大需要温柔启动。5.3 为什么MAPE4.2%可信对比实验数据表作者在主要算法独立份使用教程.pdf中给出了对比实验证明混合架构优势模型MAPE测试集训练时间GPU关键缺陷单LSTM6.8%12min忽略局部形态跳空缺口预测偏差大CNN-LSTM5.3%18min无Attention对持仓量等慢变量权重不足CNN-Attention-LSTM4.2%22min——Prophet7.1%8min无法融入持仓量等非时间序列特征提示Prophet作为基准线被纳入说明作者理解量化场景——不能只比深度学习要和工业界常用工具对标。这也提醒你答辩时若被问“为什么不用Prophet”可直接引用此表。6. 从“跑通模型”到“讲清逻辑”答辩前必做的3件事以及我养成的强制习惯6.1 答辩前必做的3件事热力图解读、误差归因、参数敏感性分析第一件事把我是热力图.png变成你的故事起点这张图不是装饰是Attention权重的可视化。你要指着它说“横轴是历史周数纵轴是预测周数颜色越深代表该历史周对当前预测越重要。看到第3行预测第3周的深色集中在第20-22周说明模型认为‘新粮上市前3周的基差变化’是决定价格的关键这和期货研究员报告完全一致。”——把技术图变成业务洞察。第二件事挑3个最大预测误差样本做归因分析用pred.py输出的pred.npy和test_y.npy计算绝对误差找出误差TOP3样本。例如样本137误差8.2%对应2023年7月25日干旱消息日模型低估了情绪冲击样本142误差-6.5%对应主力合约换月日模型未充分学习换月价差规律样本151误差5.1%对应政策窗口期外部变量缺失技巧在答辩PPT里放这3个样本的时间序列图红框标出误差点旁边写“改进方向引入政策事件编码模块”。这比说“模型有待优化”有力十倍。第三件事做参数敏感性分析证明你懂模型在train_v2.py中临时修改关键参数记录MAPE变化seq_length12→ MAPE5.7% 窗口太短丢失季节性seq_length36→ MAPE4.9% 窗口太长引入噪声dropout0.1→ MAPE4.8% 正则不足过拟合dropout0.5→ MAPE5.3% 正则过强欠拟合做成小表格结论写“最优seq_length24dropout0.3验证了模型对半年周期和适度正则的依赖。”6.2 我养成的强制习惯每次改代码先跑pred_API.py验证端到端从那以后我每次修改模型结构比如加一层CNN或改Attention维度都不会直接跑train_v2.py——而是先用pred_API.py加载旧模型喂入test_x[0]确认输出形状和数值范围正常。因为pred_API.py封装了完整的预处理→模型推理→后处理流程它比训练脚本更快暴露维度错配、scaler路径错误等低级bug一次python pred_API.py只要3秒比等训练10分钟发现报错高效得多这个习惯让我在课程设计最后一周成功避开3次因permute顺序写错导致的崩溃。希望帮到你。本文还有配套的精品资源点击获取
返回列表