尧图精选

VMD-LSTM实战指南:变分模态分解与深度学习预测非平稳序列

🕒 发布时间:2026/10/2 3:36:22 📁 来源:尧图网络
简介一份面向Python时序预测学习者的完整实现包基于TensorFlow框架将变分模态分解VMD与长短期记忆网络LSTM结合适用于处理非线性、非平稳的电力负荷等复杂序列帮助快速搭建并验证高精度的时序预测模型。压缩包共9个文件包含两个带详细中文注释的Python脚本VMD-LSTM.py与VMD分解数据保存.py、电力负荷预测数据CSV、VMD中间结果XLSX、使用说明及依赖库清单txt以及3张分解过程效果图整体仅654KB结构清晰。已有54人学习代码支持单输入单步、单输入多步、多输入单步与多输入多步四种预测模式内置MSE、RMSE、R2、MAE、MAPE五类评估指标可直接替换自己的CSV或Excel数据集运行。对刚入门时序预测的研究者来说既能借助VMD分解原理图理解信号处理过程也能复用脚本快速完成数据分解、模型训练与结果评估节省从零编码的时间形成从数据分解到结果评估的完整流程。1. VMD-LSTM是什么把非平稳序列先拆开再交给深度学习做金融时序预测的同行拿来问我的标题十有八九就是“Python TensorFlow VMD-LSTM时序预测”。这套方案核心动作有两个先用变分模态分解VMD把序列拆成若干个长短期记忆网络好学的窄带分量再让TensorFlow里的LSTM对每个分量分别建模、预测、叠加。直接拿原始序列训练LSTM经常会学不动原因是价格、负荷这类序列的非平稳性强VMD恰好把趋势、周期和噪声切成了相对平稳的分量。下面按完整落地路径来写VMD怎么分解、LSTM怎么配、最终怎么评估以及哪些坑不看会翻车。2. VMD分解的数学直觉与最小实现先验证再上LSTM2.1 VMD做了什么把一段混杂信号拆成窄带IMF变分模态分解Variational Mode Decomposition解决的问题很直观输入一维信号输出K个IMF本征模态函数每个IMF围绕自己的中心频率在频域上形成窄带。一句话解释它的求解思路把“分解成K个窄带模态”写成一个约束变分问题在“所有模态之和等于原始信号”的约束下最小化各模态的带宽之和用交替方向乘子法ADMM迭代解出模态和中心频率。和EMD这种“递归剥皮”的做法相比VMD的模态混叠现象轻一些。EMD一次筛出一个本征模态误差会顺着递归一路往后传而且对极值点插值方式敏感VMD则是一次性求解整个模态集合带宽控制更直接端点效应依然存在但整体稳定度好很多。这也是VMD-LSTM这套组合在时序预测里流行起来的前提——分解本身足够干净LSTM才有机会去学每个分量的真实规律。金融时序预测场景里价格和成交量序列往往由低频趋势、高频波动和噪声混叠而成VMD把这些成分切开之后LSTM在每个窄带子序列上只需要学一个小范围的动态训练难度明显下降。2.2 用vmdpy跑通第一次分解合成信号验证通道Python这边我一般用的是vmdpy这个第三方封装本质上把论文作者的Matlab实现移植成了Python版本。先装好Python环境这里多说一句强烈建议用conda或者venv建一个干净环境来装TensorFlow 2.x和vmdpy避免系统Python里旧的NumPy把依赖弄乱。TensorFlow安装直接按官网走pip安装vmdpy同样用pip装。装完先用一段合成信号验证分解通道别一上来就丢真实数据。import numpy as np from vmdpy import VMD # 合成一段含趋势 两个周期分量 噪声的信号 n 512 t np.linspace(0, 1, n) data 10 * t np.sin(2 * np.pi * 8 * t) 0.5 * np.sin(2 * np.pi * 47 * t) data data 0.3 * np.random.randn(n) # VMD 核心参数 alpha 2000 # 惩罚因子控制模态带宽大小 tau 0.0 # 噪声容忍度0表示以保信号为主 K 4 # 模态数量最影响分解结果 DC 0 # 不单独提取直流分量 init 1 # 中心频率均匀初始化 tol 1e-7 # 迭代收敛容差 u, u_hat, omega VMD(data, alpha, tau, K, DC, init, tol) # u 的形状是 (K, n)每一行是一个IMF print(u shape:, u.shape) print(final omega:, omega[:, -1])逻辑说明u里第i行就是第i个IMF分量omega记录了每次迭代的中心频率最后一列是收敛后的中心频率。这一步的核心不是“跑完就算完”而是去看分解结果对不对。如果两个IMF的中心频率靠得太近说明K给大了如果某个IMF波形里明显混着两种频段说明alpha不合适如果最后一个IMF全是毛刺多半是过分解出来了。合成信号里我们塞了8Hz和47Hz两个周期分量噪声很小K4应该能分出一个趋势、两个周期、一个残余噪声中心频率会明显拉开。参数说明alpha是VMD里最敏感的参数按2000起步是惯例值越大带宽约束越强模态越收敛tau在数据噪声不大时保持0只有当信号本身噪声很重时才考虑微调init1表示中心频率均匀初始化比init0更稳定。这些参数后续要基于真实数据的频谱表现调整。2.3 K值和alpha怎么给先小后大再盯中心频率K值选择没有通用定理这部分很多人当玄学处理实际可以用omega的收敛情况来量化。我的习惯是三步走先设K4跑通流程然后看omega最后一列如果两个中心频率相差不到一个倍频程K减一再跑如果某个IMF波形里明显混着两段不同节奏K加一再跑。金融日频或分钟频序列里明显的周期成分相对少K选3到6比较常见电力负荷、电价这类天然带日和周周期的序列K选5到8是更常见的起跑区间。alpha从2000起步如果发现模态带宽过宽、波形互相穿插就往上调到3000到4000如果模态被压得过于规整甚至把一段完整趋势切成了两半就往下调。任何参数调整后都要重新看中心频率和IMF波形确认比上一版更合理再进入建模环节。还有一个很多人忽略的边界VMD是对整段已知信号做分解预测未来时并没有那段未知数据可供分解所以VMD通常只当作一次性预处理不是随预测滚动更新的模块。严格滚动的做法是每个预测日都用截止当天的历史重新分解计算量会翻很多倍我一般建议先跑通朴素版本确认分解有价值之后再考虑滚动更新。3. 把IMF变成LSTM能学的样本滑窗、归一化与Keras模型3.1 构造滑窗样本三种常见错误VMD分解完成后每个IMF都是一维数组。LSTM不能直接吃裸序列要切成固定长度的输入样本和对应标签。假设用过去24个点预测下1个点window_size24、horizon1实现方式很直接def make_samples(series, window_size24, horizon1): X, y [], [] # 总样本数 len(series) - window_size - horizon 1 for i in range(len(series) - window_size - horizon 1): X.append(series[i: i window_size]) y.append(series[i window_size: i window_size horizon]) # X 形状: (样本数, window_size, 1) return np.array(X).reshape(-1, window_size, 1), np.array(y)逻辑说明X的最后一个维度是1表示单变量输入。如果想把多个IMF拼成多通道输入可以把第三个维度改成K但常见做法还是一个IMF一个网络因为各IMF的动态差异比较大分开训练更容易让每个模型专注自己的带宽范围。horizon1时每个样本只预测下一个点模型容易训练horizon5时y是长度5的向量输出层节点要改成horizon。三种常见翻车第一range边界少减了1程序不报错但最后几个样本取不到标签序列尾部预测悄悄缺失第二样本打乱之后再按比例随机划分训练和验证集滑窗相邻样本高度重合随机划分会让验证集混进大量“同一段数据挪了几步”的近亲样本模型等于提前见过验证区间val_loss失真第三window_size选得太小比如日频金融数据只用4个交易日去预测第5天信息量完全不够模型只能学到一个“均值回归”的假象。金融日频序列我一般从20或24起步对应一个月的交易日窗口分钟级序列从60到90试再根据验证集表现收缩。3.2 归一化要分IMF做这一步比模型更影响效果不同IMF的幅值差很多趋势分量可能是三位数高频分量可能只有0.1甚至更小。如果所有IMF共用一套MinMaxScaler小分量会被整体压到接近0训练时几乎被当成噪声忽略。正确做法是每个IMF单独做归一化并且只用训练段来拟合scalerfrom sklearn.preprocessing import MinMaxScaler # train_imf 是某个IMF的时间序列前80%部分 scaler_imf MinMaxScaler(feature_range(0, 1)) scaler_imf.fit(train_imf.reshape(-1, 1)) train_scaled scaler_imf.transform(train_imf.reshape(-1, 1)).ravel() test_scaled scaler_imf.transform(test_imf.reshape(-1, 1)).ravel() # 预测完成后用 scaler_imf.inverse_transform 还原到原始尺度逻辑说明fit只跑在训练段上测试段和其他未来的新数据都沿用训练段的规则这是时序预测的红线。先对整个序列fit再切训练测试等于把未来片段的统计量带进了训练阶段验证集误差会显得非常好看但模型拿到真实新数据时立刻崩掉。这个问题几乎是复现VMD-LSTM时最常见的无效成功——指标好得离谱上线即翻车。feature_range(0,1)是LSTM的常见配置让数值落在Sigmoid和Tanh激活函数能很好响应的区间里。如果你发现某个IMF的缩放后数值仍然集中在一个非常窄的区间说明这个IMF本身能量太低预测它本来就不太可靠后续评估要重点关注这类分量不要试图通过把feature_range改成(-1,1)来硬拽效果一般。3.3 搭建并训练LSTMKeras里最常用的配置TensorFlow 2.x的Keras API对这类中小规模序列预测任务最顺手两步就能把模型建好。如果团队已经在用PyTorch这个流程平移也不难但单从开发和部署的便捷性看Keras更省事。import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping def build_lstm(window_size, horizon1): model Sequential([ LSTM(64, return_sequencesTrue, input_shape(window_size, 1)), Dropout(0.2), LSTM(32), Dense(horizon) ]) model.compile(optimizertf.keras.optimizers.Adam(learning_rate1e-3), lossmse) return model model build_lstm(window_size24, horizon1) early EarlyStopping(monitorval_loss, patience15, restore_best_weightsTrue) model.fit(X_train, y_train, epochs100, batch_size32, validation_data(X_val, y_val), callbacks[early], verbose0)参数说明第一层return_sequencesTrue是因为要输出完整序列给第二层LSTM如果只保留单层LSTM可以不设置。两层LSTM单元数从64和32起步比较稳不要一上来就上百滑窗样本在几千条时参数过大会明显拖慢收敛。Dropout(0.2)放在LSTM层之间缓解过拟合。lossmse对金融时序预测更合适平方误差让模型重点压低大偏差如果业务只关心方向mae也可以但收敛稳定性略差。训练配置里有个细节这里不用validation_split而是直接传validation_data因为时序样本的验证集必须是时间上的未来段不能靠框架随机切。patience15意味着验证损失连续15轮不下降就停restore_best_weightsTrue保证返回的是验证集最优的那组权重而不是最后一轮的。这个配置在金融日频数据上基本能稳定收敛。4. VMD-LSTM完整预测链路分解、分治、叠加三步走4.1 整体架构先说破缺一步都不行完整流程写出来只有三步VMD把原始序列拆成K个IMF每个IMF独立构造滑窗样本并训练LSTM各分量预测结果反归一化后相加得到最终预测。容易疏忽的是“分治”这两个字。我见过有同事跑完VMD之后只对趋势分量做了LSTM其余分量用均值预测顶替理由是高频分量“太随机学不动”。结果就是预测曲线比真实序列平滑很多方向预测还行但幅度和拐点总是慢半拍。既然做了VMD每个IMF都该是LSTM的预测对象没有谁可以被随便扔掉。幅值最小的那个IMF往往代表短期波动在金融场景里这可能就是日内成交量最活跃的那段波动扔掉它等于把最想预测的信息主动放弃了。4.2 最小可跑流程一份代码跑通五个IMF下面给出五个IMF版本的最小可跑骨架把第三章里写的make_samples和build_lstm直接复用进来。import numpy as np from vmdpy import VMD from sklearn.preprocessing import MinMaxScaler # 复用第三章的 make_samples 和 build_lstm这里省略函数体 # 原始一维序列按业务需要自行读取 data np.loadtxt(series.csv) K 5 u, u_hat, omega VMD(data, alpha2000, tau0, KK, DC0, init1, tol1e-7) horizon 1 window_size 24 split_ratio 0.8 n data.shape[0] preds [] for i in range(K): imf u[i, :] split int(n * split_ratio) train_imf imf[:split] test_imf imf[split:] # 每个IMF独立缩放只fit训练段 scaler_imf MinMaxScaler(feature_range(0, 1)) scaler_imf.fit(train_imf.reshape(-1, 1)) train_scaled scaler_imf.transform(train_imf.reshape(-1, 1)).ravel() test_scaled scaler_imf.transform(test_imf.reshape(-1, 1)).ravel() X_train, y_train make_samples(train_scaled, window_size, horizon) X_test, y_test make_samples(test_scaled, window_size, horizon) model build_lstm(window_size, horizon) model.fit(X_train, y_train, epochs50, batch_size32, validation_split0.0, verbose0) pred_scaled model.predict(X_test) pred scaler_imf.inverse_transform(pred_scaled.reshape(-1, 1)).ravel() preds.append(pred) # 各分量预测在原始尺度上叠加 final_pred np.sum(np.array(preds), axis0)逻辑说明每个IMF都是独立scaler、独立LSTM避免大幅值分量主导损失函数。叠加发生在反归一化之后的原始尺度不要在缩放过的小数域里相加否则数值会被scale偏差扭曲。split_ratio0.8是常见起点金融日频数据几千条时比较稳如果数据量大可以切0.85。这里要坦诚说一个重要限制上面打印的流程是“一次性分解再预测”的朴素版本——先对整段序列做VMD再造样本训练。这么做在复现阶段足够如果要做严格的滚动回测正确做法是每个预测日都用截至当天的数据重新VMD再分解预测代价是计算量成倍上涨。实际项目里我一般先跑朴素版本确认收益再决定要不要花时间上滚动版本。另外K5意味着同时训练5个LSTM在小数据集上几分钟到几十分钟不等提前有心理预期别被训练时间吓到。4.3 评估口径怎么定RMSE、MAE与保护MAPE评估指标最常用的三个是RMSE、MAE、MAPE。RMSE对大误差敏感MAE反映平均绝对差距MAPE相对误差读起来直观但在IMF序列上很容易出问题——真实值接近0时轻微偏差除以一个零点几的数MAPE直接爆炸。我平时这样实现def rmse(y_true, y_pred): return np.sqrt(np.mean((y_true - y_pred) ** 2)) def mae(y_true, y_pred): return np.mean(np.abs(y_true - y_pred)) def mape(y_true, y_pred): # 真实值接近0时会造成除零爆炸这里保护一下 y_true_safe np.where(np.abs(y_true) 1e-6, np.nan, y_true) return np.nanmean(np.abs((y_true - y_pred) / y_true_safe)) * 100逻辑说明np.where把绝对值小于1e-6的真实值替换成NaNnp.nanmean在求平均时跳过这些点。报告指标时不要只给一个数高频IMF的真实值很小单独看RMSE看不出问题MAPE会把相对误差暴露出来只有当RMSE下降、MAE同步下降且MAPE没有大幅恶化时才是真的预测变好了。5. VMD-LSTM避坑指南5个反复出现的翻车现场5.1 模态混叠或过分解现象VMD分解出的某两个IMF中心频率几乎挨在一起波形互相穿插个别IMF只剩纯噪声的形态。原因K设得过大或者alpha偏小模态带宽过宽两个模态共享了同一条频带反过来K过小时本该分开的趋势和周期又会被强行揉在一起。解决先打印omega最后一列看中心频率分布相邻两个中心频率如果相差不到一个倍频程K减一重跑如果某个IMF在频域上没有清晰主峰就把alpha往上调。我一般让K从4起跑最多加到7不在没有充分依据的情况下设K10以上否则后面要同时训练十个LSTM时间成本也扛不住。5.2 数据泄漏缩放统计量算到了未来头上现象验证集RMSE好得离谱但模型接到新数据上立刻打回原形交易策略按预测信号下单反而亏得更多。原因先对整个序列做MinMaxScaler.fit再切训练测试测试段的最大值和最小值在训练阶段就已经被模型间接看到过了梯度信息里带着未来分布。解决所有统计量只fit训练段测试段和新数据都沿用训练段的scaler做transform。代码顺序必须是“先切分、再fit”这个顺序一旦写反后面所有指标都不可信。这不算模型技巧是数据伦理但翻车的人最多。5.3 小IMF的MAPE爆炸现象整体RMSE看着稳了MAPE却输出几千甚至上万汇报时完全没法解释。原因小IMF的真实值集中在0附近预测值和真实值之间相差不大但分母是个零点零几的数相对误差当然被放大百倍。解决MAPE实现里加保护把绝对值过小的真实值排除再平均汇报指标用“RMSE MAE 保护MAPE”三件套。不要只看一个指标就下结论更不要拿单个分量的MAPE去做模型对比。5.4 滑窗重叠让验证集不再干净现象使用validation_split0.15训练后验证损失低得不正常但换到新数据上误差反弹剧烈。原因Keras的validation_split是从训练数据尾部切一部分出来滑窗每步只滑一个点验证集里每个样本的前24个位置几乎都出现在训练集的某个窗口里模型等于提前见过验证段的大部分内容。解决构造完整滑窗样本后手动按时间把最后一段划为验证集传给validation_data参数。训练时shuffleTrue没问题但训练集和验证集的来源时间段必须严格不重叠这才是时序预测里干净验证的含义。5.5 端点效应VMD分解两端抖动现象分解后各IMF首尾明显比中间抖预测序列两端的误差显著大于中部。原因VMD在信号两端缺少邻居样本做约束边界求解不稳定这是变分分解本身的特性绕不开。解决预处理时在序列两端各自补上一段镜像延拓样本分解完成后裁掉对应延拓段或者在评估时跳过首尾固定数量的点不把边界抖动计入指标。如果业务要求端到端预测就用滚动VMD反复刷新边界信息不要依赖一次性分解跑到头。6. 把VMD-LSTM用得更稳K值方法、多步预测与基线验证6.1 K值经验法小范围试错代替拍脑袋K值选择在实战里最稳的做法不是找理论最优而是“小范围试错加频率观测”。对没有明显周期的金融交易序列从K3试到K6每次记录omega和IMF波形模态重叠或纯噪声就直接弃用。对带日周期和周周期的负荷序列K7作为起跑点更常见。可以把几组候选K在相同LSTM配置下的验证集RMSE写进一个列表选最小的一组比凭感觉决定可靠得多。6.2 多步预测的两个策略最常用的是迭代法先用窗口预测下1个点再把预测点接进窗口继续预测重复到目标步数。实现简单但误差会随步数累积。另一种是直接法构造样本时把horizon设成目标步数输出层节点对应改成目标步数一次输出整段未来。直接法不累积误差但样本标签设计的灵活性下降。我一般先用直接法验证模型上限再退回迭代法做滚动预测两边结果对比着看既能定位模型能力瓶颈也能判断误差累积速度。6.3 拿朴素基线验证收益别只看训练曲线不要急着把Transformer这类大模型搬到小样本时序任务上。VMD-LSTM在有周期、非平稳的金融时序预测里能打前提是先跟LSTM单模型做对照。如果VMD-LSTM相比原始LSTM没有稳定的RMSE下降说明序列本身的非平稳性已经被LSTM吸收了分解并没有带来增量。我自己的习惯是上线前固定跑四组对照VMD-LSTM、原始LSTM、ARIMA基线、随机游走基线随机游走都赢不过的模型没有上线价值。这个流程我踩过不少坑早期复现VMD-LSTM时RMSE一直比直接LSTM还高后来定位到两个问题叠加——K值选得过大和归一化泄漏。把K值稳下来、标准化管住之后模型的收益才算体现出来。这套路不算新但每一步都有值得较真的细节希望你按上面的路径跑通后能少踩几个坑祝顺利。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →