简介基于BP神经网络的电力负荷预测研究资料包面向电气工程、数据科学方向的学生与相关从业者聚焦电网短期负荷预测场景系统展示从历史负荷数据采集到预测模型构建、训练与误差分析的完整流程。压缩包共334个文件主体为329个Excel数据文件涵盖2018年多日负荷记录与天气数据并配有main.m、my_map.m、read_load_data_from_excel.m等MATLAB脚本及README.md说明文档整体约5.32MB结构清晰、便于快速上手。已有2459人学习下载内容涉及数据预处理、特征选择、网络结构设计、梯度下降与反向传播调参、模型验证及均方误差等指标分析既可直接运行复现案例也可依据代码和数据结构学习BP神经网络在时间序列预测中的应用同时了解天气、历史负荷等因素如何融入模型适合作为课程设计、毕业设计或论文实验的基础参考资料。1. 电力负荷预测机器学习不是万能药但确实是当下最靠谱的路电力负荷预测这件事说大也大它直接决定电网调度、机组启停和现货市场报价说小也小落到一个具体园区、一台变压器上就是回答明天上午十点这条馈线大概跑多少千瓦。过去十几年行业里最常用的方法是时间序列模型和回归分析近几年机器学习方法逐步上位。别急着神话它——神经网络不是一切问题的答案但机器学习框架下的特征工程、交叉验证和误差分析确实把负荷预测从一个凭经验的玄学变成了可量化、可迭代的工程问题。这篇笔记我会按自己实际做过的方案来讲基本思路是先弄清负荷预测在预测什么、用什么数据再选模型再谈工程落地和那些容易翻车的细节。适合电网调度、售电公司、园区能源管理平台的技术人员也适合准备做相关课程设计的同学照着搭一套基线系统。2. 从问题定义到数据集构建先把负荷预测翻译成监督学习2.1 预测步长与粒度多做点预测和短期预测的工程分界线做负荷预测第一件事不是选模型而是把问题定义清楚预测未来多久、颗粒度多大、滚动更新还是定时更新。常见的分法是超短期未来15分钟到1小时、短期未来1到7天、中期未来数周到数月。机器学习方法最常落地在短期和超短期因为中期以上涉及气象长期预报、节假日安排和经济增长等不确定性纯数据驱动模型很难压住误差。粒度方面电网调度常用15分钟和1小时楼宇和园区能源管理常用30分钟或1小时做售电预测时还会看日总量。不同的粒度对数据量要求差别很大15分钟粒度一年约有35040个点小时粒度约8760个点。数据量越小越要克制模型复杂度——用个几千棵树的LightGBM在小样本上没太大问题但深度网络就容易过拟合。我给自己的工程约定是短期预测用小时粒度步长24小时超短期用15分钟粒度步长4到8个点。这个搭配既能覆盖大多数业务场景又不至于让数据预处理和特征工程过于复杂。2.2 数据清洗与对齐拿到原始负荷数据后的前三个处理动作原始负荷数据从SCADA系统或电表采集系统导出后几乎不可能直接用来训练。常见的问题包括缺失点通讯中断、跳变点采集异常、时区混乱有些系统存的是北京时间有些是当地时区。我一般会做这样三步第一步是对齐时间轴。把所有数据统一到同一时区、同一个时间戳格式重采样到目标粒度。比如原始数据是5分钟一条要聚合到小时粒度可以用取平均或取最大值——取平均适合做电量推算取最大值适合做需量管理看业务口径选。第二步是处理缺失值。对小于等于连续两个点的缺失用前后线性插值对更长的缺失段用同类型日比如都取工作日的同一时刻均值填充。这里有个易错点不能用未来数据填充历史缺失否则做时序交叉验证时会造成信息泄漏验证集上误差会很好看一上真实环境就现原形。第三步是识别和处理异常跳变。最简单的方法是用滚动窗口的均值加减三倍标准差做粗筛再结合业务判断。比如某地区夏季空调负荷大某天午后出现一个远超历史极值的读数既可能是真实需求高温工作日电价激励也可能是采集故障。这时候我会把候选异常点导出到一个Excel里人工排查一遍完全不自动删除。import pandas as pd import numpy as np def load_and_clean(raw_path, freq1h): df pd.read_csv(raw_path, parse_dates[timestamp]) df df.set_index(timestamp).sort_index() # 去重同一时间戳只保留最后一个值 df df[~df.index.duplicated(keeplast)] # 重采样缺失点置为NaN df df.resample(freq).agg({load: mean}) # 连续缺失不超过2个点线性插值 df[load] df[load].interpolate(limit2, limit_directionboth) # 滚动窗口异常值标记不直接替换 rolling_mean df[load].rolling(window24, centerTrue).mean() rolling_std df[load].rolling(window24, centerTrue).std() df[anomaly] (df[load] - rolling_mean).abs() 5 * rolling_std return df先说一下上面的逻辑。limit2和limit_directionboth保证了只填充短缺口而且是双向填充避免凌晨数据单边缺失时产生明显偏置。centerTrue让滚动窗口以当前时刻为中心两边的数据都参与统计这对标记异常点来说是合理的——但注意训练特征里不能这么用后面预测时当前时刻之后的数据不可得特征只能从过去取。异常值标记出来之后我通常会做一个散点图确认确认是真实事件就保留确认是坏数据才用前一天同时刻的值替换。2.3 构建监督学习样本滑动窗口、预测目标与防止数据泄漏负荷预测的原始形态是时间序列但机器学习模型吃的是表格数据所以要把时间序列转换成样本对特征目标。假设用过去7天的同时刻负荷去预测未来1小时负荷那么每条样本的特征是168个历史小时值加上天气、日期等外生变量目标是未来某个时刻的负荷值。窗口滑动构建样本时有一个非常容易踩的坑训练集和验证集必须按时间顺序切分不能在构建样本时打乱。另一个坑是滚动窗口的重叠度过高会让相邻样本高度相关模型很容易记忆而不是学习。我的做法是对小时粒度数据窗口长度24或168步长1小时对15分钟粒度窗口长度96一天步长1个点。这样相邻样本之间存在信息重叠但业务上预测任务就是需要连续输出所以重叠本身不是问题问题是切分不干净。def build_samples(df, feature_cols, target_col, window168, horizon1): X, y [], [] values df[feature_cols].values targets df[target_col].values for i in range(window, len(df) - horizon): X.append(values[i - window:i]) y.append(targets[i horizon]) return np.array(X), np.array(y)这个函数有三个参数要特别注意。window是历史窗口长度取168意味着利用过去整整一周的负荷模式对周期性强、工作日和休息日差异明显的场景很有效如果数据只有一个月那窗口取24更稳妥否则前168个点都只能放弃不做样本。horizon是预测提前量取1就是预测下一时刻取24就是预测明天同一时刻。特征矩阵的维度是(样本数, window, 特征数)如果特征是168个小时负荷加温度、湿度、星期几、节假日标记那第三维就是4到6个通道后面的机器学习模型要把二维矩阵展平或做特征聚合直接三维灌进LightGBM是不行的。3. 特征工程是负荷预测的主战场时序特征、气象特征与节假日编码3.1 负荷序列的三种周期日周期、周周期与季节周期怎么编码做负荷预测的人常说一句话负荷数据最稳定的信息不是数值本身而是它的周期性。居民负荷、商业负荷、工业负荷都有明显的日周期——早上和晚上各有一个峰午间和深夜是谷周周期也很明显工作日和休息日形态完全不同季节周期更不用说冬季取暖和夏季制冷的负荷曲线相差甚远。把周期性喂给机器学习模型有三种做法。第一种是直接取历史同一时刻的值作为特征比如预测明天上午10点的负荷就把今天上午10点、昨天上午10点、上周同一天的上午10点作为特征——这是最简单的效果通常也很好。第二种是用时间戳的周期编码比如把小时转换成sin(2π*hour/24)和cos(2π*hour/24)把星期几用同样的方式编码成周周期。第三种是把周期信息做成类别特征比如第几小时星期几直接喂进树模型LightGBM和XGBoost能自己找到合适的分裂点但也容易过拟合一些冷门组合——比如某节假日恰好在周二、正好又碰上极端天气这种组合样本量极少。我一般会把三种做法混用历史同时刻值是必选周周期和周几同时给周期编码则根据模型选——树模型给原始类别线性模型给sin/cos神经网络直接嵌入。3.2 温度与湿度特征用感受温度代替单一温度值负荷对气象的敏感度很高但直接用单一温度值做特征往往效果不理想。原因有二一是有滞后性连续三天高温时第三天的负荷通常比第一天高因为建筑物和地面蓄热二是湿度的影响不能单独看闷热天即使温度不是极高空调负荷也可能很大。实操中我会构造一个感受温度特征用温度加湿度做一个简单加权再叠加累积效应——过去48小时的小时温度加权平均值权重按小时递减。这样做的效果比单纯加一个温度列好很多尤其在夏季场景下能把午后负荷峰值的拟合误差压低几个百分点。另外光照对光伏渗透率高的地区也很重要如果手头有辐照度数据也同样要做累积特征。3.3 节假日与特殊日处理为什么重大节假日总是预测翻车节假日是负荷预测的老大难。春节、国庆这类长假期间工厂停工、商场歇业负荷水平可能比正常工作日低20%到40%而且每天的形态完全不同于常规。如果训练数据里没有足够的同类样本模型会把长假误判成普通周末甚至工作日导致预测偏差非常大。处理节假日有几个工程上常见的做法。第一种是加距最近节假日天数和节假日第几天特征让模型知道当前日期离节假日的远近这在节前和节后尤其有效。第二种是单独建一个节假日样本集节假日期间用专门的模型或规则修正。第三种是直接剔除重大节假日样本然后另做一套节假日预测方案。我一般会先用第一种做通用模型然后看节假日期间的实际误差——如果平均绝对百分比误差超过常规时段的1.5倍就会给节假日单独建一个模型。一个容易犯的错是在节假日特征里把调休日也当作普通工作日或休息日。中国特有的调休制度意味着有些周六上班、有些周日放假如果只用周几做特征模型永远学不会这种规则。要把日历中的是否调休作为一个独立特征输入这个数据可以从公开节假日接口获取也可以每年年初人工维护一张日期表。4. 选型与训练从线性回归到LightGBM再到LSTM怎么选不翻车4.1 基线模型先立住线性回归和决策树回归的定位机器学习方法做负荷预测第一步不是直接上深度学习而是先跑一个可解释的基线模型。我通常先做线性回归和决策树回归两个基线前者用于理解特征的线性相关性后者用于理解非线性切分的潜力。线性回归有个很大的好处是可以直接看每个特征的系数——过去几天同时刻负荷的特征权重通常是最大的温度特征的权重是正的夏天或负的冬天日期特征的权重能反映工作日和休息日的差异。基线模型不需要精细调参主要用来建立后续对比的基准。如果后面复杂模型比基线的提升不到5%就要怀疑是不是特征工程出了问题而不是模型不够强。这个习惯帮我避免了不少无用功——有次在某个园区的数据上LSTM调了一周参数最后效果只比线性回归好3%检查下来发现是温度特征的滞后效应没做好把累积温度特征加进去后LightGBM直接把差距拉开到12%。from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_absolute_percentage_error def train_baseline(X_train, y_train, X_test, y_test): model LinearRegression() model.fit(X_train, y_train) y_pred model.predict(X_test) mape mean_absolute_percentage_error(y_test, y_pred) * 100 coef sorted(zip(feature_names, model.coef_), keylambda x: abs(x[1]), reverseTrue) return model, mape, coef[:10]这段代码里有个细节mean_absolute_percentage_error对接近零的真实值非常敏感如果某些时段负荷趋近于零MAPE会被极小的分母推到一个很大的数。工程上我更常用RMSE加MAPE两个指标一起看并且在做误差统计时剔除负荷低于某个阈值的样本。另外线性回归对特征尺度敏感如果进入模型的温度是两位数、负荷是四位数最好先做标准化或至少统一量纲。4.2 树模型是当下的主流选择LightGBM的四个必调参数如果只能选一个模型做负荷预测我推荐LightGBM。它在表格数据上的表现稳健、训练快、对特征工程的要求比深度网络低而且在相对小的数据集上不容易过拟合。XGBoost也很好但LightGBM在训练速度和内存占用上更友好尤其是特征维度到几百之后差别明显。负荷预测的LightGBM模型四个参数最值得调num_leaves控制树的复杂度数据量在几万条量级时取31到127之间learning_rate一般取0.03到0.1调低后可以配合更大的n_estimatorsmax_depth限制树深防止在噪声特征上学到太细的分裂min_child_samples设到20以上防止叶子节点样本太少。还有一个容易被忽视的点feature_fraction和bagging_fraction可以缓解时间序列样本间的相关性带来的过拟合我通常把两者都设在0.8左右。import lightgbm as lgb params { objective: regression, metric: rmse, num_leaves: 63, max_depth: 8, learning_rate: 0.05, n_estimators: 800, min_child_samples: 20, feature_fraction: 0.8, bagging_fraction: 0.8, bagging_freq: 1, } model lgb.LGBMRegressor(**params) model.fit(X_train, y_train, eval_set[(X_valid, y_valid)], callbacks[lgb.early_stopping(50), lgb.log_evaluation(100)])注意这里用了early_stopping它在验证集上评估每一次迭代的性能当连续50轮没有提升时自动停止。这个机制对有较强周期性但偶尔出现异常样本的负荷数据特别有用——模型会在前几百轮快速下降然后进入一个平台期如果没有早停继续训练只是在记住噪声。4.3 深度学习要不要上LSTM和TCN在不同场景下的取舍深度学习在负荷预测领域的名声有点两极分化。做时序预测的论文几乎必上LSTM、GRU、Transformer但在实际工程项目里深度模型能稳定打赢LightGBM的场景并不多。我的经验是数据量超过5万条、特征中包含多条长时间依赖的序列比如同时给过去96个时刻的负荷、温度和电价、并且允许投入较多时间做调参和数据预处理时深度学习才有明显的优势。否则树模型的性价比更高。如果决定上深度模型我建议先从LSTM开始。它对中等长度序列几十到几百个时间步的建模能力是足够的而且Keras和PyTorch都有很成熟的API。要注意三个坑第一输入需要做归一化而且归一化的统计量必须从训练集计算验证集和测试集用同一组参数转换第二序列样本的构建方式和前面提到的滑动窗口一致但训练时必须打乱样本顺序——这一点和时间序列交叉验证不矛盾因为LSTM处理的是样本内的时序依赖而样本间的顺序不影响这个依赖关系第三验证集的切分仍然要按时间顺序模拟真实预测时的样本分布。TCN时间卷积网络是另一个值得尝试的方向它的训练比LSTM稳定不存在梯度消失问题而且可以并行计算。在超短期负荷预测中TCN往往能取得和LSTM相当或更好的效果训练时间还短很多。如果团队没有专人负责调参我一般建议先做TCN把LSTM留给后续研究。4.4 时序交叉验证普通K折在这里是错的机器学习的常规做法是K折交叉验证——把数据随机分成K份轮流做验证。但负荷数据是时间序列随机切分会把未来的样本混进训练集验证集上当然表现很好但那是虚假的。正确做法是按时间顺序切分训练集永远在验证集之前。具体实现上我会用滚动推进的方式假设有24个月数据第一次用前12个月训练、第13个月验证第二次用前13个月训练、第14个月验证以此类推。这样做既能模拟真实预测时用过去预测未来的约束又能通过多次评估判断模型在不同季节的稳定性。import numpy as np from sklearn.model_selection import TimeSeriesSplit tscv TimeSeriesSplit(n_splits5) for train_idx, val_idx in tscv.split(X): X_train_t, X_val_t X[train_idx], X[val_idx] y_train_t, y_val_t y[train_idx], y[val_idx] model.fit(X_train_t, y_train_t) preds model.predict(X_val_t) mape mean_absolute_percentage_error(y_val_t, preds) * 100 print(ffold MAPE {mape:.2f}%)TimeSeriesSplit是sklearn内置的时间序列切分器它保证训练集始终是验证集之前的连续片段。但注意它默认的切分间隔是等宽的不会考虑季节跨度如果你的数据是小时粒度并且只切5折可能每折只有几个月模型学到的季节性有限。我通常会在切分前先把数据按连续X周组织成块再手动切分确保每折都覆盖完整的周周期。5. 负荷预测的常见翻车现场五个高频踩坑点与排查办法5.1 数据泄漏验证集误差很小上线就崩现象离线测试时MAPE只有3%投入在线预测后第一周误差飙到10%以上。原因特征构建或预处理过程中用了未来信息最常见的有三类——用全局均值和标准差做归一化时统计量包含验证集数据缺失值填充用了前后插值且时间窗口跨过了切分点日期特征里包含了预测时刻之后才确定的气象预报值。解决方案统一封装特征构建逻辑测试集和验证集走完全相同的管道pipeline归一化统计量只从训练集计算。一个更彻底的检查方法把数据的后1/3随机抹掉用前2/3训练后重新预测后1/3如果误差明显高于交叉验证结果基本可以判定泄漏。5.2 季节性漂移模型在夏天拟合得很好一到冬天就偏现象春夏训练的模型在秋冬验证集上误差变大而且不是轻微变大是整体抬升。原因负荷的季节性特征采暖负荷、制冷负荷在训练集中占比小或者完全没出现模型相当于面对一个没见过的数据分布。解决方案构建训练数据时保证覆盖至少一个完整年度如果数据不到一年就明确声明模型的适用季节不要盲目相信一整年的测试结果。另一个补救措施是把月份或季节作为特征输入模型至少能学到不同季节的整体差异。5.3 多步预测误差累积预测24小时后后半段误差越来越大现象做24小时滚动预测前几个小时的误差很小但第18到24小时误差明显增大。原因单步模型在每一步输入的都是真实历史值而真实在线预测时输入的是前一步的预测值误差会逐点累积。解决方案训练时加入多步拼接策略——把前一步的预测值当作下一步的特征来训练让模型适应误差累积的输入分布或者退一步讲预测24小时输出时直接用多输出模型同时输出24个未来值而不是递归滚动。前者效果好但训练复杂度高后者实现简单、在负荷场景通常也够用。5.4 极端天气日整体失准寒潮大风一来模型集体失灵现象平日预测误差5%寒潮来袭当天误差冲到20%以上。原因训练集里极端天气样本太少模型没见识过这种输入组合或者是气象特征不够直接——寒潮的影响不仅体现在温度绝对值上还体现在温度变化速度上。解决方案构造温度24小时变化量特征对极端天气样本单独加权或做上采样备一条规则兜底——当天气预报出现极端天气预警时使用一个简单但鲁棒的经验模型比如按历史同期温度-负荷回归曲线直接查表替换机器学习预测。5.5 样本加权失效试图让模型更关注近期的数据反而失真现象给近期样本分配更高权重验证集误差没降反升。原因负荷分布不是平稳的近期样本的形态不一定能代表未来——比如刚过去的国庆假期会压低模型对未来普通工作日的预测。解决方案权重只应放在业务上明确相关的时段比如知道未来一周有新项目投产才需要把相似的投产历史样本加权。平时不需要做时间衰减加权树模型对时间远近天然不敏感LSTM可以通过训练时调整序列内的注意力机制来关注近期趋势不需要显式加权。6. 模型上线前的验证技巧残差分析和滚动回测帮你少交点学费模型训练完之后先别急着部署到生产环境。我习惯再做两件事残差可视化分析和滚动回测。残差分析能帮助判断误差是否在时间轴上均匀分布滚动回测能模拟连续预测场景暴露数据泄漏和误差累积的问题。这两步加起来半天内就能完成但往往能发现前面所有指标都看不出来的隐患。残差分析有一个特别实用的做法把预测残差按小时、星期几、月份各画一张分布图。如果某个时段残差明显偏大就说明特征工程没捕捉到这个时段特有的模式。我有一次就是通过这个分析发现晚间20点到22点居民负荷在周五和周六差异极大——平时用星期几一个特征根本不够加上是否为周末前夜之后误差立刻降下来。另外可以做一个滚动起点的快速回测脚本从验证期的每一天作为起点依次预测未来24小时每天只保留当天的预测结果汇总成一条连续预测曲线再和真实值逐点对比。这样做比一次性预测更接近真实的在线运行逻辑因为每一天用的训练数据都在前一天之后。要注意这个回测脚本会跑很多次模型训练所以先确保单次训练时间在可接受范围内。def rolling_backtest(df, model, feature_cols, target_col, horizon24): errors [] for start in range(len(df) - horizon - 168): train df.iloc[:start 168] test df.iloc[start 168:start 168 horizon] model.fit(train[feature_cols], train[target_col]) pred model.predict(test[feature_cols]) true test[target_col].values errors.append(np.abs(pred - true) / (true 1e-6)) errors np.concatenate(errors) print(frolling MAPE {np.mean(errors) * 100:.2f}%)这里起始训练数据不能是空的所以从第168个点之后才作为第一次预测起点。1e-6是为了防止负荷接近零时计算比值为无穷大实际业务里大部分时段负荷都有底数这个保护仍然建议保留。运行这个回测会比较耗时但它是上线前最有价值的一步——如果滚动MAPE和单次验证MAPE差距超过两个百分点基本可以确认流程里还有泄漏或特征错位问题。最后说一下我的习惯上线后我会把每天的预测值和真实值存成一张对比表每周画一次趋势图哪怕不做任何调整也至少坚持看两个月。因为负荷模式会随季节、经济活动和用电行为缓慢变化一个模型在某个时间段看起来很准不代表三个月后依然可靠。机器学习负荷预测的本质是持续迭代不是一次训练一劳永逸。这个方案的方向是对的关键是用好交叉验证、控制好数据泄漏、盯紧残差。希望帮到你。本文还有配套的精品资源点击获取