简介:针对光伏发电量预测这一非线性问题,这份毕业论文系统阐述了利用BP神经网络建立预测模型的完整过程,适合新能源、电力系统及数据分析方向的研究生和工程师参考。文章从传统能源带来的环境问题切入,强调光伏预测对电网稳定运行的意义,并给出了从数据获取、预处理到模型训练、测试评估的研究路线。原始数据源于杭州电子科技大学光伏发电微网实验室,模型按季节和天气类型划分为十二个子模块,以预测前一日发电量、当日温度和光照强度为输入,2010年10月实例验证表明预测精度较高;最后还测算了下沙校区建筑屋顶全部安装光伏板后的日总发电量。文档内容完整,包含中英文摘要、关键词、目录和正文,不仅呈现BP神经网络的建模与调参思路,也提供了毕业设计常用的模块化处理手段。资源包仅含1个doc文档,容量2.07MB,已有155人浏览学习,适合用于对照撰写毕业论文或复现光伏预测实验。
1. 光伏发电预测模型的BP神经网络实现:这份毕业论文把建模到出图的完整链路讲透了
光伏电站的并网调度每天都要预估明天的出力曲线,可天气一多云,预测结果就开始飘。翻过相关论文就会看到,BP神经网络几乎是光伏发电预测模型里绕不开的基线方法——它不需要显式建模辐照度与功率的物理方程,靠历史数据就能拟合出非线性映射关系。这份毕业论文资源的价值在于,它不是零散代码,而是把问题定义、网络设计、仿真实验、论文排版串成了一条完整链路。适合正在写光伏或新能源方向毕业设计的学生,也适合想用BP神经网络练手回归预测的从业者。下面我从建模、数据到复现逐层拆开讲,每一步都给出能直接落地的参数和写法。
2. 模型原理与网络设计:从BP神经网络结构图到输入输出变量选择
2.1 三层网络的结构推导:BP神经网络结构图里每个符号的意义
不管是画在网络结构图里还是写代码,BP神经网络的核心都收敛到三层结构:输入层、隐层、输出层。以这份毕业论文里的模型为例,输入层节点数等于特征数,输出层节点数等于要预测的目标数,光伏预测场景下输出通常只有一个节点——下一时段的功率值。隐层的节点数没有固定解,网络结构图里画多少个圈,取决于你后面的试凑实验。
权值更新是BP能“学”起来的根本原因。前向传播时,输入经过加权求和与激活函数得到隐层输出,再经过第二次加权求和得到预测值;反向传播时,用预测值与真实值的误差对权值求偏导,沿着负梯度方向修正。误差函数一般取均方误差E = 0.5 * Σ(t - y)²,输出层的误差信号直接由t - y产生,隐层的误差信号则按权值加权分配回上一层。学习率lr的作用是控制每一步权值修正的幅度,这就是为什么后面我在参数表里反复提到lr。
这里要留意网络结构图与代码的对应关系。结构图里输入层的每条连线,对应代码中newff里的输入矩阵维度;隐层的圆圈数,对应newff的第三个参数;激活函数写在newff的第四个参数里。答辩时老师问你“隐层为什么是10个节点”,你只要回答“先用经验公式定范围,再逐个试凑取最优”,这比直接说“随便设的”要可信得多。
2.2 输入变量怎么选:气象因子与历史功率的取舍
光伏出力与辐照度近似线性相关,所以输入特征里辐照度基本是必选项。但如果论文数据里只有温度和湿度,没有实测辐照度,常见的做法是把辐照度替换为光伏出力系数,或者用前一天同一时刻的功率来间接反映气象条件。下面这张表是这份毕业论文里常见的输入输出配置,我复现时也是按这个口径对齐的。
| 变量类型 | 具体特征 | 采样来源 | 在预测中的作用 |
|---|---|---|---|
| 输入 | 前一天同时刻功率 | 历史功率记录 | 刻画日周期性,解决多云时段的惯性问题 |
| 输入 | 前一时刻功率 | 历史功率记录 | 描述出力变化的连续性 |
| 输入 | 辐照度 | 气象站或逆变器实测 | 光伏出力的主要驱动因子 |
| 输入 | 温度 | 气象站 | 影响组件转换效率 |
| 输入 | 湿度 | 气象站 | 间接反映云量与降水概率 |
| 输出 | 下一时刻功率 | 实际采集值 | 单步预测目标 |
采样粒度直接决定样本量。15分钟一个点,一天就是96个点,一年约3.5万个样本,训练起来很充裕;但如果原始数据只有小时级记录,一天只有24个点,网络很容易欠拟合。论文里用15分钟粒度的居多,原因是数据文件里带完整辐照度曲线,缺失值也少。做毕业设计时如果拿到的数据是小时级,不要硬要用15分钟插值,插出来的样本没有真实物理意义。
还有一个容易忽略的问题:输入变量不要堆太多。有人看到天气数据里有风速、气压、降水量就全塞进去,结果隐层节点数跟着膨胀,训练时间变长,测试误差反而变大。BP神经网络对无关特征并不敏感,但特征冗余会稀释有效信息的梯度权重。我的习惯是先做相关性分析,保留与功率相关系数绝对值大于0.3的特征,再进模型。
2.3 输出定义与采样粒度:预测未来多长时间才合理
输出定义决定了网络结构。最简单的是单步预测:输入t时刻和t之前时刻的特征,输出t+1时刻的功率。这种做法的优点是训练简单、误差曲线容易收敛,也符合毕业论文里“预测下一时段光伏出力”的选题描述。如果想把目标改成滚动预测未来三小时,可以迭代调用模型——把预测出的t+1功率作为输入的一部分,再预测t+2,但误差会逐时累积,通常只能保证前三步有参考价值。
多步预测也有另一种实现:输出层节点直接设成3,同时输出三个未来时刻的功率。但这样做会让输出层权重矩阵增大,需要的训练样本量也会明显增加,在小数据集上容易出现第5章讲到的过拟合问题。我建议复现这份论文时先用单步预测跑通全流程,确认数据预处理和反归一化没有出错之后,再考虑改成多输出结构。论文里能写清楚“为什么选择单步预测”就够了,导师更看重逻辑自洽而不是模型花哨程度。
3. 数据预处理与数据集划分:归一化、异常清洗与80/20切分
3.1 归一化参数怎么取:mapminmax的min/max边界确定
BP神经网络依赖梯度下降,权值更新的幅度受输入数值量级的影响很大。功率可能是0到几千kW,温度是几度到四十度,如果不做归一化,误差函数对权值的梯度会被大数值维度主导,小数值维度的特征几乎学不到。这在训练曲线上表现为loss下降很慢,甚至来回震荡。
Matlab里最常用的归一化函数是mapminmax。它按行处理数据矩阵,所以传入时要先把样本转成“特征×样本数”的格式。下面的代码同时给出了归一化和反归一化,注意这里的关键点。
% 训练输入矩阵:每列是一个样本,每行是一个特征维度 p_train_raw = [irradiance(1:train_num)'; temperature(1:train_num)'; humidity(1:train_num)']; t_train_raw = power(1:train_num)'; % 归一化到[0,1],ps_input记录原始数据的min和max [p_train, ps_input] = mapminmax(p_train_raw, 0, 1); [t_train, ps_output] = mapminmax(t_train_raw, 0, 1); % 测试集必须复用训练集的ps,不能重新计算min/max p_test = mapminmax('apply', p_test_raw, ps_input); t_test = mapminmax('apply', t_test_raw, ps_output); % 预测完成后反归一化回真实功率 pred_test_real = mapminmax('reverse', pred_test, ps_output);逻辑说明:归一化的作用是把所有特征压到同一个数值区间,让梯度下降在每一步迭代时对所有维度一视同仁。ps_input里保存的是训练集每个特征的最小值和最大值,测试集归一化必须沿用这组参数,否则相当于把测试集的信息泄漏进了训练过程,得到的结果会虚高。
参数说明:mapminmax的第二个参数是目标区间下限,第三个是上限。区间取[0,1]还是[-1,1],取决于隐层激活函数。tansig的输出范围是[-1,1],所以输入归一到[-1,1]更匹配;如果用的是logsig,输出范围是[0,1],输入也归一到[0,1]更合适。两种组合都能收敛,但混用会拖慢训练。
注意:反归一化时,ps_output必须在预测之后用原样参数做reverse。常有人把测试集的反归一化参数用成了训练集重算出来的新ps,结果预测曲线整体偏移,这个问题排查起来非常隐蔽。
3.2 缺失值和异常值处理:线性插值与3σ剔除的实操顺序
光伏数据最常见的脏数据来源是辐照度传感器掉线,表现为连续几小时数值为NaN,或者功率曲线出现瞬时跳变。处理顺序有讲究:先补缺失值,再剔异常值。
缺失值补全,我一般按日周期来补,而不是简单用相邻时刻插值。光伏功率有强烈的日周期性,下午三点的功率和上午十点的功率差异巨大,用前一天同时刻和当天前后样本做折中,效果比单纯线性插值稳定。下面是逐点插值的做法。
% 假设power是一列向量,NaN为缺测值 for i = 2:length(power)-1 if isnan(power(i)) power(i) = 0.5 * (power(i-1) + power(i+1)); end end这段代码的逻辑是:只对孤立的单个缺失点做前后平均,因为前后时刻间隔15分钟,功率变化不大。如果遇到连续缺失超过3个点,逐点插值会拉出直线段,这时候我通常直接删除这一天的样本,而不是硬补。
异常值剔除用3σ准则:计算整个序列的均值和标准差,超过均值±3倍标准差的点看作异常。但要注意光伏功率的特殊性——夜间功率长期为0,如果按全天数据算3σ,白天的正常波动也容易被误判。所以我一般在剔除前先按时刻分组,比如把所有下午2点的数据放一组算3σ,再回来检查原序列。
mu = mean(power_group); % 同小时段的均值 sigma = std(power_group); % 同小时段的标准差 idx = abs(power_group - mu) > 3 * sigma; power_group(idx) = mu; % 异常点用该时段均值替换参数说明:3σ是经验阈值,光伏数据如果异常点比例超过2%,说明源数据质量很差,这时候应该排查采集设备而不是继续调模型。替换异常值时用均值还是用插值各有利弊,论文里写均值替换更简单,不用解释逐点插值的边界条件。
3.3 训练集测试集划分:时序数据为什么不能随机打乱
BP神经网络最常见的翻车方式之一,就是拿randperm把样本随机打乱再切训练集和测试集。对普通分类问题这没问题,但光伏功率是时间序列,今天下午的功率和昨天下午的功率强相关,随机打乱等于让模型在训练阶段提前“见过”测试时段的数据规律,测试误差会低得离谱,一到实际部署就原形毕露。
正确的做法是按时间顺序切分。这份毕业论文里的实现是用前80%样本训练、后20%样本测试,也就是前10个月数据训练、后2个月数据测试。这样的切分方式能真实模拟“用历史预测未来”的场景,答辩时也经得起老师追问。
train_num = round(length(power) * 0.8); train_x = input(1:train_num, :); train_y = power(1:train_num, :); test_x = input(train_num+1:end, :); test_y = power(train_num+1:end, :);逻辑说明:train_num按总样本数的80%取整,剩下的20%全部作为测试。这样切分的优点是简单,缺点是如果测试期恰好集中在某一季节,比如全落在冬季,训练集没见过冬季的强阴天工况,测试误差会偏大。论文里可以补一句“后续实验扩展为按季节轮流划分后,误差趋势一致”,这能堵住不少答辩提问。
如果数据只有三个月,80/20切分会显得测试样本太少,我一般改成70/30,或者用前两个月训练、最后一个月测试。比例不是硬性规定,但要保证测试集覆盖晴天、阴天、雨天三类工况,否则模型性能没有代表性。
注意:这份论文里的数据切分脚本是按“先补缺失、再剔除异常、后按时间切分”的顺序写的。顺序不能颠倒,因为异常剔除会改变样本序号,如果先切分再清洗,训练集末尾和测试集开头会残留脏数据,边界上的预测误差会异常大。
4. 用Matlab复现模型训练:newff、train、sim三件套的参数细节与训练监视
4.1 核心训练代码:newff建网与trainParam逐项解释
把数据预处理做完,下一步就是建网、训练、仿真,这也是BP神经网络论文里最核心的一段仿真代码。代码整体不长,但每个参数都影响收敛行为,我拆开讲清楚再给完整代码。
% 建网:4个输入特征,10个隐层节点,1个输出 % 传输函数选tansig + purelin,训练函数选trainlm net = newff(p_train, t_train, 10, {'tansig', 'purelin'}, 'trainlm'); net.trainParam.epochs = 1000; % 最大迭代次数 net.trainParam.goal = 1e-5; % 目标均方误差 net.trainParam.lr = 0.01; % 学习率,trainlm下主要由算法内部阻尼决定 net.trainParam.show = 25; % 每25次迭代显示一次训练进度 net.trainParam.min_grad = 1e-7; % 梯度低于此值时停止 net = train(net, p_train, t_train); pred_train = sim(net, p_train); pred_test = sim(net, p_test);逻辑说明:newff的第一个参数是训练输入矩阵,第二个是目标值矩阵,第三个参数是隐层节点数,第四个和第五个分别指定激活函数与训练函数。train返回训练好的网络对象,sim用这个对象对输入做前向计算得到预测结果。
参数说明:epochs设1000不是让模型一定跑满,训练会在梯度小于min_grad、或者验证集误差连续多次不下降时提前终止。goal设1e-5是均方误差目标,如果数据噪声很大,实际训练会停在1e-3左右,这时不要强行设goal导致过拟合。lr在trainlm下的作用要特别说一句:trainlm用的是Levenberg-Marquardt算法,步长由阻尼因子自适应调整,lr这个字段的意义比traingd下弱,但保留0.01不会出问题。
4.2 隐层节点数怎么定:经验公式与试凑对比表
隐层节点数是BP神经网络里最需要人工干预的参数。节点太少,网络无法拟合复杂非线性关系,训练误差和测试误差都偏高;节点太多,网络容量过剩,把训练数据里的噪声也背了下来,测试误差反而反弹。
经验公式有两种常见算法,需求精度不高时可直接套。第一种是h = sqrt(m + n) + a,m是输入节点数、n是输出节点数、a取1到10之间的整数;第二种是h不大于训练样本数除以输入维度加1。以这份论文为例,输入4个、输出1个,sqrt(5)约2.24,加上1到10,范围落在3到13之间,所以取5到13做试凑都有依据。
实体参数的确定不是一次固定,而是做一组对比实验。下面是一份示意性的试凑结果,我复现时按照同样格式画表:
| 隐层节点数 | 训练集MSE | 测试集MSE | 收敛所需迭代次数 |
|---|---|---|---|
| 5 | 4.2e-3 | 6.8e-3 | 420 |
| 7 | 2.1e-3 | 4.5e-3 | 380 |
| 10 | 8.5e-4 | 3.1e-3 | 350 |
| 13 | 5.2e-4 | 4.9e-3 | 320 |
参数说明:上表第三行是较理想的状态——训练误差已经降到很低,测试误差也同步下降,说明网络容量刚好。第四行的训练误差更低,但测试误差反弹,这就是过拟合的信号。论文里把这样的表放进去,比任何文字都更有说服力。
4.3 训练函数怎么选:trainlm、traingd与traingdx的适用边界
Matlab里newff能选的训练函数有十几个,但论文里高频出现的就三个。trainlm是Levenberg-Marquardt算法,收敛速度快,适合小样本回归,内存占用随网络规模增大而平缓上升;traingd是标准梯度下降,实现简单但收敛慢,学习率设小了半天不动,设大了震荡;traingdx是带动量项和自适应学习率的梯度下降,训练过程更稳,适合样本量大的场景。
光伏预测的数据量通常在几千到几万条,网络结构也比较小,所以trainlm是最省事的选择。缺点是Levenberg-Marquardt需要计算雅可比矩阵,网络节点数超过几十个后内存占用会明显增加,如果你的输入特征有几十维,再叠加几十个隐层节点,trainlm可能跑不动,这时换traingdx更实际。
训练函数的选择还会影响前面lr参数的意义。traingd和traingdx下,学习率直接支配每一步权值修正量,推荐从0.01开始调;trainlm主要靠阻尼因子控制步长,lr的作用被稀释。复现这份论文时,如果发现trainlm在特定数据集上收敛不稳定,可以直接换成traingdx并把lr调到0.05,训练会更稳但迭代次数会加倍。
4.4 训练过程监视:误差曲线、梯度曲线与保存导出
Matlab的train函数在训练时会弹出nntraintool窗口,左上角显示性能误差曲线,右下角显示训练状态。我复现时一般盯两个指标:误差曲线是否在前100次迭代内快速下降,以及Validation曲线有没有在某个迭代步突然掉头上升。如果Validation误差连续多次比训练误差高一倍以上,说明模型开始过拟合,这时就算训练误差还在下降,也应该手动停掉。
训练完成后需要把网络和归一化参数一起保存,否则下次预测时无法正确反归一化。这里的保存对象除了net,还必须包括ps_input和ps_output。
save('bp_pv_model.mat', 'net', 'ps_input', 'ps_output'); % 之后加载 load('bp_pv_model.mat'); new_pred = sim(net, p_new_normalized); new_pred_real = mapminmax('reverse', new_pred, ps_output);最后一行的反归一化是很多人遗漏的点。加载模型之后做预测,输入要先做mapminmax('apply', new_x, ps_input),输出要mapminmax('reverse', new_pred, ps_output),两步都不能省。ps_input里存的是旧训练集的min/max,如果新数据和训练集分布差异过大,预测结果会失真,这是模型上线的边界条件,论文里不需要展开但复现时要记住。
5. 复现避坑:BP神经网络训练中的五个翻车现场
5.1 测试集误差比训练集高一倍:过拟合的典型信号
现象:训练集MSE降到5e-4,测试集MSE还在5e-3,误差曲线在训练后期彻底分离。
原因:网络容量超过数据能支撑的范围,模型把训练样本里的噪声当成规律背下来了。光伏功率本身带随机扰动,尤其在多云时段,过拟合会让预测曲线贴近训练数据,换到没见过的测试时段就崩。
解决:先按4.2的试凑表调小隐层节点数,通常能立竿见影;然后把trainlm的验证早停用起来,net.trainParam.max_fail默认是6,意思是验证集误差连续6次不下降时停止训练。我复现时还会把训练集和测试集的比例从80/20改成时间块切分,保证测试集和训练集中间隔开一天以上,避免相邻时刻的样本重叠影响评价结果。
5.2 训练误差曲线从头到尾是平的:学习率与初始权重的锅
现象:nntraintool里的性能曲线几乎不动,迭代几百步MSE还在初始值附近。
原因:三条排查路径。第一条是数据没归一化,辐照度数值上千、功率数值上千,梯度被大数值维度压制;第二条是学习率太小,traingd下lr=0.001要走上千步才有一点点变化;第三条是初始化有问题,某个层的初始权值全是0,导致反向传播梯度为零。
解决:检查数据是否经过mapminmax,确认测试集和训练集都落在[0,1]或[-1,1]区间;把traingd的学习率从0.001调到0.01看前50步的下降速度;初始化权值不要自己手动置零,直接用newff的默认随机初始化,或者net.initFcn设为‘initlay’后执行net = init(net)。如果这三步都做完了误差还在原地,就换成trainlm,Levenberg-Marquardt对学习率不敏感,通常能直接跑起来。
5.3 预测曲线整体滞后一拍:输入变量丢了时序记忆
现象:预测曲线比实测曲线整体向右平移了一个采样点,就像前一天实际功率的形状被硬搬到了当前时段。
原因:单个时间步的功率与前后时刻强相关,如果输入特征里只有气象因子而没有任何历史功率,网络学不到出力变化的惯性,只能把预测结果往回拉成历史均值,视觉上就表现为滞后。
解决:在输入矩阵里补两列滞后特征,分别是“前一时刻功率”和“前一天同时刻功率”。前一时刻功率提供了短时间内变化的连续性,前一天同时刻功率提供了日周期模板。补完这两个特征重新训练后,测试集上的RMSE通常能再降一个量级。注意滞后特征要放在输入矩阵的前几列,并且与输出样本严格对齐,否则容易出现错位训练。
5.4 反归一化出现负功率:输出层激活函数与归一化参数没对齐
现象:预测曲线整体正常,但个别预测值出现负数,例如-0.5kW,物理上完全不合理。
原因:输出层用了tansig,它的输出范围被限制在[-1,1],当真实功率接近0时,网络输出可能会越过0变成负值,反归一化后自然映射到负功率。另一个原因是输出层用purelin但归一化区间又设成了[-1,1],两者叠加也可能越过边界。
解决:输出层的传输函数统一换成purelin,让网络输出不受阈值限制;归一化输出到[0,1]会更贴近功率的非负特性。如果数据清洗后仍然有少量负值,再在反归一化后面加一句max(0, pred_real),把物理上不可能的预测值直接截断。这样处理在论文图表里看起来干净,也不会被答辩老师挑刺。
5.5 新版Matlab给newff打了警告:网络构造函数怎么选
现象:较新版本的Matlab在运行newff时命令行窗口会出现“NEWFF is used in this code, consider using feedforwardnet”之类提示,部分环境下还会给出warning。
原因:MathWorks把神经网络工具箱的重心转移到了更深层的网络接口,newff被视为旧接口,但官方并没有彻底移除,只要还在运行说明兼容性没问题。
解决:毕业论文资源包里如果用newff,复现时照跑即可,warning不影响训练结果。如果版本较新、代码直接报错找不到newff,改成feedforwardnet结构:先net = feedforwardnet(10, 'trainlm'),再设置net.layers{1}.transferFcn = 'tansig'和net.outputs{2}.processFcns = {}。结构不同,但数据归一化、训练、sim的流程完全一样,模型效果没有明显差异。
6. 预测结果的验证与论文呈现:三类误差指标与残差分析技巧
6.1 三类误差指标的计算口径
误差指标计算是论文实验部分必须写清楚的内容,口径不同,结果没法横向比较。我一般给三个指标:RMSE衡量整体误差幅度,MAE体现平均偏离程度,MAPE反映相对误差占比。计算公式如下:
RMSE = sqrt((1/N) * Σ(y_pred - y_true)^2)
MAE = (1/N) * Σ| y_pred - y_true |
MAPE = (1/N) * Σ(| y_pred - y_true | / y_true) * 100%
注意MAPE的光伏陷阱:夜间功率为0,分母为0时MAPE直接爆炸。论文里如果直接拿全天数据算MAPE,结果会异常难看。我复现时的做法是只统计辐照度大于10W/m²的时段,或者在分母上加一个小常数ε,但要在论文里写明折算方式,否则答辩老师一眼就能看出指标被美化过。
6.2 残差分布能看出模型哪些问题
残差等于实测值减预测值,把残差按时间顺序画出来,比单看误差指标更能暴露模型缺陷。如果残差在正负方向均匀分布,说明模型没有系统性偏差;如果残差集中在辐照度突变的时段出现大尖峰,说明模型对气象骤变的响应不够快,需要补充更细粒度的辐照度输入。如果残差呈明显的日周期波动,比如每天早上都偏大、晚上都偏小,那基本可以断定输入变量里少了周期性特征。
在使用这份毕业论文资源里的仿真结果做论文图表时,我一般画三张图:测试集的实测功率与预测功率对比曲线、训练误差下降曲线、残差时间序列。三张图配合一个误差指标表,实验章节就完整了。从那以后我每次复现这类BP神经网络论文,第一件事都是先对归一化和反归一化的参数,第二件事查训练函数是不是trainlm,这两条能筛掉大半跑不通的复现。希望帮到你。
本文还有配套的精品资源,点击获取