BP神经网络负荷预测实战:从特征工程到PyTorch与MATLAB实现
简介这份资源面向电力系统调度、能源管理及电气工程方向的学习者与研究人员聚焦基于BP神经网络的电力负荷预测方法帮助解决历史负荷数据建模、未来用电需求预估等实际问题适合具备一定MATLAB基础、希望快速上手负荷预测实践的中级读者。压缩包共8个文件约410KB包含4个doc文档、2个m脚本和2个xls数据表文档侧重BP神经网络原理与负荷预报实现思路m文件提供可直接运行的MATLAB源码xls表格则存放训练与测试用的负荷样本数据便于对照调试。目前已有724人学习下载。资源将理论讲解与可执行代码结合读者可据此完成数据预处理、网络结构设计、权重训练、验证测试与误差分析等完整流程并参考改进BP策略如自适应学习率、动量项优化预测精度为电力系统运营与调度决策提供支持。1. BP负荷预测从一张日负荷曲线说起电力负荷预测这件事真正做过的人都知道难点从来不是能不能跑出一个数而是这个数在尖峰时段敢不敢用。我最早接触 BP 负荷预测是因为一个园区配电站的日负荷曲线白天平稳、傍晚陡升、夜间回落典型的双峰结构。用线性回归拟合MAPE 能到 8% 左右但一到 18:00—20:00 的爬坡段就集体翻车误差直接飙到 15% 以上。换成 BP 神经网络之后同样的输入尖峰段误差压到了 5% 以内——这就是为什么负荷预测这个场景里BP 神经网络至今仍是绕不开的基线模型。BP 负荷预测本质是用前馈神经网络去拟合历史负荷 气象 时间特征到未来负荷的非线性映射。它适合谁适合手上有 13 年历史负荷数据、想做短期日前/日内预测、又不想一上来就上 LSTM 或 Transformer 的工程同学。MATLAB 的newff和 Python 的 PyTorch 都能做前者上手快、适合快速验证后者灵活、适合嵌入生产系统。下面把我自己踩过的路完整讲一遍。2. 先把数据这道关过了负荷预测的输入到底怎么选2.1 负荷预测的特征工程三类输入缺一不可很多人一上来就把整列负荷丢进网络结果模型学了个昨天多少今天多少的恒等映射一到周末就崩。负荷预测的输入必须分三类历史负荷特征前 1 天同时刻负荷、前 2 天同时刻负荷、前 1 小时负荷、前 24 小时均值。这几个是最强特征尤其是前 1 天同时刻。时间特征小时0—23、星期1—7、是否周末、是否节假日。注意小时和星期要做 sin/cos 周期编码否则 23 点和 0 点在网络眼里差着 23 个单位。气象特征温度、湿度、体感温度。温度对负荷的影响是非线性的——夏天高温升负荷、冬天低温也升负荷所以温度最好做分段或加平方项。我一般会先算一遍皮尔逊相关系数把相关系数低于 0.1 的特征直接砍掉避免给网络喂噪声。2.2 归一化与滑窗两个最容易埋雷的步骤归一化必须用训练集的最大最小值不能拿全体数据算。这是血泪经验我见过有人用全量数据归一化测试集 MAPE 看着只有 3%一上线就废因为线上根本拿不到未来数据的最大值。import numpy as np import pandas as pd def make_dataset(load_series, temp_series, lookback24, horizon1): 构造滑窗样本用过去 lookback 小时预测未来 horizon 小时 X, y [], [] for i in range(lookback, len(load_series) - horizon 1): # 历史负荷窗口 load_win load_series[i - lookback:i] # 对应时段温度 temp_win temp_series[i - lookback:i] # 时间编码小时 sin/cos hours np.arange(i - lookback, i) % 24 hour_sin np.sin(2 * np.pi * hours / 24) hour_cos np.cos(2 * np.pi * hours / 24) feat np.concatenate([load_win, temp_win, hour_sin, hour_cos]) X.append(feat) y.append(load_series[i horizon - 1]) return np.array(X), np.array(y) # 关键归一化参数只用训练段计算 train_end int(len(load) * 0.7) l_min, l_max load[:train_end].min(), load[:train_end].max() load_norm (load - l_min) / (l_max - l_min) X, y make_dataset(load_norm, temp_norm, lookback24, horizon1)这段代码里lookback24表示用过去 24 小时预测下 1 小时horizon1是单步预测。如果要预测未来 24 小时把 horizon 改成 24但要注意别用未来信息——滑窗的边界必须严格卡在预测时刻之前。归一化的l_min/l_max只从训练段取这是防止数据泄漏的底线。2.3 训练集/验证集/测试集怎么切才不骗自己负荷数据是时间序列绝对不能随机打乱切分。常见做法是按 7:1.5:1.5 的时间顺序切。更稳的是滚动验证用前 6 个月训练、第 7 个月验证然后窗口往后滑。我一般会留出最近 1 个月做测试因为最近的数据分布最接近线上。提示如果你的数据里有春节、国庆这种极端日训练时要么剔除要么单独打标签否则网络会被这几天的异常负荷带偏。3. 用 MATLAB newff 和 PyTorch 各跑一版 BP 负荷预测3.1 MATLAB newff三行核心代码与参数含义MATLAB 做 BP 负荷预测newff是最经典的入口。虽然新版本推荐feedforwardnet但newff在大量老项目和教程里还在用理解它对读别人的代码很有帮助。% 假设 P 是输入矩阵特征×样本T 是目标矩阵1×样本 net newff(P, T, [10 5], {tansig,tansig,purelin}, trainlm); net.trainParam.epochs 1000; % 最大迭代次数 net.trainParam.goal 1e-5; % 训练目标误差 net.trainParam.lr 0.01; % 学习率 net.trainParam.max_fail 20; % 验证失败容忍次数 net.divideParam.trainRatio 0.7; net.divideParam.valRatio 0.15; net.divideParam.testRatio 0.15; [net, tr] train(net, P, T); Y net(P); % 预测[10 5]表示两个隐层分别 10 个和 5 个神经元。隐层激活用tansig输出层必须用purelin——因为负荷是连续值输出层再用 sigmoid 会把范围压死在 01。trainlm是 Levenberg-Marquardt 算法收敛快但对内存敏感样本超过几千条时容易爆内存这时候换trainscg更稳。max_fail20是早停的关键验证集连续 20 次不下降就停防止过拟合。3.2 PyTorch 版本把 BP 负荷预测嵌进生产流程MATLAB 适合验证真要上线我一般用 PyTorch。下面是一个最小可用的 BP 网络结构对应上面的 MATLAB 版本。import torch import torch.nn as nn class BPLoadNet(nn.Module): def __init__(self, in_dim): super().__init__() self.net nn.Sequential( nn.Linear(in_dim, 10), nn.Tanh(), # 对应 tansig nn.Linear(10, 5), nn.Tanh(), nn.Linear(5, 1) # 输出层无激活对应 purelin ) def forward(self, x): return self.net(x) model BPLoadNet(in_dimX.shape[1]) criterion nn.MSELoss() optimizer torch.optim.Adam(model.parameters(), lr1e-3) for epoch in range(500): model.train() pred model(torch.tensor(X_train, dtypetorch.float32)) loss criterion(pred, torch.tensor(y_train, dtypetorch.float32).view(-1,1)) optimizer.zero_grad() loss.backward() optimizer.step() if epoch % 50 0: print(fepoch {epoch}, loss {loss.item():.6f})in_dim是特征维度等于lookback*2 2负荷窗口 温度窗口 两个时间编码。损失用 MSE因为负荷预测关心的是数值误差。优化器用 Adam 而不是 SGD是因为负荷数据量通常不大Adam 收敛更稳。训练完记得把预测值反归一化pred * (l_max - l_min) l_min。3.3 隐层节点数怎么定别迷信公式网上流传的sqrt(mn)a公式只能当起点。我的做法是先固定 1 个隐层从 8 个神经元开始每次翻倍到 64看验证集 MAPE。负荷预测这种任务隐层节点超过 64 基本就是过拟合验证误差会先降后升。两个隐层的话我一般用[16 8]或[32 16]第二层是第一层的一半。节点太多最典型的症状是训练集 MAPE 0.5%测试集 8%这就是网络把训练样本背下来了。4. 训练过程里的坑为什么你的 BP 负荷预测误差下不去4.1 现象训练 loss 一直震荡不下降原因学习率太大或者输入特征没归一化。负荷值动辄几千 kW直接喂进网络梯度会爆炸。解决先把所有输入归一化到 [0,1] 或标准化到均值 0 方差 1学习率从 1e-3 往下调MATLAB 里把lr从 0.01 降到 0.001 试试。如果还震荡换trainscg或 Adam。4.2 现象验证集误差远大于训练集原因过拟合。隐层节点太多、训练轮数太多、样本太少三者占一个。解决加早停max_fail或 PyTorch 里手动 early stop、减隐层节点、加 L2 正则。我一般还会在输入里加一点高斯噪声做数据增强负荷预测里这招对抑制过拟合很有效。4.3 现象尖峰时段预测总是偏低原因MSE 损失对全体样本一视同仁而尖峰样本数量少网络倾向于预测均值。这是负荷预测最典型的削峰问题。解决两个办法。一是对尖峰样本加权损失里给高负荷样本更大权重二是把损失换成对峰值敏感的比如在 MSE 基础上加一项峰值误差惩罚。我一般用加权 MSE权重按负荷值分档尖峰段权重给到 35 倍。4.4 现象换一批数据 MAPE 就翻倍原因归一化参数、特征顺序、滑窗方式在不同数据集上不一致或者新数据分布漂移比如新增了充电桩负荷。解决把预处理流程固化成函数训练和推理走同一套代码。分布漂移的话定期用最近数据微调模型或者干脆上在线学习。4.5 现象MATLAB 训练正常导出到 Python 结果对不上原因MATLAB 的mapminmax默认按行归一化Python 里很多人按列方向反了或者激活函数对应错了tansig对应tanh不是sigmoid。解决逐层核对权重和偏置用同一组输入分别跑 MATLAB 和 Python比对第一层输出。这个黑匣子对不上的问题十有八九是归一化方向或激活函数映射错了。5. 把 BP 负荷预测做扎实的两个进阶技巧5.1 用误差分布验证模型而不是只看 MAPEMAPE 是个平均数会掩盖很多问题。我习惯把测试集的预测误差画成分布图再看三个指标误差中位数、90 分位误差、最大误差。如果中位数很小但 90 分位很大说明模型在少数时段崩了通常是尖峰或节假日。这时候要回去看那些时段的特征是不是缺失。下面这段代码可以直接抄import numpy as np err (pred_inv - y_test_inv) / y_test_inv * 100 # 百分比误差 print(MAPE:, np.mean(np.abs(err))) print(中位数误差:, np.median(err)) print(90分位误差:, np.percentile(np.abs(err), 90)) print(最大误差:, np.max(np.abs(err))) # 找出误差最大的10个时刻 worst np.argsort(np.abs(err))[-10:] print(最差时刻索引:, worst, 对应误差:, err[worst])跑完这段如果最差时刻集中在某几个小时基本能定位到是特征问题还是样本问题。这个习惯帮我省了很多瞎调参的时间。5.2 多步预测别用单步模型硬递归日前负荷预测要一次输出未来 24 个点。很多人用单步模型递归预测把预测值当输入喂回去误差会累积到第 24 步基本没法看。更稳的做法是直接改成多输出输出层放 24 个神经元一次预测全天。代价是样本数变少每个样本对应一整天但误差不会累积。如果样本实在不够可以用 Seq2Seq 结构但那就不是纯 BP 了。方案优点缺点适用场景单步递归样本多、训练快误差累积预测步长 ≤ 4多输出 24 节点无累积误差样本少日前预测Seq2Seq精度高结构复杂数据充足我自己的习惯是每次做完一版 BP 负荷预测先不急着调网络结构而是把误差最大的 20 个时刻拉出来逐个看那天的天气、节假日、有没有突发事件。十次里有七次问题出在数据而不是模型。BP 神经网络再强也救不了一列填错的历史负荷。希望这个思路帮到你。本文还有配套的精品资源点击获取