简介这是一份面向高校学生与机器学习入门者的光伏功率预测实战项目以Python为实现语言围绕历史发电数据完成从训练到预测的完整流程适合用作毕业设计、期末大作业或课程设计课题。压缩包共19个文件、约4.64MB包含8个csv训练与测试数据集、4个py核心脚本、1个ipynb实验笔记、1个md说明文档及docx任务说明等数据与代码分离便于直接训练、验证与二次修改。项目脚本带有详细注释覆盖数据加载、预处理、模型训练与预测等环节新手也能顺着代码理解机器学习预测光伏功率的整体思路。目前已有65人学习下载。读者可获得一套可运行的光伏功率预测方案、配套训练与测试数据、任务说明文档以及清晰的目录结构既能用于课程实践也能作为理解机器学习回归预测流程的参考。1. 光伏功率预测项目从一份源码和数据集说起光伏电站最头疼的事不是组件脏了也不是逆变器报警而是明天到底能发多少电。电网调度要报数储能充放要排计划售电合同要考核偏差预测偏 10% 就可能是一笔真金白银的罚款。基于机器学习的 Python 光伏功率预测项目源码及数据集解决的正是这件事用历史发电数据加气象数据训练一个模型把未来一段时间的功率曲线算出来。它适合三类人想拿光伏预测做课程设计或毕设的学生、刚转机器学习想找一个完整闭环练手的工程师、以及电站侧想做功率预测原型验证的技术人员。这套东西的门槛不高Python 加常见机器学习库就能跑但真正决定效果的不是模型多花哨而是数据对齐、特征构造和评估口径这三件事。下面我按自己搭过几版的顺序把源码结构、数据集处理、模型训练和踩过的坑讲清楚。2. 光伏功率预测的数据集长什么样字段、粒度与对齐2.1 一份能用的光伏数据集至少要有哪几列很多人拿到数据集第一反应是直接read_csv然后扔进模型结果 RMSE 高得离谱回头一看时间戳根本没对齐。光伏功率预测的数据集本质是两条时间序列的拼接一条是电站侧的发电功率一条是气象侧的辐照度、温度、湿度、风速。常见做法是做成一张宽表每行一个时间点列包含时间戳、功率、以及若干气象特征。一个典型的数据集字段大致是这样字段名含义单位典型粒度timestamp采样时间—15min / 1hpower实际发电功率kW 或 MW15min 均值irradiance水平面总辐照度W/m²15min 均值temp_module组件温度℃15min 均值temp_ambient环境温度℃15min 均值humidity相对湿度%15min 均值wind_speed风速m/s15min 均值capacity装机容量kW常数装机容量这一列经常被忽略但它是把功率归一化的关键。不同电站容量不同直接拿 kW 做标签模型学到的量纲没有可比性。我一般会把功率除以装机容量转成 0 到 1 的功率比这样换一个电站也能复用同一套特征工程。提示如果数据集里只有功率没有气象那只能做纯时序预测精度上限很低晴天还行多云天基本靠猜。做光伏预测辐照度是命根子。2.2 时间粒度选 15 分钟还是 1 小时粒度选择直接决定任务难度和可用模型。15 分钟粒度是国内很多电站的默认采样间隔超短期预测未来 0 到 4 小时通常就用这个粒度。1 小时粒度适合做日前预测数据量小、噪声低但丢掉了云层快速遮挡带来的波动信息。我的经验是如果数据集本身是 15 分钟不要急着降采样到 1 小时。先用 15 分钟跑一版基线看看波动有多大。如果发现相邻点功率跳变超过装机容量 30% 的样本占比很高说明数据里有云遮挡或限电这时候要么加平滑要么在特征里加入前一时刻的功率作为滞后项。import pandas as pd # 读取原始数据parse_dates 把时间列转成 datetime df pd.read_csv(pv_data.csv, parse_dates[timestamp]) df df.sort_values(timestamp).reset_index(dropTrue) # 检查时间间隔是否均匀 diff df[timestamp].diff().dropna() print(diff.value_counts().head()) # 功率归一化除以装机容量得到 0-1 的功率比 capacity df[capacity].iloc[0] df[power_ratio] df[power] / capacity # 标记白天样本辐照度大于 10 W/m² 才算有效发电时段 df[is_day] (df[irradiance] 10).astype(int)这段代码做了三件事排序保证时间单调、检查采样间隔是否均匀、构造归一化功率和白天标记。value_counts那行很关键如果出现大量非 15 分钟间隔说明数据有缺失或重复后面训练会出问题。is_day用来在评估时剔除夜间样本否则夜间功率恒为 0会把整体误差拉低看起来模型很好实际白天一塌糊涂。2.3 缺失值和异常值光伏数据里最常见的两种脏光伏数据集几乎没有干净的。常见脏数据有三类夜间辐照度为负、白天功率为负、以及连续多个时间点功率完全相同。前两种是传感器漂移第三种多半是通信中断后的补值。处理方式我一般分三步走。第一步把辐照度小于 0 的置为 0功率小于 0 的置为 0。第二步对连续相同值超过 4 个点的片段标记为可疑用前后均值插补。第三步对缺失时间戳用resample补齐再插值但插值不要超过 2 个点否则就是编数据。# 负值清洗 df.loc[df[irradiance] 0, irradiance] 0 df.loc[df[power] 0, power] 0 # 重新采样到固定 15 分钟缺失处先置 NaN df df.set_index(timestamp).resample(15min).mean().reset_index() # 对功率和辐照度做线性插值最多补 2 个点 df[power] df[power].interpolate(methodlinear, limit2) df[irradiance] df[irradiance].interpolate(methodlinear, limit2) # 插值后仍有缺失的行直接删掉 df df.dropna(subset[power, irradiance]).reset_index(dropTrue)resample(15min).mean()会把重复时间戳合并同时把缺失时间点补成 NaN。limit2是防止连续缺失被过度平滑。最后dropna删掉的是那些插值也救不回来的长缺口。这一步做完数据量通常会减少 5% 到 15%属于正常范围。3. 特征工程把气象数据变成模型能吃的输入3.1 辐照度、温度、时间戳怎么组合成特征向量原始字段直接喂给模型不是不行但效果一般。光伏功率和辐照度之间不是线性关系组件温度高了效率还会下降所以需要构造交叉特征。我常用的特征集包括当前辐照度、辐照度的平方、组件温度、温度与辐照度的乘积、太阳高度角的正弦值、以及小时和月份的独热编码。太阳高度角可以用pvlib算但如果你不想引入额外依赖用时间戳近似也能凑合。下面这段代码不依赖 pvlib只用 pandas 和 numpy 构造基础特征。import numpy as np df[hour] df[timestamp].dt.hour df[month] df[timestamp].dt.month # 辐照度平方捕捉非线性 df[irr_sq] df[irradiance] ** 2 # 温度与辐照度交互项 df[temp_irr] df[temp_module] * df[irradiance] # 用小时近似太阳高度角的正弦正午最大 df[sun_sin] np.sin(np.pi * (df[hour] - 6) / 12).clip(lower0) # 滞后特征前一时刻功率比 df[power_lag1] df[power_ratio].shift(1) df[power_lag2] df[power_ratio].shift(2) # 去掉因为 shift 产生的空行 df df.dropna().reset_index(dropTrue)sun_sin这一项在 6 点到 18 点之间为正夜间为 0能帮模型区分早晚。power_lag1和power_lag2是超短期预测里最有效的特征因为光伏功率有很强的自相关性。但要注意如果是做日前预测滞后特征不能用因为预测时拿不到未来时刻的功率。3.2 训练集、验证集、测试集怎么切才不泄露时间序列不能随机切分这是血泪经验。随机切分会让模型在训练时看到未来信息验证集误差虚低上线就翻车。正确做法是按时间顺序切前 70% 训练中间 15% 验证最后 15% 测试。n len(df) train_end int(n * 0.7) val_end int(n * 0.85) train df.iloc[:train_end] val df.iloc[train_end:val_end] test df.iloc[val_end:] feature_cols [irradiance, irr_sq, temp_module, temp_irr, humidity, wind_speed, sun_sin, power_lag1, power_lag2] X_train, y_train train[feature_cols], train[power_ratio] X_val, y_val val[feature_cols], val[power_ratio] X_test, y_test test[feature_cols], test[power_ratio]切完之后还要检查一件事训练集和测试集的辐照度分布是否接近。如果测试集里连续阴雨天特别多而训练集全是晴天模型表现会差很多。这时候要么调整切分点要么在训练集里做样本加权。3.3 标准化和独热编码放在管道里做标准化必须用训练集的均值和方差不能把验证集和测试集一起算进去。我一般用 sklearn 的Pipeline把标准化和模型串起来避免手动处理出错。from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestRegressor pipe Pipeline([ (scaler, StandardScaler()), (model, RandomForestRegressor(n_estimators200, max_depth12, random_state42)) ]) pipe.fit(X_train, y_train)StandardScaler在fit时只看到训练数据transform时对验证和测试用同样的均值方差。树模型其实对标准化不敏感但如果你后面换神经网络或线性模型这一步就是必须的。max_depth12是我在光伏数据上常用的值太深容易过拟合太浅欠拟合。4. 模型训练与评估从线性回归到梯度提升4.1 先跑一个线性回归基线别一上来就上深度学习很多人拿到项目直接上 LSTM结果调了两周还不如线性回归。光伏功率和辐照度之间虽然有非线性但主趋势是线性的。先用线性回归跑一个基线知道误差大概在什么量级再决定要不要上复杂模型。from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, mean_absolute_error import numpy as np lr LinearRegression() lr.fit(X_train, y_train) pred_lr lr.predict(X_test) rmse np.sqrt(mean_squared_error(y_test, pred_lr)) mae mean_absolute_error(y_test, pred_lr) print(fLR RMSE: {rmse:.4f}, MAE: {mae:.4f})评估指标我只看两个RMSE 和 MAE。RMSE 对大误差敏感MAE 更直观。功率比在 0 到 1 之间RMSE 0.05 大概对应 5% 的装机容量误差已经算不错。如果线性回归 RMSE 在 0.08 以内说明特征工程做到位了换复杂模型提升空间有限。4.2 随机森林和梯度提升在光伏数据上的表现差异随机森林和梯度提升是表格数据上的两把好手。随机森林抗噪、不容易过拟合梯度提升精度更高但需要调参。我在同一份数据上跑过对比梯度提升的 RMSE 通常比随机森林低 10% 到 20%但训练时间长 3 到 5 倍。from sklearn.ensemble import GradientBoostingRegressor gbr GradientBoostingRegressor( n_estimators300, learning_rate0.05, max_depth5, subsample0.8, random_state42 ) gbr.fit(X_train, y_train) pred_gbr gbr.predict(X_test) rmse_gbr np.sqrt(mean_squared_error(y_test, pred_gbr)) print(fGBR RMSE: {rmse_gbr:.4f})learning_rate0.05配n_estimators300是常见的慢学习组合精度比默认的 0.1 配 100 要好。subsample0.8引入随机性防止过拟合。max_depth5控制每棵树复杂度光伏数据特征不多树太深没意义。4.3 用时间序列交叉验证代替单次验证单次验证集切分有随机性换一个切分点结果可能差很多。时间序列交叉验证能给出更稳定的评估。sklearn 的TimeSeriesSplit可以直接用。from sklearn.model_selection import TimeSeriesSplit, cross_val_score tscv TimeSeriesSplit(n_splits5) scores cross_val_score(pipe, X_train, y_train, cvtscv, scoringneg_root_mean_squared_error) print(CV RMSE:, -scores.mean())TimeSeriesSplit保证每次训练集都在验证集之前不会泄露未来信息。neg_root_mean_squared_error是 sklearn 里 RMSE 的负值写法取负号还原。如果 5 折的 RMSE 标准差很大说明数据分布不稳定需要检查是不是有季节性差异。5. 避坑与排查光伏预测项目里最容易翻车的五件事5.1 夜间样本把误差指标拉低模型看起来很好现象测试集 RMSE 只有 0.02但白天预测曲线明显偏离实际。原因夜间功率恒为 0模型只要输出 0 就能拿高分整体指标被夜间样本稀释。解决评估时只统计is_day 1的样本或者单独报告白天 RMSE。训练时也可以给白天样本更高权重。5.2 滞后特征在日前预测里造成数据泄露现象离线评估精度极高上线后误差翻倍。原因用了power_lag1这类特征但日前预测时未来时刻的功率根本拿不到训练时却用了真实值。解决明确预测任务。超短期预测可以用滞后特征日前预测只能用气象预报和日历特征。两者特征集必须分开。5.3 辐照度单位不统一模型学到错误量纲现象换一个数据集后模型完全失效。原因有的数据集辐照度单位是 W/m²有的是 kW/m²数值差 1000 倍。解决读数据后先看describe()确认辐照度最大值在 1000 左右还是 1 左右。统一转成 W/m² 再进模型。5.4 装机容量变化后没有重新归一化现象电站扩容后功率预测值系统性偏低。原因模型训练时用的功率比是基于旧容量算的扩容后同样的辐照度对应更大功率。解决容量变化后重新计算power_ratio并用新数据微调模型。或者直接把容量作为特征输入让模型自己学。5.5 多云天误差突然增大模型像失灵现象晴天 RMSE 0.03多云天 RMSE 0.15。原因云层快速移动导致功率剧烈波动15 分钟粒度的气象数据跟不上变化。解决引入更细粒度的气象数据或者用天空相机图像做辅助。如果拿不到至少在评估时把多云天单独分组不要用整体指标掩盖问题。6. 把模型推到能用的程度一个后处理技巧和我的习惯模型训练完直接输出预测值往往不够。光伏功率有物理约束不能为负不能超过装机容量。我一般会在预测后加一个裁剪再做简单的滑动平均平滑。# 裁剪到 0 到 1 之间 pred_final np.clip(pred_gbr, 0, 1) # 滑动平均平滑窗口 3 pred_smooth pd.Series(pred_final).rolling(window3, centerTrue).mean() pred_smooth pred_smooth.fillna(pd.Series(pred_final)).valuesnp.clip保证物理合理rolling平滑去掉毛刺。窗口不要太大3 到 5 就够了太大反而把真实的爬坡过程抹掉。验证模型是否真的能用我习惯做一件事把测试集最后一周的预测曲线和实际曲线画在一起不看指标用眼睛看。如果模型在晴天能贴合在多云天能跟上趋势哪怕 RMSE 不是最低我也认为它可用。指标是给别人看的曲线是给自己看的。这个项目最值得投入的地方不是换更深的网络而是把数据对齐和特征工程做扎实。我踩过最大的坑是花了一周调 LSTM 参数最后发现数据里有一半时间戳是错的。从那以后我拿到任何光伏数据集第一件事就是画时间序列图第二件事是检查采样间隔。希望帮到你。本文还有配套的精品资源点击获取