简介:本资源面向计算机、电子信息工程、数学等专业的大学生及算法初学者,提供一套基于CNN-GRU卷积神经网络与门控循环单元的多变量多步光伏功率预测完整方案,可用于课程设计、期末大作业与毕业设计。压缩包共7个文件,约2.46MB,包含Matlab源码、数据集与结果图,其中m文件负责模型搭建与误差计算,mat与xlsx文件存放训练数据与网络参数,png图片展示预测曲线与误差对比,txt文件则对MAPE指标在真实功率含零时失效的情况作出说明。代码采用参数化编程,参数修改方便,思路清晰且注释详细,运行环境为Matlab2023及以上。读者可据此获得从数据读取、模型训练到多步预测评估的完整流程,并输出预测图、误差图以及R2、MAE、MAPE、MSE、RMSE等评价指标,便于快速复现与二次开发。目前已有102人学习下载。
1. 光伏功率预测为什么盯上 CNN-GRU:从标题拆出可复现的路线
光伏电站最头疼的不是发电,而是"说好的功率去哪了"。云层飘过来又飘走,功率曲线像心电图一样抖,调度端要的是未来一段时间逐点的功率值,不是一句"今天大概多云"。这就是多变量多步预测的真实场景:输入辐照度、温度、湿度、风速等多个气象变量,输出未来若干个时间步的功率序列。标题里的CNN-GRU正是为这件事设计的组合结构——卷积神经网络先在一维时间窗上扫出局部波动模式,门控循环单元再把这些特征按时间顺序串起来记忆长期依赖,最后一次性吐出多步结果。整套东西用Matlab的深度学习工具箱就能跑通,不需要额外装 Python 环境。这篇笔记面向两类人:一类是刚拿到一份 CNN-GRU 光伏预测源码、想搞懂每行在干什么的新手;另一类是跑过 LSTM 单步、想升级到多变量多步、却卡在数据组织和输出维度上的熟手。我会把数据怎么切、网络怎么搭、参数怎么调、坑在哪,按能直接抄作业的方式讲清楚。
2. 多变量多步预测的数据组织:先把输入输出张量想明白
2.1 为什么光伏预测必须做多变量而不是单变量
单变量预测只拿历史功率推未来功率,本质是在拟合一条自回归曲线。光伏功率的物理驱动是辐照度,辐照度又被云、温度、湿度影响,只喂功率等于让模型盲猜天气。多变量输入把辐照度、组件温度、环境温度、湿度、风速这些可测的协变量一起送进去,模型才有机会在云层到来前从辐照度骤降里提前捕捉到功率要跌的信号。
但多变量不是变量越多越好。我一般先做一轮相关性筛选:算每个气象变量与功率的皮尔逊相关系数,低于 0.2 的直接扔掉,否则噪声变量会稀释 CNN 卷积核学到的局部模式。常见保留的是辐照度、环境温度、组件温度、湿度这四个,风速在平原电站相关性弱可以砍掉。
2.2 滑动窗口切出 CNN-GRU 要的三维张量
CNN-GRU 在 Matlab 里吃的是序列数据,但 CNN 那层需要的是"通道×时间"的二维结构,GRU 那层需要的是"时间×特征"的序列。多变量多步的关键在于:输入窗口长度和预测步长是两个独立参数,别混。
假设采样间隔 15 分钟,我想用过去 8 个点(2 小时)预测未来 4 个点(1 小时),输入就是 8×4 的矩阵(8 个时间步,4 个变量),输出是 4×1 的功率向量。滑动窗口按步长 1 在整段数据上平移,切出成千上万个样本。
% 多变量多步滑动窗口切分 % data: T×F 矩阵,T 为总时间步,F 为变量数(最后一列为功率) % inStep: 输入窗口长度 outStep: 预测步长 function [X, Y] = makeWindow(data, inStep, outStep) T = size(data, 1); F = size(data, 2); numSample = T - inStep - outStep + 1; X = zeros(inStep, F, 1, numSample); % CNN 要的 4-D 张量 Y = zeros(outStep, numSample); % 多步输出 for i = 1:numSample X(:, :, 1, i) = data(i : i+inStep-1, :); Y(:, i) = data(i+inStep : i+inStep+outStep-1, end); end end逻辑说明:Matlab 的trainNetwork对含卷积层的序列网络要求输入是SSCB格式(空间×空间×通道×批量),所以这里把单通道塞进第三维,第四维是样本数。Y保持outStep×numSample,对应sequenceOutput回归。
参数说明:inStep太短学不到日周期,太长会引入无关的昨日信息,光伏一般取 4 到 16 个点;outStep按调度需求定,1 小时用 4,4 小时用 16。切完记得检查numSample是否为正,数据不足会静默出错。
2.3 归一化必须按变量分别做,且只能用训练集统计量
这是翻车重灾区。辐照度量纲是 0 到 1200,温度是 -10 到 40,湿度是 0 到 100,不归一化的话 CNN 卷积核会被大数值变量主导。正确做法是对每个变量单独做 min-max 或 z-score,而且均值和方差只能从训练集算,再套用到验证集和测试集。
% 按列归一化,统计量仅来自训练段 trainLen = round(0.7 * size(data, 1)); mu = mean(data(1:trainLen, :)); sigma = std(data(1:trainLen, :)); sigma(sigma == 0) = 1; % 防止常数列除零 dataNorm = (data - mu) ./ sigma;逻辑说明:先切出训练段算统计量,再对全量数据做变换,保证验证和测试不泄露未来信息。参数说明:trainLen取 70% 是常见划分,光伏有强季节性,最好按整月切而不是随机切,否则相邻点泄漏会让指标虚高。
3. CNN-GRU 网络在 Matlab 里怎么搭:层序、参数与训练选项
3.1 一维卷积层到底在时间窗上扫什么
一维卷积神经网络处理时序时,卷积核沿时间轴滑动,每个核学一种局部波形。光伏场景里,有的核会学"辐照度快速上升沿",有的学"午后骤降沿",这些局部模式正是功率突变的先兆。卷积核数量决定能学多少种模式,核大小决定看多宽的时间邻域。
常见做法是堆两层一维卷积,第一层核小一点抓细节,第二层核大一点抓组合模式,每层后面接批归一化和 ReLU。池化层要慎用:最大池化会丢时间分辨率,对多步预测不利,我一般用步长为 1 的卷积代替下采样,或者干脆不池化。
3.2 把卷积输出接进 GRU 的维度对齐
CNN 输出是SSCB,GRU 要的是CBT(通道×批量×时间)。中间必须做维度重排,否则训练直接报维度不匹配。Matlab 里用sequenceFoldingLayer和sequenceUnfoldingLayer把序列折叠成图像再展开,这是官方推荐做法。
layers = [ sequenceInputLayer(F, 'Name', 'in') sequenceFoldingLayer('Name', 'fold') convolution1dLayer(3, 32, 'Padding', 'same', 'Name', 'conv1') batchNormalizationLayer('Name', 'bn1') reluLayer('Name', 'relu1') convolution1dLayer(5, 64, 'Padding', 'same', 'Name', 'conv2') batchNormalizationLayer('Name', 'bn2') reluLayer('Name', 'relu2') sequenceUnfoldingLayer('Name', 'unfold') flattenLayer('Name', 'flat') gruLayer(64, 'OutputMode', 'sequence', 'Name', 'gru') dropoutLayer(0.2, 'Name', 'drop') fullyConnectedLayer(outStep, 'Name', 'fc') regressionLayer('Name', 'out') ]; layers = connectLayers(layers, 'fold/miniBatchSize', 'unfold/miniBatchSize');逻辑说明:sequenceFoldingLayer把CBT折成SSCB给卷积用,sequenceUnfoldingLayer再展回序列,flattenLayer把空间维压掉只留通道和时间。connectLayers那行是把折叠层记录的批量大小传给展开层,漏了这行会报未定义。
参数说明:conv1核大小 3、32 个核,适合抓短时波动;conv2核大小 5、64 个核,抓更宽的模式。gruLayer的 64 是隐藏单元数,光伏数据量在几千样本时 64 够用,上万样本可以加到 128。dropoutLayer的 0.2 是防过拟合,样本少可以提到 0.3。fullyConnectedLayer(outStep)直接输出多步,这是多步预测的关键——一次前向出全部步长,而不是滚动预测。
3.3 训练选项里三个必调参数
options = trainingOptions('adam', ... 'MaxEpochs', 100, ... 'MiniBatchSize', 64, ... 'InitialLearnRate', 1e-3, ... 'LearnRateSchedule', 'piecewise', ... 'LearnRateDropPeriod', 30, ... 'LearnRateDropFactor', 0.5, ... 'ValidationData', {XVal, YVal}, ... 'ValidationFrequency', 20, ... 'Shuffle', 'every-epoch', ... 'Plots', 'training-progress', ... 'Verbose', false);逻辑说明:Adam 对时序回归收敛稳,学习率用分段衰减,每 30 轮砍半,避免后期震荡。Shuffle设为每轮打乱,防止样本顺序带来的伪周期。
参数说明:MiniBatchSize64 是显存和梯度稳定性的折中,样本少就降到 32。MaxEpochs100 配合验证集早停,验证损失连续不降就手动停。InitialLearnRate1e-3 是起点,若前几轮损失不降就降到 1e-4。
4. 训练完怎么验证:指标、残差与多步误差累积
4.1 反归一化后再算指标,别在归一化空间自欺
预测出来是归一化值,必须用训练集的均值和方差反变换回真实功率再算 RMSE、MAE、MAPE。在归一化空间算的指标没有物理意义,也没法和别人比。
YPred = predict(net, XTest); YPred = YPred .* sigma(end) + mu(end); % 只反归一化功率列 YTrue = YTest .* sigma(end) + mu(end); rmse = sqrt(mean((YPred - YTrue).^2, 'all')); mae = mean(abs(YPred - YTrue), 'all');逻辑说明:sigma(end)和mu(end)是功率列的统计量,因为输出只有功率。'all'把多步和多样本一起算,若想看每步误差就按行算。
参数说明:RMSE 对尖峰敏感,光伏爬坡时段误差大是正常的;MAPE 在夜间功率接近零时会爆炸,建议只在功率大于额定 5% 的时段算。
4.2 多步预测的误差随步长怎么涨
多步预测的固有问题是误差累积:第 4 步的输入里含第 3 步的估计误差。画一张"步长 vs RMSE"的曲线,能看出模型在几步内还靠谱。光伏 15 分钟粒度下,前 4 步(1 小时)RMSE 通常还能接受,到 16 步(4 小时)误差可能翻倍。如果曲线在第 2 步就陡增,说明 GRU 记忆没学好或者输入窗口太短。
4.3 残差里藏着数据问题
把残差按时间画出来,如果发现每天正午残差系统性偏正,多半是训练集里晴天样本占比过高,模型没见过足够多的多云天。如果残差在某个辐照度区间集中偏大,说明该区间样本稀疏。这些不是调网络能解决的,得回去补数据或做样本加权。
5. 避坑与排查:光伏 CNN-GRU 多步预测的五个血泪坑
5.1 损失不降反升,检查归一化和学习率
现象:训练前几轮 loss 就飙到 NaN 或持续上升。原因:要么归一化用了全量数据统计量导致泄漏,要么学习率 1e-3 对当前数据太大。解决:确认统计量只来自训练段,把InitialLearnRate降到 1e-4,加'GradientThreshold', 1做梯度裁剪。
5.2 验证集很好测试集崩,多半是随机切分惹的祸
现象:验证 RMSE 0.02,测试 RMSE 0.15。原因:随机打乱切分让相邻时间点同时进了训练和测试,模型记住了而非学会了。解决:按时间顺序切分,训练段、验证段、测试段首尾相接,中间不留重叠。
5.3 多步输出全是同一个值,检查输出层和损失
现象:预测的 4 步功率几乎相等。原因:fullyConnectedLayer输出维度写成了 1,或者OutputMode设成了last导致 GRU 只吐最后一步。解决:确认fullyConnectedLayer(outStep),GRU 的OutputMode为'sequence',regressionLayer接在最后。
5.4 训练极慢,卷积层通道数开太大
现象:一个 epoch 要十几分钟。原因:conv通道数 128、256 往上堆,参数量爆炸。解决:光伏数据特征维度低,32 和 64 足够,先跑通再考虑加宽。另外把数据转成single精度,Matlab 默认 double 会慢一倍。
5.5 夜间功率全预测成零,MAPE 报错
现象:MAPE 算出 Inf 或 NaN。原因:夜间真实功率为零,分母为零。解决:算 MAPE 时过滤掉功率小于额定 5% 的点,或者改用 RMSE 为主指标。夜间样本可以保留训练但别参与 MAPE 统计。
6. 让多步预测再稳一点:两个我常用的进阶技巧
第一个技巧是把预测步长做成可配置的多输出头。与其为 1 小时和 4 小时各训一个网络,不如在fullyConnectedLayer前接一个小的全连接,输出maxStep维,训练时按需截取前outStep维算损失。这样一套权重能服务多个调度周期,省去重复训练。实现上把Y统一 padding 到maxStep,损失里对超出部分置零权重即可。
第二个技巧是用持续预测做基线对比。持续预测就是"未来等于当前值",光伏在超短期(15 分钟内)持续预测其实很强。如果你的 CNN-GRU 在 1 步预测上打不过持续预测,说明网络没学到东西,别急着上测试集,回去查数据泄漏或归一化。我一般把持续预测的 RMSE 当作及格线,模型必须明显低于它才有上线价值。
验证方法上,除了看整体 RMSE,我会单独抽出几段"云过境"的剧烈波动时段,肉眼看预测曲线有没有跟上拐点。整体指标好看但拐点全丢的模型,调度端是不敢用的。下面这张表是我常用的验收清单:
| 检查项 | 合格标准 | 不合格时的动作 |
|---|---|---|
| 1 步 RMSE | 低于持续预测 | 查数据泄漏、归一化 |
| 4 步 RMSE | 不超过 1 步的 1.5 倍 | 加长输入窗口、加 GRU 单元 |
| 爬坡时段跟踪 | 拐点延迟小于 2 步 | 减小卷积核、去掉池化 |
| 残差分布 | 无明显时段偏置 | 补多云样本、样本加权 |
| 训练耗时 | 单 epoch 分钟级 | 降通道数、转 single |
这套流程我踩过最深的坑是早期用随机切分,测试指标漂亮得不像话,上线后一塌糊涂,后来强制按时间切分才把真实水平暴露出来。做光伏预测,数据组织的严谨程度比网络结构的花哨程度重要得多。希望帮到你。
本文还有配套的精品资源,点击获取