简介:基于matlab平台,利用BP神经网络实现电力负荷预测,配套小型数据集可直接测试验证,适合计算机、数学、电子信息等专业学生用于课程设计、期末大作业或毕业设计参考。资源包共2个文件,包含一份MATLAB源码(.m)与一份数据表格(.xlsx),压缩包仅8KB,便于快速下载与本地运行。模型源码完整,数据量简洁,能够帮助读者理解BP网络的样本组织、训练与预测流程,并可在此基础上调试参数或扩展数据做更深一步改进。目前已吸引262人学习查看,可作为电力负荷预测方向入门实践的参考资料。
1. 用 MATLAB 跑 BP 神经网络预测电力负荷:小型数据集为什么反而适合入门
用 MATLAB 跑 BP 神经网络预测电力负荷,听起来是个已经被写烂的课题,但真拿一份小型数据集从头做测试验证时,还是会踩到不少坑。这套资源把源码和数据集打在一起,省去了到处找数据的功夫。我当年做课程设计就是靠类似的包起步:先用小样本把 BP 神经网络的训练、验证、误差分析这条链路打通,再迁移到大项目,你会发现 90% 的工程时间都花在数据处理和反归一化上,而不是算法本身。它适合刚在学 bp 神经网络原理的人,也适合想快速验证一个负荷预测基线、不打算自己造轮子的从业者。
2. 从数据到输入矩阵:电力负荷预测的预处理与两种划分策略
BP 神经网络本身不认时间戳,只认数字矩阵。做电力负荷预测的第一步,永远是把原始负荷序列变成“过去一段窗口 → 未来一个点”的输入输出对。这一步做对了,后面训练就是顺水推舟;做错了,哪怕网络结构再漂亮,结果也是废的。
2.1 原始数据长什么样:从 Excel/CSV 读进来的第一步
小型负荷数据集一般就是一张两列表格:第一列是时间,第二列是负荷值,单位可能是 MW 或 kW,采集间隔常见的是 15 分钟、1 小时或 1 天。我拿到的这类资源里,通常默认是整点小时负荷,一天 24 个点,连续记录 1 到 3 个月。文件格式不外乎.xlsx或.csv。
% load_data.m data = readmatrix('load_data.xlsx'); % 读取 Excel 中的纯数值区域 loadVal = data(:, 2); % 第二列是负荷值 loadVal = loadVal(~isnan(loadVal)); % 去掉表头或空行造成的 NaN逻辑说明:readmatrix在 MATLAB R2019a 之后很稳定,能自动跳过字符串列;如果你的版本更老,可以改用xlsread或csvread,但要注意老接口对纯数值文件更友好。data(:, 2)取的是第二列负荷值,因为第一列是时间字符串,读进来往往被解析成 NaN 或文本。
参数说明:如果负荷列不在第二列,把2改成实际列号;如果文件是 CSV,直接把后缀换成.csv,readmatrix同样支持。去空行这步不能省,很多公开数据集的表尾会带空行,留着会让后续mapminmax直接报错。
2.2 构造滑动窗口:BP 做时序预测的常规打法
负荷预测本质上是回归问题,但直接把“当前时刻负荷”作为输入,学到的只是恒等映射,没有意义。常规做法是滑动窗口:用前n个历史负荷值预测下一个时刻。这一步决定了模型的记忆长度,n太小记不住趋势,n太大样本数量会明显变少。
n = 24; % 用前 24 小时预测下一小时 X = []; % 输入矩阵,每行是一个窗口 Y = []; % 输出向量,每行是目标值 for i = n+1:length(loadVal) X(end+1, :) = loadVal(i-n:i-1)'; % 取第 i-n 到 i-1 共 n 个值 Y(end+1, :) = loadVal(i); % 第 i 个值作为预测目标 end逻辑说明:循环从n+1开始,保证第一个窗口里全是有效的历史数据。loadVal(i-n:i-1)'把长度为n的行向量转成行向量存入X,最终X是“样本数 × n”的矩阵,每一行对应一个时间窗口。Y是“样本数 × 1”的向量,代表窗口后紧邻的那个负荷值。
参数说明:n是这段代码的核心参数。做小时级负荷预测时,我一般先试24(一天周期),再试24*7=168(一周周期)。如果样本总量只有几百条,168会吃掉太多数据,这时候宁可降回24,也不要让训练样本少于总数据量的一半。
2.3 归一化:mapminmax 的参数与反归一化
BP 网络的隐藏层激活函数通常是双曲正切或 Sigmoid,输入输出范围太大或太小都会让梯度计算变得奇怪。所以负荷数据一定要先归一化。MATLAB 里最常用的是mapminmax,它有个容易翻车的特点:默认按行处理。
[dataNorm, ps] = mapminmax(loadVal', 0, 1); % loadVal' 变成行向量 dataNorm = dataNorm'; % 转回列向量,方便后续构造窗口逻辑说明:mapminmax的第一个参数必须是矩阵,且它按行做归一化。loadVal是列向量,直接传进去它会当成“1 行多列”处理,得到的结果就不是预期了。所以先转置成行向量,归一化后再转回列向量。ps是一个结构体,记录了归一化的最小值、最大值和映射公式,后面反归一化全靠它。
参数说明:0, 1表示把数据缩放到 [0, 1] 区间。如果你想让激活函数工作在线性区附近,也可以写成-1, 1。对负荷预测来说,0, 1更直观,而且后期 MAPE 计算不会遇到负数分母。
2.4 训练集/测试集划分:时间顺序划分和随机划分的差别
很多新手在这步直接用randperm把样本打乱再划分,这在普通分类问题上没错,但在时间序列预测上是原则性错误。因为相邻窗口之间高度重叠,随机划分会让训练集和测试集“互相见过”,测出来的误差严重偏小。
trainNum = round(0.8 * size(dataNorm, 1)); Xtr = X(1:trainNum, :); Ytr = Y(1:trainNum); Xte = X(trainNum+1:end, :); Yte = Y(trainNum+1:end);逻辑说明:先构造窗口,再按时间顺序切分。前 80% 的窗口进训练集,后 20% 进测试集。这样测试集是模型从未见过的一段连续区间,更贴近实际部署时“拿历史预测未来”的场景。
参数说明:0.8是训练集占比。如果数据量少,可以调到0.85,但要保证测试集至少有 20 条样本,否则误差指标波动会非常大。两种划分方式的差别可以参照下表:
| 划分方式 | 训练集样本来源 | 测试集样本来源 | 风险 |
|---|---|---|---|
| 随机划分 | 全时间范围随机抽样 | 全时间范围随机抽样 | 窗口重叠导致信息泄漏,误差虚低 |
| 时间顺序划分 | 前 80% 时间段 | 后 20% 时间段 | 更接近真实场景,但模型可能学不到末期的新趋势 |
3. 核心代码走读:MATLAB 里写 BP 网络其实只要 20 行
数据准备好后,训练一个 BP 网络的代码非常短。MATLAB 的神经网络工具箱把反向传播、梯度下降、动量更新全部封装进了train函数,看起来像个黑匣子,但关键参数必须自己把关。
3.1 网络创建:feedforwardnet 与隐藏层节点数
feedforwardnet是构造前馈 BP 网络最直接的函数。它接受一个向量表示每层隐藏层的神经元数量,可以是单层,也可以是多层。负荷预测这类连续回归问题,单层隐藏层往往就够用,节点数在 10 到 30 之间;如果你数据非线性很强,可以多加一层但不要超过两层,否则训练时间翻倍还容易过拟合。
net = feedforwardnet([12 6]); % 两层隐藏层:第一层 12 个节点,第二层 6 个节点 net.trainParam.showWindow = true; % 打开训练过程的曲线窗口 net.trainParam.epochs = 1000; % 最大迭代次数 net = train(net, Xtr', Ytr'); % 注意输入和输出都要转置,列是样本逻辑说明:feedforwardnet([12 6])创建了一个带两层隐藏层的网络,输入维度由后面的train自动推断。train函数要求输入矩阵的每一列是一个样本,而我们构造的Xtr是“样本数 × 特征数”,所以必须转置成“特征数 × 样本数”。同理Ytr'是“1 × 样本数”。
参数说明:隐藏层节点数没有金标准。我的习惯是先跑[12]单层,看测试集 MAPE;如果欠拟合,再试[12 6]或[20]。节点数太多会出现“训练集 R 值 0.99,测试集一塌糊涂”的过拟合症状,这时候要优先减节点而不是加数据。
3.2 训练函数与学习率:trainlm 还是 trainscg
MATLAB 的trainlm(Levenberg-Marquardt)是默认训练函数,在中小型数据集上收敛极快,但会存储一个近似 Hessian 矩阵,内存占用随样本数上升。如果你的数据只有几百条,trainlm完全没有问题;如果超过几千条,我一般换成trainscg,它对内存更友好且不容易在病态数据上产生 NaN。
net.trainFcn = 'trainscg'; % 换成 Scaled Conjugate Gradient net.trainParam.lr = 0.01; % 学习率 net.trainParam.goal = 1e-5; % 目标误差,达到就提前停止 net.trainParam.min_grad = 1e-6; % 最小梯度阈值逻辑说明:trainFcn决定了反向传播的优化算法。lr是梯度更新步长,在trainlm下它只是参考值,LM 算法会自适应调整;但在trainscg下,学习率直接影响收敛行为。goal和min_grad是早停条件,满足其一训练就结束。
参数说明:学习率从0.01起步,如果损失曲线震荡,降到0.001;如果学习率过低导致半天不收敛,可以试0.05,但必须配合观察plotperform曲线。训练完成后,务必执行view(net)看一眼 BP 神经网络结构图,确认输入维度和输出维度和你预期一致。
3.3 保存现场:把训练好的网络和归一化参数一起存盘
训练一次 BP 网络虽然快,但不同初始化权重会带来不同结果。为了复现实验,一定要把net和归一化用的ps一起保存。很多新手只存网络,忘记存ps,之后预测新数据时完全不知道该怎么反归一化。
save('bp_net.mat', 'net', 'ps', 'Xte', 'Yte');逻辑说明:save把指定变量写入.mat文件。下次直接load('bp_net.mat')就能恢复网络结构、训练好的权重、归一化参数以及测试集。ps是反归一化的钥匙,丢了它等于预测结果全部作废。
实际工程里,我会额外存一个net.trainFcn和net.trainParam的快照,因为不同 MATLAB 版本自动配置的默认训练参数有差异,存下来方便排查“为什么别人跑出的曲线和我不同”。
4. 跑通后的验证:三种误差指标与两张必须看的图
网络训练完,最怕看到一组漂亮数字就收工。正确的验证流程是把测试集的预测值反归一化回真实负荷单位,再算误差指标、画回归图、看误差分布。这个过程能暴露数据泄漏、过拟合、窗口大小不合适等问题。
4.1 反归一化:把预测值拉回真实的 MW 单位
sim函数用训练好的网络计算测试集预测值。此时网络输出还在 [0, 1] 区间,必须用第 2 章保存的ps做反变换。
YtePredRaw = sim(net, Xte')'; % 先算出归一化的预测值,列向量 YtePred = mapminmax('reverse', YtePredRaw', ps)'; % 反归一化回真实量纲 YteReal = mapminmax('reverse', Yte', ps)'; % 测试集真实值同样还原逻辑说明:sim(net, Xte')输出是“1 × 样本数”,转置成“样本数 × 1”得到YtePredRaw。反归一化时再次转置成行向量,调用mapminmax('reverse', ...),结果再转回列向量。这里的顺序错一步,数值就会差一个量级。
参数说明:ps是从整个loadVal序列上计算得到的。如果你担心这种全局归一化有信息泄漏,可以只用训练集算ps,再用同一组ps变换测试集,代码完全一样,只是算ps的输入改成了Xtr对应的那段时间序列。
4.2 回归图与误差分布图:两张必看的图
MATLAB 神经网络工具箱提供了两个画图函数,一个是plotregression,一个是ploterrhist。前者看预测与真实值之间的线性相关程度,后者看误差分布是否服从正态。
plotregression(YteReal, YtePred); % 真实值 vs 预测值的回归散点 ploterrhist(YteReal - YtePred); % 预测误差直方图逻辑说明:plotregression的横纵坐标分别是真实值和预测值,散点越贴合对角线,回归效果越好。输出的 R 值接近 1 是好事,但也要警惕:如果所有样本都集中在一个很小的负荷区间内,R 值虚高并没有实际意义。ploterrhist画出误差分布,理想情况是围绕 0 的钟形分布;如果分布明显偏向一侧,说明模型存在系统性偏差。
4.3 评价指标:MAE、RMSE、MAPE 的计算脚本
画图只能定性看,最终汇报还得靠三个数值指标。MAE 衡量平均绝对误差,RMSE 惩罚大幅偏差,MAPE 是相对误差百分比,在电力负荷预测里最常用。
mae = mean(abs(YtePred - YteReal)); rmse = sqrt(mean((YtePred - YteReal).^2)); mape = mean(abs((YtePred - YteReal) ./ YteReal)) * 100; fprintf('MAE: %.2f MW\n', mae); fprintf('RMSE: %.2f MW\n', rmse); fprintf('MAPE: %.2f%%\n', mape);逻辑说明:三个指标都直接对反归一化后的真实值计算。./是逐元素除法,* 100把比例转成百分比。fprintf的%.2f控制输出保留两位小数,MW 单位按你的数据实际量纲来。
参数说明:MAPE 在负荷值接近 0 的时候会异常增大。如果你数据里有夜间低谷接近 0 的点,建议把 MAPE 计算限定在负荷大于某个阈值的样本上,否则一个极端点就能把整个 MAPE 拉高到离谱。
5. 避坑指南:在 MATLAB 里跑 BP 负荷预测最容易翻车的五个点
这条路径我帮别人调试过无数次,自己也踩过不少坑。下面五个问题出现频率最高,每个都按“现象 → 原因 → 解决”记录,可以直接对照排查。
5.1 中文注释乱码:新版 MATLAB 最常见的开局问题
现象:打开资源里的.m文件,中文注释全部显示成乱码,甚至出现语法报错。
原因:文件用 GBK 或 GB2312 编码保存,而新版 MATLAB(比如 R2023b 之后的版本)默认用 UTF-8 读取,编码不匹配就乱码。macOS 和 Linux 系统更容易触发。
解决:用记事本或 VS Code 把文件另存为 UTF-8 编码,再重新打开。如果文件较多,可以用一段脚本批量转换。更省事的方法是直接在 MATLAB 里先用edit打开一遍,大部分情况下它会自动识别;实在不行就把中文注释改成英文,毕竟算法代码本身不受影响。
5.2 预测结果是一条平滑直线:模型根本没学到规律
现象:测试集预测曲线几乎是水平直线,或者只在均值附近小幅波动,完全跟不上真实负荷的峰谷变化。
原因:隐藏层节点太少,模型容量不足以拟合非线性关系;或者输入窗口n太小,比如只用前 1 个小时预测下 1 小时,序列基本平稳,学出来就是个平均值。再常见的原因是 epoch 数太少,训练根本没收敛。
解决:先把n改到 24,让模型看到至少一个完整的日周期;同时把隐藏层节点从[12]提升到[20]或[20 10];再把epochs提到 2000,并训练后看一眼tr.epoch确认真正跑了几轮。
5.3 反归一化后数值整体漂移:全量归一化的信息泄漏
现象:训练集误差很低,但测试集预测曲线整体比真实值高出一截,或者低一截,趋势对但绝对值不对。
原因:如果对全数据集做了一次mapminmax,再划分训练集和测试集,测试集的最小值和最大值已经参与了归一化参数的估计,相当于测试集信息提前泄漏给了训练过程。另一个原因是反归一化时用了错误的ps。
解决:先划分窗口,再只用训练集部分计算psTrain,然后用psTrain变换训练集和测试集。修改量不大,核心代码就是:
psTrain = mapminmax(Xtr', 0, 1); % 用训练窗口估计映射参数 XtrN = mapminmax('apply', Xtr', psTrain)'; XteN = mapminmax('apply', Xte', psTrain)';这段代码和之前逻辑的区别在于,psTrain只来自训练数据,测试数据只是被“套用”同样的线性映射,不再参与 min/max 估计。这是我的血泪经验:先全量归一化再切分,指标好看全是假象。
5.4 训练损失震荡不降或出现 NaN:学习率与数据质量冲突
现象:训练过程中误差曲线剧烈震荡,有时第 50 轮突然变成 NaN,后面全部报废。
原因:最常见是学习率太大,更新步长越过最优点;其次是trainlm在小数据集上遇到病态矩阵,Hessian 近似失败;还有极小概率是数据里还有 NaN 或 Inf 没清理干净。
解决:把lr降到 0.001,或直接换成trainscg。检查数据用sum(isnan(loadVal)),确保结果为 0。如果是trainlm报“Matrix is singular”,说明训练集特征之间有强烈线性相关性,滑动窗口n太大导致相邻特征几乎一样,减小n可以缓解。
5.5 测试集 R 为负:过拟合与划分混乱的信号
现象:训练集 R 值 0.98,测试集 R 值却是负数,散点图完全不可用。
原因:一个是随机划分造成的窗口重叠泄漏,模型在测试集上“作弊”但没完全学会,反而被异常点带偏;另一个是网络节点数过多,把训练集噪声都背了下来,测试集一换场景就完全失效。
解决:坚决按时间顺序划分。如果你确认划分没问题,就把隐藏层节点数量减半,比如从[20 10]降回[10],多跑几次取平均预测。BP 网络的初始化是随机的,单次结果波动很大,至少跑 5 次,记录 MAPE 的均值和标准差再下结论。
6. 进阶玩法:把单步预测改成多步滚动预测的小模板
单步预测只能知道明天,实际调度经常要未来 24 小时甚至 168 小时的曲线。这时候不能直接拿测试集整段输入网络,而要做一个滚动预测:先用已知窗口预测下一个点,再把预测点补进窗口,丢弃最旧的点,继续预测新下一个点。这个循环会把误差不断累积,但观察累积速度本身就是验证模型稳定性的好办法。
histWin = Xte(1, :); % 用测试集第一个窗口作为初始历史 steps = 24; % 预测未来 24 个点 preds = zeros(steps, 1); for k = 1:steps yk = sim(net, histWin')'; % 单步预测,结果在 [0,1] preds(k) = yk; histWin = [histWin(2:end), yk]; % 窗口右移,新预测值进入历史 end loadPred = mapminmax('reverse', preds', ps)';逻辑说明:histWin初始值是测试集里第一个输入窗口,长度为n。每轮用sim预测窗口后的一个点,然后用[histWin(2:end), yk]把窗口挪一格:丢掉最旧的历史值,接上刚预测出来的值。这样网络每次看到的都是最新窗口,和真实在线预测完全一致。
参数说明:steps可以小于或大于n。如果steps大于n,预测一段时间后窗口里全是预测值,误差累积会很明显,这属于正常现象。你可以把这个滚动预测和真实测试集后 24 个点对比,画出两条曲线,观察模型在第几步开始明显偏离——这比单个 MAPE 指标更有工程参考价值。
我自己的习惯是哪怕只做单步预测,也会跑一遍这个滚动模板,因为如果滚动前 6 步的误差就开始爆掉,说明模型对窗口结构的依赖太强,基本不能用于实际调度。从那以后,我每次在 MATLAB 里做 BP 负荷预测,都强制走一遍“数据读取 → 窗口构造 → 归一化 → 训练 → 反归一化 → 滚动验证”的完整链条,全绿了才会换 LSTM 或其他模型。这套资源源码的代码结构也是按这个顺序组织的,建议解压后先找到main.m,按注释顺序从load_data跑到analyze_error,跑通了再改隐藏层节点和窗口长度。希望帮到你。
本文还有配套的精品资源,点击获取