ARTICLE DETAIL

资讯详情

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

基于BP神经网络的光伏辐照度预测Matlab仿真实现与优化

基于BP神经网络的光伏辐照度预测Matlab仿真实现与优化 简介基于BP神经网络实现太阳辐照度预测的Matlab仿真包适合光伏发电预测方向的研究者、相关课题学生及对神经网络建模感兴趣的工程师。资源从人工神经网络逼近复杂非线性函数的特性出发给出完整的BP网络预测输出实现覆盖数据加载、网络训练、预测输出等环节可用于学习辐照度预测建模及数据预处理方法。压缩包共4个文件包含3个MAT格式数据文件误差数据、辐照温度数据等和1个M脚本主程序包体大小15.32MB结构精简便于直接运行调试。已有311人下载学习是光伏预测入门实践的实用参考。读者可获取完整可运行的BP网络预测源代码、配套训练数据及误差记录既能对照研究BP网络参数设置与训练流程也可在原有基础上扩展改进算法整体兼顾理论理解与动手实践。1. 光伏辐照度预测为什么非BP不可一个回归问题的工程化选择做光伏电站的发电量预测时太阳辐照度是最难拿到的输入量。数值天气预报通常给出云量、温度和湿度但不会直接给一张辐照度曲线现场实测数据又常常缺一段坏一段。于是很多人把目光投向BP神经网络手里有历史辐照度配合气象数据用Matlab仿真源码训练一个网络把“未来的辐照度”当作回归输出来预测。这份标题里的“基于BP神经网络的光伏发电太阳辐照度预测Matlab仿真源码.rar”本质上就是数据预处理、网络构建、训练、预测、画图这几段脚本的组合。这篇内容想帮你做的不是把源码包打开看一遍而是三件事搞清楚网络结构为什么这样设把训练和预测脚本改成能跑自己数据的版本避开时序预测里最容易踩的数据泄漏坑。如果你有基础的Matlab操作能力手里有至少几十天的辐照度或气象历史数据这篇东西可以当一份可复现的作业指导来用。装好Matlab和神经网络工具箱就能跑不依赖额外硬件也不需要高配置电脑。下面从BP网络的输入输出设计开始一步步把仿真源码改成自己能用、敢用的预测工具。2. 先定输入输出再谈结构BP神经网络做辐照度预测的设计顺序2.1 辐照度预测是时序外推问题样本构造决定预测上限很多照着源码改的人第一件事是去调隐藏层节点数这其实把顺序搞反了。辐照度预测不是普通回归它的样本来自时间序列今天的辐照度和明天同一时刻的辐照度有强自相关。如果你已经读过bp神经网络原理应该知道误差反向传播只是学习规则真正决定预测上限的是样本怎么构造。普通回归里样本可以随机打乱模型学的是特征与目标之间的静态关系。辐照度预测不行它是时序外推输入是过去一段时间窗口的气象与辐照度序列输出是未来某个时刻的辐照度。这带来两个实际约束第一训练集和测试集必须按时间切分不能用randperm随机抽第二输入特征里必须包含历史辐照度只给温度、湿度是不够的。以15分钟采样间隔为例假设要做“提前一个采样点”的预测最常见的构造方式是把t时刻的温度、湿度、辐照度作为输入t1时刻的辐照度作为目标。如果要做“提前1小时”的预测输入窗口就要往前多取几个时刻。源码里的脚本无论包装得多复杂落到数据矩阵上就是“特征矩阵X”和“目标向量Y”之间的关系这一步想清楚后面调网络才有意义。时间粒度不同输入输出结构也不一样。5分钟粒度的数据一天有288个点特征波动细节多但相邻样本相关性非常高模型容易学到短时惯性1小时粒度的数据一天只有24个点噪声更平滑但样本量也随之变小。源码包如果是按某一特定粒度写的换数据时要连输入窗口和输出步长一起改不能只替换数据文件。2.2 网络结构tansig隐藏层加purelin输出层多数方案的默认解网上搜bp神经网络结构图会看到输入层、隐藏层、输出层画成各种三层四层的结构。落到Matlab代码里结构就是newff那一行参数。输入层节点数等于特征维度newff会根据输入矩阵自动识别需要手动决定的只有隐藏层层数和每层神经元数。辐照度预测属于中小规模回归任务单隐藏层基本够用。隐藏层神经元数可以先按“输入特征数乘2”来试比如特征维度是6隐藏层取12个神经元起步。隐藏层激活函数用tansig输出层用purelin。输出层用purelin这个选择很多人会忽略辐照度是连续数值线性输出能直接拟合任意范围的目标如果输出层用logsig会把输出压到[0,1]区间还得再做一次反向映射预报负值也容易卡在边界上。如果训练误差一直偏高再考虑加第二隐藏层比如[10 5]这样的结构。但加层会明显增加训练时间和过拟合风险不是越深越好。BP网络的深度优势主要体现在图像、文本这类特征层次分明的任务上辐照度预测的特征维度不高一层隐藏层通常足够。另一个需要提醒的细节是输出层的神经元个数。辐照度预测的输出通常只有一个节点也就是未来那个时刻的辐照度值。有些教程把输出层设计成多个节点去预测一整条日曲线这也不是不行但每个节点的训练难度都会上升误差会分散到多个目标上。单点预测是工程里更稳的起步方式先把单点预测做好再考虑曲线预测。2.3 输入特征不是越多越好一张常用特征清单拿到气象数据后常见的输入特征组合包括历史辐照度、历史温度、历史湿度、风速、云量、太阳时角或年积日。这里有一个常见的误区以为输入特征越多模型掌握的信息越多。实际上辐照度预测的样本量通常只有几千条每加一个特征就多一组待训练权重特征过多很容易过拟合。我一般按三档来选先看数据可靠度再决定加不加特征建议理由历史辐照度必选时序自相关最强去掉它模型基本等于盲猜温度、湿度推荐辅助区分晴天和阴天数据容易获得云量、风速可选数据质量好时可用人工记录或拼接数据宁可不用还要提一下太阳时角和年积日。辐照度有明确的周期特征一天内随时间变化一年内随季节变化。把“一天内的小时数”和“一年中的第几天”作为输入特征传进去模型更容易学到光照的周期规律。这个做法在很多源码包里不一定有但我建议自己加上因为它不依赖外部数据源计算成本几乎为零。选好特征后可以跑一组对比实验一组带云量一组不带云量比较两者在验证集上的MAE。如果加了特征验证集误差没有明显下降就把它从输入里删掉。这个做法比单纯看训练集误差靠谱得多也是避免BP网络过拟合的实用手段。3. 把Matlab仿真源码跑通数据归一化、网络训练和误差评估的完整脚本3.1 数据导入与归一化mapminmax的正确用法大多数源码包的数据文件是.mat或者Excel导出的表格先统一整理成“每行一个样本”的矩阵格式。下面的脚本假设data.mat里已经有三列变量irradiance辐照度、temp温度、humidity湿度都是以15分钟为间隔的列向量。% 加载原始数据假设三个变量都是列向量 % irradiance: 辐照度单位 W/m^2 % temp : 环境温度单位 degC % humidity : 相对湿度单位 % load(data.mat); % 构造特征矩阵用当前时刻的气象与辐照度预测下一时刻辐照度 X [temp(1:end-1), humidity(1:end-1), irradiance(1:end-1)]; Y irradiance(2:end); % 归一化到[-1,1]mapminmax默认按行处理所以先转置 [X_norm, X_ps] mapminmax(X, -1, 1); [Y_norm, Y_ps] mapminmax(Y, -1, 1); % 转置回“每行一个样本”的格式 X_norm X_norm; Y_norm Y_norm; % 按时间顺序切分训练集和测试集前70%训练后30%测试 trainLen floor(0.7 * length(Y)); X_train X_norm(1:trainLen, :); Y_train Y_norm(1:trainLen, :); X_test X_norm(trainLen1:end, :); Y_test Y_norm(trainLen1:end, :);mapminmax函数的矩阵处理是按行归一化的所以输入矩阵要先转置成“每行一个特征”的形式归一化完再转置回来。X_ps和Y_ps是归一化参数结构体后面预测新数据时还要用同一个ps去归一化这个细节漏掉的话测试集效果会被高估。切分这一步顺序不能乱先用时间顺序切分再对训练集计算归一化参数最后用同一组参数映射测试集。很多翻车案例都是反过来做先整体归一化再随机切分测试集信息悄悄混进了训练集验证指标虚高得离谱。提示先按时间切分再计算归一化参数。ps只能从训练集统计测试集只做映射不参与统计。3.2 创建网络并训练newff、train与关键参数数据准备好之后创建BP网络。这里用两层隐藏层做示范实际使用中如果数据量不大单隐藏层也够。% 创建BP网络隐藏层分别有10个和5个神经元 % 第一个tansig对应第一隐藏层第二个tansig对应第二隐藏层purelin对应输出层 net newff(X_train, Y_train, [10 5], {tansig,tansig,purelin}, trainlm); % 训练参数设置 net.trainParam.epochs 1000; % 最大迭代次数 net.trainParam.goal 1e-4; % 目标均方误差 net.trainParam.min_grad 1e-6; % 梯度下限防止长时间空转 % 训练网络 [net, tr] train(net, X_train, Y_train);newff的输入需要“每列一个样本”所以这里传X_train而不是X_train。trainlm是Levenberg-Marquardt算法适合几百到几千条样本的中小规模数据收敛快但内存占用偏高如果数据量到了几万条换成trainscg或trainbr会更稳。trainlm对学习率不敏感所以脚本里没有设置lr如果换成traingd这类梯度下降算法再把学习率设成0.01左右。epochs设1000并不是说一定会跑满达到goal或min_grad后train会自动停。训练过程中可以打开训练窗口观察误差曲线如果误差前面降得很快、后面平了说明已经收敛不需要等1000次跑完。3.3 预测与误差评估MAE、RMSE和R²一起看训练完成后用sim函数对测试集做预测然后反归一化。反归一化必须使用训练时保存的Y_ps这是最容易出错的地方。% 用训练好的网络对测试集预测 Y_pred_norm sim(net, X_test); % 反归一化 Y_pred mapminmax(reverse, Y_pred_norm, Y_ps); Y_pred Y_pred; Y_true mapminmax(reverse, Y_test, Y_ps); Y_true Y_true; % 误差指标 err Y_true - Y_pred; MAE mean(abs(err)); RMSE sqrt(mean(err.^2)); SS_res sum(err.^2); SS_tot sum((Y_true - mean(Y_true)).^2); R2 1 - SS_res / SS_tot; fprintf(MAE%.2f W/m^2RMSE%.2f W/m^2R2%.3f\n, MAE, RMSE, R2); % 画对比曲线 figure; plot(Y_true, b-, LineWidth, 1.2); hold on; plot(Y_pred, r--, LineWidth, 1.2); legend(实测辐照度, BP预测辐照度); xlabel(测试集样本序号); ylabel(辐照度 (W/m^2)); grid on;如果重新对Y_test做一次mapminmax得到的结果范围是基于测试集统计的很多“验证效果极好、一上真实数据就翻车”的情况就是在这里埋下的。指标只看RMSE也不够R²能告诉你模型相比“直接取均值”提升了多少MAE则更接近实际误差量级。三组数字一起看才能真正判断模型是不是在靠历史平均值混日子。4. 辐照度预测BP网络的排查记录5条高频踩坑点训练网络就像调一台不透明的机器很多问题在训练曲线上一眼能看出来但新手往往不知道看哪里。下面这几条来自真实使用Matlab仿真的血泪经验按出现频率排序每一条都按“现象、原因、解决”说清楚。4.1 训练误差不下降误差曲线在0.1附近震荡现象epochs设到2000训练误差曲线像锯齿一样来回跳稳定不下来最终误差停在0.1附近。原因最常见的是数据没归一化。辐照度的数值范围是0到1100左右湿度是0到100温度是-10到40量级不统一时梯度更新会被大数值特征主导。其次是学习率偏大权重更新一步跨过最优点。隐藏层神经元太少也会让网络表达能力不足。解决先用mapminmax把所有输入输出归到[-1,1]这是绝大多数BP仿真能正常收敛的前提。如果归一化之后还在震荡把学习率从0.01降到0.001。隐藏层神经元数如果只有3个先加到10个以上再观察。改完一般能看到误差曲线变成平滑下降。4.2 预测曲线趋近于一条水平线现象测试集的预测值基本都在同一个数值附近曲线大致是平的实测辐照度的起伏完全没跟上。原因两个方向。第一输出层激活函数用了logsig或tansig模型输出被压缩在一个小区间里而辐照度经过反归一化后范围很大网络的表达能力不够。第二输入特征与目标的相关性太弱比如只给了温度和湿度没有给历史辐照度模型学不到时序关系只能退化成“预测平均值”。解决把输出层激活函数换成purelin。检查输入特征里是否有历史辐照度没有就重新构造样本。顺手把延迟阶数加大比如用t-3到t时刻的辐照度作为输入而不是只用t时刻。水平线问题多半不是网络深度不够而是输入信息本身就不够。4.3 预测结果出现负辐照度现象夜间或清晨样本的预测结果是负几十W/m²物理上不可能画图时看起来非常刺眼。原因输出层是purelin线性输出本质上可以产生任意实数。训练数据里夜间辐照度为0但网络拟合的是一个连续曲面边界外推时很容易越过0点。这是BP做物理量回归的常见现象不是代码报错。解决在sim之后加一句“Y_pred max(Y_pred, 0);”就好。如果想更详细一些可以在训练时给目标向量加一个很小的下边界约束比如把0值样本的权重调高但工程上直接裁剪最简单也够用。不要因为几个负点就怀疑整个模型裁剪之后看整体指标才是正事。4.4 同一份数据两次训练结果不一样现象代码没改跑两次训练验证集R²一会0.85一会0.79随机性明显交付指标没法复现。原因BP网络的初始权值是随机的trainlm对初始值敏感。另外train函数内部对样本顺序的采样也有随机性。这在预测类任务里属于正常波动但对工程交付不友好因为谁都不想让指标不可复现。解决在训练前固定随机种子Matlab里用rng(2023)这样一条语句就能让每次训练从同一组初始权值出发。如果想更稳一点可以训练5次取预测均值用集成结果输出代价是训练时间变成5倍。固定随机种子之后至少能保证同一份数据跑出来的指标是一致的。4.5 验证集效果很好真实场景效果差现象测试集R²有0.9以上把模型接到实时数据上预测结果明显偏离误差远大于测试集指标。原因这是时序预测中最隐蔽的一种数据泄漏。常见来源是用全量数据做mapminmax归一化参数包含了测试集的信息另一个来源是切分时用了randperm随机打乱把未来数据混进了训练集。模型等于提前看过答案测试指标自然好看。解决先按时间切分再做归一化ps只从训练集计算。测试集只做映射不要参与统计。这属于数据规范化流程问题和网络结构无关但翻车率最高。改完流程后重新评估如果指标明显下降说明之前的0.9本来就是假的。5. 从仿真源码到能用时间序列切分、滑动窗口和模型更新的三个工程改法5.1 按时间顺序切分数据别用randperm随机划分在分类任务里没问题在时序预测里直接等于作弊。原因很简单训练集里如果混入了某一天下午的样本而测试集里有同一天上午的样本模型相当于已经见过“附近时刻”的数据测试指标虚高。切分时把前70%按时间排序给训练后30%给测试就行。N length(Y); trainLen floor(0.7 * N); X_train X(1:trainLen, :); Y_train Y(1:trainLen, :); X_test X(trainLen1:end, :); Y_test Y(trainLen1:end, :);切分是放在归一化之前还是之后这里再强调一次。时序预测的正确顺序是先切分再对训练集计算X_ps和Y_ps然后用同一组参数映射测试集。如果先归一化再切分训练和测试的边界信息已经互串后面的评估指标就失去了参考价值。5.2 滑动窗口构造样本把源码里的单步预测升级为多步预测单步预测是“用t预测t1”但实际工程要的是未来1小时、未来3小时。如果只能做单步仿真源码就只能用来算指标不能用来排产。常见的改法是滑动窗口输入取过去若干时刻的辐照度和气象数据输出取未来某一时刻的辐照度然后逐窗口移动构造样本。function [X, Y] makeSlidingWindow(data, inputSteps, outputSteps) % data: 列向量每个采样点一个值 % inputSteps: 输入窗口长度比如12表示过去1小时(5分钟采样) % outputSteps: 未来步数比如12表示预测未来1小时 N length(data) - inputSteps - outputSteps 1; X zeros(N, inputSteps); Y zeros(N, 1); for i 1:N X(i, :) data(i : iinputSteps-1); Y(i, 1) data(iinputStepsoutputSteps-1); end end这个函数里Y是未来第outputSteps个点的辐照度不是未来第一条。比如15分钟采样inputSteps4表示过去1小时outputSteps4表示预测未来第1小时的那个点中间隔着3个点。窗口移动的步长设成1样本会高度重叠训练集样本量变大但相邻样本相关性变强步长设成outputSteps可以降采样去相关训练慢一些但更干净。两种方式可以都试一下对比验证集指标再定。5.3 模型更新策略按月重训比在线微调更省心光伏辐照度随季节变化明显同一个模型在夏天训出来到冬天误差必然变大。源码包里训练一次就跑一辈子的做法放到实际场景里三个月后就开始不准。我一般用滚动重训固定使用最近90天数据训练每个自然月结束重训一次并把旧的预测误差存档观察模型的季节性漂移。% 每次预测前检查当前模型是否已超龄 if days(datetime(now) - net.userdata.trainDate) 30 % 用最近90天的历史数据重新训练覆盖net [net, tr] train(net, recentX, recentY); net.userdata.trainDate datetime(now); end如果不想按月固定重训也可以改成滚动误差触发式维护一个30天的预测MAE窗口当窗口MAE从稳定值放大到1.5倍以上时触发重训。这个策略能自动响应季节突变但实现起来需要额外的历史预测存储适合已经跑起来一段时间后的优化。对于刚接手源码包的阶段先按月重训就够了。6. 用晴空模型校验预测上限最后一道防线辐照度预测的BP模型再怎么调都不应该违背一条物理定律任意时刻的地面辐照度不可能超过“晴空辐照度”。晴空模型描述的是无云情况下到达地面的辐照度理论值虽然不是绝对精确但用来卡上限足够了。简化做法是用正弦包络近似一天内的晴空辐照度曲线正午峰值按当地实际情况取一个经验值比如900 W/m²。把BP预测结果与这条曲线叠在一起画一旦预测值明显超过晴空曲线多半不是网络能力问题而是输入特征里出现了异常值或时间对齐错误。% 假设测试集样本对应的小时数保存在 hourOfSample 列向量中 % 简化晴空辐照度上限正午峰值按900估算可按当地纬度调整 t hourOfSample; % 0~24之间的实数 G_clear 900 * max(0, sin((t - 6) * pi / 12)); % 6点到18点之间为正弦包络 % 比较预测值超过晴空上限1.05倍则报警 overLimit Y_pred 1.05 * G_clear; if any(overLimit) warning(预测辐照度超过晴空上限请检查输入数据); end这里只做上限校验不需要精确计算大气透过率sin包络的近似已经够用。超过上限的点先查对应时刻的输入数据是不是坏样本再查时间对齐是否有偏移最后才考虑是不是网络参数问题。我后来做辐照度预测只要看到预测曲线里出现超过晴空模型的点第一反应不是去调网络结构而是回头查这组输入数据有没有混入脏值。这个检查比任何误差指标都直观建议把它放在出图环节里固定执行希望帮到你。本文还有配套的精品资源点击获取
返回列表