EMD-KPCA-LSTM多维时间序列预测:非平稳光伏功率的MATLAB实现

发布时间:2026/10/29 0:13:33
EMD-KPCA-LSTM多维时间序列预测:非平稳光伏功率的MATLAB实现

简介:面向光伏功率预测与多变量时间序列建模需求,这份程序包提供完整的EMD-KPCA-LSTM组合模型实现。资源适合有一定深度学习基础、希望将信号分解与特征提取方法融入LSTM预测流程的研究者。包内共24个文件,以MATLAB源程序(.m)、数据文件(.mat/.xlsx)为核心,附带结果图片(.png)、算法说明(.pdf)与说明文档(.txt),压缩包仅2.64MB,轻量易部署。案例基于北半球光伏数据,包含太阳辐射度、气温、气压、大气湿度四个输入特征,先通过经验模态分解降低环境序列非平稳性,再用核主成分分析提取关键因子并降维,最后由LSTM完成动态时序建模和功率预测。已有182人学习下载,适合用于论文复现、课程设计或相关课题的算法对比。资源内还包含运行截图与误差计算脚本,可快速校验模型效果。

1. EMD-KPCA-LSTM 多维时间序列预测:这份源程序解决的是非平稳光伏功率预测

做多维时间序列预测的同学,大概率遇到过这种事:辐射度、气温、气压、湿度四列数据看着齐全,直接丢进 LSTM,预测曲线一碰到多云天气就乱跳。问题不在 LSTM 本身,而在输入序列的非平稳性和多变量冗余。我拆了一版 EMD-KPCA-LSTM 源程序,思路很直接:先用经验模态分解(EMD)把每个环境序列拆成不同时间尺度的本征模态,再用核主成分分析(KPCA)消除模态之间的冗余,最后把降维后的特征序列交给 LSTM 做动态时序建模。这是一套完整的 MATLAB 流水线,光伏功率预测场景能直接跑通,风速、负荷、交通流这类非平稳多变量数据也能复用。适合想尽快跑通代码、又想把每个环节为什么这么接搞清楚的一线从业者。

2. 先做 EMD:把非平稳环境序列拆成本征模态,拒绝模态混叠

EMD 是这条流水线的第一级,任务是降低输入序列的非平稳性。滑动平均和差分只能把趋势和波动混合压制,EMD 从信号自身极值出发,按时间尺度筛出一组本征模态函数(IMF)加一个残差。源码里的 emd.m 就是筛分实现,emdtest.m 是它配套的测试入口。在动代码之前,得先弄清楚为什么非平稳性对 LSTM 是致命伤。

2.1 为什么输入要分解:LSTM 怕的是趋势和突变混在一起

光伏功率的直接输入是气象变量。太阳辐射度有明显的日周期,但云层遮挡会在几十分钟内制造剧烈突变;气温有季节趋势,气压和湿度又有各自的缓慢漂移。把这样的原始序列直接喂给 LSTM,网络要同时记住长期趋势、周期规律和突变细节,梯度在反向传播时很容易被突变样本带偏。表现就是晴天时段预测准,一到多云天气误差陡增。信号处理的说法是频率成分随时间变化,这是典型非平稳序列。

EMD 的思路是不预设基函数,根据信号局部极值分布,自适应地筛出若干本征模态函数。每个 IMF 只保留一个主导时间尺度,低频趋势进残差,高频突变留在靠前的 IMF。分解之后,LSTM 不需要用同一个神经元同时消化“今天是晴天还是多云”和“此刻辐射度具体是多少”两类信息,而是按尺度分开学。工程意义上,这比先做滑动平均再训练要彻底得多。

分解完成后有一个很容易验证的性质:所有 IMF 加上最后一行残差,应该能精确还原原始信号。我跑完 emdtest.m 的习惯是拿 sum(imf, 1) 和原序列作差,残差峰值小于 1e-10 才继续往下走。这一步不是可选的,因为后面 KPCA 吃的是拼接后的 IMF 矩阵,如果分解阶段就丢了信息,整套下游结果都不可信。

2.2 文件与调用方式:emd.m 的输出格式要先搞清楚

资源里与 EMD 相关的文件有 emd.m、emdtest.m、emd_data.mat,以及原始数据 origin_data.mat。我拿到代码的第一步是跑 emdtest.m,它把 emd.m 包装成能观察中间结果的脚本,可以直接看到每个输入特征分解出了几个 IMF 以及各 IMF 的波形。需要特别提醒:emd.m 对每个序列返回的 IMF 行数不保证一致,因为筛分停止条件依赖信号自身的极值分布,这是自适应方法的正常行为,但直接横向拼接就会报“矩阵维度不一致”。

% 读取北半球光伏数据,前四列为输入特征,最后一列为光伏功率 raw = xlsread('北半球光伏数据.xlsx'); X = raw(:, 1:4); % 辐射度、气温、气压、湿度 y = raw(:, end); % 光伏功率,待预测目标 % 逐列 EMD,结果放 cell 而不是矩阵,因为各列 IMF 数量可能不同 imf_cell = cell(1, 4); for col = 1:4 imf_cell{col} = emd(X(:, col)); end

逻辑说明:循环对四列特征分别调用 emd.m,结果暂存在 cell 数组里,这一步刻意避开矩阵拼接,是为了规避各列 IMF 行数不一致导致的维度报错。用 cell 存中间结果,是这个阶段最重要的习惯,后面做对齐时再统一长度。

参数说明:emd.m 内部的筛分停止阈值源码里是默认经验值,我通常不调整。真正该关注的是下一步模态截断——保留哪些 IMF。保留太少,高频突变的细节会丢;保留太多,模态之间的冗余会留给 KPCA 更多负担。光伏这类日周期明显的数据,默认配置分解出的 IMF 数量在 5 到 8 个之间(含残差),如果明显偏少,先检查是不是数据里连续 0 值太多,而不是急着改筛分阈值。

2.3 剔除 0 值和尾部对齐:预处理里最容易被跳过的一步

资源描述里专门提了“剔除 0 值”。北半球光伏数据在夜间和无光照时段,辐射度与功率会长时间为 0。如果不处理,EMD 会把这段平坦区域识别成某个主导模态,分解结果整体被拉偏。常见做法是先按辐射度是否大于 0 剔除无效段,或者干脆只截取日照时段做预测,而不是把 24 小时全部送进模型。源码注释里那句“分类亦然”,意思是这套前处理在分类任务里同样适用,目标换成辐照等级或天气类别时,EMD 和 KPCA 两级完全不用改,只换 LSTM 的输出层即可。

% 剔除辐射度 <= 0 的样本段,避免 EMD 把平坦零值区筛成主模态 valid = X(:,1) > 0; X = X(valid, :); y = y(valid, :); % 对齐各列 IMF 长度:统一补零到最长长度 max_len = max(cellfun(@(c) size(c,1), imf_cell)); imf_aligned = []; for col = 1:4 c = imf_cell{col}; if size(c,1) < max_len c = [c; zeros(max_len - size(c,1), size(c,2))]; % 尾部补零对齐 end imf_aligned = [imf_aligned, c]; end

逻辑说明:valid 用第一列辐射度是否大于 0 生成逻辑索引,把夜间段直接过滤掉,保证进入 EMD 的信号没有大段人为平坦区。对齐段把不足 max_len 的 IMF 矩阵在尾部补零,使 KPCA 阶段拿到规则的二维矩阵,这是后面一切操作的前提。

参数说明:补零是对齐的最简单方案,代价是新引入一小段人为零值。我倾向补零而非截断,因为 KPCA 之前还有一步归一化,零点不会产生主导方向;如果你发现某列原本就只有极少几条 IMF,补零行数占比过高,那就反过来截到最短长度,两种做法的分界线是补零行数不超过原行数的 20%。

3. 再做 KPCA:核主成分分析比 PCA 强在哪,参数怎么设

EMD 做完,原本 4 个特征变成了十几个甚至二十几个 IMF 列,直接进 LSTM 会让参数量和训练时间翻倍,何况 IMF 之间还有相关性。这一级用 KPCA 压缩维度。源程序中的 kernel.m、distanceMatrix.m、kPCA.m 三个文件构成一条完整链路,kpcaTest.m 负责把它们串起来验证。

3.1 为什么用 KPCA:气象变量的相关性是非线性的

普通 PCA 只能做线性投影。辐射度与气温之间的关系、湿度与气压之间的关系,在小时间尺度上经常是弯曲的,线性 PCA 会把这些非线性相关当作噪声丢掉。KPCA 的差别是先把原始特征通过核函数映射到高维空间,在高维空间做线性 PCA,再借助核技巧隐式回到原空间。用一句话概括:它能在不显式构造高维坐标的前提下,拿到非线性主成分。

在 kPCA.m 里有个容易被忽略的步骤是核矩阵中心化。实际实现是 K_centered = K - mean(K,1) - mean(K,2) + mean(K(:)),把高维映射后的样本平移到均值零点,这是 PCA 的前提。只对 K 做 eig 而不做中心化,第一特征向量会退化成全 1 向量,降维结果完全错误。我初学这套代码时就踩过这个坑,所以看到 kpcaTest.m 输出第一个特征值明显偏大时,第一反应就是去检查中心化那一步是不是被注释掉了。

3.2 核函数与保留维度:两个直接影响结果的参数

先看调用方式。kpcaTest.m 的流程是:归一化输入、计算核矩阵、调用 kPCA 得到特征向量和特征值。

% 先归一化,再计算核矩阵,顺序不能反 imf_norm = (imf_aligned - mean(imf_aligned)) ./ std(imf_aligned); % 高斯核,sigma=8 是常见起点,需要按数据尺度调整 K = kernel(imf_norm, imf_norm, 8); % kPCA 降维,保留前 12 个核主成分 [coeff, score, latent] = kPCA(K, 12);

逻辑说明:归一化放在计算核矩阵之前,因为高斯核对输入尺度极其敏感。kernel.m 内部调用 distanceMatrix.m 计算样本两两欧氏距离,再套高斯核公式;kPCA.m 对 K 做中心化和特征值分解,score 是降维后的新特征矩阵,latent 是特征值,coeff 是特征向量。

参数说明:sigma=8 不是固定值。数据经过 z-score 归一化后,我把 sigma 在 1、4、8、16 之间扫过,sigma=8 时核矩阵数值相对集中在 [0.05, 1] 区间,特征值衰减平缓,保留前 12 个主成分累计贡献率能到 95% 以上。sigma 怎么选多少有点玄学,但判断标准是确定的:sigma 太大,所有核值都接近 1,样本被压成一点;sigma 太小,核值接近 0,信息全部丢光。判断依据是 latent 的累计贡献率曲线,我一般要求前 8 到 12 个成分覆盖 90% 到 95% 方差,而不是硬编码某个固定维度。

3.3 KPCA_data.mat:作者保存的中间结果不是摆设

资源包里有 KPCA_data.mat 和 emd_data.mat 两个中间结果文件。它们的直接价值是:如果只想复现 LSTM 部分,load 进来就能跳过 EMD 和 KPCA 两步;如果要改参数重新跑,又能拿作者保存的结果做对照,判断自己的前两级实现有没有偏差。

% 直接读作者保存的中间结果,先跑通 LSTM 再回头调前两级 load('emd_data.mat'); load('KPCA_data.mat');

这两个文件的变量名与主程序 EMD_KPCA_LSTM.m 里的命名一致,load 后可以直接覆盖同名变量。我复盘这套代码的习惯是,先 load 结果跑通整条链路,再逐步替换成自己重新生成的 imf_aligned 和 score,每替换一级就对比一次预测误差,这样能精确知道是 EMD 的问题还是 KPCA 的问题。对比时有个细节:如果 score 的符号和作者保存的相反,属正常现象,特征向量的正负是任意的,贡献率和重建误差一致就说明实现是对的。

资源标题里同时出现过 emd-pca-lstm 和 emd-kpca-lstm 两种写法,实际用的是 KPCA。如果你拿普通 PCA 替换 KPCA,在光伏这类非线性相关明显的数据上,前几个主成分的方差贡献率会明显偏低,最终预测 RMSE 通常会高几个百分点。这个差异在云量突变时段尤其明显,因为辐射度与功率的关系在那段时间接近非线性。

4. 最后进 LSTM:主程序 EMD_KPCA_LSTM.m 的结构与训练参数

前两级输出的是干净的低维特征序列,这一级交给 LSTM 做时间动态建模。主程序 EMD_KPCA_LSTM.m 是唯一需要整体理解的入口,它把数据读取、前两级处理、样本构造、网络训练和误差计算全部串起来。资源里的运行结果截图就是从这个主程序跑出来的。

4.1 主流程串联:三个环节如何衔接成端到端预测

EMD_KPCA_LSTM.m 的代码顺序是固定套路:读数据、EMD、KPCA、构造时序样本、训练 LSTM、调用 calc_error.m。从工程角度看,最值得学习的是它把前级输出转换为 LSTM 输入的方式,这决定了预测形态是单步回归还是多步滚动。整套 lstm 模型代码以这个主程序为入口,数据集就是资源里的北半球光伏数据.xlsx,四列特征一列功率。

% 主程序骨架逻辑 X_new = KPCA_data; % 降维后的特征序列 y = origin_data(:, end); % 原始功率标签 % 构造 sequence-to-one 样本:用过去 look_back 步预测当前值 look_back = 24; XTrain = []; YTrain = []; for t = look_back+1 : length(y) XTrain(:, :, end+1) = X_new(t-look_back:t-1, :)'; YTrain(end+1, 1) = y(t); end % 按时间顺序切分训练集和测试集,不随机打乱 cut = round(length(y) * 0.8); XTrain_set = XTrain(:, :, 1:cut); YTrain_set = YTrain(1:cut, :); XTest_set = XTrain(:, :, cut+1:end); YTest_set = YTrain(cut+1:end, :);

逻辑说明:滑窗每次取过去 24 个时间步的多变量特征作为输入,输出是当前时间步的功率值,输入区间是 t-look_back 到 t-1,严格不包含当前时刻,避免信息泄漏。XTrain 的维度是特征数 × look_back × 观测数,与 MATLAB sequenceInputLayer 的输入格式一致。切分按时间顺序进行,时序预测一旦随机打乱,LSTM 学到的依赖关系就废了。

参数说明:look_back=24 在光伏场景里近似过去一天的逐时记录,如果数据是 15 分钟粒度,24 就是 6 小时。这个参数的调整优先级最高,它直接决定模型的时间记忆长度:取值太小学不到日周期,太大则把早先的无用信息也塞进来。我通常以 12、24、48 为三档做简单对比,而不是一上来就用网格搜索。

4.2 LSTM 网络结构与训练参数:先给一组能训练收敛的配置

LSTM 部分采用 sequence-to-one 回归结构,输入多元多步特征序列,输出单值功率。源码里的网络层数不多,我按等价结构说明关键参数。

layers = [ sequenceInputLayer(size(XTrain,1)) % 输入维度=降维后的特征数 lstmLayer(64, 'OutputMode', 'last') % 取最后时间步隐状态 fullyConnectedLayer(32) fullyConnectedLayer(1) regressionLayer]; options = trainingOptions('adam', ... 'MaxEpochs', 300, ... 'MiniBatchSize', 32, ... 'InitialLearnRate', 0.005, ... 'LearnRateSchedule', 'piecewise', ... 'LearnRateDropFactor', 0.5, ... 'LearnRateDropPeriod', 50, ... 'Plots', 'training-progress', ... 'Verbose', 0); net = trainNetwork(XTrain_set, YTrain_set, layers, options);

逻辑说明:lstmLayer 的 OutputMode 设为 'last',表示取最后一个时间步的隐状态接全连接层输出,这是 sequence-to-one 的标准配置。regressionLayer 对应回归任务,如果要做分类,就把它换成 softmaxLayer 加 classificationLayer。

参数说明:InitialLearnRate=0.005 对功率这类数值在几十到几百的量纲是安全的起点;数据量纲上千时降到 0.001。MiniBatchSize 与样本量相关,样本不足 200 时用 16 更稳,样本上千再用 32。MaxEpochs=300 是为了保证收敛,但实际要根据训练曲线提前停,曲线前 50 轮已经平稳,后面跑完只是浪费。注意 YTrain 里的功率没有做归一化时,学习率必须按下限来,防止梯度爆炸。

4.3 calc_error.m 和运行结果截图:怎么判断模型好坏

calc_error.m 是误差计算脚本,运行结果截图是作者已经跑好的训练过程图和预测对比图。拿到代码后我的流程是先跑原图复现,再换自己的数据。calc_error.m 里通常包含三个指标:平均绝对误差(MAE)、均方根误差(RMSE)、决定系数(R²)。MAE 表示预测平均偏多少,RMSE 放大大的误差样本,R² 表示模型解释掉的方差比例。截图里的训练曲线如果显示 loss 前 50 轮快速下降、后 100 轮平稳,说明数据划分和网络初始化都没有问题。

如果只看三个数字,很容易被光伏数据的大基数骗过。功率在几百瓦时,MAE 四十多看上去不错,但突变时段的误差可能超过三百。所以我每次跑完不只是看 calc_error.m 的输出,还会把预测残差按真实功率段分组统计,看高功率段的绝对误差是否失控。这一步没有现成脚本,通常就是几行 MATLAB 的散点图代码,但信息量比单一 RMSE 大得多。

5. 避坑:把 EMD-KPCA-LSTM 跑通的过程中,最容易翻车的五个细节

这套代码链路长,前一级的输出是后一级的输入,错误会被逐级放大。下面五条是我在复现和改参数时实际遇到的,按现象、原因、解决的顺序记录,优先级从前到后。

5.1 IMF 数量不一致导致矩阵拼接直接报维度错误

现象:跑主程序时报错 “Matrix dimensions must agree”,位置在 EMD 分解结果拼接附近。

原因:emd.m 对不同特征序列筛分出的 IMF 个数不同,代码直接横向拼接,左侧矩阵行数不一致。

解决:用 cell 暂存各列 EMD 结果,统一补零或截断到同一长度再拼接。补零只能补在尾部,不能在头部补,否则破坏信号起始相位,后续 LSTM 对序列起点很敏感。如果报错出现在 kpcaTest.m 而不是主程序,多半是 load 的 emd_data.mat 与你重新生成的 imf_all 维度不一致,这类前后级中间文件版本不匹配的问题,清理工作区后重新跑一遍完整链路即可。

5.2 EMD 边界效应让预测头部和尾部抖动特别大

现象:训练误差不错,但预测序列开头几拍和结尾几拍误差明显大于中段。

原因:EMD 在序列两端做包络插值时边界极值点不足,IMF 两端发散,这是经验模态分解的固有毛病。

解决:常见做法是分解前做镜像延拓,预测后裁掉延拓段。另一个工程化选择是在训练 LSTM 时把样本序列前 2% 到 5% 剔除,因为这些样本的输入特征已经被边界失真污染,光伏数据样本量足够时这招最省事。边界效应对短序列尤其致命,数据只有几百个点的时候镜像延拓几乎是必选项;样本超过两千点时可以直接关注测试集后 20% 的稳定段。

5.3 KPCA 没归一化就计算核矩阵,特征值全被量纲大的变量统治

现象:KPCA 降维后第一主成分贡献率接近 99%,但预测精度没有提升甚至下降。

原因:核矩阵的距离项由量纲最大的特征主导。辐射度数值可到八百以上,湿度在 0 到 100,气压数值大但波动小,高斯核计算距离时没有归一化,小尺度变量信息被稀释掉。

解决:先对 imf_aligned 每列做 z-score 归一化再进 kernel.m。这是我复盘这套代码后确认最该最先检查的一步,顺序反了后面所有参数都是错的。

注意:KPCA 阶段的归一化统计量要固定下来,测试集沿用训练集算出的 mean 和 std,不能在全序列上重新计算,否则就是数据泄漏。

还有一个隐蔽版本:有人先对原特征用 PCA,再对 IMF 用 KPCA,两套降维统计量混用。要么全程 KPCA,要么全程 PCA,混用会让训练和测试的特征空间不一致。

5.4 LSTM 训练损失不下降,或者 loss 直接变成 NaN

现象:训练曲线前几十轮完全不动,或某次迭代后 loss 变 NaN。

原因:最常见两个。一是 YTrain 功率值量纲大,配合 InitialLearnRate=0.01 梯度爆炸;二是输入里有 NaN,EMD 插值或补零过程引入 inf,LSTM 反向传播直接算崩。

解决:把输入和标签做过归一化,功率也可以用 z-score 或 min-max 到 0 到 1;学习率从 0.005 起步。训练前用 isnan 和 isinf 逐列扫描 imf_aligned、XTrain、YTrain,有异常值先反查是 EMD 还是对齐步骤引入的。另外不要忽略数据集较大时设备显存不足的问题,MATLAB 会静默降级到 CPU 训练,loss 曲线变慢但不报错,看到训练时间异常长先查设备。

5.5 预测曲线比真实值滞后一拍,形状对但相位错位

现象:预测曲线和真实曲线几乎完全平行,但整体右移一个时间步,RMSE 很高。

原因:滑窗构造样本时时间对齐出错。常见的是输入窗口用了未来数据,或者 t 从 1 开始而不是从 look_back+1 开始,导致序列整体偏移。

解决:严格按 4.1 的循环写法,输入取 X_new(t-look_back:t-1, :),输出取 y(t),t 从 look_back+1 起步。数据如果是逐时且列本身按时间倒序,还要先做排序检查,这类隐藏问题最容易让人误判成模型能力不行。滞后一拍的另一种来源是预测值被做了平滑后处理,如果输出端接了移动平均,相位滞后是正常的,此时要看原始预测和滤波后的两组曲线,别把滤波伪影当模型问题。

6. 验证与进阶:用三个误差指标和一个副图,把预测结果做实

6.1 从 calc_error.m 出发,搭一组可对比的验收流程

calc_error.m 算出的 MAE、RMSE、R² 只看任何一项都会被骗:MAE 低但 RMSE 高,说明多数样本准但有极大误差;R² 高但突变段偏差大,说明晴天准确度拉高了整体。我固定的验收两件套是:先跑 calc_error.m 拿指标,再画真实功率对比预测功率的时间序列副图,图上标出晴天、多云、阴雨三段。多云段残差如果超过晴天段三倍,模型不可用,哪怕 R² 到了 0.95。

6.2 进阶:把单步预测改成多步滚动预测

如果生产环境要求提前 4 小时预测,单步结构不够。常见做法是滚动预测:模型输出的功率值作为下一拍的已知信息拼回输入窗口,每次前进一个时间步,循环 4 次。这里的坑是,EMD 和 KPCA 不能对滚动窗口重新计算,因为分解和降维的基准是训练集的统计量。正确做法是在训练前完成一次 EMD 加 KPCA,得到完整降维序列,再在其中做滑窗。滚动预测的前一两步通常准,越往后误差累积越快,所以要每隔一步就把最新实测值插进窗口,而不是只用预测值迭代到底。

这套源码是 MATLAB 环境,迁移到 Python 时注意对应关系:sequenceInputLayer 对应把输入整成 (seq_len, batch, input_size),lstmLayer 对应 nn.LSTM,OutputMode='last' 对应只取最后一步输出接全连接。结构迁移不难,真正要搬过去的是 EMD 和 KPCA 统计量固定的逻辑。

我从拆这套源码里学到的最大习惯是:跑通之前先对齐中间产物。那以后我每次拿到这类“先分解、再降维、再进网络”的多维时间序列预测代码,第一件事永远是分别 load 或者单独运行 emd_data.mat 与 KPCA_data.mat 的生成段,对比自己的中间结果与作者保存的中间结果是否一致,一致才往下走 LSTM。这一步省掉的排查时间,远比想象的少。希望帮到你。完整的源程序包在 146 号资源里,说明.txt 和运行结果截图都在,配套博文解读可以对着看,按上面的坑过一遍,你跑出来的曲线不会比作者截图差。

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