EEMD-LSTM实战:集合经验模态分解与LSTM组合的时间序列预测
2026/9/12 22:01:57 网站建设 项目流程

简介:这是一份基于EEMD-LSTM组合模型的时间序列预测Python实现,包含完整源码与配套数据,适合计算机、电子信息、数学等相关专业的学生用于课程设计、期末大作业或毕业设计,也可供算法入门者学习信号分解与深度学习预测结合的思路。压缩包共3个文件,含1个.py主程序和2个.csv数据集,整体仅47KB,轻量易部署;代码采用参数化编程,几乎每行均带详细注释,可方便替换数据与调整参数,预测流程涵盖数据读取、EEMD模态分解、训练集与测试集划分、LSTM模型构建及结果评估等关键环节,能帮助读者直观理解组合模型的完整搭建过程。目前已有383人学习下载,适合希望借助完整样例快速上手EEMD-LSTM预测任务的学习者。

1. EEMD-LSTM:把“拆解”和“记忆”组合起来的时间序列预测方案

“先分解、再分别预测、最后求和”这个思路,在时间序列预测里已经是很成熟的做法。EEMD-LSTM 就是其中被验证过最多的一条路:先用集合经验模态分解(EEMD)把一条波动复杂的原始序列拆成若干条相对平稳的本征模态函数(IMF),再对每个 IMF 单独训练 LSTM 模型,最后把各模型的预测值叠加成最终结果。它解决的问题很具体——原始序列里同时混着趋势、周期、噪声和突变,直接喂给 LSTM 会让网络把噪声也学进去,分解之后每个分量都变得容易建模,预测精度通常能明显提升。适合的读者是正在做销量、流量、负荷、水位这类单变量预测,手里有 Python 环境,想复现一套完整流程而不是只看论文的人。下面从分解讲到训练,再讲到怎么检验分解结果,尽量让每一步都能直接跑通。

2. 先做分解:用 PyEMD 跑通 EEMD 的最小代码

2.1 EMD 到 EEMD:为什么要加白噪声

经验模态分解(EMD)做的事情,是把一条时间序列自适应地拆成若干个 IMF 和一个残差项。每个 IMF 要满足两个条件:极值点个数与过零点个数相等或至多差一个;上下包络的均值在任意点上都接近零。这相当于把原始信号按时间尺度从高频到低频逐层剥离,不需要预先指定基函数,这一点和傅里叶变换、小波变换有本质区别。

但 EMD 有个著名的毛病叫模态混叠:当原始信号里存在间断性扰动或噪声时,同一个 IMF 里会混入不同时间尺度的成分。最典型的例子是一段连续正弦波中间突然出现一个高频毛刺,EMD 会把毛刺附近的频率全搅在一起。EEMD 的解法很粗暴也很有效——在原始序列上多次添加白噪声,每次都重新做一次 EMD,最后把多次结果取平均。白噪声在每次分解中随机出现,多次平均后会相互抵消,而真实信号在平均中保留下来。这就是“集合”二字的来历。

用 Python 实现 EEMD 不需要自己写迭代筛分过程,直接用PyEMD库就行。安装时注意,这个库在 PyPI 上的包名是PyEMD,导入语句是from PyEMD import EEMD,字母大小写不一样很容易在第一步就报错。Python 环境建议 3.8 到 3.11,太新的版本某些预编译依赖可能会缺失。

2.2 用 PyEMD 对序列做集合经验模态分解

假设数据是单列 CSV 文件,第一列是时间点或索引,第二列是观测值。加载数据后,先转成一维数组,拿出最后一个值之前的所有数据参与分解。核心代码只有几行:

import pandas as pd import numpy as np from PyEMD import EEMD # 读取数据,假设只有一列数值,没有表头 series = pd.read_csv('series.csv', header=None, usecols=[1]).values.flatten().astype(float) # EEMD 的关键参数:试验次数和噪声宽度 eemd = EEMD(trials=100, noise_width=0.05, parallel=False) IMFs = eemd.eemd(series) # 打印形状:行数是 IMF 个数 + 最后一行为残差,列数等于序列长度 print("IMF shape:", IMFs.shape) print("Reconstruction error:", np.max(np.abs(np.sum(IMFs, axis=0) - series)))

运行后IMFs是一个二维数组:每一行是一个 IMF 分量,最后一行是残差趋势项。sum(IMFs, axis=0)应该能还原出原始序列,重建误差一般在1e-10量级,如果差出几个数量级,说明分解过程出了数值问题。

trials是添加白噪声并重复 EMD 的次数,noise_width是白噪声的标准差与原始序列标准差的比值。这两个参数直接决定分解质量,论文里常见的组合是 trials 在 100 到 300、noise_width 在 0.01 到 0.2 之间。parallel参数在多核机器上可以设为 True 加速,但 Windows 下有时会有多进程启动问题,单机小数据量建议先关掉。

2.3 EEMD 的可调参数怎么设

新手最常见的错误是把 noise_width 设得过大或过小。过大会把真实信号也当成噪声均掉,IMF 出现明显变形;过小则起不到抑制模态混叠的作用。我一般会先按0.05跑一遍,然后分别试0.020.1,对比同一个模态的波动形态是否稳定。如果某条 IMF 在三次试验里形状差很多,说明 trials 不够或者噪声宽度不合适。

参数默认建议调参方向失效表现
trials100增大到 300 可降方差同一序列多次运行结果波动大
noise_width0.05数据噪声大时调到 0.1IMF 形状失真或产生多余模态
parallelFalse长序列可开 TrueWindows 下多进程不稳定

另外要注意:eemd.eemd()每次调用都会重新生成随机白噪声,所以同样的代码两次运行结果不完全相同。要复现实验结果,可以在构造EEMD对象之前调用np.random.seed(42),但 PyEMD 内部随机状态的控制并不完全受外部 seed 约束,所以严谨的做法是在固定 seed 后把分解好的 IMF 保存为.npy文件,后续建模和训练都从文件读取。这样既保证多次实验可比,也避免了每次训练前都要重跑一遍分解的开销。

3. 构建 EEMD-LSTM:从 IMF 到监督学习样本

3.1 为什么每个 IMF 要单独建 LSTM

分解完成后,一个自然的疑问是:为什么不把所有 IMF 拼成一个多维输入,让 LSTM 一次性学习多条序列?从工程角度看,这个想法可行,但会引入一个麻烦:不同 IMF 的频率和幅值差异太大,一个 LSTM 很难同时捕捉高频分量的短期波动和低频分量提取出的长期趋势。高频 IMF 需要小的时序窗口和短记忆,低频分量需要大的时序窗口和长记忆,这两者在同一个网络结构里很难兼顾。

所以 EEMD-LSTM 的标准做法是:对第 1 到第 N-1 个 IMF 和残差项分别建模,每个分量都有自己独立的一组训练测试集、独立的归一化参数、独立的 LSTM 结构。这样做的另一个好处是单个模型变简单了,过拟合风险降低——每个 LSTM 只需学到一条相对平稳序列的规律。

数据划分上有一个常被忽略的细节:EEMD 是对整条原始序列做的分解,所以训练集和测试集在分解时是“见过彼此的”。严格做在线预测的场景,应该只用预测点之前的数据做分解,再往后滚动。但离线实验里,先用全序列分解、再按时间切训练集和测试集是论文里最普遍的做法,因为 EEMD 本身不利用未来信息去拟合 IMF 数值,它只是尺度分离工具。

3.2 用滑动窗口把一维 IMF 转成监督学习输入

LSTM 不能直接吃一维序列,它需要“时间步”结构:每个样本是最近look_back个时间点,标签是下一个时间点。对每个 IMF 做同样的窗口切分,代码如下:

def create_dataset(series, look_back=24): X, y = [], [] for i in range(len(series) - look_back - 1): X.append(series[i : i + look_back]) y.append(series[i + look_back]) return np.array(X), np.array(y) # 以第 0 个 IMF 为例 look_back = 24 X_imf0, y_imf0 = create_dataset(IMFs[0], look_back) print("样本形状:", X_imf0.shape, y_imf0.shape)

look_back的选择要参考数据本身的周期。日粒度数据做销量预测,周期是 7 或 30,窗口至少要覆盖一个完整周期;分钟级电力负荷预测周期是 24 小时,窗口就得用 96 或 144。窗口太大训练样本会少很多——样本数是len(series) - look_back - 1,每多 1 个窗口长度就少 1 个样本。

划分训练集和测试集时,千万不能像普通分类任务那样随机打乱。时间序列必须按时间顺序切分:前 80% 做训练,后 20% 做测试。这里直接用数组切片就行,因为输入的序列本身严格按时间排列。

3.3 每个 IMF 的归一化与逆变换

LSTM 对输入数据的尺度非常敏感。IMF 里高频分量的幅值通常远小于低频分量和残差,如果不归一化,网络会把精力全放在大数值的分量上。一般做法是对每个 IMF 分别做 MinMax 归一化,把值压到 [0, 1] 区间。

这里要注意一个隐蔽的坑:MinMaxScaler.fit_transform只能作用在训练集上,对测试集必须用transform,因为测试集的minmax不能参与训练阶段的计算。可是对时间序列来说,切出测试集之后再训练归一化参数,相当于用了未来信息。常见解法是:先把整条 IMF 的minmax存下来,或者先在全集上 fit 一次,再对训练集和测试集分别 transform。如果你想严格一点,就用滚动预测式的归一化——只用训练集 fit,测试集用训练集的范围做 transform,然后接受测试集可能出现超出 [0,1] 的值。

归一化方式适用场景缺点
全集 fit 后统一 transform离线实验,样本量小测试集信息泄漏,结果偏乐观
仅训练集 fit,测试集 transform更接近真实预测测试集分布漂移时精度下降
每个 IMF 独立归一化EEMD-LSTM 标配逆变换时要记录每个分量的 min/max

逆变换时要为每个 IMF 单独保存一个scaler_imf[i],预测完成后先逐分量 inverse_transform,再叠加求和。很多人在这里图省事只对整个原始序列做一个归一化,结果高频 IMF 的值被压缩到接近零,LSTM 学不动,预测只剩趋势项。逆变换的兜底检查是:把训练集的预测分量都 inverse 之后,序列最后一个值附近应该能接上原始序列。

4. 训练 LSTM 与多分量重建:参数和避坑

4.1 分 IMF 构建模型与训练循环

在 Keras 里,单变量 LSTM 的输入形状是(samples, time_steps, features),features 固定为 1。构建模型时把return_sequences设为 False,让最后一层 LSTM 只输出最后一个时间步的隐状态,再接一个 Dense 层输出单个预测值。对整个 EEMD-LSTM 做训练,主循环长这样:

from keras.models import Sequential from keras.layers import LSTM, Dense, Dropout n_imfs = IMFs.shape[0] look_back = 24 train_size = int(len(series) * 0.8) models = [] scalers = [] pred_imfs = [] test_len = len(series) - look_back - 1 - train_size for i in range(n_imfs): # 每个分量独立归一化和建窗 from sklearn.preprocessing import MinMaxScaler sc = MinMaxScaler() imf_2d = IMFs[i].reshape(-1, 1) imf_norm = sc.fit_transform(imf_2d).flatten() X, y = create_dataset(imf_norm, look_back) X_train, X_test = X[:train_size], X[train_size:] y_train, y_test = y[:train_size], y[train_size:] X_train = X_train.reshape((X_train.shape[0], look_back, 1)) X_test = X_test.reshape((X_test.shape[0], look_back, 1)) # 每个分量一个简洁的 LSTM model = Sequential([ LSTM(units=64, input_shape=(look_back, 1)), Dropout(0.2), Dense(1) ]) model.compile(optimizer='adam', loss='mse') model.fit(X_train, y_train, epochs=30, batch_size=32, verbose=0) pred_norm = model.predict(X_test, verbose=0) # 逆归一化后再存,避免后面叠加时尺度混乱 pred = sc.inverse_transform(pred_norm).flatten() pred_imfs.append(pred) models.append(model) scalers.append(sc) final_pred = np.sum(pred_imfs, axis=0)

每个分量的 LSTM 结构完全一样,这有它的合理性:EEMD 把复杂序列拆成了复杂度相近的分量,所以“每个分量用相同容量的循环神经网络”是可行的;如果某个 IMF 明显更平滑,可以把它的units减小到 32,减少过拟合。

train_size的切分位置要对所有 IMF 保持一致,否则各分量预测的步数对不齐,最后无法叠加。train_size是从原始序列长度算的,不是从建窗后的样本数算的——代码里create_dataset后样本总数变小了,但 slicing 用的还是原始索引,所以要先想清楚哪个维度是对齐的。

4.2 LSTM 结构参数怎么定

对稳定平滑的 IMF,LSTM 的units设置在 32 到 64 之间就够用了;残差项往往是一条接近单调的趋势线,本质上可以理解为一个缓慢变化的数值,用 16 到 32 个单元足够。units过大的副作用是训练变慢外加过拟合,不会让精度显著提升。look_back对每个 IMF 可以不同,但那样实现上会比较繁琐,一般先统一设一个值,跑出基线后再对高频 IMF 调小、低频 IMF 调大。

参数取值范围对预测的影响快检方法
look_back周期长度 ± 50%过小丢失周期,过大引入噪声对比 7/24/48 三档
units16 ~ 128过小欠拟合,过大过拟合看验证 loss 是否回升
dropout0.1 ~ 0.4抑制过拟合验证集 loss 与训练 loss 差距大时调大
epochs20 ~ 100过大会记住噪声加 EarlyStopping 更稳

训练时加EarlyStopping是非常值的做法,尤其是数据量大时,固定轮数训练往往会在最后几轮把模型带到过拟合区。可以设置patience=10,监控验证集 loss,如果连续 10 轮不降就停止。

4.3 EEMD-LSTM 常见的三个坑

第一个坑是测试集泄露,也就是前面说的全集归一化问题。第二个坑是分量预测后再叠加,却没有看单个分量的拟合效果。高频 IMF 用 LSTM 预测会明显滞后一拍,如果高频分量占原始信号的比重不小,叠加后的整体预测会呈现出“钝化”的效果。这种情况下不要急着加模型复杂度,先检查一下该 IMF 是否真的含有可预测的结构,还是已经接近纯噪声。进入纯噪声区间的 IMF,直接对它预测并叠加进去,反而会拉低整体精度。

第三个坑是 EEMD 的重建误差被错误地当成训练集噪声。很多时候原始序列被 IMF 完全重建,误差只有1e-10,但高频 IMF 的幅值却只有1e-3量级——这时候训练一个 LSTM 去拟合这个微小的分量没有意义,它不过是被分解算法剥离出来的白噪声。对这样的分量,可以设一个能量阈值:该 IMF 的方差占原始序列方差的比例小于某个值(比如 1%)就放弃预测,叠加时不考虑它。这能节省不少训练时间,也能让最终预测曲线更平滑。

5. 验证分解质量:重建误差和分量独立性检查

EEMD-LSTM 最终效果不好,很多时候问题不在 LSTM,而在分解这一环。所以训练模型之前,我会先对 EEMD 结果做两个快速检查,大概几十秒就能判断分解靠不靠谱。

第一个检查是重建误差。对IMFs的每一行按列求和,理论上应该得到原始序列。实际做的时候用np.max(np.abs(np.sum(IMFs, axis=0) - series))就能算出逐点最大误差。误差应该在1e-8以下,如果跑到1e-2这种量级,说明某个 IMF 的迭代筛分没有收敛,直接把trials翻倍或者调小noise_width再跑一次。还有一个更细的检查:看残差项的极值点个数,正常的残差应该是单调或极缓慢变化的曲线,如果残差还保留着明显的震荡,说明分解没有拆干净,这时应该调大trials

第二个检查是分量独立性。把相邻两个 IMF 画在同一张图上,如果两个分量在时间轴上有明显的同步抖动,就说明它们之间存在能量泄漏——EEMD 的本意是把不同尺度的成分拆开,一旦相邻 IMF 形态高度相似,后续 LSTM 拟合时就会出现“两条分量相互抵消”的怪异现象:每个模型拟合目标都带着对方的影子,最后叠加结果虽然能还原,但每一步的预测误差被放大了。

针对分量泄漏,我常用的做法是做一次“只留一半分量”的消融对比。例如只用前一半高频 IMF 叠加做训练,或者只用低频部分加残差做训练,分别看验证集的 RMSE 和 MAPE。如果删掉某些 IMF 后误差反而下降,说明那些分量不仅是噪声,还在干扰低频建模。这个验证非常快,因为每个 IMF 的模型训练都是独立的,可以并行跑。

做完这两个检查,EEMD-LSTM 的效果基本就有底了,再回头调 LSTM 参数才有意义。整套流程跑通之后,主循环里每个 MPI 任务、每套模型参数和每次归一化的逆变换参数应该以字典或列表形式持久化到本地。后面每次做新数据预测,直接复用这套已拆好的分解器配置,要比每次现算快得多。最好的状态是:训练代码、模型权重、每个分量的 scaler 和 EEMD 参数四项都存在一个固定的目录结构里,任何时候拿新点进来都能在几秒内给出预测。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询