☰
MATLAB CNN-LSTM风电功率预测代码:从数据预处理到多步预测实战
2026/10/31 19:44:30 网站建设 项目流程

简介:本资源面向风电功率预测方向的学习者与研究人员,提供基于卷积长短期神经网络(CNN-LSTM)的完整MATLAB实现方案,同时包含纯LSTM对比模型,便于理解卷积特征提取与长短期记忆网络在时序预测中的协同机制。压缩包共45个文件,约7.94MB,以m脚本文件为主,涵盖网络搭建、训练、前向传播与梯度更新等核心模块,另含mat数据文件、xlsx四季数据表及少量图片与备份文件,数据齐全可直接运行。资源已有254人学习下载,代码注释清晰,适合本科及以上层次用于课程设计、毕业设计或科研扩展。读者可据此掌握CNN-LSTM风电功率预测的完整流程,包括数据归一化、网络初始化、训练调参与结果可视化,并可通过混淆矩阵、ROC曲线等脚本评估模型性能,为后续创新改进提供扎实基础。

1. 风电功率预测这套 CNN-LSTM 代码,为什么值得你花一个下午跑通

如果你做过风电场的功率预测,大概经历过这种场景:SCADA 里攒了几年的风速、风向、温度、功率数据,用 LSTM 单独跑,短期还行,一到风速骤变或者限电时段就明显跟不上;换成纯 CNN,又抓不住功率随时间缓慢爬升的趋势。这套基于 MATLAB 的 CNN-LSTM 风电功率预测代码,解决的正是这个「空间特征 + 时间依赖」两头都要顾的问题——CNN 负责从多变量气象序列里抽局部耦合特征,LSTM 接着建模时序演化,最后回归出功率值。资源包里cnnlstm.m、cnnff_lstm.m、lstmfun.m是主干,datan.mat、maydata.mat、四季.xlsx是配套数据,zscore.m、normalize.m、expand.m负责预处理,plotroc.m、myAUC.m、confusion_matrix1.m做评估可视化。适合本科毕设、研究生课题起步,或者手里有数据想快速搭个可扩展基线的从业者。下面按「是什么 → 怎么跑 → 坑在哪 → 怎么改」的顺序拆开讲。

2. 从数据到网络:CNN-LSTM 预测链路怎么搭起来

2.1 为什么是 CNN 接 LSTM,而不是二选一

风电功率序列有两个绕不开的特性。一是多变量之间的局部耦合:风速、风向、温度、气压在同一时间段内互相影响,某个时刻的功率往往由邻近几个时间步的多个变量共同决定,这种「局部感受野内的特征组合」正是卷积核擅长的事。二是长程时序依赖:今天下午的功率走势和昨天夜间、甚至前两天的风况都有关系,LSTM 的门控结构就是为这种跨时间步记忆设计的。

把两者串起来,常见做法是 CNN 做特征提取器,把原始多变量序列先卷成一组更紧凑的特征图,再把这组特征按时间顺序喂给 LSTM。这样 LSTM 的输入不再是原始的几个变量,而是被卷积抽象过的特征,既降了维度,又保留了局部模式。纯 LSTM 的短板在于它把每个时间步的所有变量当成独立输入,局部耦合靠隐状态隐式学,样本少时容易欠拟合;纯 CNN 的短板是感受野有限,抓不住长周期趋势。这套代码走的是串联路线,cnnff_lstm.m里能看到前向传播时卷积层输出直接接 LSTM 输入的逻辑。

选型上还要注意一点:风电功率预测的数据量通常不算大,几年 SCADA 按分钟或小时采样,去掉停机、限电、异常点后有效样本可能就几千到几万条。这种规模下,网络不宜堆太深,卷积层两三层、LSTM 一层到两层是比较稳的配置,再深就容易过拟合,除非你有跨风场的大数据集。

2.2 数据预处理:zscore 与归一化到底该用哪个

拿到datan.mat和四季.xlsx之后,第一件事不是急着喂网络,而是把缺失值、异常值、量纲统一处理掉。风电数据里常见的脏数据包括:风速传感器结冰导致的恒定值、限电时段的功率截断、通信中断造成的连续缺失。这些不处理,后面训练 loss 会莫名其妙地震荡。

代码包里给了zscore.m和normalize.m两个预处理脚本,用途不同,别混用。zscore 是标准化,把每个变量变成均值 0、方差 1 的分布,适合风速、温度这类近似正态的连续量;normalize 一般做 min-max 归一化,压到 [0,1],适合功率这种有明确上下界的量。下面是我一般会走的预处理流程:

% 读取原始数据,假设 datan.mat 里是 [样本数 x 变量数] 的矩阵 load('datan.mat'); % 载入数据,变量名按实际文件调整 raw = data; % 假设原始矩阵变量名为 data % 1. 缺失值处理:线性插值填补,连续缺失超过阈值的整段丢弃 missIdx = isnan(raw); raw = fillmissing(raw, 'linear', 1); % 按列线性插值 % 2. 异常值处理:3-sigma 之外的点用中位数替换 for j = 1:size(raw,2) col = raw(:,j); mu = mean(col, 'omitnan'); sd = std(col, 'omitnan'); outIdx = abs(col - mu) > 3*sd; col(outIdx) = median(col, 'omitnan'); raw(:,j) = col; end % 3. 标准化:输入变量用 zscore,功率目标用 min-max inputVars = raw(:, 1:end-1); target = raw(:, end); inputNorm = zscore(inputVars); % 均值0方差1 targetNorm = (target - min(target)) / (max(target) - min(target)); % 压到[0,1] % 4. 构造监督学习样本:用前 T 个时间步预测下一步 T = 24; % 时间窗长度,按采样频率调整 [X, Y] = buildSequence(inputNorm, targetNorm, T);

这段逻辑里,fillmissing的linear参数是按列线性插值,适合短缺口;如果某段连续缺失超过几十个点,插值会引入虚假趋势,我一般直接把这整段样本剔除。3-sigma 替换用中位数而不是均值,是因为均值本身会被异常值拉偏。T=24是时间窗,如果数据是小时级,24 代表用过去一天预测下一小时;如果是 15 分钟级,24 就是过去 6 小时。这个参数直接决定 LSTM 能看多远,太小抓不住趋势,太大引入噪声且训练变慢,通常从 12 到 48 之间试。

buildSequence不是包里现成的,需要自己写,核心就是把滑窗切出来的[样本数 x T x 变量数]三维张量整理好,这是 CNN-LSTM 输入的常见形状。注意 MATLAB 里维度顺序和 Python 不同,卷积层默认按[高度 宽度 通道]理解,喂数据前要确认cnnsetup.m里对输入维度的假设,否则会报维度不匹配。

2.3 网络搭建与训练:cnnsetup、cnntrain 的参数怎么定

主干训练入口是cnntrain.m,网络结构在cnnsetup.m里定义。这套代码沿用了类似 DeepLearnToolbox 的组织方式,卷积层、下采样层、LSTM 层、全连接输出层逐层配置。下面是一个可跑的配置骨架:

% cnnsetup 里定义网络结构,这里给出关键参数 cnn.layers = { struct('type', 'i') % 输入层 struct('type', 'c', 'outputmaps', 6, 'kernelsize', 3) % 卷积层:6个特征图,3x3核 struct('type', 's', 'scale', 2) % 下采样层:2x2池化 struct('type', 'c', 'outputmaps', 12, 'kernelsize', 3) % 第二卷积层 struct('type', 's', 'scale', 2) struct('type', 'lstm', 'hidden', 32) % LSTM层:32个隐藏单元 struct('type', 'f') % 全连接输出 }; cnn.learningRate = 0.01; % 学习率,太大震荡太小收敛慢 cnn.batchsize = 32; % 批大小,样本少时可降到16 cnn.numepochs = 100; % 训练轮数,配合早停 cnn.momentum = 0.9; % 动量项,加速收敛

outputmaps是卷积核个数,也就是输出特征图通道数,第一层 6、第二层 12 是轻量配置,数据量大可以翻倍。kernelsize是卷积核尺寸,3x3 在时序特征提取里够用,再大感受野宽但参数多。LSTM 的hidden是隐藏单元数,32 偏保守,样本上万可以试 64 或 128,但要注意显存和训练时间。learningRate是最需要调的,0.01 是起点,loss 震荡就减半,收敛太慢就适当加,但别超过 0.1。batchsize和numepochs要配合早停用,包里没内置早停的话,我一般手动在验证集 loss 连续几轮不降时停。

训练时cnntrain.m会调用cnnff_lstm.m做前向、cnnbp.m做反向传播。这里有个容易忽略的点:LSTM 的反向传播比普通层复杂,涉及门控的梯度链,如果发现梯度爆炸(loss 变 NaN),先检查学习率,再考虑加梯度裁剪。包里sigm.m、tanh_opt.m是激活函数,sigmrnd.m是随机采样相关,rnd.m是随机数封装,这些一般不用改,除非你要换激活函数做对比实验。

2.4 评估与可视化:ROC、AUC 和混淆矩阵怎么读

预测任务做完,plotroc.m、myAUC.m、confusion_matrix1.m负责出评估图。风电功率预测严格说是回归问题,但很多论文会把它转成分类来做(比如按功率区间分档),这时候 ROC 和混淆矩阵才有意义。如果你做的是纯回归,重点看 RMSE、MAE、MAPE,ROC 那套可以留着做分类对比实验。

% 回归评估:计算 RMSE、MAE、MAPE pred = predicts(:); % 模型输出,反归一化后 true = targetTest(:); % 真实值 rmse = sqrt(mean((pred - true).^2)); mae = mean(abs(pred - true)); mape = mean(abs((pred - true) ./ true)) * 100; % 分类评估:把功率分档后画 ROC thresholds = [0.2 0.4 0.6 0.8]; % 功率分档阈值 labels = discretize(true, [0 thresholds 1]); [predLabels, scores] = classifyPower(pred, thresholds); plotroc(labels, scores); % 调用包里的绘图函数 auc = myAUC(labels, scores);

myAUC.m算的是曲线下面积,越接近 1 分类越好,0.5 等于瞎猜。confusion_matrix1.m出混淆矩阵,重点看对角线,以及有没有某一类被系统性误判。风电里常见的是低功率段和高功率段容易混,因为这两段样本相对少,中间段样本多,模型会偏向中间。遇到这种情况,要么对样本加权,要么做分层采样。

3. 避坑与排查:这套代码跑不起来时先看这几条

3.1 报「维度不匹配」或「索引超出范围」

现象:运行cnntrain.m直接报维度错误,或者cnnff_lstm.m里矩阵乘法对不上。原因通常是输入张量的维度顺序和网络假设不一致。MATLAB 的卷积层习惯[高度 宽度 通道 样本数],而很多人整理数据时按[样本数 时间步 变量数]排,直接喂进去就错位。解决:在cnnsetup.m里确认输入层对维度的定义,用permute把数据转成网络期望的顺序,比如X = permute(X, [2 3 1 4]),具体顺序看代码里卷积核的滑动方向。

3.2 loss 一直是 NaN 或者不下降

现象:训练几轮后 loss 变 NaN,或者从头到尾几乎不动。原因有两个常见来源:一是学习率太大,LSTM 的梯度链长,容易爆炸;二是数据没归一化,量纲差异大导致梯度尺度失衡。解决:先把learningRate降到 0.001 试,确认数据都过了 zscore 或 min-max,检查有没有残留的 NaN 或 Inf。如果还不行,在反向传播里加梯度裁剪,把梯度范数限制在某个阈值内。

3.3 预测结果是一条直线,完全不跟随真实功率

现象:画出来的预测曲线几乎是平的,或者只输出均值。原因通常是模型没学到东西,可能是 LSTM 隐藏单元太少、训练轮数不够,也可能是输入特征里根本没有有效信息(比如把功率本身也当输入了,造成信息泄漏又学不到映射)。解决:先检查输入变量里有没有混入目标功率,确认时间窗T设置合理,再把 LSTM 隐藏单元从 32 加到 64 试,训练轮数加到 200 并观察验证集 loss 是否下降。

3.4 训练集表现好,测试集一塌糊涂

现象:训练集 RMSE 很低,测试集 RMSE 高出一大截。原因:过拟合,网络容量相对数据量太大,或者训练集和测试集划分时没有按时间顺序切,造成未来信息泄漏。解决:风电时序数据必须按时间先后切分,不能随机打乱,常见做法是前 70% 训练、中间 15% 验证、后 15% 测试。同时减小网络规模,或者加 dropout、L2 正则。包里如果有expand.m做数据增强,注意增强后的样本不能跨训练测试边界。

3.5 四季数据混在一起训练效果反而差

现象:把四季.xlsx里春夏秋冬的数据合并训练,效果不如单季。原因:不同季节的风况分布差异大,春季大风、夏季小风、冬季寒潮,混在一起模型要同时拟合几种分布,容易顾此失彼。解决:要么按季节分别建模,要么在输入里加入季节标识或月份作为额外特征,让模型有条件地区分。我一般先分季跑一遍看基线,再决定要不要合并。

4. 进阶玩法:把 CNN-LSTM 改成多步预测和滚动更新

跑通单步预测只是起点,实际风电场要的是未来几小时甚至一天的功率曲线,这就涉及多步预测。最直接的做法是递归预测:用模型预测下一步,把预测值当输入再预测下下步。但递归会累积误差,几步之后曲线就飘了。更稳的是直接多输出:把输出层改成同时输出未来 N 步,训练时标签就是[t+1, t+2, ..., t+N]的功率序列。

% 直接多步预测:修改输出层和标签构造 N = 6; % 预测未来6步 % 标签构造:每个样本对应未来N步的功率 Y = zeros(size(targetNorm,1) - T - N + 1, N); for i = 1:size(Y,1) Y(i,:) = targetNorm(i+T : i+T+N-1)'; end % 输出层节点数改为 N,损失函数用多输出MSE cnn.layers{end} = struct('type', 'f', 'outputsize', N);

这样模型一次前向就出未来 N 步,避免了递归误差累积。代价是输出维度变高,需要更多样本才能训稳。另一个进阶方向是滚动更新:风电场数据是持续产生的,模型上线后要定期用新数据微调,而不是一劳永逸。常见做法是每周或每月用最近的数据增量训练几轮,学习率调小,避免把旧知识冲掉。

验证多步预测效果时,别只看整体 RMSE,要分步看:t+1 的误差通常最小,t+6 会明显变大。如果某一步误差突然跳升,说明那个时间尺度上有模型没抓到的模式,可以针对性加特征或调整时间窗。我一般会把分步误差画成柱状图,一眼就能看出模型在哪个预测步长上最吃力。

还有个容易被忽略的技巧:把数值天气预报(NWP)的预测风速作为额外输入。SCADA 的历史功率是「已经发生」的,而 NWP 给的是「即将发生」的气象条件,两者结合能显著提升超前预测的精度。包里expand.m如果支持加列,可以直接把 NWP 字段拼到输入矩阵后面,注意同样要做归一化。

从那以后我每次拿到新的时序预测代码,都强制先跑一遍「只换数据不换结构」的基线,确认数据管道没问题,再动网络结构。这套 CNN-LSTM 代码的价值不在于它多先进,而在于它把预处理、网络、评估的链路都摆出来了,你能看清每一步在干什么,改起来有抓手。希望帮到你。

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

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

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

立即咨询