ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python机器学习光伏功率预测项目实战:从数据清洗到模型融合

Python机器学习光伏功率预测项目实战:从数据清洗到模型融合 简介本资源为基于Python机器学习的光伏功率预测完整项目包面向新能源数据分析、电力负荷预测方向的学习者与算法入门者帮助解决从原始气象数据到功率预测建模的全流程实践问题。包内共16个文件以8个csv训练与测试数据集、4个py脚本、1个ipynb笔记本为主另含md说明、docx任务文档与gitignore配置压缩包约4.26MB结构清晰便于按模块查阅。项目覆盖数据加载与合并、数据探索、特征处理到模型训练预测的完整链路脚本中演示了用pandas合并多份训练集与测试集、用散点图验证实发辐照度与实际功率的强相关性、用info与describe排查空值与统计分布并针对时间字符格式、夜晚零功率等细节给出处理思路。目前已有983人学习下载适合希望掌握机器学习回归建模流程、积累新能源预测实战经验的读者参考复用。1. 光伏功率预测项目为什么值得用 Python 机器学习重做一遍做光伏电站运维的人大多有过这种经历中午云层一过功率曲线像坐过山车调度电话紧跟着就来了。传统物理模型依赖数值天气预报和组件参数遇到局部云团、积灰、逆变器限功率这些情况误差经常压不下来。Python 机器学习的光伏功率预测项目核心思路是用历史功率、辐照度、温度、湿度这些实测数据训练模型让算法自己找气象因子和发电功率之间的非线性关系。这套源码加训练数据加测试数据的组合适合三类人想入门新能源 AI 的算法新手、需要快速搭预测基线的运维工程师、以及做课程设计或毕业设计的学生。它解决的不是预测准不准这一个问题而是给你一条从数据到模型到评估的完整链路让你能在这个基础上改特征、换模型、调参数。2. 光伏功率预测的数据管道从原始数据到模型输入2.1 训练数据和测试数据到底长什么样拿到一份光伏功率预测数据集第一件事不是急着跑模型而是搞清楚字段含义。常见的光伏功率预测数据集一般包含时间戳、辐照度GHI 或 POA、环境温度、组件温度、风速、湿度、实际功率这几列。采样粒度从 15 分钟到 1 小时不等超短期预测通常用 15 分钟粒度。训练数据和测试数据的划分不能随机打乱必须按时间顺序切分否则会出现用未来数据预测过去的穿越问题模型评估结果会虚高得离谱。我一般拿到数据先做三件事看时间范围有没有断档、看功率列有没有负值或超过装机容量的异常点、看气象列缺失比例。这三步用 pandas 几行就能完成。import pandas as pd import numpy as np # 读取训练数据parse_dates 把时间列直接转成 datetime df pd.read_csv(train_data.csv, parse_dates[timestamp]) df df.sort_values(timestamp).reset_index(dropTrue) # 基础体检时间范围、缺失率、功率异常值 print(时间范围:, df[timestamp].min(), -, df[timestamp].max()) print(缺失率:\n, df.isnull().mean().round(4)) # 功率列合理性检查负值和超过装机容量的点 capacity 100.0 # 假设装机 100MW按实际改 bad_low (df[power] 0).sum() bad_high (df[power] capacity).sum() print(f负功率点: {bad_low}, 超容量点: {bad_high}) # 按时间顺序切分前 80% 训练后 20% 测试 split int(len(df) * 0.8) train, test df.iloc[:split], df.iloc[split:]这段代码的关键在sort_values和iloc切分。很多新手用train_test_split默认的随机切分结果测试集里混进了训练集时间之后的数据评估指标好看但上线就崩。参数方面capacity要按你电站的实际装机容量填split比例可以根据数据量调整数据少于一年的话建议 7:3。2.2 特征工程把气象数据变成模型能吃的信号原始气象列直接喂给模型不是不行但效果通常一般。光伏功率和辐照度之间是强非线性关系和温度之间也有交互效应。我一般会构造这几类特征时间特征小时、月份、季节、滞后特征前几个时刻的功率和辐照度、滚动统计过去 1 小时辐照度均值、以及物理衍生特征辐照度与温度的比值。def build_features(df): df df.copy() # 时间特征小时和月份能捕捉日周期和季节周期 df[hour] df[timestamp].dt.hour df[month] df[timestamp].dt.month df[dayofyear] df[timestamp].dt.dayofyear # 滞后特征前 1、2、4 个时刻的功率捕捉惯性 for lag in [1, 2, 4]: df[fpower_lag_{lag}] df[power].shift(lag) df[firradiance_lag_{lag}] df[irradiance].shift(lag) # 滚动均值过去 4 个点的辐照度均值平滑云层扰动 df[irradiance_roll_mean_4] df[irradiance].rolling(4).mean() # 物理衍生辐照度与温度比值间接反映组件效率 df[irr_temp_ratio] df[irradiance] / (df[temp] 1) return df.dropna().reset_index(dropTrue) train_feat build_features(train) test_feat build_features(test)滞后特征的数量不是越多越好。我试过加到 8 个滞后结果模型在训练集上拟合得很好测试集反而变差典型的过拟合。一般 2 到 4 个滞后够用。rolling(4)的窗口大小对应你采样粒度15 分钟粒度下 4 个点就是 1 小时。dropna会去掉前面几行因为 shift 产生的空值这是正常的。2.3 数据清洗里最容易翻车的三个点第一个是缺失值填充方式。气象数据缺失时用全局均值填充会破坏时间连续性我一般用前向填充加线性插值组合。第二个是夜间功率归零处理。光伏夜间功率本来就是零但有些数据集里夜间功率是 NaN如果直接 dropna 会把大量夜间样本删掉导致模型只学到白天模式。正确做法是把夜间功率填零而不是删行。第三个是异常值检测。逆变器限功率时功率会被削平这种点如果不剔除模型会学到错误的上限。# 夜间功率填零辐照度小于阈值时功率应为零 df.loc[df[irradiance] 5, power] 0 # 前向填充 线性插值处理气象缺失 df[irradiance] df[irradiance].ffill().interpolate() # 简单异常值处理用 IQR 方法标记功率异常 q1, q3 df[power].quantile([0.25, 0.75]) iqr q3 - q1 lower, upper q1 - 1.5 * iqr, q3 1.5 * iqr df[power] df[power].clip(lower, upper)irradiance 5这个阈值按传感器精度调一般 5 到 10 W/m² 之间。clip是保守做法把异常值拉到边界而不是直接删保留样本量的同时降低异常影响。3. 模型选型与训练从线性回归到梯度提升树3.1 为什么光伏功率预测首选树模型而不是神经网络这是被问得最多的问题。光伏功率预测的数据量通常不大一个电站一年的 15 分钟数据也就三万多条。这个量级下XGBoost 或 LightGBM 这类梯度提升树的表现往往比 LSTM 稳定训练快、调参少、对特征尺度不敏感。神经网络在数据量上万条以上、且有多站点联合训练需求时才更有优势。我一般先用 LightGBM 跑一个基线如果误差还压不下去再考虑上时序模型。import lightgbm as lgb from sklearn.metrics import mean_absolute_error, mean_squared_error feature_cols [c for c in train_feat.columns if c not in [timestamp, power]] X_train, y_train train_feat[feature_cols], train_feat[power] X_test, y_test test_feat[feature_cols], test_feat[power] # LightGBM 基线参数树数量 500学习率 0.05防止过拟合 model lgb.LGBMRegressor( n_estimators500, learning_rate0.05, num_leaves31, max_depth6, subsample0.8, colsample_bytree0.8, random_state42 ) model.fit(X_train, y_train, eval_set[(X_test, y_test)], eval_metricmae, callbacks[lgb.early_stopping(50)]) pred model.predict(X_test) print(MAE:, mean_absolute_error(y_test, pred)) print(RMSE:, np.sqrt(mean_squared_error(y_test, pred)))num_leaves31和max_depth6是控制模型复杂度的核心参数。光伏数据噪声大树太深容易记住噪声。early_stopping(50)表示验证集 50 轮没提升就停这是防过拟合的后悔药。subsample和colsample_bytree都设 0.8增加随机性提升泛化。3.2 评价指标不能只看 RMSE光伏功率预测的评估有个坑RMSE 对夜间零功率点不敏感因为零值附近误差本来就小。如果测试集里夜间样本占一半RMSE 会被拉低看起来模型很好实际白天高峰时段误差可能很大。我一般会分时段评估把白天辐照度大于阈值和夜间分开算 MAE再算一个全天归一化 RMSE。# 分时段评估白天和夜间分开看 test_eval test_feat.copy() test_eval[pred] pred test_eval[error] np.abs(test_eval[power] - test_eval[pred]) day_mask test_eval[irradiance] 50 night_mask ~day_mask print(白天 MAE:, test_eval.loc[day_mask, error].mean()) print(夜间 MAE:, test_eval.loc[night_mask, error].mean()) # 归一化 RMSE除以装机容量方便跨电站对比 nrmse np.sqrt(mean_squared_error(y_test, pred)) / capacity print(nRMSE:, round(nrmse, 4))irradiance 50这个阈值区分白天夜间按实际数据调。归一化 RMSE 除以装机容量后不同规模电站的误差可以横向对比行业里常用这个指标。3.3 特征重要性分析哪些气象因子真正在起作用训练完模型别急着收工看一眼特征重要性。LightGBM 自带feature_importances_能告诉你哪些特征贡献大。我做过几次辐照度相关特征永远排第一滞后功率第二温度排第三。如果发现时间特征重要性异常高可能是数据泄漏——比如训练集和测试集时间重叠了。importance pd.DataFrame({ feature: feature_cols, importance: model.feature_importances_ }).sort_values(importance, ascendingFalse) print(importance.head(10))如果某个滞后特征重要性特别高要警惕。功率的滞后 1 期和当前功率相关性极强模型可能主要靠这个特征在抄答案而不是真正学气象到功率的映射。这种情况下超短期预测未来 15 分钟可能还行但提前几小时的预测会崩。4. 避坑与排查光伏功率预测项目里那些血泪经验4.1 现象测试集 MAE 很低上线后误差翻倍原因训练测试划分用了随机切分测试集里混入了时间上靠后的数据模型实际上见过未来。解决严格按时间顺序切分训练集在前、测试集在后中间可以留一段 gap 避免边界泄漏。4.2 现象模型预测曲线整体偏低高峰时段尤其明显原因逆变器限功率导致训练数据里高峰功率被削平模型学到了错误的上限。解决识别限功率时段功率长时间恒定在某个值把这些样本标记或剔除或者单独建模。4.3 现象夜间预测出现负功率原因模型是回归模型输出没有物理约束夜间输入特征接近零时可能输出小负值。解决预测后做 clip把负值截为零或者在训练时对功率做 log 变换再还原。4.4 现象换了新电站数据模型完全不能用原因不同电站的装机容量、组件类型、朝向、遮挡情况都不同模型学到的映射关系不通用。解决要么每个电站单独训练要么做迁移学习用新电站少量数据微调。跨电站直接用是常见的翻车场景。4.5 现象特征重要性里时间特征排第一原因数据泄漏。时间特征本身和功率没有因果关系如果它重要性异常高说明训练集和测试集在时间上有重叠模型通过时间特征认出了测试样本。解决检查切分逻辑确保测试集时间完全在训练集之后。5. 把预测误差再压一档残差修正与多模型融合基线模型跑通之后想进一步降误差我一般从两个方向入手。第一个是残差修正用 LightGBM 跑完得到预测值再训练一个模型去预测残差把两个结果相加。残差里往往藏着基线模型没学到的模式比如云层快速移动导致的短时波动。第二个是多模型融合LightGBM 和 XGBoost 各跑一遍按验证集误差加权平均通常能比单模型降 3% 到 8% 的 MAE。# 残差修正用第二个模型学习基线模型的误差 residual y_train - model.predict(X_train) residual_model lgb.LGBMRegressor( n_estimators200, learning_rate0.03, num_leaves15, max_depth4, random_state42 ) residual_model.fit(X_train, residual) # 最终预测 基线预测 残差预测 final_pred model.predict(X_test) residual_model.predict(X_test) print(修正后 MAE:, mean_absolute_error(y_test, final_pred))残差模型的树要浅、学习率要低因为它学的是补丁不是主信号。num_leaves15和max_depth4是保守设置防止残差模型过拟合噪声。如果修正后 MAE 没降反升说明基线模型已经学得比较充分残差里主要是噪声这时候就别硬修了。验证方法上我习惯留一段最近的数据做滚动验证用前 11 个月训练第 12 个月测试然后往前滚。这样能看出模型在不同季节的稳定性。光伏数据季节性很强夏天和冬天的功率分布差异大单次切分评估可能掩盖季节性问题。一个具体技巧预测结果后处理时加一个简单的物理约束——功率不能超过当前辐照度对应的理论最大值。这个上限可以用装机容量乘以辐照度归一化值估算超过就截断。这一步几乎零成本但能消掉大部分异常尖峰。我自己踩过最深的坑是早期做光伏预测时花了两周调 LSTM 的超参数结果还不如 LightGBM 默认参数跑出来的效果好。后来才明白数据质量和特征工程的重要性远大于模型选择。把数据清洗和特征做扎实比换模型带来的提升大得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表