公司动态

光伏发电量预测与多模型对比:CNN-LSTM、GRU等深度学习时序建模实践

📅 2026/8/28 3:06:58
光伏发电量预测与多模型对比:CNN-LSTM、GRU等深度学习时序建模实践
简介时间序列预测是深度学习在工业与能源领域的重要应用方向其核心在于从历史数据中捕捉周期规律与随机波动。针对光伏发电量预测这类典型回归任务模型需要同时处理强周期性、天气突变带来的非平稳性以及长时间依赖。LSTM能有效建模时序关系但对局部特征敏感度不足CNN擅长提取局部模式却缺乏长程记忆能力。因此CNN-LSTM、CNN-GRU等组合结构通过卷积层提取局部特征、循环层建模依赖关系成为提升预测精度的常用方案。多模型对比不仅有助于验证不同结构在具体场景下的适用性也为工程选型和论文论证提供了量化依据。本文围绕数据预处理、滑窗构造、归一化方法及五个模型的设计与训练要点展开结合测试集指标解读为光伏发电预测及相关时序项目的落地与答辩提供完整参考。1. 光伏发电量预测为什么毕设选这个方向以及多模型对比的价值在哪我见过太多做毕设的同学一上来就急着跑代码结果模型训练完了论文里除了几张曲线图什么都写不出来。光伏发电量预测这个选题之所以值得做是因为它天然具备一个完整的深度学习落地方案闭环有公开数据集、有明确的时间序列预测任务、有可以横向对比的多种网络结构而且成果能用具体的评价指标量化——这对于毕设评审来说几乎是送分题级别的结构。但也是因为做的人多很多人的毕设最后都长一个样用LSTM预测一下画个图写个结论。而这篇项目标题里最值得注意的是它一口气做了五个模型——CNN-LSTM、CNN-GRU、GRU、LSTM-transform、LSTM——并且在测试集上做了完整的对比。我自己的经验是多模型对比从来不是为了凑工作量而是为了回答一个核心问题在光伏发电量这个具体场景下不同的时序建模思路到底谁更有效是卷积结构提取局部特征更关键还是循环结构的长期记忆更重要还是两者结合才是最优解这个答案恰恰就是毕业论文里最有含金量的讨论章节。从技术角度看光伏发电量预测是一个非常典型的时间序列回归问题。输入是历史发电量序列以及可能的外部因素温度、辐照度、湿度、风速等输出是未来一段时间比如下一个小时、未来24小时的发电量。它和股票预测、交通流量预测的差异在于光伏发电受物理规律约束明显——白天有电晚上没有晴天多云差别大这导致数据里既存在强周期成分又有因天气突变带来的随机波动。所以这个任务对模型的要求相当刁钻既要能捕捉日内、日间的周期性规律又要能对短时突变做出响应还要在长时间跨度上不丢记忆。单一的LSTM能建模时序依赖但对局部特征的敏感度不够单纯的CNN擅长提取局部模式却又缺乏跨时间步的长程记忆能力。于是CNN-LSTM、CNN-GRU这类组合结构应运而生——先让卷积层像筛子一样扫描输入序列提取出有意义的局部特征再把压缩后的特征序列交给循环网络去做时序建模。这也解释了为什么毕设项目里要做多个模型对比而不是只跑一个最好的模型没有对比你无法证明组合结构的优势到底来自哪里有了对比你才能告诉评审老师我选这个结构是有依据的我用五个模型验证了它在测试集上确实更稳定。下面我把整个项目的实现链路拆开讲从数据预处理到每个模型的内部结构再到训练时容易踩的坑最后是结果怎么读、怎么写进论文里。2. 数据集与预处理预测效果的天花板其实在进模型之前就定了2.1 数据长什么样决定了你能做什么样的预测做光伏预测手头数据通常分两类一类是光伏电站的实际运行数据比如某地区某光伏电站的发电量记录时间分辨率可能是15分钟一条、30分钟一条或1小时一条另一类是气象数据包括总辐照度GHI、水平面散射辐照度DHI、直接辐照度DNI、环境温度、组件温度、湿度、风速、风向、气压等。这个项目里的数据从标题看是自带的通常在解压后会看到一个CSV文件列名大概是time、power或者generation、temperature、irradiance、humidity、wind_speed之类的。我拿到这种数据第一步做的永远不是建模而是先花半小时把数据看穿时间段跨度是多长是一年、半年还是几个月光伏数据至少要覆盖一个季度以上才有意义如果只有一两个星期模型学到的周期规律会非常脆弱。时间分辨率是多少如果原始数据是15分钟一条而你需要预测小时级那就涉及重采样。有没有夜间数据夜间发电量是0这部分数据保留会让模型学到晚上就是0这个简单规则但也容易让模型对白天的小幅波动不敏感。到底保不保留取决于你要预测的时间粒度和目标。很多项目会直接保留因为剔除夜间数据反而会破坏序列的连续性。有没有缺失值和异常值光伏数据常见的异常有传感器故障导致的连续0值、辐照度突跳、发电量超过装机容量不可能的情况。这些不清理模型会被带偏。说实话我在做这类项目时发现数据里的坑远比模型结构多。有一次我拿到一份数据白天发电量在晴天、多云、雨天三种天气下波动极大若直接用原始序列训练模型基本是看天吃饭收敛很慢。后来我把时间特征拆出来小时、星期、是否节假日同时把滞后24小时的发电量加进特征效果立刻上了一个台阶——这种特征工程的经验比调一周的网络结构参数都管用。2.2 滑窗样本构造时间序列预测的核心数据组织方式时间序列数据不能像图像那样直接扔给模型我们需要把它切成过去一段 - 预测未来一段的样本对。这个过去一段就叫滑窗window未来一段就是预测长度horizon。假设原始序列长度是N输入窗口是L个小时预测步长是H个小时那么能构造出的样本数量大约是 N - L - H 1。比如你有一整年8760个小时的数据输入窗口设为72小时3天预测未来1小时那大约能构造出8600多个样本。对深度学习来说这个数据量在光伏预测里算是够用的。这里有一个关键参数选择要说清楚窗口长度到底取多少太短比如6小时模型看不到完整的日内周期无法区分上午和下午的变化趋势。太长比如168小时即一周模型需要学习的信息量太大容易把早期信息当作噪声。我常用的是72小时到120小时之间。因为光伏预测最基本的周期是24小时窗口至少要覆盖2~3个完整周期模型才能识别昨天这个时候前天这个时候的发电量模式。如果预测目标是未来24小时的发电量曲线那窗口通常在48小时以上。滑窗构造的代码逻辑并不复杂。直接用Python循环也能写但效率偏低。我习惯先通过数组索引的偏移生成样本索引矩阵再一次性批量切片速度会快很多。也可以用TensorFlow的tf.keras.preprocessing.timeseries_dataset_from_array或PyTorch的Dataset自定义类来做。import numpy as np def create_sequences(data, input_steps, output_steps): X, y [], [] for i in range(len(data) - input_steps - output_steps 1): X.append(data[i:iinput_steps, :]) # 输入窗口内的所有特征 y.append(data[iinput_steps:iinput_stepsoutput_steps, 0]) # 预测目标取发电量列 return np.array(X), np.array(y)注意代码里y取的是第0列也就是发电量。如果你要预测多步比如未来24小时y的shape就是(batch_size, 24)如果只预测下一步就是(batch_size, 1)。这个设计差异对最终模型输出的Dense层维度有直接影响后面讲模型结构时还会提到。2.3 归一化不做这一步LSTM的收敛速度会让你怀疑人生光伏发电量的数值范围通常在0到几百KW甚至MW级别而辐照度可能是0到1000W/m²温度是-10到40℃不同特征的量纲差异极大。LSTM内部用的是sigmoid和tanh激活函数它们的输出范围是0~1和-1~1如果输入数据数值范围悬殊梯度计算很容易震荡甚至爆炸。最稳妥的做法是使用MinMaxScaler归一化到[0,1]区间公式是x_scaled (x - x_min) / (x_max - x_min)sklearn里直接有现成的MinMaxScalerfit时只在训练集上做然后transform应用到验证集和测试集。这一点极其重要很多人图省事把全部数据一起fit了这属于信息泄露——测试集的极值参与了训练数据的缩放评测指标会虚高论文里被评审抓到就是硬伤。from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler(feature_range(0, 1)) scaled_data scaler.fit_transform(raw_data) # 只用训练部分fit还要注意预测出的结果也是归一化后的数值在评估指标和画图前需要 inverse_transform 还原成真实发电量单位这样比较MAE/RMSE才有物理意义。3. 五个模型结构拆解CNN-LSTM、CNN-GRU、GRU、LSTM-transform、LSTM各自的定位与参数配置3.1 标准LSTM一切对比的基准线LSTM长短期记忆网络是1997年就被提出的结构到今天依然是时间序列预测里最稳的baseline之一。它通过输入门、遗忘门、输出门三个门控机制对信息进行有选择的保留和丢弃解决了传统RNN的梯度消失问题。在这个项目里LSTM作为基准模型非常合适。配置上我通常这样设置第一层LSTMunits64return_sequencesTrue输入shape为(input_steps, n_features)第二层LSTMunits32return_sequencesFalse只输出最后一个时间步第三层Dropout(0.2)防止过拟合第四层Dense(24)或Dense(1)取决于预测步长为什么是两层LSTM而不是一层单层LSTM对简单序列够用但光伏数据本身的时序模式是周期 随机扰动两层结构让上层LSTM有机会对下层提取到的时间特征做更高阶的抽象。两层之后再堆第三层收益就很有限了反而增加训练时间。3.2 GRU更轻量的循环网络选择GRU是LSTM的简化变体只有更新门和重置门参数更少训练更快在很多任务上效果和LSTM持平甚至更好。它的优势在于在小数据集上不容易过拟合收敛速度更快。对于毕设这种规模的项目GRU往往是最省心的选择——你不用花太多时间调参跑出来的结果通常都说得过去。结构上和LSTM几乎一一对应只需把LSTM层换成GRU层model.add(GRU(64, return_sequencesTrue)) model.add(GRU(32, return_sequencesFalse))3.3 CNN-LSTM先用卷积层看局部模式再用LSTM建模长程依赖这是这个项目里最值得讲的模型也是我推荐你在论文里重点分析的对象。CNN-LSTM的思路是把输入的时间序列先经过一维卷积层Conv1D扫描。卷积核相当于一个滑动窗口每次卷积操作会提取窗口内的局部模式——比如过去3小时发电量的变化斜率、辐照度和发电量的同步关系。多个卷积核并行扫描就能得到多个不同的特征映射。然后这些特征映射作为LSTM的输入让LSTM从这些局部特征中再学习时间上的先后依赖关系。打个比方CNN像是负责看细节的观察员它把每个局部片段中最有代表性的信息提炼出来LSTM则是记笔记的规划员它把这些观察按时间顺序串联起来形成一个完整的理解。一个典型的CNN-LSTM层配置model.add(Conv1D(filters64, kernel_size3, activationrelu, paddingsame)) model.add(MaxPooling1D(pool_size2)) model.add(Conv1D(filters32, kernel_size3, activationrelu, paddingsame)) model.add(LSTM(64, return_sequencesTrue)) model.add(LSTM(32))kernel_size3表示每次看3个时间步filters64表示用64个不同的卷积核提取特征。MaxPooling1D降低序列长度减少LSTM的计算量。这里要注意如果原始输入是72步经过pool_size2后变成36步这36步再进LSTM模型处理速度明显变快。3.4 CNN-GRU同样的组合思路但用GRU替代LSTMCNN-GRU和CNN-LSTM的区别只在于后半段用GRU替换了LSTM。既然之前说过GRU参数更少、训练更快那么CNN-GRU在理论上应该比CNN-LSTM更轻量。但在光伏预测场景里两者的差距通常不大关键看数据本身的复杂程度和序列长度。我见过的情况是如果数据里包含多种天气状态的突变CNN-LSTM的遗忘门机制可能更有优势——它能更精细地控制忘记什么、记住什么而如果数据相对平稳CNN-GRU以更少的参数就能达到同级别效果。选择哪个作为最终模型最硬核的做法是直接跑对比实验用测试集指标说话。这也是为什么同时做这两个模型是有意义的——它们是很好的对照组。3.5 LSTM-transform对输入序列做变换再交给LSTM建模从标题看这个项目里还有一个LSTM-transform模型。我的理解是它可能指在输入进LSTM之前对原始序列做某种变换处理比如差分、傅里叶变换、小波变换或者简单地对特征做加权/重构然后再进行LSTM建模。我在类似项目里会这样实现对原始序列做一阶差分消除趋势性让序列更平稳再把差分序列输入LSTM预测结果再做累加还原。这种先变换、后建模、再反变换的三段式结构对非平稳光伏序列其实挺有用。但要注意差分会让噪声也放大如果原始数据采集质量一般差分后反而可能更糟。另一种理解是transformer-like结构——但既然模型名叫LSTM-transform而不是Transformer我更倾向于认为是序列变换 LSTM。在论文里描述时我会明确写出自己采用的变换方式不要含糊。3.6 五个模型参数汇总对比模型第一层第二层输出层参数量级特点LSTMLSTM(64, seqTrue)LSTM(32)Dense中等最经典的baseline稳GRUGRU(64, seqTrue)GRU(32)Dense较小参数少、收敛快CNN-LSTMConv1D(64,k3)MaxPoolLSTM(64) LSTM(32)Dense中等局部特征长程依赖CNN-GRUConv1D(64,k3)MaxPoolGRU(64) GRU(32)Dense中等偏小两全但更轻量LSTM-transform变换层reshapeLSTM(64)Dense中等需要预定义变换逻辑4. 训练过程和调优那些不跑一遍就不会发现的坑4.1 优化器、学习率和损失函数怎么选光伏发电量预测是回归任务损失函数最常用的还是MSE均方误差。MSE对大误差的惩罚更强所以模型会优先拟合那些偏离较大的点——这对发电量这种有明显峰值的序列来说其实是有利的因为峰值预测准了整体曲线才像样。如果想更看重绝对误差可以尝试MAE但一般MSE效果更稳。优化器我个人推荐Adam初期收敛快、对学习率不敏感。初始学习率1e-3起步同时加一个ReduceLROnPlateau回调验证集loss连续5个epoch不降就把学习率乘以0.5。这在光伏数据上特别管用——模型常常在前20轮快速下降后面进入平台期适当降低学习率能再进一步。4.2 早停法别让模型把噪声也背下来深度学习训练的一大风险是过拟合训练集loss一直降验证集loss却开始回升。光伏数据本身噪声很大天气突变、传感器误差模型很容易把个别异常点当作规律记住。我一般设置EarlyStopping(monitorval_loss, patience10)连续10个epoch验证集不改善就停止训练并恢复最佳权重。跑的时候盯着训练曲线如果训练loss和验证loss差距越拉越大基本就可以确定是过拟合了这时候优先加大Dropout比例或者减小模型容量减少units。4.3 随机种子不做固定你连结果都复现不了这点我必须特别提醒PyTorch和TensorFlow默认都有随机性同样的代码跑两次结果可能差不少。毕设里如果连自己的实验都复现不了答辩时老师让你当场跑一遍结果和论文不一样那就非常尴尬。固定随机种子的标准操作import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) torch.cuda.manual_seed_all(seed)在训练前调用一次之后所有实验都用同一个seed模型对比才有意义——否则你无法分辨效果提升是模型结构带来的还是随机性带来的。4.4 一个真实的踩坑案例验证集loss正常测试集却崩了我在做类似项目时遇到过一件让人抓狂的事LSTM在验证集上loss是0.01效果很好结果在测试集上一跑发现预测曲线直接比真实值滞后了整整一天。图一画出来预测和实际曲线形状几乎一样但平移了24小时。排查过程花了一天。最后发现原因在于滑窗构造样本时没有做time-based切分我是随机打乱所有样本后划分训练集和测试集的导致测试集里混进了和训练集时间相邻的样本。模型相当于见过测试集前一天的发电量所以它学到的策略是复制前一天而一到真正未见过的连续时段这个策略就失效了。正确的做法是按时间顺序划分数据集。比如前80%的时间段作为训练集中间10%作为验证集最后10%作为测试集。千万不要洗牌后再切分。这个坑在几乎所有时间序列预测任务里都存在写进论文的实验设置一节是很好的加分项。5. 测试集结果对比与解读指标、图表和论文写作的完整逻辑5.1 三个指标看懂模型好坏光伏预测用的最多的回归指标是这三个我在论文里建议全部列出MAE平均绝对误差误差绝对值的平均值单位是kW直观但大误差影响被平均掉了。RMSE均方根误差误差平方后再开方对大误差更敏感。RMSE和MAE的比值如果明显大于1说明存在少量预测很差的点。R²决定系数越接近1越好表示模型解释了百分之多少的方差。用于判断模型的整体拟合程度。假设测试集上五个模型的结果如下表格式数据具体数值以实际运行项目为准模型MAERMSER²LSTM12.34 kW18.56 kW0.932GRU11.82 kW17.90 kW0.938CNN-LSTM9.67 kW14.23 kW0.961CNN-GRU10.12 kW15.04 kW0.955LSTM-transform11.05 kW16.78 kW0.944从这种典型结果里可以读出几层信息组合结构CNN-LSTM、CNN-GRU明显优于单一循环结构说明光伏发电量序列里的局部特征对预测非常关键卷积提特征这一步是有效的。CNN-LSTM略优于CNN-GRU说明在数据存在较强随机扰动时LSTM更精细的门控机制确实有帮助。GRU整体不输LSTM但参数量更少、训练更快属于性价比之选。LSTM-transform介于两者之间说明输入变换有效果但不如卷积特征提取的收益大。5.2 画图时的专业细节除了指标表预测曲线对比图几乎是毕设必备。我强烈建议画三张图第一张是完整测试集的时序对比图真实值和五个模型的预测值叠在一起。因为测集时间段可能很长曲线会非常拥挤最好选连续3到5天的片段放大展示。第二张是某一天的对比图把晴天、多云、阴天三种典型天气各挑一天单独画三组子图。这样能直观看出模型在天气突变时容易在哪里失手。第三张是误差分布图可以画误差直方图或箱线图。通常你会发现误差不是正态分布而是右偏——大部分时候预测很好偶尔某几个点偏差极大。这些点对应的往往是云层突然飘过导致发电量骤降模型没能及时反应。论文里写结果分析时别只停留在CNN-LSTM的MAE最低要解释为什么。我通常这样写单一LSTM面对发电量的剧烈波动时容易受近几个时间步的异常值干扰而CNN-LSTM先通过卷积层平滑局部突变并提取稳定的形态特征再输入LSTM捕捉长程趋势因此对突变有更强的抗干扰能力。这就是结果原因的专业写法。6. 代码运行环境与复现建议帮你在自己的机器上把项目跑起来6.1 环境配置清单拿到项目源代码后第一步肯定是把环境跑通。一个能直接运行的组合是Python 3.83.10问题也不大TensorFlow 2.10或PyTorch 1.13看项目源码用哪个框架NumPy、Pandas、Matplotlib、scikit-learn如果有GPUCUDA和cuDNN版本要匹配好没有GPU用CPU跑小规模数据也完全可行只是训练时间会长一些装好环境后不要急着跑模型先看目录结构。一般完整的毕设项目会包含数据文件CSV格式数据预处理脚本.py或.ipynb模型定义文件训练脚本预测和画图脚本README说明6.2 按什么顺序跑代码才算高效我会按这个顺序操作每一行输出都对照预期结果先跑数据加载和可视化代码确认能正确画出原始发电量曲线。这一步是为了验证环境没问题、数据路径是对的。再跑数据预处理脚本看一眼生成的样本shape是否和预期一致比如X的shape是不是(samples, 72, n_features)。单模型训练一次用比较小的epoch数比如5轮跑通完整流程确认没有维度错误。全量训练。如果是CPU建议把epoch调到50左右、配合早停耐心等如果是GPU100-200轮也就几分钟到十几分钟的事。最后跑对比和画图代码生成结果表格和曲线图。6.3 常见运行错误排查我自己跑这类项目时遇到最多的报错就是shape不匹配。这里给几个高频问题的排查思路Conv1D的输入需要3D张量(batch, steps, features)如果你构造的X是2D的先检查有没有在reshape。LSTM对输入shape的要求和Conv1D一样。如果遇到Dense层输入维度和标签不匹配检查你的y到底预测的是单步还是多步对应修改最后一层Dense的units。预测结果画图时记得先inverse_transform再画否则纵坐标范围不对看起来就是一条很怪的曲线。如果训练loss是NaN大概率是学习率太大或者数据里包含NaN值。先用np.isnan或者df.isnull().sum()检查数据然后调小学习率。7. 个人实践经验总结如何让这个项目在答辩中更强最后分享一点我的个人体会。这个项目最大的优势是模型对比维度多但要想拿高分光有对比还不够你得知道每个模型发挥好坏背后的原因。我的建议是额外做两组小实验来强化你的分析第一做特征消融实验。比如把辐照度去掉重新训练CNN-LSTM看指标下降多少。这能证明你使用的每个特征都是有价值的。第二做窗口长度敏感性实验。把输入窗口从24小时改成48、72、120小时看模型表现的变化趋势。这能说明你选的窗口长度是经过验证的而不是拍脑袋定的。这两组实验不需要改太多代码却能让你的论文从我跑了个模型上升到我系统研究了这个问题。答辩时老师问你为什么要用72小时而不是48小时你直接甩出一张窗口敏感性测试的表格这个问题的回答就已经赢了。另外强烈建议把测试集预测结果和真实值以CSV格式导出保存答辩演示时如果现场环境有问题也可以基于保存的结果画图不至于因为环境问题卡壳。代码库的清晰注释也尽量保留任何一个变量名、一个字都不要是乱写的因为你不知道评审老师会不会现场翻阅你的源代码。整个项目跑下来我对光伏发电量预测这个任务的体会是它并不需要多么高深的网络结构创新真正拉开差距的是对数据的理解深度、对模型对比实验设计的完整性、以及对每一个参数选择的合理解释。能做到这三点无论用什么模型这个毕设都已经站在了优秀线以上。本文还有配套的精品资源点击获取