简介这是一套基于LSTM的短期光伏预测算法Python源码与配套数据集面向计算机、人工智能、数据科学与新能源等相关专业的学生、科研新手及企业算法工程师可用于课程设计、毕设项目或生产场景的前期验证。压缩包共11个文件总大小约3.89MB其中6个Jupyter Notebook为核心代码涵盖光伏预测单变量建模、园区数据实测、负荷预测LSTM及基于规则集的储能模拟等实验另包含1份Excel数据集、1个Python脚本、多张结果图片和README说明文档。源码均经过运行测试完整覆盖数据清洗、LSTM网络设计、模型训练、误差评估与可视化等环节读者可直接仿照流程复现实验并替换为本地气象或电站数据进一步拓展。目前已有1023人学习下载适合希望快速掌握时序预测算法实现、缩短开发验证周期的学习者。1. 基于LSTM的短期光伏预测是算法包更是一套完整的落地路径围绕「基于LSTM的短期光伏预测算法python源码数据集.zip」这套内容你能拿到的不仅是模型代码和一份功率历史数据而是一条完整的技术路径从光伏电站历史发电功率出发用LSTM神经网络建模预测未来1至6小时的输出功率。这套方案直接服务于微网调度、并网功率申报和储能充放电策略优化。适合谁刚接触时间序列预测的Python开发者以及需要为电站落地预测模块的电气工程师。本文按数据准备、网络设计、训练评估和排错顺序逐层展开把每一步的选型理由、可复现参数和踩坑位置讲清楚。2. 为什么光伏功率预测绕不开LSTM时序特征与数据集的正确打开方式2.1 LSTM凭什么比BP和线性回归更适合作光伏功率光伏功率序列本质上是一个强非平稳、强周期、带天气突变的时间序列。白天跟随日照强度呈单峰曲线夜间接近零阴雨天气整体被压低云层遮挡时又出现高频波动。把这样的序列交给普通BP神经网络或线性回归模型默认每个时刻是独立的学不出「当前功率与后半小时功率在统计上高度相关」这个关键性质。LSTM作为循环神经网络的一种内部有输入门、遗忘门、输出门和细胞状态可以把前面若干时刻的信息按权重保留或丢弃天然适配这类带记忆效应的序列建模。光伏功率的另一个特征是多时间尺度叠加分钟级的云层波动、小时级的天气变化、日级的太阳高度角变化。LSTM的细胞状态能同时记住不同时间尺度的信息这是它的第二个优势。如果用Transformer或TCN这类模型也不是不行但LSTM在中小数据集上的收敛速度更快调参量级也小得多。对一个长度可能只有一年的电站数据集LSTM是性价比最高的选择。2.2 读懂数据集结构时间戳、辐照度、温度与功率的对应关系拿到「源码数据集.zip」里的内容第一件事不是跑代码而是确认数据集里每个字段的单位与时间粒度。常见做法是CSV或Excel存储一行一个时刻列包括时间戳、环境温度、组件温度、水平面辐照度、风速、风向以及逆变器输出的有功功率。时间粒度通常是15分钟也有1分钟或1小时的。15分钟粒度意味着一天96个点按1小时预测窗口来看一条样本就是24个输入点。我的经验是先打印前5行、检查时间戳是否有跳跃。光伏数据最常见的问题是夜间辐照度为0时功率出现小幅非零值以及多云天气下辐照度与功率曲线毛刺明显。若数据集里已经有归一化处理过的字段务必确认归一化参数是否随包提供。缺失归一化参数的话反归一化时会多花一晚上做事后校准。2.3 数据清洗与归一化让模型从数据里学出规律的第一步光伏预测的建模管线在PyTorch或Keras里并不复杂但清洗这一步逃不掉。我的流程是时间戳转成datetime索引并按时间排序对功率列做缺失值处理——连续缺失超过10个点就用前一天同时刻补齐少于10个点用线性插值剔除辐照度异常高但功率为0的矛盾记录最后把数值列做min-max归一化到[0,1]。辐照度和功率的物理上限相对稳定min-max比Z-score更直观反变换也简单。import pandas as pd import numpy as np df pd.read_csv(pv_power_data.csv, parse_dates[timestamp], index_coltimestamp) df df.sort_index() # 缺失值处理10个点以内线性插值超过10个点保留NaN df[power] df[power].interpolate(limit10, limit_areainside) for col in [irradiance, temperature]: df[col] df[col].interpolate(limit10, limit_areainside) # 异常值处理辐照度大于200W/m²但功率接近0的记录剔除 mask (df[irradiance] 200) (df[power] 1) df.loc[mask, power] np.nan df[power] df[power].interpolate() # 归一化到[0,1]功率单独一个scaler from sklearn.preprocessing import MinMaxScaler scaler_power MinMaxScaler() scaler_weather MinMaxScaler() df[power_scaled] scaler_power.fit_transform(df[[power]]) df[[irradiance_scaled, temperature_scaled]] scaler_weather.fit_transform( df[[irradiance, temperature]] )这段代码的精髓在于「分列归一化」功率单独用一个scaler天气特征共用另一个scaler。原因是反归一化时只需要对功率列做inverse_transform天气特征在预测时通常不参与反变换。如果所有列共用一个scaler反变换后的数值范围会被辐照度量级污染。limit10的含义是连续缺失超过10个点不插值保留NaN供后续按天填充我这里简化成整列插值实际工程中建议按天分组处理避免跨天插值把夜间零点与白天首点连成一条虚假斜坡。提示min-max归一化要求训练集、验证集、测试集使用同一组scaler参数。先用训练集fit再用同一个对象transform验证集和测试集否则测试集的最大最小值会泄露进归一化过程直接拉低验证集上的RMSE——这是新手最容易犯的时序数据「未来信息泄露」。3. 搭建LSTM短期光伏预测模型网络结构与训练流程3.1 输入窗口与预测步长怎么定24步输入预测1小时窗口与步长这个问题在光伏预测里最需要动手实验没有统一答案。常见做法是「24步输入、1步输出」15分钟一个点24个输入点代表过去6小时输出下一个15分钟的功率。如果电站要求的是未来1小时的功率可以在输出层接一个4单元的全连接层同时输出4个点再取平均值也可以串联做滚动预测。我的选择是先用「24进1出」把单步效果跑通再扩展成多步。窗口大小影响模型的记忆长度。窗口太短模型看不到一个完整的上午功率上升过程窗口太长引入大量夜间接近0的冗余序列训练速度变慢且不一定提升精度。我一般会在{12, 24, 48, 72}里做一组对照实验用验证集RMSE选择。24是多数场景表现稳定的起点但如果你所在地区夏季傍晚常有雷暴模型需要在窗口里看到足够长的辐照度下降过程这时48步可能更合适。3.2 网络结构设计从单层LSTM到多层叠加的取舍单层LSTM在光伏预测的大多数场景已经够用因为功率序列的时序依赖并不像自然语言那样复杂。常见做法是「一层LSTM加一层全连接」LSTM输出维度设为64全连接层输出功率预测值。当数据量超过一年、或者想加入辐照度预报值作为额外输入时可以升级成双层LSTM第二层返回序列的最后一个时间步再接全连接层。双层结构能捕获更高阶的时序模式但参数量翻倍在小数据集上更容易过拟合。import torch import torch.nn as nn class PVLSTM(nn.Module): def __init__(self, input_dim, hidden_dim64, num_layers1, output_dim1, dropout0.2): super().__init__() self.lstm nn.LSTM(input_dim, hidden_dim, num_layers, batch_firstTrue, dropoutdropout) self.fc nn.Linear(hidden_dim, output_dim) self.dropout nn.Dropout(dropout) def forward(self, x): out, _ self.lstm(x) # 取序列最后一个时间步的输出 out out[:, -1, :] out self.dropout(out) return self.fc(out)这里的关键在out[:, -1, :]LSTM在batch_firstTrue下返回的形状是(batch, seq_len, hidden_dim)我们只需要最后一个时间步的隐藏状态作为整个输入序列的浓缩表示。nn.LSTM的dropout参数只在num_layers1时生效所以单层结构里额外加了一个Dropout层测试时PyTorch会自动关闭它不需要手动处理。3.3 训练流程与关键超参数学习率、batch size、epochs光伏预测的训练流程和一般回归问题没有本质区别但三个超参数影响很大。学习率我用Adam优化器配1e-3起步前几个epoch观察损失下降速率若震荡明显就降到3e-4。batch size设64数据量为一年96×365约35000条样本时64能在梯度稳定性和训练速度之间取得平衡。epochs跑50轮配合早停机制在验证损失连续10轮不降时终止避免无效训练。from torch.utils.data import DataLoader, TensorDataset import torch.optim as optim def make_sequences(data, weather_cols, power_col, input_len24, pred_len1): x, y [], [] for i in range(len(data) - input_len - pred_len 1): x.append(data.iloc[i:iinput_len][weather_cols [power_col]].values) y.append(data.iloc[iinput_len:iinput_lenpred_len][power_col].values) return np.array(x), np.array(y) X, y make_sequences(df, [irradiance_scaled, temperature_scaled], power_scaled) split_idx int(len(X) * 0.8) X_train, X_val X[:split_idx], X[split_idx:] y_train, y_val y[:split_idx], y[split_idx:] train_loader DataLoader(TensorDataset(torch.tensor(X_train).float(), torch.tensor(y_train).float()), batch_size64, shuffleTrue) model PVLSTM(input_dimX.shape[2], hidden_dim64, num_layers1) optimizer optim.Adam(model.parameters(), lr1e-3) loss_fn nn.MSELoss() for epoch in range(50): model.train() total_loss 0 for xb, yb in train_loader: optimizer.zero_grad() pred model(xb) loss loss_fn(pred, yb) loss.backward() optimizer.step() total_loss loss.item() # 验证集评估 model.eval() with torch.no_grad(): val_pred model(torch.tensor(X_val).float()) val_loss loss_fn(val_pred, torch.tensor(y_val).float()).item() if (epoch 1) % 10 0: print(fepoch {epoch1}, train loss {total_loss/len(train_loader):.5f}, fval loss {val_loss:.5f})注意shuffleTrue只用在训练集Loader上验证集保持原始时序顺序。随机打乱能打破相邻样本的强相关性防止模型把上一条样本的答案背进梯度验证集一旦打乱评估结果就没有意义——你无法判断模型预测的是下一个时刻还是在记忆同一个batch里出现过的相邻时刻。特征里把上一时刻功率也包进去是光伏预测里最实用的一招功率序列的自相关性远高于辐照度和温度模型有了「惯性」可依预测稳定性会明显提升。4. 训练跑通后如何评估预测效果指标、可视化与误差分析4.1 RMSE、MAE、MAPE三个指标各自在说什么光伏功率预测的评估不太依赖R²因为序列的强周期性会让R²虚高。我更看重RMSE、MAE和MAPE的组合。RMSE对大误差敏感能反映预测是否在个别时段猛烈偏差MAE反映平均绝对误差的稳定水平MAPE把误差除以真实值但在夜间功率接近0的时刻会爆炸——真实值很小或为0分母趋近0一个不大的绝对误差也可能被放大成几百个百分点的百分比。所以MAPE要限定在功率大于额定功率5%的时段计算这是光伏预报里的通用做法。import numpy as np def evaluate_metrics(y_true, y_pred, rated_power1000.0, min_threshold0.05): # 仅统计功率大于额定功率5%的时段 mask y_true rated_power * min_threshold yt, yp y_true[mask], y_pred[mask] rmse np.sqrt(np.mean((yt - yp) ** 2)) mae np.mean(np.abs(yt - yp)) mape np.mean(np.abs((yt - yp) / (yt 1e-6))) * 100 return rmse, mae, mape pred val_pred.numpy().flatten() true y_val.flatten() rmse, mae, mape evaluate_metrics(true, pred, rated_power500.0) print(fRMSE: {rmse:.2f} kW, MAE: {mae:.2f} kW, MAPE: {mape:.2f}%)代码里的yt 1e-6是防分母为零的保险丝但真正的保护是前面的mask只有真实功率高于额定功率5%的时段才参与MAPE统计。额定功率从数据集描述里获取如果没有可以取功率列99.9%分位作为近似值。4.2 预测曲线与真实曲线的对齐看趋势还是看数值训练完第一版模型我最先画的是连续三天的预测值和真实值叠线图挑一天晴、一天阴、一天多云。晴天的单峰曲线如果拟合得好说明模型学到了日照的周期规律阴雨天如果紧跟真实值的小幅波动说明模型真的在用辐照度特征而不只是把昨天的功率平移过来。真正需要警惕的是预测曲线整体滞后真实曲线——滞后意味着模型在「背答案」它的预测实质上是前几个时刻真实功率的加权平均根本没有学到天气特征与功率的因果联系。绘图这一步用matplotlib就够把归一化后的预测值反变换回实际功率单位再画图上的刻度才对业务有意义。你会发现晴天的误差集中在功率爬坡段的拐点附近这几乎是光伏预测的共性后续章节会专门讲。这个可视化过程不是只看一眼而是要形成习惯每次改完模型结构或特征组合先看图再比较指标指标相同但曲线形态不同的情况经常发生。4.3 评价集的切分方式按时间切别随机切这是时序预测和普通机器学习任务最不一样的地方。做分类任务时随机切分训练集验证集没问题但光伏功率序列有着极强的日周期和季节性随机切分会让训练集里混入未来信息——模型的记忆能力本质上是在背序列片段验证集里随机出现的某一天可能在训练集里有一个几乎相同的相邻时段。正确做法是按时间顺序切分前80%训练后20%验证。进一步的做法是「滚动切分」用去年前11个月训练、12月验证模型要预测的就是它从未见过的时间段这更接近真实运行场景。再进一步可以在训练集内部再切一个小的early stopping集用来决定早停时机。这个内部验证集同样按时间顺序从训练集尾部切出10%左右避免和测试集重叠。早停集决定「什么时候停止训练」测试集决定「模型到底行不行」两个职责分开才不会被一份数据带偏。5. 短期光伏预测踩坑记录五个让模型翻车的真实问题5.1 现象预测曲线整体滞后真实曲线半小时晴天的预测曲线比真实功率曲线晚到约两步上午的上升段和下午的下降段都平移了半小时。原因几乎都是输入窗口里没有辐照度特征或者辐照度特征权重太低。模型发现只用历史功率就能以「把上一步功率稍微衰减一下」的方式降低损失根本不需要去学光照和功率的关系。解决方法是把辐照度、温度等天气特征重新加回输入特征列并检查归一化后的特征量级——辐照度归一化后范围是[0,1]功率也是[0,1]两者在线性尺度上才是可比的。5.2 现象夜间功率预测值恒为负归一化到[0,1]后反变换夜间预测值出现-20kW左右的负功率这在物理上不允许。排查下来是训练集里包含了夜间真实的小幅负功率记录——部分逆变器夜间待机功耗表现为负值模型把负值作为正常模式学习了。解决方法是数据清洗阶段把夜间功率统一置0标准是辐照度低于10W/m²时的功率全部归零相当于用物理常识约束模型输出范围。如果模型在白天偶尔输出负值还可以在预测后加一步np.maximum(pred, 0)兜底。5.3 现象训练损失下降但验证损失上升这是标准的过拟合信号。光伏数据集通常只有一两年时长样本量并不算大LSTM的隐层维度如果设到128甚至256模型容量足够把训练集背下来。解决思路按优先级排序先增加Dropout到0.3再减小hidden_dim到32或48然后检查验证集是否包含模型没见过的高温极端日——如果验证集恰好覆盖了7月最热的一周模型确实难以外推这不完全是模型的错需要把验证集切分得更均匀。5.4 现象阴雨天预测误差突然飙升晴天的RMSE可能只有3%到了连续阴雨天误差会翻三倍。原因是模型在训练数据里见过大量晴天样本而阴天样本占比低模型对低辐照度区间的拟合能力不足。解决思路是数据增强与样本加权——对阴天时段样本重复采样或在损失函数里按样本天气类型赋予更高权重。另一个常见做法是把「天气类型」作为类别特征传入模型前提是数据集里已有这一列否则需要在数据清洗阶段用辐照度阈值划分阴晴。5.5 现象归一化反变换后数值对不上训练时用的scaler和预测时用的scaler不是同一个对象或验证集的归一化用了重新fit的scaler导致反变换后的预测值整体偏移。这是时序预测最隐蔽的黑匣子坑RMSE在归一化空间里计算时数值很低反变换到物理单位后完全对不上。解决方法是把三个scaler对象用joblib.dump保存训练、验证、预测三阶段加载同一个文件并且评估指标全部在物理单位上计算归一化空间里的RMSE只作训练过程中的调试信号。6. 进阶从单步预测到多步滚动预测的实践技巧如果业务要求预测未来4小时的功率曲线第一步输出后无法直接拿到后续时刻的真值需要用滚动预测把上一步的预测值拼到输入序列末尾去掉最旧的一个时间步再输入模型得到下一步。这个循环在Python里就是for循环加np.concatenate但有一个关键细节需要处理。def rolling_predict(model, input_seq, steps16, weather_futureNone): model.eval() seq input_seq.copy() # shape: (input_len, feature_dim) preds [] with torch.no_grad(): for i in range(steps): x torch.tensor(seq[None, :, :]).float() p model(x).item() preds.append(p) # 构造下一步输入天气特征用预报值功率用上一步预测值 new_row seq[-1].copy() if weather_future is not None: new_row[:2] weather_future[i] # 辐照度、温度预报值 new_row[-1] p # 功率用预测值接上 seq np.vstack([seq[1:], new_row]) return np.array(preds)代码里seq np.vstack([seq[1:], new_row])就是滑动窗口的核心——丢弃最早的时间步把最新预测值补到末尾保持窗口长度不变。weather_future参数让外部辐照度预报值参与滚动这是预测精度提升最明显的一步光伏功率的自相关性在高频段较弱天气预报能打断纯自回归的误差累积。注意滚动预测的误差会随时间步长累积第16步的置信区间比第1步宽得多做调度决策时主要参考前4至8步。验证多步预测效果时我习惯引入「预测合格率」这一额外指标——预测值与真实值偏差在额定功率10%以内的时段占比电网侧考核经常用这个指标。建议在5月和10月的典型天气月各取一周做滚动预测评估比全年平均指标更有参考价值。最后分享一个我的教训刚拿到类似源码包时我直接把训练好的模型拿去做第二天的预测结果那天下雨预测值是对应晴天形状的完整单峰完全翻车。后来才明白短期光伏预测模型在日常场景相对稳定但极端天气下的泛化仍然是模型的边界。要提高这类场景的表现需要引入数值天气预报数据作为特征并保留足够长的历史数据来覆盖多种天气模式。这一点不是模型玄学而是训练数据分布与预测场景的匹配问题。如果你正在规划光伏预测模块的落地先把数据清洗和时序切分做扎实再上LSTM这条路能少走一半弯路。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?