机器学习驱动的光伏功率预测与Python源码实战
简介面向毕业设计、课程设计以及机器学习初学者的Python光伏功率预测项目源码包旨在帮助读者从零实现一套完整、可运行的光伏功率预测流程。项目以历史功率与环境数据为基础通过Python脚本依次完成数据清洗、特征构建、模型训练与未来功率预测并配套独立的训练集与测试集便于检验模型的泛化能力也方便用户单独调试。包内共19个文件包括8个CSV数据文件历史功率与气象数据、4个Python脚本数据处理与模型预测、1个Jupyter Notebook交互演示、1个说明文档和1个Word任务书等覆盖数据处理、模型预测、结果保存与任务说明压缩包大小仅4.64MB体积轻量、便于快速部署目前已有65人学习或下载使用。源码逐段附有详细注释即使新手也能看懂既能作为毕业设计或期末大作业的完整课题方案也可帮助读者掌握回归建模、时间序列预测等机器学习应用方法下载后稍作配置即可运行。1. 光伏功率预测为什么机器学习方案比物理模型更接地气光伏功率预测本质上是一个时间序列回归问题给定历史功率、气象观测和数值天气预报数据预测未来 15 分钟到 72 小时的光伏电站输出功率。物理模型需要精确的光伏板倾角、方位角、组件效率曲线和逆变器损耗参数而这些参数在实际电站中往往缺失或严重老化导致模型误差随运行时间持续放大。机器学习方案不要求建立精确的物理模型它直接从历史数据中学习辐照度、温度、湿度与输出功率之间的映射关系因此在中小型分布式电站和数据集相对完整的集中式电站中已成为替代物理方法的可靠路径。对于 Python 开发者而言这个项目标题锁定的核心不是算法本身而是「源码 数据集」组合的完整落地方案数据清洗、特征工程、模型训练与超参数调优、预测结果评估缺一环都跑不出可用结果。适合有 Python 基础、正在找机器学习落地项目的开发者也适合电力行业工程师用公开数据集快速验证算法思路。下面从数据理解开始逐步搭建一套可复现的光伏功率预测流程。2. 光伏功率预测数据集的时间特征分析与预处理2.1 光伏功率预测数据集的核心字段与采样粒度光伏功率预测数据集通常包含两个来源电站本地采集的 SCADA 数据功率、组件温度、环境温度、辐照度、湿度和数值天气预报数据总辐照度 GHI、直接辐照度 DNI、云量、风速。采样粒度决定预测任务的定义方式15 分钟粒度对应超短期预测未来 04 小时小时粒度对应短期预测未来 13 天。处理前先用代码确认数据的实际时间范围和缺失情况import pandas as pd df pd.read_csv(pv_power_dataset.csv, parse_dates[timestamp], index_coltimestamp) print(f时间范围: {df.index.min()} 至 {df.index.max()}) print(f采样频率: {pd.infer_freq(df.index)}) print(f缺失值统计:\n{df.isnull().sum()}) print(f列名: {df.columns.tolist()})这是进入特征工程前必须做的第一步。pd.infer_freq能自动判断数据是 15 分钟粒度还是小时粒度如果返回None说明时间戳不连续需要检查是否有跨天缺失或夏令时问题。缺失值统计决定后续用插值还是分段删除——对功率序列超过 2 小时的连续缺失建议直接删除该时段插值会引入虚假的功率爬坡特征。2.2 时间特征构造辐照度为零的时段如何影响机器学习模型光伏功率有一个强物理约束夜间功率恒为零。如果不加处理直接把原始时间戳喂给模型大量零值样本会让模型偏向预测低功率导致白天的峰值预测被系统性压低。构造时间特征的标准做法是分解出周期分量import numpy as np hour df.index.hour dayofyear df.index.dayofyear df[hour_sin] np.sin(2 * np.pi * hour / 24) df[hour_cos] np.cos(2 * np.pi * hour / 24) df[day_sin] np.sin(2 * np.pi * dayofyear / 365.25) df[day_cos] np.cos(2 * np.pi * dayofyear / 365.25)用正弦余弦编码而不是原始小时值是因为小时 23 和 0 之间的距离在数值上应该很近但直接编码会让模型认为它们差异巨大。日期特征同理12 月 31 日和 1 月 1 日之间只隔一天但数值上相差 364。除了周期特征光伏功率预测数据和温度组合还能构造一个高效特征晴空指数即实际辐照度与理论晴空辐照度的比值它直接反映云层遮挡程度是预测误差的主要来源。2.3 光伏功率预测的异常值过滤与平滑策略辐照度传感器在日出日落时会产生低信噪比读数逆变器通讯中断会产生持续零值或定格值。这两种情况都要在进入模型前过滤。常用做法是联合辐照度和功率做物理约束校验当辐照度大于某阈值但功率为零且持续时间超过 30 分钟判断为通讯故障当功率跳变超过额定容量的 60% 且 15 分钟内又恢复判断为尖峰噪声。处理方式rated_power 1000 # 单位 kW按实际情况调整 # 辐照度100 W/m2 但功率为0持续超过2个采样点则剔除 invalid_mask (df[ghi] 100) (df[power] 0) df.loc[invalid_mask, power] np.nan # 功率一阶差分超过额定容量60%视为尖峰 diff df[power].diff().abs() spike_mask diff rated_power * 0.6 df.loc[spike_mask, power] np.nan # 线性插值填补单点缺失 df[power] df[power].interpolate(methodlinear, limit6) df df.dropna(subset[power])这里interpolate的limit6是关键参数对应 15 分钟采样粒度下最多插值 1.5 小时的空洞超过就不插值。线性插值只填补孤立缺失不做长期趋势外推避免在功率爬坡段制造虚假的平滑曲线。所有过滤操作完成后用df.describe()复查各列分布确认没有负功率和超过额定容量 1.2 倍的数据。3. 光伏功率预测特征选择机器学习模型输入到底该用哪些变量3.1 光伏功率预测的特征相关性分析与滞后效应光伏功率的物理决定因素是总辐照度但辐照度传感器和功率表计之间存在响应延迟因此历史功率本身就是强预测变量。用相关性矩阵和滞后相关系数确定特征集合# 计算滞后0-3步的功率自相关 for lag in range(1, 5): corr df[power].autocorr(laglag) print(f滞后{lag * 15}分钟自相关系数: {corr:.4f}) # 特征与目标的相关性 features [ghi, dni, temp_air, temp_module, humidity, wind_speed] for col in features: corr df[col].corr(df[power]) print(f{col} 与功率的相关性: {corr:.4f})自相关系数在滞后 1 步15 分钟通常能达到 0.95 以上到滞后 4 步1 小时会降到 0.7 左右。这意味着特征工程必须包含功率的历史滑窗只靠辐照度做单点回归会丢失功率的时间惯性。推荐的初始特征集合当前时刻及滞后 13 步的辐照度、滞后 14 步的功率、当前温度与湿度的交互项、小时正弦余弦。3.2 光伏功率预测的数据集划分按时间切而不是随机切光伏功率预测最常见的数据集划分错误是随机抽样。光伏数据有强季节性随机划分会让训练集和测试集包含同一天的相邻时刻造成数据泄漏评估结果虚高。正确做法是按时间顺序切分并且保证测试集覆盖完整的日周期train_end 2023-09-30 23:45:00 val_end 2023-11-30 23:45:00 train df.loc[:train_end] val df.loc[train_end:val_end] test df.loc[val_end:] print(f训练集: {len(train)} 条, {train.index.min()} 至 {train.index.max()}) print(f验证集: {len(val)} 条, {val.index.min()} 至 {val.index.max()}) print(f测试集: {len(test)} 条, {test.index.min()} 至 {test.index.max()})训练集至少覆盖一个完整季度验证集用于早停和超参数选择测试集只用一次做最终评估。即使数据量很大也要保留这个三段结构因为光伏功率预测模型在跨季节迁移时的衰减比很多人预期的严重得多验证集是判断是否过拟合到某段天气模式的唯一依据。3.3 光伏功率预测中的天气类型聚类特征只依赖数值特征会遗漏一个关键信息天气类型。晴天、多云、阴雨三种天气下的功率曲线形态差异巨大而这些信息已经隐含在辐照度序列的变化率中。可以构造天气敏感特征来增强模型区分能力# 辐照度变化率反映云层快速移动 df[ghi_diff] df[ghi].diff().fillna(0) # 分钟级波动强度过去30分钟辐照度标准差 df[ghi_std_30min] df[ghi].rolling(window2, min_periods1).std() # 天气类型标签基于当日辐照度峰值与最大值比例 daily_max df[ghi].resample(D).max() ghi_ratio df[ghi] / daily_max.reindex(df.index, methodffill) df[is_cloudy] (ghi_ratio 0.6).astype(int)is_cloudy是一个粗粒度的晴空判断实际使用中可以选择将其加入特征或作为分组训练的依据。做分类模型时可以按天气类型分别训练多个专用模型每个模型只学习一种天气模式预测时先用分类器判断天气再路由到对应回归模型。这个多模型策略在光伏功率预测中很常用能明显改善多云天的过拟合问题。如果数据集没有提供天气代码字段就用ghi_std_30min做无监督聚类分成 34 类后再训练。4. 基于机器学习的光伏功率预测 Python 源码实现训练与验证4.1 光伏功率预测模型选型比较线性回归、随机森林与 LSTM光伏功率预测建模有两条路线传统机器学习路线梯度提升树、随机森林、支持向量回归和深度学习路线LSTM、TCN、Transformer。对 15 分钟粒度的超短期光伏功率预测梯度提升树通常已经足够它天然处理特征非线性关系对异常值鲁棒训练速度快LSTM 的优势在于自动学习长程时间依赖但需要更多数据且调参成本高。选型依据是数据量和预测时效模型训练数据量15分钟预测 MAE4小时预测 MAE训练时长可解释性线性回归1万条以上可用偏高但稳定误差大秒级高随机森林3万条以上较好中等中等分钟级高LightGBM3万条以上较好中低中低分钟级中高LSTM10万条以上低调参后低小时级低TCN10万条以上低调参后低小时级低实际项目中常用做法是先跑 LightGBM 建立基线再决定是否有必要上 LSTM。如果 LightGBM 已经达到业务要求的精度就不必为深度学习增加复杂度。4.2 用 LightGBM 训练光伏功率预测模型的最小可执行代码以下代码是光伏功率预测项目源码中核心训练逻辑的简化版本import lightgbm as lgb from sklearn.metrics import mean_absolute_error, mean_squared_error feature_cols [hour_sin, hour_cos, day_sin, day_cos, ghi, ghi_diff, ghi_std_30min, temp_air, temp_module, humidity, wind_speed] target_col power # 构造滞后特征加入历史功率 for lag in range(1, 5): train[fpower_lag{lag}] train[power].shift(lag) val[fpower_lag{lag}] val[power].shift(lag) test[fpower_lag{lag}] test[power].shift(lag) # 删除因构造滞后特征产生的空行 train_ml train.dropna() val_ml val.dropna() test_ml test.dropna() X_train, y_train train_ml[feature_cols [power_lag1, power_lag2, power_lag3, power_lag4]], train_ml[target_col] X_val, y_val val_ml[feature_cols [power_lag1, power_lag2, power_lag3, power_lag4]], val_ml[target_col] X_test, y_test test_ml[feature_cols [power_lag1, power_lag2, power_lag3, power_lag4]], test_ml[target_col] model lgb.LGBMRegressor( n_estimators1000, learning_rate0.05, num_leaves31, max_depth7, subsample0.8, colsample_bytree0.8, random_state42 ) model.fit( X_train, y_train, eval_set[(X_val, y_val)], eval_metricmae, callbacks[lgb.early_stopping(stopping_rounds50), lgb.log_evaluation(100)] ) y_pred model.predict(X_test) mae mean_absolute_error(y_test, y_pred) print(f测试集 MAE: {mae:.3f} kW) print(f测试集 RMSE: {mean_squared_error(y_test, y_pred, squaredFalse):.3f} kW)代码中early_stopping的stopping_rounds50表示连续 50 轮验证集 MAE 不下降就停止训练这是防过拟合的最有效手段比手动限制树数量更可靠。subsample0.8和colsample_bytree0.8是行采样和列采样增加了模型的随机性降低方差。滞后特征数量选 4 是权衡了时间记忆长度和特征维度更多滞后项带来的收益会迅速衰减还会增加训练耗时。eval_metricmae意味着模型在验证集上按平均绝对误差选最优模型这与光伏功率预测的业务目标一致——电网调度更关心平均偏差而非大误差被平方放大。4.3 用 LSTM 训练光伏功率预测模型序列样本构造与训练参数如果数据集规模达到 10 万条样本以上可以切换到 LSTM 尝试更低的预测误差。核心差异在于样本构造方式LightGBM 用独立特征向量LSTM 用时间窗口序列。import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from sklearn.preprocessing import StandardScaler SEQ_LEN 24 # 过去6小时(15分钟粒度)的观测 NUM_FEATURES len(feature_cols) # 构造滑动窗口样本 def make_sequences(data, seq_len): X, y [], [] for i in range(seq_len, len(data)): X.append(data.iloc[i - seq_len:i][feature_cols].values) y.append(data.iloc[i][target_col]) return np.array(X), np.array(y) scaler StandardScaler() train_scaled scaler.fit_transform(train_ml[feature_cols]) val_scaled scaler.transform(val_ml[feature_cols]) X_train_seq, y_train_seq make_sequences(train_ml, SEQ_LEN) X_val_seq, y_val_seq make_sequences(val_ml, SEQ_LEN) model Sequential([ LSTM(64, return_sequencesTrue, input_shape(SEQ_LEN, NUM_FEATURES)), Dropout(0.2), LSTM(32, return_sequencesFalse), Dropout(0.2), Dense(16, activationrelu), Dense(1) ]) model.compile(optimizertf.keras.optimizers.Adam(learning_rate1e-3), lossmse, metrics[mae]) history model.fit( X_train_seq, y_train_seq, validation_data(X_val_seq, y_val_seq), epochs50, batch_size256, callbacks[tf.keras.callbacks.EarlyStopping(patience5, restore_best_weightsTrue)] )LSTM 的SEQ_LEN24对应 6 小时历史窗口这个值需要根据数据粒度调整不宜过大过长序列反而引入无关噪声训练效率也差。return_sequencesTrue保证第一层 LSTM 输出完整的序列给第二层这是堆叠 LSTM 的标准配置。patience5配合restore_best_weightsTrue保证模型回滚到验证集最优权重而不是早停时的末轮权重。注意 LSTM 输入要求三维张量(样本数, 时间步长, 特征数)样本构造时的make_sequences函数中 i 从seq_len开始避免使用不足 24 步的短序列。训练完成后预测结果需要用scaler.inverse_transform恢复原始功率量纲再计算误差指标。5. 光伏功率预测模型的评估指标与误差分布分析5.1 光伏功率预测的核心评估指标MAE、RMSE 与归一化误差光伏功率预测模型评估不能只看一个指标要同时看平均偏差MAE、大误差惩罚RMSE和相对误差。RMSE 对峰值误差敏感适合反映极端天气下的预测风险。工程上还需要归一化指标来跨电站比较def evaluate_forecast(y_true, y_pred, rated_power): mae mean_absolute_error(y_true, y_pred) rmse mean_squared_error(y_true, y_pred, squaredFalse) nmae mae / rated_power * 100 nrmse rmse / rated_power * 100 # 计算归一化误差 peak_capacity np.percentile(y_true, 95) return { MAE: mae, RMSE: rmse, NMAE (%): nmae, NRMSE (%): nrmse, P95归一化RMSE (%): rmse / peak_capacity * 100 } metrics evaluate_forecast(y_test, y_pred, rated_power1000) for k, v in metrics.items(): print(f{k}: {v:.3f})P95 归一化 RMSE 是行业常用指标用功率分布的 95 分位数做分母避免极端峰值主导误差统计。光伏功率预测中NMAE 在 5%10% 之间属于可接受水平超过 15% 说明模型或数据存在问题。建议保存测试集所有时刻的真实值和预测值后续做分时段误差分析时不需要重新跑模型。5.2 光伏功率预测的分时段误差分析与误差来源定位光伏功率预测误差呈现明显的时段聚集性。按小时分组计算误差能快速定位问题result pd.DataFrame({y_true: y_test, y_pred: y_pred}, indextest_ml.index) result[hour] result.index.hour hourly_mae result.groupby(hour).apply( lambda x: mean_absolute_error(x[y_true], x[y_pred]) ) # 记录辐照度快速变化时的误差 result[ghi_diff_abs] np.abs(test_ml[ghi_diff]) ramp_mask result[ghi_diff_abs] result[ghi_diff_abs].quantile(0.9) ramp_mae mean_absolute_error(result.loc[ramp_mask, y_true], result.loc[ramp_mask, y_pred]) normal_mae mean_absolute_error(result.loc[~ramp_mask, y_true], result.loc[~ramp_mask, y_pred]) print(f辐照度剧烈波动时段 MAE: {ramp_mae:.3f} kW) print(f辐照度平稳时段 MAE: {normal_mae:.3f} kW)光伏功率预测误差通常在日出后 1 小时和日落前 1 小时最大这两个时段的辐照度变化率极高模型难以跟上功率爬坡速度。若波动时段误差是平稳时段的 2 倍以上说明模型缺少对云团移动速度的建模此时应增加辐照度变化率的滞后特征或使用更大时间窗口的 LSTM。还有一种常见现象是晴天时段模型总体误差小但午间存在 10%15% 的系统性负偏差这通常是温度特征缺失——组件温度升高会降低发电效率而特征中只有环境温度需要构造温度对功率的衰减系数特征。误差分析完成后针对定位到的问题修改特征或模型结构再回到训练流程验证效果。5.3 光伏功率预测的不确定性量化与区间预测点预测无法反映预测风险电网调度和电力交易需要知道预测的置信区间。用 LightGBM 的quantile目标函数可以直接输出分位数预测# 训练低分位和高分位模型 q_model_low lgb.LGBMRegressor( objectivequantile, alpha0.1, n_estimators500, learning_rate0.05 ) q_model_high lgb.LGBMRegressor( objectivequantile, alpha0.9, n_estimators500, learning_rate0.05 ) q_model_low.fit(X_train, y_train, eval_set[(X_val, y_val)], callbacks[lgb.early_stopping(50)]) q_model_high.fit(X_train, y_train, eval_set[(X_val, y_val)], callbacks[lgb.early_stopping(50)]) y_low q_model_low.predict(X_test) y_high q_model_high.predict(X_test) coverage np.mean((y_test y_low) (y_test y_high)) print(f80%预测区间覆盖率: {coverage:.3f})两个分位数模型分别用alpha0.1和alpha0.9训练测试集上的实际覆盖率应在 0.750.85 之间偏离过远说明数据分布漂移或模型欠拟合。分位数模型可以作为光伏功率预测项目源码中的可选模块实际业务中通常把这 80% 区间作为调度计划的置信边界超过区间上界时提前准备备用容量。6. 光伏功率预测的递归预测外推与多步预测策略实际业务需要的不只是预测未来 15 分钟而是未来 4 小时甚至更长时间。单步模型外推多步有两种做法递归预测和直接多输出。递归预测把上一步的预测值作为下一步的滞后特征输入误差会随步长累积但实现简单直接多输出需要对每个预测步长训练独立模型或用多输出回归器。常用做法是递归预测但在特征中加入预测时刻的数值天气预报辐照度限制误差累积def recursive_forecast(model, last_seq, steps, nwp_ghi, feature_cols): predictions [] current last_seq.copy() for i in range(steps): pred model.predict(current.reshape(1, -1))[0] predictions.append(pred) # 更新滞后功率特征 current[power_lag4] current[power_lag3] current[power_lag3] current[power_lag2] current[power_lag2] current[power_lag1] current[power_lag1] pred # 更新当前时刻特征为NWP预报值 current[ghi] nwp_ghi[i] current[hour_sin] np.sin(2 * np.pi * ((last_time (i 1) * 15).hour) / 24) current[hour_cos] np.cos(2 * np.pi * ((last_time (i 1) * 15).hour) / 24) return predictions递归预测的步长上限取决于辐照度预报的质量。15 分钟粒度下递归 4 步以内误差累积可控超过 16 步则建议改用数值天气预报辐照度直接作为主导特征功率滞后特征的权重会随步长增加而快速下降。递归预测还有一个干扰源夜间时段预测值趋近于零此时更新滞后特征会让模型在次日早晨的预测出现偏差处理办法是当日出前 1 小时检测预测功率连续为 0则重置滞后特征为当日晴空模型的估计值。这一步是光伏功率预测项目源码中常见的工程细节也是离线评估和线上运行效果差异最大的环节之一。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →