光伏功率预测机器学习实战:完整项目包与避坑指南
简介这是一份面向计算机相关专业学生与项目实战学习者的光伏功率预测完整项目包以Python与机器学习为核心可用于毕业设计、课程设计或期末大作业。资源包含可直接运行的源码、训练数据与测试数据覆盖数据加载、预处理、模型训练与预测等完整流程并配有任务说明文档与Jupyter Notebook便于理解建模思路与实验步骤。压缩包共16个文件以8个csv数据文件、4个py脚本为主另含1个ipynb笔记本、1个md说明、1个docx文档及gitignore配置整体约4.64MB结构清晰、轻量易上手。项目经导师指导并获评审99分代码完整可运行适合零基础小白按目录逐步复现。目前已有56人学习读者可借此掌握光伏功率预测的机器学习实现方法积累数据处理、模型训练与结果验证的实战经验并作为高分项目参考模板。1. 光伏功率预测项目拆包一份能跑通的机器学习实战资源光伏功率预测这个方向做的人不少但真正能拿到一份「数据齐全、代码能跑、流程闭环」的完整项目包其实没那么容易。很多开源仓库要么只给模型代码不给数据要么数据是脱敏过的残缺版本跑起来一堆报错。这份资源不一样的地方在于它把训练数据、测试数据、数据处理脚本、训练预测脚本、Notebook 实验记录全部打包在一起了解压就能看到DC_Data目录下按编号分好的train_1.csv到train_4.csv、test_1.csv到test_4.csv配套Data_Process.py、Train_Predict.py、Load_Save_Data.py三个核心脚本外加一个DC_PV_Power_Predict_2018.ipynb用来做交互式实验。适合正在做机器学习课程设计、期末大作业或者毕业设计的同学也适合想找一个完整光伏预测 pipeline 来练手的从业者。下面我按「这份资源是什么 → 怎么跑起来 → 坑在哪 → 怎么改出自己的东西」的顺序把整个包拆一遍。2. 数据管线拆解从 CSV 到模型输入的四步处理链2.1 数据文件的组织逻辑与字段含义先看数据。DC_Data目录下的文件命名很直白train_1.csv到train_4.csv是四组训练集test_1.csv到test_4.csv是对应的测试集。这种按编号分组的方式常见做法是不同时间段、不同季节或者不同电站的切片数据用来做交叉验证或者分场景评估。每组数据内部通常包含时间戳、辐照度、温度、湿度、风速等气象特征列以及目标列——光伏功率输出。拿到数据后第一件事不是急着喂模型而是确认字段类型和缺失情况。我一般会先跑一段探查代码import pandas as pd # 读取第一组训练数据先看结构和缺失 df pd.read_csv(DC_Data/train_1.csv) print(df.shape) print(df.dtypes) print(df.isnull().sum()) print(df.describe())这段代码做三件事看数据规模行数列数、看每列的数据类型有没有把数值列读成 object、看缺失值分布。光伏数据里最常见的缺失是夜间功率为 0 或者传感器掉线导致的空值处理方式不同后面模型表现差异很大。describe()能快速看出有没有异常极值比如辐照度出现负值或者功率超过装机容量这些都要在清洗阶段处理掉。2.2 Data_Process.py 里的清洗与特征工程Data_Process.py是整个管线的入口脚本负责把原始 CSV 转成模型能吃的格式。虽然具体实现要看源码但这类光伏预测项目的处理逻辑基本跑不出几个固定动作时间戳解析、缺失值填充、异常值剔除、特征归一化、训练测试集划分。常见的处理顺序是这样的import pandas as pd import numpy as np from sklearn.preprocessing import MinMaxScaler def process_data(filepath): df pd.read_csv(filepath) # 时间戳解析按需提取小时特征 df[timestamp] pd.to_datetime(df[timestamp]) df[hour] df[timestamp].dt.hour # 夜间功率置零处理辐照度为 0 时功率强制归零 df.loc[df[irradiance] 0, power] 0 # 缺失值用前向填充 后向填充兜底 df df.fillna(methodffill).fillna(methodbfill) # 归一化到 [0,1] scaler MinMaxScaler() feature_cols [irradiance, temperature, humidity, wind_speed] df[feature_cols] scaler.fit_transform(df[feature_cols]) return df这里有几个参数值得注意。MinMaxScaler对光伏数据是合理选择因为辐照度和功率都有明确物理边界不像金融数据那样存在长尾分布。夜间功率置零这一步很关键——如果不做模型会学到「辐照度为 0 但功率不为 0」的矛盾样本预测时容易出现夜间输出虚高。缺失值填充用前向后向组合是保守做法更精细的方案是按天气类型分组填充但对课程设计来说够用了。2.3 Load_Save_Data.py 的读写封装Load_Save_Data.py从命名看是负责数据加载和结果保存的封装模块。这类脚本的价值在于把重复的 IO 操作抽出来避免在每个实验脚本里写一遍pd.read_csv。典型实现会包含两个函数一个负责按编号加载指定组的训练/测试数据另一个负责把预测结果写回 CSV 或者 pkl 文件。import pandas as pd import os DATA_DIR DC_Data def load_group(group_id): 按编号加载一组训练和测试数据 train_path os.path.join(DATA_DIR, ftrain_{group_id}.csv) test_path os.path.join(DATA_DIR, ftest_{group_id}.csv) train_df pd.read_csv(train_path) test_df pd.read_csv(test_path) return train_df, test_df def save_predictions(preds, filenamepredictions.csv): 保存预测结果 pd.DataFrame(preds, columns[predicted_power]).to_csv(filename, indexFalse)这种封装看起来简单但实际做实验时能省很多事。比如你想跑四组数据的对比实验只需要循环调用load_group(i)不用手动改路径。save_predictions统一了输出格式后面画对比图或者算误差指标时直接读这个文件就行。2.4 训练测试集的划分策略四组 train/test 的划分方式决定了你后面评估模型时的可信度。如果train_1和test_1是随机切分的那评估结果会偏乐观如果是按时间顺序切的比如前 80% 训练、后 20% 测试那更接近真实部署场景。光伏功率预测里时间序列切分是更合理的做法因为随机切分会导致未来信息泄漏——模型在训练时见过了「未来」的天气模式测试时自然表现好但上线就翻车。判断方法很简单看测试集的时间戳范围是不是紧接在训练集之后train_df, test_df load_group(1) print(训练集时间范围:, train_df[timestamp].min(), 至, train_df[timestamp].max()) print(测试集时间范围:, test_df[timestamp].min(), 至, test_df[timestamp].max())如果测试集的起始时间晚于训练集结束时间说明是按时间切的评估结果更可信。如果两组时间范围重叠那就要注意过拟合风险后续调参时不能只看测试集指标。3. 模型训练与预测Train_Predict.py 的完整执行链路3.1 模型选型与 sklearn 接口约定Train_Predict.py是核心训练脚本。从项目定位机器学习课程设计/毕业设计来看模型选型大概率落在随机森林、梯度提升树GBDT/XGBoost/LightGBM或者支持向量回归SVR这几类。光伏功率预测这个任务特征维度不高、样本量中等树模型通常是性价比最高的选择——不用太多调参就能出合理结果而且特征重要性可以直接解释。sklearn 风格的接口约定是fit(X_train, y_train)→predict(X_test)整个流程很标准from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score import numpy as np # 假设 X_train, y_train, X_test, y_test 已经准备好 model RandomForestRegressor( n_estimators200, # 树的数量200 是常用起点 max_depth12, # 限制深度防止过拟合 min_samples_leaf5, # 叶节点最少样本数 random_state42 # 固定随机种子保证可复现 ) model.fit(X_train, y_train) y_pred model.predict(X_test) # 三个核心指标 mae mean_absolute_error(y_test, y_pred) rmse np.sqrt(mean_squared_error(y_test, y_pred)) r2 r2_score(y_test, y_pred) print(fMAE: {mae:.4f}, RMSE: {rmse:.4f}, R2: {r2:.4f})参数说明n_estimators从 100 到 500 都常见200 是平衡训练时间和效果的起点max_depth不设的话树会一直长到叶子纯净光伏数据噪声不小限制到 10-15 层通常更稳min_samples_leaf设 3-5 能防止模型记住个别异常样本。random_state一定要固定不然每次跑出来的指标都不一样调参时根本分不清是参数变了还是随机波动。3.2 特征与目标的拆分逻辑训练之前要把 DataFrame 拆成特征矩阵 X 和目标向量 y。光伏预测里目标列就是功率特征列是气象因子加上时间特征。拆分时最容易犯的错是把目标列也塞进特征里或者把时间戳直接当数值特征喂进去。# 假设 df 已经过 Data_Process 处理 target_col power drop_cols [timestamp, power] # 时间戳不直接作为数值特征 feature_cols [c for c in df.columns if c not in drop_cols] X df[feature_cols].values y df[target_col].values # 按时间顺序切分前 80% 训练 split_idx int(len(X) * 0.8) X_train, X_test X[:split_idx], X[split_idx:] y_train, y_test y[:split_idx], y[split_idx:]时间戳不直接作为特征但可以从里面提取小时、月份、季节等周期特征。hour对光伏预测特别重要因为它隐含了太阳高度角信息。如果Data_Process.py里已经做了这步这里就不用重复。按 80/20 切分是常见做法样本量大的话可以调到 90/10样本少就 70/30。3.3 预测结果的反归一化与保存如果训练前对目标列做了归一化预测出来的是 [0,1] 区间的值必须反归一化才能还原成实际功率。这一步经常被漏掉导致预测结果看起来「小得离谱」。# 假设 scaler_y 是训练时对 power 列拟合的 scaler y_pred_original scaler_y.inverse_transform(y_pred.reshape(-1, 1)).flatten() y_test_original scaler_y.inverse_transform(y_test.reshape(-1, 1)).flatten() # 保存结果 result_df pd.DataFrame({ actual: y_test_original, predicted: y_pred_original }) result_df.to_csv(DC_Data/prediction_result.csv, indexFalse)反归一化要用训练集拟合的 scaler不能用测试集重新拟合否则等于引入了测试集信息。保存结果时把实际值和预测值放在一起后面画对比曲线或者算分时段误差都方便。3.4 Notebook 实验记录的使用方式DC_PV_Power_Predict_2018.ipynb是交互式实验记录通常包含数据探索、特征可视化、模型对比、调参过程这些内容。Notebook 的价值在于它保留了中间输出——你能看到每一步的图表和数值而不是只拿到一个最终脚本。打开 Notebook 后建议按顺序执行不要跳着跑。如果某个 cell 报错先检查前面的数据加载 cell 是否成功执行。Notebook 里常见的坑是变量名和脚本文件不一致比如 Notebook 里叫df_train脚本里叫train_df混着用会报 NameError。我一般会先把 Notebook 完整跑一遍确认所有 cell 都能通过再对照Train_Predict.py看两者的逻辑差异。4. 避坑与排查跑这份源码时最容易翻车的五个地方4.1 路径问题导致 FileNotFoundError现象运行Data_Process.py或Train_Predict.py时报FileNotFoundError: [Errno 2] No such file or directory: DC_Data/train_1.csv。原因脚本里的相对路径是相对于当前工作目录的不是相对于脚本文件所在目录。如果你在项目根目录的上一级运行python 项目文件夹/Train_Predict.py工作目录就不对了。解决要么cd到项目根目录再运行要么在脚本开头加一段路径修正import os os.chdir(os.path.dirname(os.path.abspath(__file__)))这行代码把工作目录切到脚本所在目录后面所有相对路径就都对了。4.2 pandas 版本差异导致 fillna 报错现象df.fillna(methodffill)报TypeError: fillna() got an unexpected keyword argument method。原因pandas 2.0 之后废弃了method参数改用ffill()和bfill()方法。解决把df.fillna(methodffill)改成df.ffill()df.fillna(methodbfill)改成df.bfill()。如果代码里还有inplaceTrue也建议改成赋值形式因为 pandas 3.0 会进一步限制 inplace 行为。4.3 归一化后目标列反归一化遗漏现象模型输出的预测值都在 0 到 1 之间和实际功率几百到几千 kW完全对不上。原因训练时对目标列做了归一化预测后忘了反归一化。解决检查Data_Process.py或Train_Predict.py里是否对power列做了fit_transform。如果有预测后必须用同一个 scaler 做inverse_transform。注意 scaler 要保存下来不能重新拟合。4.4 随机种子未固定导致结果不可复现现象同样的代码跑两次R2 一次 0.92 一次 0.87差距明显。原因模型初始化、数据切分、特征采样里只要有一处没固定random_state结果就会有随机波动。解决在模型初始化、train_test_split、任何涉及随机采样的地方都加上random_state42。如果是深度学习框架还要固定 numpy 和框架自身的随机种子。4.5 测试集评估指标异常好现象测试集 R2 超过 0.98但画预测曲线发现模型在夜间也输出了高功率。原因训练集和测试集随机切分导致时间泄漏模型学到了「未来」的信息。或者测试集里混入了训练集样本。解决改成按时间顺序切分确保测试集时间戳全部晚于训练集。同时检查数据去重确认没有同一条记录同时出现在 train 和 test 里。5. 从跑通到改出自己的一套特征构造与模型对比的实操建议跑通这份源码只是起点真正让它变成你自己的东西得在特征和模型上做文章。光伏功率预测的精度瓶颈往往不在模型本身而在特征质量。原始数据里的辐照度、温度、湿度是最基础的但太阳高度角、云量变化率、历史功率滑动平均这些衍生特征对提升预测精度帮助很大。一个我常用的特征构造套路是加「滞后特征」和「滑动窗口统计」# 滞后特征前一时刻的功率 df[power_lag1] df[power].shift(1) df[power_lag2] df[power].shift(2) # 滑动窗口过去 3 个时刻的均值和标准差 df[power_roll_mean3] df[power].rolling(window3).mean() df[power_roll_std3] df[power].rolling(window3).std() # 辐照度变化率 df[irradiance_diff] df[irradiance].diff() # 去掉因 shift/rolling 产生的首行空值 df df.dropna()滞后特征让模型能看到「上一时刻功率是多少」这对超短期光伏功率预测特别有用因为功率变化有惯性。滑动标准差捕捉的是波动剧烈程度云层快速移动时这个值会变大模型可以据此调整预测策略。辐照度变化率是判断天气突变的一个简单指标。模型对比方面建议至少跑三个基线随机森林、梯度提升树、和一个线性模型岭回归或 Lasso。线性模型虽然精度通常不如树模型但它的系数能告诉你哪些特征最重要对理解数据有帮助。对比时统一用同一组训练测试数据、同一套评估指标不要这个模型用 MAE 那个用 RMSE不然没法比。from sklearn.linear_model import Ridge from sklearn.ensemble import GradientBoostingRegressor models { Ridge: Ridge(alpha1.0), RandomForest: RandomForestRegressor(n_estimators200, max_depth12, random_state42), GBDT: GradientBoostingRegressor(n_estimators200, max_depth5, random_state42) } for name, model in models.items(): model.fit(X_train, y_train) pred model.predict(X_test) r2 r2_score(y_test, pred) print(f{name} R2: {r2:.4f})如果 GBDT 明显好于随机森林可以进一步试 XGBoost 或 LightGBM但要注意安装依赖。如果三个模型差距不大说明特征信息量已经榨得差不多了该回去补特征而不是换模型。还有一个容易被忽略的点分时段评估。光伏功率在早晨、正午、傍晚的预测难度完全不同正午辐照度稳定时预测很准日出日落时段功率爬坡快误差会大很多。把测试集按小时分组算 MAE能看出模型在哪些时段最弱test_df[hour] pd.to_datetime(test_df[timestamp]).dt.hour test_df[pred] y_pred_original test_df[actual] y_test_original hourly_mae test_df.groupby(hour).apply( lambda g: mean_absolute_error(g[actual], g[pred]) ) print(hourly_mae)这个分时段误差表比一个总体 R2 有用得多答辩时拿出来也能说明你对问题有深入理解。从那以后我每次拿到一个新的预测项目都会先跑一遍分时段误差确认模型不是只在「好预测」的时段表现好而是全时段都稳。希望帮到你。本文还有配套的精品资源点击获取