简介:这份资源是面向高校学生与机器学习入门者的光伏功率预测完整项目包,围绕毕业设计、期末大作业与课程设计场景,提供从数据处理到模型训练预测的全流程代码实现。包内共16个文件,以8个csv训练与测试数据集、4个Python脚本、1个Jupyter Notebook为主,另附说明文档与项目说明文件,压缩包约4.64MB,结构清晰、便于直接部署运行。代码含详细注释,新手也能看懂,作者自评98分并获导师认可。项目涵盖数据加载保存、数据预处理、模型训练与预测等模块,读者可据此理解光伏功率预测的建模思路与特征处理方式,并在此基础上替换数据或调整模型完成自己的课题。目前已有316人学习下载,适合需要快速搭建可运行项目、对照代码查漏补缺的读者参考使用。
1. 光伏功率预测项目拆包:一份能直接跑通的毕业设计源码长什么样
光伏功率预测这个方向,这两年做毕业设计和课程大作业的人特别多,原因很直接:数据能拿到、模型有说法、指标好写论文。但真正动手时,大部分人卡在同一个地方——网上找到的代码要么缺数据,要么跑不起来,要么注释等于没有,改一个参数就报错。这份DC_PV_Power_Predict_2018-master的资源包,是我近期拆过的同类项目里结构比较完整的一个:它同时给了源码、训练数据、测试数据,还有一份Task_Info.docx说明任务背景,main.py和DC_PV_Power_Predict_2018.ipynb两条入口都能走通。技术栈是 Python + 机器学习,核心文件Data_Process.py、Train_Predict.py、Load_Save_Data.py分工明确,DC_Data目录下按train_1.csv到train_4.csv、test_1.csv到test_4.csv切好了数据。适合谁?如果你正在做光伏功率预测的毕业设计、期末大作业,或者想找一个带完整数据管道的机器学习练手项目,这份东西能省掉你至少一周的数据清洗和调参试错时间。下面我按实际拆解顺序,把这份资源从结构到跑通、从参数到坑,一层层讲清楚。
2. 项目结构与数据管道:四个 CSV 怎么喂给模型
2.1 目录拆解与文件职责
拿到压缩包解压后,根目录下是DC_PV_Power_Predict_2018-master,里面文件不多,但每个都有明确用途。先看整体结构:
| 文件/目录 | 类型 | 作用 |
|---|---|---|
main.py | 入口脚本 | 串联数据加载、处理、训练、预测全流程 |
DC_PV_Power_Predict_2018.ipynb | Notebook | 分步演示,适合调试和看中间结果 |
Data_Process.py | 模块 | 数据清洗、特征构造、归一化 |
Train_Predict.py | 模块 | 模型定义、训练、评估、预测输出 |
Load_Save_Data.py | 模块 | 统一读写 CSV,避免路径硬编码 |
Task_Info.docx | 文档 | 任务背景、字段说明、预期指标 |
DC_Data/ | 数据目录 | 存放 train/test 的 8 个 CSV |
README.md | 说明 | 环境依赖和运行方式 |
这个拆分方式在毕业设计里算规范的:数据读写、处理、训练三件事分开,改一处不影响其他。Load_Save_Data.py的存在尤其关键,它把文件路径和读写逻辑收口,后面换数据集只需要改这一个文件里的路径常量,不用满项目搜pd.read_csv。
2.2 数据字段与四个 CSV 的切分逻辑
DC_Data下有 8 个文件:train_1.csv到train_4.csv,test_1.csv到test_4.csv。这种按编号切分的方式,常见做法是按时段或按季节划分——光伏功率受天气和季节影响极大,把不同月份或不同季度的数据分开,能验证模型在不同分布下的泛化能力。具体每个文件对应哪段时间,Task_Info.docx里应该有说明,跑之前先翻一下。
数据字段通常包含时间戳、辐照度、温度、湿度、风速、历史功率等。光伏功率预测的核心输入是辐照度,但单独用辐照度做特征容易过拟合,所以Data_Process.py里一般会构造时间特征(小时、月份)、滞后特征(前几个时刻的功率)和滚动统计量。先看数据加载这一层怎么写:
# Load_Save_Data.py 典型写法 import pandas as pd import os # 数据目录常量,换数据集只改这里 DATA_DIR = os.path.join(os.path.dirname(__file__), 'DC_Data') def load_csv(filename): """读取单个 CSV,返回 DataFrame""" path = os.path.join(DATA_DIR, filename) df = pd.read_csv(path, parse_dates=['time']) # 时间列解析成 datetime return df def save_csv(df, filename): """保存结果到 DC_Data 目录""" path = os.path.join(DATA_DIR, filename) df.to_csv(path, index=False)逻辑说明:parse_dates=['time']把时间列转成 datetime 类型,后面做时间特征提取时不用再转换。参数上,如果你的 CSV 时间列名不是time,改这个参数即可。DATA_DIR用os.path.dirname(__file__)拼绝对路径,避免从不同目录运行时找不到文件——这是新手最容易翻车的地方,直接写相对路径'DC_Data/train_1.csv',换个工作目录就报FileNotFoundError。
2.3 数据清洗与特征构造的关键步骤
Data_Process.py是整条管道里最值得细看的部分。光伏数据常见的脏数据包括:夜间功率为负、传感器掉线导致的连续缺失、辐照度突变。处理顺序一般是先补缺、再剔异常、最后构造特征。
# Data_Process.py 核心处理逻辑 import pandas as pd import numpy as np def clean_data(df): """清洗:插值补缺 + 剔除夜间负功率""" # 时间序列插值,limit=3 表示最多连续补 3 个点 df = df.interpolate(method='linear', limit=3) # 夜间功率理论上为 0,负值视为传感器噪声 df.loc[df['power'] < 0, 'power'] = 0 # 删除仍存在的缺失行 df = df.dropna() return df def build_features(df): """构造时间特征和滞后特征""" df['hour'] = df['time'].dt.hour df['month'] = df['time'].dt.month # 滞后 1、2、3 个时刻的功率作为历史特征 for lag in [1, 2, 3]: df[f'power_lag{lag}'] = df['power'].shift(lag) # 滚动均值,窗口 3 df['power_roll_mean3'] = df['power'].rolling(window=3).mean() df = df.dropna() # 滞后会产生 NaN,去掉 return df逻辑说明:interpolate的limit=3是防止连续大段缺失被线性插值填成假数据,超过 3 个点的缺失宁可丢掉。滞后特征shift(lag)把过去时刻的功率挪到当前行,让模型能利用时序信息。滚动均值窗口设 3,对应 3 个采样间隔,具体间隔看数据粒度——如果是 15 分钟采样,窗口 3 就是 45 分钟平滑。参数怎么改:数据缺失严重就把limit调大,但别超过 6;滞后阶数从 3 加到 6 可能提升精度,但特征维度上升,训练变慢,毕业设计里 3 到 4 阶够用。
提示:
build_features里dropna()会删掉前几行,因为滞后特征在开头是 NaN。如果测试集很短,删完可能没剩几行,这时候要么减小滞后阶数,要么用fillna(0)兜底。
3. 模型训练与预测:从 Train_Predict.py 到可复现的评估指标
3.1 模型选型与训练脚本拆解
Train_Predict.py是模型核心。光伏功率预测在毕业设计里常用的模型有:随机森林、XGBoost、LSTM、BP 神经网络。这份项目标题写的是“机器学习”,从文件命名和 2018 年的项目背景看,大概率是随机森林或梯度提升树这类传统模型,也可能是 sklearn 的 MLP。不管具体是哪个,训练脚本的结构都绕不开这几步:切分特征和标签、划分训练验证集、拟合、预测、算指标。
# Train_Predict.py 训练主流程 from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import train_test_split from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score import numpy as np def train_model(X, y): """训练随机森林回归模型""" # 8:2 划分,random_state 固定保证可复现 X_train, X_val, y_train, y_val = train_test_split( X, y, test_size=0.2, random_state=42 ) # n_estimators=100 棵树,max_depth 限制防止过拟合 model = RandomForestRegressor( n_estimators=100, max_depth=10, random_state=42, n_jobs=-1 # 用满 CPU 核心 ) model.fit(X_train, y_train) y_pred = model.predict(X_val) # 三个指标:RMSE、MAE、R2 rmse = np.sqrt(mean_squared_error(y_val, y_pred)) mae = mean_absolute_error(y_val, y_pred) r2 = r2_score(y_val, y_pred) print(f'RMSE: {rmse:.4f}, MAE: {mae:.4f}, R2: {r2:.4f}') return model逻辑说明:random_state=42固定随机种子,保证每次跑结果一致,论文里写指标才站得住。n_estimators=100是精度和速度的平衡点,加到 200 精度提升有限但训练时间翻倍。max_depth=10限制树深,光伏数据噪声大,不限制深度容易过拟合,验证集 R2 反而下降。n_jobs=-1用满所有 CPU 核心,数据量大时能省不少时间。参数怎么调:先跑默认看 R2,如果低于 0.8,把n_estimators加到 200、max_depth加到 15 再试;如果验证集 R2 远低于训练集,说明过拟合,反过来减小max_depth。
3.2 评估指标怎么看:RMSE、MAE、R2 的实际含义
光伏功率预测的评估指标不是随便报一个就行,答辩时导师一定会问。RMSE(均方根误差)对大误差敏感,能反映模型在功率突变时的表现;MAE(平均绝对误差)更直观,单位跟功率一样;R2 决定系数反映拟合优度,越接近 1 越好。三个指标要一起看:RMSE 大但 MAE 小,说明有个别时刻预测偏得离谱;R2 高但 RMSE 也高,可能是数据方差大导致的假象。
常见做法是再补一个 MAPE(平均绝对百分比误差),但光伏功率在夜间接近 0,MAPE 会爆炸,所以这个项目里用 RMSE 和 MAE 更稳。如果你要在论文里对比多个模型,建议统一用同一组测试集、同一套指标,别这个模型报 RMSE、那个报 MAPE,导师一眼就能看出问题。
3.3 预测结果输出与可视化
训练完模型,main.py里一般会把预测结果存回 CSV,方便画图。Load_Save_Data.py的save_csv这时候就派上用场了。
# main.py 预测并保存结果 from Load_Save_Data import load_csv, save_csv from Data_Process import clean_data, build_features from Train_Predict import train_model import pandas as pd # 加载训练数据 df_train = load_csv('train_1.csv') df_train = clean_data(df_train) df_train = build_features(df_train) # 特征列和标签列 feature_cols = [c for c in df_train.columns if c not in ['time', 'power']] X = df_train[feature_cols].values y = df_train['power'].values model = train_model(X, y) # 加载测试数据,同样处理 df_test = load_csv('test_1.csv') df_test = clean_data(df_test) df_test = build_features(df_test) X_test = df_test[feature_cols].values y_test = df_test['power'].values # 预测并保存 y_pred = model.predict(X_test) df_test['power_pred'] = y_pred save_csv(df_test[['time', 'power', 'power_pred']], 'result_1.csv')逻辑说明:feature_cols用列表推导排除time和power,自动拿到所有特征列,加新特征不用改代码。测试集必须走跟训练集完全一样的clean_data和build_features,否则特征对不上,预测结果没意义。保存时只留时间、真实功率、预测功率三列,画对比曲线够用。参数上,如果测试集文件名不是test_1.csv,改load_csv的参数即可,四个测试集可以循环跑一遍,看模型在不同时段的表现差异。
注意:
build_features里的滞后特征在测试集开头也会产生 NaN,dropna后测试集前几行被删掉,预测结果的时间起点会比原始测试集晚几个采样点。画图时对齐时间轴,别直接按行号画,否则曲线会错位。
4. 避坑与排查:跑这份源码时最容易翻车的五个地方
4.1 现象:运行 main.py 报 FileNotFoundError
原因:Load_Save_Data.py里如果用了相对路径,而你在项目根目录之外的目录运行脚本,Python 找不到DC_Data文件夹。解决:把DATA_DIR改成基于__file__的绝对路径,或者运行前先cd到项目根目录。我一般会在main.py开头加一行os.chdir(os.path.dirname(os.path.abspath(__file__))),强制把工作目录切到脚本所在目录,一劳永逸。
4.2 现象:训练完 R2 是负数
原因:特征里混入了时间戳或 ID 这类无意义列,或者标签列选错了。光伏功率预测的标签必须是功率列,如果feature_cols把power也包含进去,模型直接拿答案当特征,验证集上看着好,测试集上崩。解决:打印feature_cols确认没包含标签列,同时检查time列有没有被误转成数值特征。另外,如果数据没做归一化,某些模型(如 MLP、SVM)会收敛困难,R2 也可能为负,加一个StandardScaler试试。
4.3 现象:四个 train CSV 跑出来指标差异巨大
原因:四个文件对应不同季节或天气类型,光伏功率分布本身就不一样。夏季辐照强、功率高,冬季功率低,模型在夏季数据上训练、冬季数据上测试,R2 掉到 0.5 以下很正常。解决:这不是代码 bug,是数据特性。论文里可以按季节分别训练模型,或者把四个文件合并后随机划分,看整体泛化能力。如果导师要求单模型跨季节,考虑加天气类型作为特征,或者用对分布偏移更鲁棒的模型。
4.4 现象:Notebook 里能跑,main.py 报模块导入错误
原因:Notebook 的工作目录和脚本运行目录不一致,import Data_Process在 Notebook 里能找到,命令行跑python main.py时找不到。解决:确保所有.py文件在同一目录下,或者在main.py开头把项目根目录加到sys.path:
import sys, os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))这样无论从哪个目录运行,Python 都能找到同目录的模块。
4.5 现象:预测曲线整体偏低或偏高
原因:训练集和测试集的功率量纲不一致,或者归一化只做了一半。常见情况是训练时用了MinMaxScaler把功率缩到 0-1,预测完忘了inverse_transform,结果输出全是小数。解决:检查Train_Predict.py里有没有 scaler 的 fit 和 inverse 配对。如果用了 scaler,训练时fit_transform,预测时先transform再inverse_transform,顺序别搞反。另外,光伏功率夜间为 0,如果测试集包含夜间时段,预测值也应该接近 0,如果夜间预测出正值,说明模型没学好,检查夜间样本有没有被正确标记。
5. 进阶技巧:把单文件脚本改成可复用的实验框架
跑通main.py只是第一步。如果你要拿这份代码做毕业设计,导师大概率会让你对比不同模型、不同特征组合的效果。这时候把Train_Predict.py里的训练逻辑抽成一个可配置的函数,会省很多重复劳动。
# 可复用的实验函数 from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor from sklearn.neural_network import MLPRegressor from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline from sklearn.metrics import mean_squared_error, r2_score import numpy as np def run_experiment(X_train, y_train, X_test, y_test, model_name='rf'): """统一实验入口,换模型只改 model_name""" models = { 'rf': RandomForestRegressor(n_estimators=100, max_depth=10, random_state=42, n_jobs=-1), 'gbdt': GradientBoostingRegressor(n_estimators=100, max_depth=5, random_state=42), 'mlp': Pipeline([ ('scaler', StandardScaler()), ('mlp', MLPRegressor(hidden_layer_sizes=(64, 32), max_iter=500, random_state=42)) ]) } model = models[model_name] model.fit(X_train, y_train) y_pred = model.predict(X_test) rmse = np.sqrt(mean_squared_error(y_test, y_pred)) r2 = r2_score(y_test, y_pred) return {'model': model_name, 'rmse': rmse, 'r2': r2, 'pred': y_pred}逻辑说明:models字典把三个模型的配置收在一起,换模型只改model_name参数。MLP 前面套了StandardScaler,因为神经网络对特征尺度敏感,不归一化收敛慢甚至不收敛。hidden_layer_sizes=(64, 32)是两层隐藏层,节点数按特征维度的大致比例设,特征多就加宽。max_iter=500是迭代上限,MLP 默认 200 有时不够,加到 500 看损失曲线是否平稳。
跑对比实验时,把四个 train CSV 合并成一个大数据集,用train_test_split随机划分,然后循环调用run_experiment:
import pandas as pd from Load_Save_Data import load_csv from Data_Process import clean_data, build_features # 合并四个训练文件 dfs = [load_csv(f'train_{i}.csv') for i in range(1, 5)] df_all = pd.concat(dfs, ignore_index=True) df_all = clean_data(df_all) df_all = build_features(df_all) feature_cols = [c for c in df_all.columns if c not in ['time', 'power']] X = df_all[feature_cols].values y = df_all['power'].values from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) results = [] for name in ['rf', 'gbdt', 'mlp']: res = run_experiment(X_train, y_train, X_test, y_test, model_name=name) results.append(res) print(f"{res['model']}: RMSE={res['rmse']:.4f}, R2={res['r2']:.4f}")这样一轮跑下来,三个模型的指标直接对比,论文里的实验表格就有了。我一般还会把results存成 CSV,方便后面画柱状图。参数上,test_size=0.2是常用划分比例,数据量小可以调到 0.3 留更多测试样本;random_state固定住,保证每次划分一致,对比才公平。
还有一个容易被忽略的点:光伏功率预测的时序特性意味着不能简单随机划分。随机划分会让未来时刻的数据混进训练集,造成信息泄露,测试指标虚高。更严谨的做法是按时间顺序切分,前 80% 做训练、后 20% 做测试。改法很简单:
# 按时间顺序切分,避免信息泄露 split_idx = int(len(df_all) * 0.8) train_df = df_all.iloc[:split_idx] test_df = df_all.iloc[split_idx:] X_train = train_df[feature_cols].values y_train = train_df['power'].values X_test = test_df[feature_cols].values y_test = test_df['power'].values这个细节在答辩时是加分项,导师问“你怎么保证没有数据泄露”,你能答上来按时间切分,说明你真正理解时序预测的边界。从那以后我每次做时序项目,都强制先按时间切分再构造特征,绝不先train_test_split再处理——这个习惯帮我避开了好几次指标虚高的坑。希望帮到你。
本文还有配套的精品资源,点击获取