ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CEEMDAN-ISOS-VMD-GRU-ARIMA混合模型:时序分解与预测实战

2026/10/2 17:57:23 拓冰建站 浏览量
CEEMDAN-ISOS-VMD-GRU-ARIMA混合模型:时序分解与预测实战 简介提供基于CEEMDAN-ISOS-VMD-GRU-ARIMA组合模型的时间序列预测Python实现适合计算机、电子信息、数学等专业学生用于课程设计、期末大作业或毕业设计也适合希望系统学习组合预测的入门者。资源包含完整可运行源码与两个CSV数据集代码在AnacondaPyCharmTensorFlow环境下编写采用参数化设计关键参数可灵活调整并配有近乎逐行的保姆级注释即便零基础也能快速理解模型调用与数据流转逻辑便于替换为自己的数据开展实验整个项目结构紧凑脚本内附有明确注释提示可快速定位到数据读取、模型训练与结果输出等模块。压缩包共3个文件以.py源码和.csv数据为主整体仅52KB轻量易部署。目前已有387人学习下载作者为资深算法工程师擅长智能优化与神经网络预测代码思路清晰、可直接运行适合二次开发。可用于交通流量、气象、电力等单变量序列的预测实验也可作为组合预测方法的论文复现基底。1. 从CEEMDAN到ARIMA这套时间序列预测源码到底在解决什么做时间序列预测的人十有八九都卡在同一个问题上原始序列太“脏”。趋势、周期、噪声、突变全部叠在一起你拿GRU直接去拟合模型要么被噪声带偏要么长周期记忆根本学不到位你拿ARIMA去预测非线性部分又拟合不了。所以这几年工程上流行“先分解、再分别预测、最后重构”的思路——把一只完整的时间序列拆成若干个相对干净的子序列让每个模型只干自己擅长的那一段。这套CEEMDAN-ISOS-VMD-GRU-ARIMA源码就是把这条链路完整做了一遍CEEMDAN做第一层分解ISOS优化VMD做第二层分解GRU处理非线性分量ARIMA处理残差分量最后重构输出预测结果。适合做课程设计、毕业论文或者想抄一套完整时序预测流程的人。关键是它几乎一行一注释环境是anaconda pycharm tensorflow代码里全是参数化配置改数据、调参都不需要翻山越岭。2. 混合模型的完整流程为什么是这两级分解加两类预测器2.1 整体的流水线结构这套模型的数据流大致是这样走的原始序列 → CEEMDAN分解 → 得到IMF分量和残差 → 对高频IMF再做ISOS-VMD二次分解 → 把所有分量分别交给GRU或ARIMA预测 → 叠加重构。你拿到手的源码里主程序就是一个完整的pipeline从读csv到出预测图一步到位。代码包的逻辑顺序我帮你拆开看# 1. 读取数据焦作.csv df pd.read_csv(焦作.csv, parse_dates[date], index_coldate) data df[value].values # 2. CEEMDAN分解原始序列 imfs, residue ceemdan_decompose(data, max_imf8) # 3. 对每个IMF计算样本熵判断复杂度 # 复杂度过高的IMF进入ISOS-VMD二次分解 for i, imf in enumerate(imfs): se sample_entropy(imf, m2, r0.2 * np.std(imf)) if se threshold: vmd_imfs isos_vmd_decompose(imf, K_range(3, 10)) # 二次分解后的分量进入待预测列表这里需要理解两个关键点。第一CEEMDAN是自适应分解不需要预先指定模态数量它会按残差准则自己决定出几个IMF比纯EMD的模态混叠问题轻很多第二ISOS在这里是优化器用来搜索VMD的最优分解层数K和惩罚因子alpha。VMD这东西对参数敏感K设小了分解不干净K设大了会把一个分量硬生生拆成两段所以源码里用ISOS把K和alpha当成优化变量去搜。2.2 GRU和ARIMA的分工逻辑整套模型选GRU和ARIMA组合的原因不复杂——GRU擅长抓非线性特征和短期依赖ARIMA擅长抓线性趋势和残差中的自相关结构。高频IMF波动剧烈交给GRU低频IMF和残差相对平稳或有明显趋势交给ARIMA。源码里对这一段的封装很清晰# 高频IMF序列 - GRU预测 def gru_predict(series, look_back12, epochs150, units64): # 构造滑窗样本 X, Y create_dataset(series, look_back) model Sequential([ GRU(units, activationtanh, return_sequencesFalse), Dense(1) ]) model.compile(optimizeradam, lossmse) model.fit(X, Y, epochsepochs, batch_size32, verbose0) return model.predict(X_test) # 低频IMF/残差 - ARIMA预测 def arima_predict(series, order(2,1,2)): model ARIMA(series, orderorder) result model.fit() return result.forecast(stepshorizon)GRU部分有三个参数值得你动手调look_back决定用过去多少步预测下一步时间序列的周期性越明显这个值越应该对齐周期长度epochs对中等长度的序列一般150轮左右就收敛太多反而过拟合units是隐藏单元数数据量不大时64够用。ARIMA部分的order三元组是(p,d,q)p是自回归阶数d是差分次数q是移动平均阶数如果不想手动试参可以先对残差序列做ADF检验确定d再看ACF/PACF图定p和q。实际跑的时候我更喜欢在CEEMDAN分出IMF后先打一个样本熵直方图看分布样本熵大于某个阈值的分量才扔给VMD二次分解别一刀切全部二次分解那样计算量暴涨不说还容易把有效信息拆碎。2.3 重构输出与评估指标最后一步是简单求和重构把GRU预测的高频部分、ARIMA预测的低频和残差部分全部加起来得到最终预测序列。# 重构预测结果 final_pred np.zeros(len(test)) for comp_pred in all_comp_preds: final_pred comp_pred # 评估指标 rmse np.sqrt(mean_squared_error(test, final_pred)) mae mean_absolute_error(test, final_pred) mape np.mean(np.abs((test - final_pred) / test)) * 100 r2 r2_score(test, final_pred) print(fRMSE: {rmse:.4f}, MAE: {mae:.4f}, MAPE: {mape:.2f}%, R2: {r2:.4f})四个指标里MAPE对量纲不敏感适合跟其他模型横向对比R2如果低于0.8大概率是分解环节出了问题优先回去查VMD的K值是否合适而不是急着加GRU的层数。这套流程跑通以后你换任何数据集只要把csv读进来剩下的链路都不用动。3. 核心算法实现细节CEEMDAN分解与ISOS-VMD参数寻优3.1 CEEMDAN在源码里怎么用CEEMDAN的全称是自适应噪声完备集合经验模态分解它是在EMD基础上加了有限次自适应白噪声辅助分解解决模态混叠问题。from PyEMD import CEEMDAN def ceemdan_decompose(data, max_imf8): ceemdan CEEMDAN(trials100, epsilon0.005, ext_EMDNone) imfs ceemdan.ceemdan(data, max_imfmax_imf) # ceemdan返回的最后一行为残差 return imfs[:-1], imfs[-1]trials参数是添加白噪声的试验次数源码里给了100次这个值越大分解越稳定但计算耗时越长。epsilon是噪声幅度的比例系数0.005是经验值数据量小可以适当放宽到0.01分解结果会更平滑但会牺牲部分高频细节。max_imf是最大分解层数默认8层如果数据本身就简单实际分解出的IMF数会少于这个上限CEEMDAN会自动停。提示PyEMD库只负责分解它不会告诉你每个IMF物理上对应什么成分。建议分解后把每个IMF单独画出来看一眼——第一个IMF通常是最高频成分接近纯噪声后面的依次变低频最后的残差是一条单调或缓慢变化的曲线。如果中间某一个IMF长得跟噪声一样说明分解层数多了需要调小max_imf。3.2 ISOS优化VMD的搜索空间VMD变分模态分解的思路是把信号分解成K个带限模态每个模态围绕中心频率分布。它有两个核心参数K模态个数和alpha惩罚因子控制保真度与平滑度的平衡。K太小多个频率成分可能被压进同一个模态里K太大一个成分会被人为撕裂成相邻频段的两个模态alpha太大模态曲线过度平滑细节失真alpha太小模态会碎。ISOS在这份源码里的作用是自动搜索最优的(K, alpha)组合。ISOS是浣熊优化算法的一个变体属于比较新的元启发式算法用它而不是遗传算法或粒子群的原因主要是它在小维度搜索问题上收敛快、参数少。def isos_search_vmd_params(series, K_range(3, 10), alpha_range(1000, 3000)): def fitness(params): K int(round(params[0])) alpha params[1] vmfs vmd_decompose(series, KK, alphaalpha) # 以包络熵作为适应度包络熵越小分解越紧凑 return envelope_entropy(vmfs) best_K, best_alpha isos_optimize(fitness, K_range, alpha_range) return best_K, best_alpha适应度函数用的是包络熵这是VMD参数寻优里的常用套路——用希尔伯特变换求每个模态的包络信号再算包络的熵值包络熵越小说明模态能量越集中、分解效果越好。如果你自己改代码可以在这个适应度函数里加一个惩罚项比如模态之间中心频率太接近就扣分能进一步防止K过大的撕裂问题。3.3 VMD分解与参数落地的实操建议VMD分解本身有现成库源码里用的是标准的VMD实现。拿到新数据时我一般会先做一个sweep测试固定alpha2000把K从3试到10每个K跑一次VMD看中心频率分布表。如果相邻两个模态的中心频率差值小于一倍频程说明K设大了减下来。这个办法比单纯依赖ISOS跑出来的结果更有解释力——优化算法只保证包络熵最小不保证分解结果物理可解释。实际跑的时候ISOS的种群数和迭代次数也值得关注源码默认种群20、迭代30次速度较快。如果你发现VMD结果不稳定优先调整ISOS的搜索范围把alpha的区间缩窄比如从(1500, 2500)开始而不是盲目加大种群。4. 数据集与参数化代码拿到手怎么跑通、怎么改自己的数据4.1 项目文件结构和两个数据集有什么区别压缩包里包含主程序CEEMDAN-ISOS-VMD-GRU-ARIMA.py、焦作.csv和焦作全.csv两个数据文件。焦作.csv可能是较短时间的序列或采样更稀疏适合快速测试跑通流程焦作全.csv数据量更大是完整实验用的。# 建议的目录结构 CEEMDAN-ISOS-VMD-GRU-ARIMA/ ├── CEEMDAN-ISOS-VMD-GRU-ARIMA.py ├── 焦作.csv ├── 焦作全.csv └── requirements.txt # 自己建一个便于复现环境先跑焦作.csv把流程走通再换焦作全.csv做正式实验。两个csv的结构是一样的都有date列和value列唯一要注意的是日期的频率可能是日级也可能是月级不影响代码逻辑但会影响滑窗look_back的取值。4.2 环境安装与依赖清单推荐直接在anaconda里建一个独立环境避免依赖冲突。conda create -n tsforecast python3.9 conda activate tsforecast pip install tensorflow2.10.0 pip install pandas numpy matplotlib PyEMD vmdpy scipy statsmodelsTensorFlow 2.10是CPU和GPU版本的稳定分界线2.11以后Windows上GPU支持变麻烦。如果你的机器没有NVIDIA显卡装CPU版就够跑这套代码了因为GRU这部分序列长度和样本量都不大CPU跑150轮也就是一两分钟的事。注意PyEMD库的导入路径在不同版本里不一样老版本用from PyEMD import CEEMDAN新版本可能改成了from PyEMD import EEMD或需要指定子模块。源码如果报导入错误优先检查库版本而不是急着改代码逻辑。4.3 参数化配置在代码里的位置这份源码的一个重要特点就是参数化编程——所有该调的参数都集中放在文件开头的配置区。# 参数配置区 DATA_PATH 焦作.csv TEST_RATIO 0.2 # 测试集占比 LOOK_BACK 12 # GRU滑窗步长 GRU_UNITS 64 # GRU隐藏单元数 GRU_EPOCHS 150 # 训练轮数 BATCH_SIZE 32 # 批大小 ARIMA_ORDER (2, 1, 2) # ARIMA阶数 CEEMDAN_MAX_IMF 8 # CEEMDAN最大IMF数 ISOS_POP 20 # ISOS种群数 ISOS_ITER 30 # ISOS迭代次数 VMD_K_RANGE (3, 10) # VMD模态数搜索范围 VMD_ALPHA_RANGE (1000, 3000) # VMD惩罚因子范围 SAMPLE_ENTROPY_THRESHOLD 0.5 # 二次分解判定的熵阈值 # 把所有参数集中到一个区域好处是你不用在几百行代码里到处翻找魔数。我做实验的时候会把参数配置区单独复制一份存成config.py每次跑不同的数据集就改DATA_PATH和TEST_RATIO其他参数先不动等跑完一轮基线再逐个调。这是避免改崩的土办法但很实用。5. 避坑指南分解、预测与重构环节的常见翻车现场5.1 坑一CEEMDAN分解速度慢到怀疑人生现象跑焦作全.csv时CEEMDAN的trials100跑了几分钟还没结束整个程序看起来像卡死了。原因trials100意味着要做100次带噪EMD后取平均数据量一大计算量成倍增长。焦作全.csv的样本量可能是焦作.csv的好几倍直接套同样的参数就翻车了。解决把trials从100降到3050分解结果差异不大速度却能提升一倍。另外确认max_imf没设太大8层对大多数时间序列够用数据本身简单时设10层纯属浪费计算资源。5.2 坑二VMD分解出来的模态全是乱的现象ISOS-VMD分解后某个IMF被拆成了三段而且中心频率相互重叠包络熵指标显示“最优”但人眼一看就知道不对。原因ISOS优化的是数学指标包络熵它不感知物理含义。某些极端情况下包络熵最小化会引导算法把能量分散到多个相邻模态里。另一个可能原因是alpha搜索范围太大导致优化器在错误的参数区间里找到了“局部最优”。解决手工缩小alpha的搜索区间我一般先用1000、1500、2000、2500四个固定值各跑一次看中心频率分布再决定区间。如果固定alpha2000时K5效果最好就把alpha范围设成(1500, 2500)K范围设成(4, 6)让ISOS在小范围内精搜。5.3 坑三GRU训练损失下降缓慢预测结果是一条平线现象loss降到某个值后不再下降预测曲线变成接近水平的直线完全丢失了波动特征。原因一是look_back设置太小GRU看不到足够的历史上下文二是数据没做归一化GRU的tanh激活函数在大数值输入下梯度饱和三是时序数据本身太平稳分解后交给GRU的全是低频分量本身就没多少波动。解决先检查归一化——源码里如果对每个IMF单独做了归一化处理GRU的输入范围应该在[-1,1]附近如果没有自己加一层MinMaxScaler。其次把look_back从12调到24或48让GRU看到更长依赖。最后确认交给GRU的确实是高频IMF别把残差也扔给GRU。5.4 坑四测试集和训练集的索引错位现象重构预测结果后画图发现预测曲线比真实曲线“平移”了一段RMSE计算出来异常大但单独看每个分量的预测又都正常。原因时间序列预测里最常见的错误——测试集划分后没有重新索引导致滑窗构造样本时预测值的索引概念上错位了一步或多步。GRU输入序列的最后一步对应的是未来第一步的预测如果你把预测结果数组直接跟测试集对齐就会差一个look_back的长度。解决重构预测结果时显式记录训练集最后一个样本的时间索引然后从那里开始对齐预测值。源码里如果没做这个对齐逻辑自己补一个数组平移操作把预测结果整体移动到正确的时间轴上。5.5 坑五ARIMA在预测区间外的表现崩盘现象所有分量都预测得不错但最终重构后的MAPE偏大画图发现ARIMA预测的低频分量在后半段发散或变成一条平滑的负斜率直线。原因ARIMA本质是线性模型在样本外预测时预测值会快速回归到序列均值或沿线性趋势延伸。当残差分量包含末端拐点时ARIMA的预测方向就会和真实值相反。解决降低对ARIMA的依赖——把残差分量也拆成更细的子序列交给GRU处理或者对ARIMA预测结果做残差修正——用ARIMA预测误差的均值做一个简单的偏置校正。另一个土办法是把ARIMA的阶数调低overfit的ARIMA模型样本外预测更不稳定。6. 验证模型的可靠度残差白噪声检验与换数据集复现6.1 对重构结果做残差白噪声检验整套流程跑完别只看RMSE和MAPE就收工。一个能说明模型可靠性的关键验证是最终预测残差必须是白噪声。如果残差里还有明显的周期性或相关性说明某个分量没有被分解干净属于“信息泄漏到残差里”。from statsmodels.stats.diagnostic import acorr_ljungbox # 计算最终残差 residual test - final_pred # Ljung-Box检验p值大于0.05说明残差无显著自相关 lb_test acorr_ljungbox(residual, lags[10, 20], return_dfTrue) print(lb_test)如果p值小于0.05说明残差里还有可提取的信息模型没学透。我一般会先回到CEEMDAN那一步把SAMPLE_ENTROPY_THRESHOLD调低0.050.1让更多分量进入VMD二次分解然后重新跑一遍全流程。通常折腾一两轮残差的自相关就消失了。6.2 用焦作全.csv做完整复现并校准超参数焦作全.csv是完整数据集拿它做最终实验之前先用焦作.csv快速完成参数探索然后把探索出的参数迁移到完整数据上做验证。迁移时只看三个量训练时间、验证集loss、重构后的R2。# 换数据的操作 DATA_PATH 焦作全.csv # 只需要改这一行 TEST_RATIO 0.2 LOOK_BACK 24 # 如果完整数据集频率更高适当调大 GRU_EPOCHS 200 # 数据量大时可以多训练50轮有一个细节要保持一致两个csv的特征列名字必须完全一致如果你只改了DATA_PATH就报KeyError先去看焦作全.csv的表头跟焦作.csv是不是一样大概率是列名多了一个空格或大小写不同。6.3 一个我常用的收敛判别法我会保存每一轮实验的分解图、各分量预测图、重构对比图按数据集和时间戳命名归档。调参时看两张图第一张是VMD的中心频率分布图确认K没有过度分解第二张是重构预测曲线跟真实曲线的叠加图看局部拐点位置是否对齐。如果拐点全部滞后一拍说明GRU的look_back偏小如果拐点方向反了说明ARIMA阶数设置有问题。从那以后我每次跑混合分解模型都会强制走一遍同样的验证流程分解完先看IMF波形是否混叠预测完先看残差白噪声检验最后才看RMSE和R2。这套流程虽然土但帮我挡掉了至少五次“指标漂亮、图一塌糊涂”的尴尬。这套CEEMDAN-ISOS-VMD-GRU-ARIMA源码本身就是把这些步骤串好的成品你只需要在关键节点加上你自己的验证习惯就能少走我当初走过的弯路。希望帮到你。本文还有配套的精品资源点击获取