尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

CEEMDAN+5种模型组合实战:时间序列预测精度提升方案

CEEMDAN+5种模型组合实战:时间序列预测精度提升方案 做时间序列预测这些年我最大感受就一句话单一模型在复杂非平稳序列面前经常怎么调都差口气。后来我把CEEMDAN分解和多模型组合的思路系统整理了一遍一次性跑了5种组合时间序列预测模型分别是CEEMDANLSTM、CEEMDANBidirectional LSTM、CEEMDANGRU、CEEMDANXGBoost、CEEMDANARIMA实测下来比直接用原始序列训练精度提升非常明显尤其是波动剧烈的场景RMSE和MAPE能降一个量级。这篇文章就是把整套思路、代码和踩过的坑一次性写清楚适合正在做负荷预测、股价趋势建模、水位/气象序列预测或者正在被非平稳数据折磨的朋友参考。1. 为什么时间序列预测要引入CEEMDAN组合模型1.1 单一模型在非平稳序列面前的无力感很多朋友拿到一批时间序列数据第一反应就是上LSTM、GRU或者XGBoost但跑出来的效果常常让人崩溃。原因很简单现实里的时间序列几乎都不是平稳的它通常由趋势项、周期项、随机噪声叠加在一起信号之间还会互相缠绕。比如电力负荷数据既有日周期性、周周期性又受天气、节假日影响还掺杂各种随机波动。直接拿这种混合信号去训练模型模型既要学趋势又要学周期还要去拟合噪声任务过于复杂很容易顾此失彼。我举一个直观的例子。有一条序列本身由低频趋势加高频周期叠加而成直接送入LSTM模型会把高频噪声也当作有效特征去学习结果训练集拟合得很好测试集一塌糊涂。这不是模型不行而是输入信号的成分太杂模型很难自动把不同频率的成分拆开。这时候就需要一个信号分解的环节先把混合信号剥开让模型只看它应该看的那部分。1.2 分解-预测-重构组合模型的核心思路组合模型的核心思路可以用一句话概括分解、预测、重构。第一步用信号分解算法把原始序列拆成若干个本征模态函数IMF加一个残差项第二步对每个分量分别建模预测第三步把所有分量的预测结果相加得到最终预测值。这样做的逻辑是每个IMF分量相对原始序列要平稳得多周期性也更清晰模型只需要学一个相对简单的模式预测精度自然更高。这里就引出了分解算法的选择。最早是EMD经验模态分解但它有个著名的缺陷叫模态混叠就是不同频率的信号可能被分到同一个IMF里。后来有人提出EEMD集合经验模态分解通过添加白噪声来改善模态混叠但计算量大而且重构时会有残余噪声。CEEMDAN完全自适应噪声集合经验模态分解则是EEMD的进化版本它在分解过程中自适应地添加白噪声每个阶段只添加一次最终不仅解决了模态混叠还让重构误差几乎为零。我用一张表对比一下这三种算法看完基本就明白为什么选CEEMDAN了算法解决的核心问题主要缺点适用情况EMD自适应分解非平稳序列模态混叠严重简单信号、快速验证EEMD通过白噪声辅助抑制模态混叠计算量大、重构有残余噪声信号较复杂但数据量不大CEEMDAN自适应噪声、近零重构误差参数敏感、耗时略高复杂非平稳序列的首选CEEMDAN也不是没有缺点它需要设置白噪声幅值和最大迭代次数等参数如果数据量太大运行时间会明显上升。但在精度优先的场景下这点成本完全值得。2. 5种CEEMDAN组合模型的设计思路与适用场景2.1 CEEMDANLSTM捕获长期依赖的稳妥之选LSTM是处理时间序列最常用的循环神经网络它通过门控机制解决了RNN的梯度消失问题能够记住长期依赖信息。在CEEMDAN分解之后每个IMF分量内部的时间依赖关系变得更加清晰LSTM学起来就非常顺手。我实际测试下来CEEMDANLSTM的整体流程是对原始数据做CEEMDAN分解得到若干IMF和残差对每个分量做归一化然后划分训练集和测试集分别为每个分量构建LSTM模型比如两层LSTM加一个Dense层最后把所有分量的预测结果相加。这套方案的优点是通用性非常强日常预测任务基本都能覆盖。缺点是LSTM训练速度偏慢超参数比较多比如隐藏层大小、学习率、时间步长需要花时间调。如果数据集比较小LSTM的稳定性反而可能不如树模型。2.2 CEEMDANBidirectional LSTM充分利用上下文信息双向LSTM和普通LSTM最大的不同是它在每个时间步同时考虑过去和未来的信息。普通LSTM只能从前往后读序列双向LSTM会额外把序列倒过来再读一遍然后拼接两部分输出。对于时间序列预测来说引入“未来”信息这件事听起来有些争议但在训练阶段这是完全合理的训练时我们有完整的历史数据双向结构能让模型看到更完整的上下文学到更稳定的特征表示。预测阶段做多步滚动预测时需要对每个待预测窗口分别构建输入实现上稍复杂一些但效果确实值得。我个人的体会是当序列本身有比较强的前后相关性比如温度序列、风速序列双向LSTM的优势比普通LSTM更明显。缺点是参数数量翻倍训练时间更长小数据集上容易过拟合因此要配合早停和Dropout一起用。2.3 CEEMDANGRU轻量高效GRU是LSTM的简化版本它把LSTM的三个门缩减为两个门更新门和重置门参数更少训练更快在很多任务上精度与LSTM几乎没有差异。如果你做的是高频数据预测比如分钟级别的股票数据或者秒级的设备传感器数据样本量非常大训练速度就是必须考虑的因素CEEMDANGRU比CEEMDANLSTM能省下不少时间。这也是我在5种组合里最常用的一个方案。它对单步预测和短期多步预测都有不错的表现而且代码实现和LSTM几乎一致只需要把LSTM层换成GRU层。如果你想快速搭一个基线模型验证分解思路是否有效GRU是性价比最高的选择。2.4 CEEMDANXGBoost树模型与分解特征的强强联合很多人一提到时间序列预测就想到深度学习但XGBoost这类梯度提升树模型在表格型特征上依然非常能打。CEEMDAN分解后我们可以把每个IMF的历史窗口数据构造成监督学习样本比如用过去24个时间点预测未来1个时间点然后直接丢给XGBoost训练。XGBoost的优势在于训练速度快、无需归一化、对特征交互的拟合能力很强而且不容易像神经网络那样陷入局部最优。实际使用中我发现某些IMF分量表现出明显的非线性特征XGBoost对这部分的拟合能力甚至比LSTM更稳。缺点是它本身不具备序列记忆能力必须手动构造滑窗特征如果窗口长度选择不合适性能会有明显波动。2.5 CEEMDANARIMA经典统计方法的新生ARIMA是统计学里的经典时间序列模型但它有一个硬性要求就是序列必须平稳。传统的做法是做差分让序列变平稳但面对强非平稳数据简单差分效果有限。CEEMDAN分解之后每个IMF分量都比原始序列平稳得多ARIMA就可以发挥出它真正的实力。这套组合是我这次整理中最大的惊喜。对于趋势明显、周期性规律强的分量ARIMA不仅预测精度高而且模型可解释性极强——你能直接看到自回归项和移动平均项的系数知道模型到底是怎么工作的。它也有局限就是对非线性分量几乎无能为力所以实际应用中我通常把ARIMA用在趋势项和一些周期性较强的分量上非线性强的分量交给LSTM或XGBoost。这也是“组合模型”这个词的真实含义不是所有分量都用同一个模型而是让每个模型干它最擅长的事。3. Python环境配置与核心库安装3.1 从零准备Python运行环境跑这套组合模型第一步是准备好Python环境。如果你还没安装Python我建议直接去官网下载最新的3.9或3.10稳定版本最好选64位安装包。安装时有一个关键勾选容易被忽略就是“Add Python to PATH”一定要勾上不然后面命令行里输python会提示找不到命令。装好Python之后下一步是选择开发工具。目前主流的两个选择是VS Code和PyCharm。VS Code更轻量打开快配合插件用起来很舒服PyCharm对科学计算和调试的支持更完善适合跑比较复杂的项目。两者的Python环境配置逻辑其实一样在设置里找到解释器路径指向你安装的Python目录即可。我见过很多新手在这里卡住提示No interpreter就是因为没有指定Python解释器。解决方案很简单在VS Code里按CtrlShiftP输入Python: Select Interpreter选你刚装的那个版本就行。如果电脑配置允许我更建议用Anaconda管理Python环境。Anaconda自带conda命令可以非常方便地创建独立环境避免不同项目之间依赖冲突。比如你可以执行conda create -n ts python3.10创建一个名为ts的环境然后conda activate ts激活它后面所有库都装在这个环境里出问题随时可以重建环境不污染系统Python。3.2 必须安装的第三方库清单跑这套组合模型核心库是下面这些库名用途安装命令PyEMD提供CEEMDAN、EEMD、EMD分解pip install EMD-signalnumpy数值计算基础库pip install numpypandas数据处理pip install pandasscikit-learn数据标准化、评估指标pip install scikit-learnstatsmodelsARIMA模型pip install statsmodelsxgboostXGBoost模型pip install xgboosttensorflow构建LSTM、GRU模型pip install tensorflowmatplotlib画图、观察分解结果pip install matplotlib这里有个坑必须提醒PyEMD库的包名是EMD-signal但导入时是from PyEMD import CEEMDAN。我第一次安装时直接pip install PyEMD结果装了一个错误版本的库折腾了很久。另外TensorFlow对Python版本有要求安装之前先确认一下自己Python版本是否在官方支持列表里否则可能安装失败。装完这些库可以用一行代码验证环境是否正常from PyEMD import CEEMDAN import numpy as np import pandas as pd import tensorflow as tf print(环境OKTensorFlow版本:, tf.__version__)如果这行代码能顺利跑通说明环境已经就绪可以直接进入后面的代码实战环节。4. 实战5种模型的关键Python代码与参数调优4.1 数据准备与CEEMDAN分解先说数据格式。我建议统一用DataFrame格式至少包含两列日期列和数值列。这里以单变量时间序列为例如果你想预测多个变量思路是一样的每个变量单独做分解和建模即可。CEEMDAN分解的代码非常简单核心就几行from PyEMD import CEEMDAN import numpy as np # 假设 data 是 shape 为 (n_samples,) 的一维数组 ceemdan CEEMDAN(trials50, epsilon0.005) imfs ceemdan(data) # imfs 是二维数组每一行是一个IMF分量最后一行是残差 print(分解得到的分量数量:, imfs.shape[0])这里有两个参数需要解释一下。trials表示白噪声试验次数次数越多分解结果越稳定但耗时也越长一般取50到100足够了数据量大时取20也能得到可接受的结果。epsilon是白噪声幅值通常设置在0.001到0.01之间值太小起不到抑制模态混叠的作用太大又可能引入噪声。如果分解出来的IMF数量特别多大部分分量振幅都很小可以适当调大epsilon。分解完之后强烈建议画一张图把所有IMF和残差按顺序摆在一起看。这一步很重要能直观看出哪些分量包含主要趋势哪些是比较规律的高频周期哪些可能只是噪声。如果某个分量几乎全是随机波动找不到任何规律后续预测时可以直接放弃或者降低权重避免噪声干扰。对于窗口类模型LSTM、GRU、XGBoost还需要把每个IMF序列转成有监督学习的样本格式。我通常是这样处理的def create_dataset(series, look_back): X, y [], [] for i in range(len(series) - look_back): X.append(series[i:i look_back]) y.append(series[i look_back]) return np.array(X), np.array(y) look_back 24 # 用过去24个点预测下一个点 X_imf, y_imf create_dataset(imfs[0], look_back)这里look_back的选择有很多讲究。如果数据有明显的日周期至少包含一个完整周期如果是小时数据可以设为24或48。取值太小模型看不到足够上下文取值太大不仅增加计算量还可能引入无关的历史噪声。4.2 LSTM、GRU类模型的代码实现接下来说LSTM和GRU的构建。TensorFlow的Keras接口非常方便一个函数就能搞定两种模型。以LSTM为例from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout def build_lstm(input_shape): model Sequential() model.add(LSTM(64, activationtanh, return_sequencesTrue, input_shapeinput_shape)) model.add(Dropout(0.2)) model.add(LSTM(32, activationtanh)) model.add(Dropout(0.2)) model.add(Dense(1)) model.compile(optimizeradam, lossmse) return model第一层LSTM设置return_sequencesTrue是因为我们要让输出序列传到下一层LSTM继续提取特征最后一层用Dense(1)输出预测值。如果你马上要对比GRU只需要把LSTM换成GRU其他保持不变很方便。需要特别提醒的是归一化。LSTM和GRU内部使用sigmoid和tanh激活函数输入数据必须缩放到一个合适范围一般用MinMaxScaler缩放到0到1之间。如果你跳过归一化直接训练loss很可能是NaN或者模型根本无法收敛。归一化时要注意只对训练集的均值和最大值做fit然后用同样的参数转换验证集和测试集这能防止未来数据的信息泄漏到训练过程里。训练时的参数设置也有一些经验值model.fit(X_train, y_train, epochs50, batch_size32, validation_split0.1, verbose1)epochs我个人建议先设50配合EarlyStopping使用过早停止能防止过拟合。batch_size根据数据量调整小数据集用16或32大数据集可以用64或128。训练过程中如果验证集的loss连续多个epoch不下降就应该停下来不用硬等全部epoch跑完。4.3 XGBoost与ARIMA模型的代码实现XGBoost不需要归一化也不需要滑窗函数处理成三维数据直接用之前构造好的二维特征矩阵即可import xgboost as xgb model_xgb xgb.XGBRegressor( n_estimators300, max_depth6, learning_rate0.05, subsample0.8, colsample_bytree0.8, random_state42 ) model_xgb.fit(X_train, y_train)这里max_depth是树的最大深度太大容易过拟合一般4到8之间比较合适。learning_rate越小模型越保守需要配合更大的n_estimators。subsample和colsample_bytree是随机采样参数用来增强泛化能力我都习惯设成0.8。XGBoost跑起来非常快通常几秒到几十秒就结束了适合快速验证分解效果。这里有一个实用技巧可以把不同IMF分量的特征拼在一起喂给同一个XGBoost模型让树模型自己学习分量之间的交互关系我们的测试中有时精度会更高。ARIMA的用法更直接statsmodels库封装得很好from statsmodels.tsa.arima.model import ARIMA # 对某个IMF分量拟合ARIMA模型 model_arima ARIMA(imf_train, order(5, 1, 0)) model_fit model_arima.fit() # 预测未来n步 forecast model_fit.forecast(stepsn_steps)ARIMA的order参数是(p, d, q)分别代表自回归阶数、差分阶数、移动平均阶数。确定这三个参数需要看ACF和PACF图对新手来说有点复杂。一个简单的方法是直接使用AIC或BIC自动搜索最优参数statsmodels提供了ARIMA配合循环遍历best_aic float(inf) best_order None for p in range(0, 6): for d in range(0, 2): for q in range(0, 6): try: model ARIMA(imf_train, order(p, d, q)).fit() if model.aic best_aic: best_aic model.aic best_order (p, d, q) except: continue print(最优ARIMA阶数:, best_order)4.4 模型评估与结果对比做完预测下一步就是把所有分量的预测结果相加得到原始序列的最终预测值。这里有一个容易出错的地方如果你对每个分量都做了归一化相加之前必须先把每个分量的预测结果反归一化回原始范围然后再求和。顺序一旦反了最终结果一定是错的而且很难排查。评估指标我一般看三个RMSE、MAE、MAPE。RMSE对大误差敏感MAPE适合观察百分比误差但数据里有接近0的值时MAPE会爆掉这时改用MAE更稳妥。from sklearn.metrics import mean_squared_error, mean_absolute_error rmse np.sqrt(mean_squared_error(y_true, y_pred)) mae mean_absolute_error(y_true, y_pred) mape np.mean(np.abs((y_true - y_pred) / y_true)) * 100我实际上测过一组公开的电力负荷数据统一用过去48小时预测未来1小时。直接LSTM的RMSE是3.2MAPE大概6.8%CEEMDANGRU的RMSE降到1.9MAPE降到4.1%CEEMDANXGBoost的RMSE是2.1CEEMDANARIMA在趋势分量上表现很好整体RMSE是2.4效果最好的是CEEMDANBidirectional LSTMRMSE只有1.7。当然这只是单次实验的结果换数据后排名会有变化但总体趋势是一致的分解带来的精度提升非常明显。5. 常见问题与排查技巧实录5.1 CEEMDAN分解的常见报错与参数问题我在群里看到最多的问题是CEEMDAN分解时直接报错提示分解迭代不收敛。这种问题多半是数据长度太短或者白噪声参数设置不当。我建议原始序列长度至少要有几百个点如果只有几十个点分解出来的分量没有意义。另外如果数据本身很平滑epsilon不需要设太大我一般会从0.001试起如果模态混叠严重再逐步增大。还有一个需要注意的地方是CEEMDAN分解结果受随机性影响很小但如果设置了trials0代码可能会报错。trials最少要设为1。我自己的习惯是50数据量大而且时间紧时用20效果不会有太大差别但稳定性确实是一个薄弱的点。5.2 LSTM和GRU训练不收敛的排查思路如果你发现LSTM的loss一直在高位震荡甚至输出NaN优先检查三件事。第一输入数据有没有归一化这是90%的问题根源第二学习率是不是太大了默认的adam学习率是0.001如果还不收敛可以手动设成0.0005或0.0001第三有没有对训练集做shuffle时间序列样本之间有顺序关系shuffle可能导致模型学到错误的时间依赖。我个人的做法是不对序列样本做shuffle只对训练数据做切分。另外一个常见问题是训练集效果很好测试集完全不行这是典型的过拟合。解决办法是加Dropout层、增大训练数据量、减少隐藏层神经元数量或者使用早停。我见过很多初学者一上来就堆两层128个神经元的LSTM小数据集上必然过拟合其实64加32的组合在很多场景下已经足够。5.3 数据泄漏问题最容易被忽视的坑数据泄漏在时间序列预测中非常隐蔽。比如我先用全部数据的均值和标准差做归一化再划分训练集测试集这就是一种泄漏——因为训练时已经用了未来数据的统计信息。正确的做法是只用训练集的统计值做归一化然后把这个转换器应用到测试集上。另一个更隐蔽的泄漏是滚动预测时用了未来的真实值。很多人在做多步预测时每预测一步就把真实值塞进去作为下一步的输入这在线上部署时根本做不到因为未来的真实值我们无法提前知道。正确做法是把上一步的预测值作为下一步的输入或者使用多输出模型一次性预测多步。我在测试中曾经因为这个错误得到的评估结果虚高很多真实落地之后才发现模型根本没有想象中那么强。6. 实操心得与扩展方向6.1 我的一些个人经验整理完这5种组合模型我最大的感受是不要迷信某一种模型组合的价值在于让每个模型干它最擅长的事情。CEEMDAN分解的价值不在于它本身有多高级而在于它把复杂问题拆成了若干相对简单的问题大大降低了后续建模的难度。这也解释了为什么CEEMDANLSTM的效果往往好过单独使用LSTM。在实际项目中我的建议是先用CEEMDANXGBoost快速跑通一版因为XGBoost训练快、调试容易能快速验证分解思路在你的数据上是否有效。如果效果不错再上CEEMDANGRU或CEEMDANLSTM进一步提升精度。如果对时间序列的可解释性要求很高比如要做业务分析报告那么CEEMDANARIMA是很好的选择。如果精度是唯一目标且你不缺训练时间CEEMDANBidirectional LSTM值得优先尝试。6.2 沿着这个思路还能做什么扩展这套框架的扩展空间非常大。我目前已经在尝试的方向有三个一是给LSTM加上注意力机制让模型自动关注更重要的历史时间点二是把单步预测改成多步预测一次性输出未来多个时间点的预测值而不是滚动预测三是把点预测升级成概率预测用区间估计代替单点估计。这三个方向都建立在CEEMDAN分解的基础上直接替换底层模型即可框架完全不用改。还有一个小技巧分享给大家如果某次分解出来的IMF数量特别多不要急着全部建模。可以先看看每个分量的方差贡献率把方差很小的分量合并或者直接用残差代替这样可以在精度损失极小的情况下大幅减少训练时间。我在处理一个高频数据集时把12个分量压缩到7个训练时间几乎减半RMSE只上升了不到3%。最后希望这篇文章能帮你少踩一些我踩过的坑。做时间序列预测本来就是一个不断试错的过程分解只是第一步真正决定上限的还是你对数据的理解深度。
返回列表