资讯动态

考虑天气因素的短期电力负荷预测:从LSTM到贝叶斯优化的完整建模实践

发布时间:2026/10/20 2:12:56 来源:尧图企业网站定制
这几年做短期电力负荷预测相关研究最大的感受是天气因素绝对不是锦上添花而是决定模型上限的硬约束。刚开始做这个课题时我也走过弯路把一套LSTM直接丢到历史负荷数据上验证集的MAPE始终降不下来。后来把温度、湿度、风速、降水这些气象变量加进特征工程同一套模型的预测误差明显回落有些天的峰值误差从接近10%直接降到3%以内。这篇内容想和你完整梳理一遍“考虑天气因素的短期电力负荷预测”项目的建模过程重点讲清楚深度学习模型的对比设计、基于贝叶斯优化的超参数搜索思路以及一份可以在Matlab里直接跑通的代码结构。目前不少研究者和电力调度侧工程师都在探索用AI模型替代传统回归方法目标都是把短期负荷预测做得更稳、更快、更适应复杂气象场景。1. 天气因素如何真正影响短期负荷预测1.1 负荷曲线的构成拆解短期电力负荷预测简单说是预测未来几小时到一周之内的系统负荷。它是电力调度、机组组合、市场交易的基础。我习惯把负荷分解成四个成分长期趋势、周期分量、气象敏感负荷和随机扰动。长期趋势可以通过带通滤波或差分平稳化处理周期性主要体现在昼夜、周末和工作日真正让预测变难的是气象敏感负荷和随机扰动——强冷空气、台风、持续高温、阴雨闷热全都直接反映在负荷曲线上。从负荷数据本身看往往能发现一个明显的季节U型夏季高温时段负荷爬升快冬季寒潮期间负荷冲至高位春、秋两季相对平稳。如果再叠加温湿度舒适区则在18℃到22℃之间存在一个负荷低谷带。这个规律在不同地区会有所差异但总的来说温度、湿度、风速以及降雨强度是对负荷解释力最高的气象变量。我拿一个实际场景举例。某地夏季的午后晴空突然转为雷阵雨气温在30分钟内下降5℃以上空调负荷迅速回落系统负荷可能出现数百兆瓦的短时波动。这类情景只有把气象变量作为输入让模型学习到“低温降雨→负荷下调”的映射才可能提前准确响应。如果只看历史负荷模型只会学到“这个时段负荷总是很高”的惯性必然产生延迟和偏差。1.2 天气数据到特征的转化原始气象数据通常是逐小时或者更细粒度的观测值。直接把它们和负荷数据拼在一起会碰到两个问题一是气象观测存在缺失二是变量之间的多重共线性明显。我的处理经验是先做相关性分析画出温度与负荷的散点图以及湿度、风速、降水与负荷的联合分布剔除解释力弱且和其他变量高度相关的指标然后对缺失值做“同一时刻前后两日的中值填充”既不破坏突发气象过程的特征又不会引入不合理的线性外推。除此之外时间断记信息也是特征体系里不可省的部分。小时编号、星期编号、是否法定节假日、是否工作日这些时间特征对深度学习模型来说往往比一部分弱相关的气象变量更重要。我自己经常用的做法是将时间特征与气象特征共同构成候选输入再用互信息法或随机森林重要度做一轮特征筛选以控制最终输入维度在10到20之间。1.3 时序窗口的设定短期负荷预测建模需要把历史序列切成长度为T的窗口。窗口长度L选多少很关键太短模型看不到前一天的同一时段负荷形态太长会增加训练代价并可能引入与目标时刻相关度极低的噪声。以小时级数据为例我习惯用964天或168一周作为候选窗口长度再让优化过程自行在合理范围内搜索。窗口内的特征排列也很重要一般每个时刻的特征向量由气象变量和负荷值拼接而成窗口内按时间维度堆叠成二维矩阵再输入序列模型。2. 深度学习模型的对比设计与评价手段在参加对比的模型选择上我最终圈定了三种结构LSTM、GRU和CNN-LSTM。选择这三个模型是因为它们分别代表了序列建模里的三种典型思路长时间依赖记忆、轻量化的门控单元、以及先提局部特征再捕捉时序依赖。知识偏少的同学也别担心下面的内容会把它们的结构逻辑讲清楚。2.1 LSTM最正统的循环网络路线LSTM通过输入门、遗忘门、输出门三个门控以及一个长期记忆单元决定每一时刻保留多少旧信息、吸收多少新信息。在负荷预测里它可以记住“昨天同时间段的负荷水平”也能适时遗忘掉几小时前对当前预测不再有用的信息。我通常把LSTM的隐单元数设置在50到200之间输出模式选择“last”只在序列末端输出一个预测值。2.2 GRU参数精简训练更快GRU把LSTM的三门简化为更新门和重置门参数数量明显减少在数据量不是特别大的负荷预测任务里训练速度更快泛化能力往往不输LSTM。如果你的实验环境配置有限GRU是一个很好的起点。2.3 CNN-LSTM局部特征与时间依赖的组合CNN-LSTM先用一维卷积层在时间维度上提取局部特征理解气象变量和负荷之间的局部耦合模式再把卷积输出送入LSTM层让网络对上下文依赖关系有更强的建模能力。在实际实验中这种混合结构往往在特征关系复杂的场景下能比单一循环网络获得更低的误差但代价是训练时间相应增长。2.4 评价指标与交叉验证预测效果评估我固定用三个指标MAPE平均绝对百分比误差、RMSE均方根误差和R²决定系数。MAPE直观刻画预测偏差比例RMSE放大大误差的惩罚R²反映模型解释能力。我会避开单纯只报告MAPE的捷径因为MAPE靠近零负荷时刻会被异常放大必须配合RMSE看峰值时段的预测质量。深度学习模型训练存在随机性因此每个模型设定至少重复3次实验报告均值±标准差。没有这个环节模型对比很容易被特定随机种子误导。这个细节在实际复现中很重要。3. 贝叶斯优化调参为什么值得认真对待3.1 为什么不用网格搜索和随机搜索模型的超参数直接决定训练收敛速度和预测精度。如果用网格搜索假设lstmUnits、learning rate、miniBatchSize这类参数分别取6个、6个、4个候选值仅组合就有上百组。深度学习模型训练一组动辄几分钟甚至几十分钟整体耗时不现实。随机搜索虽然比网格搜索高效但每次尝试彼此独立不会从之前的结果里学习容易反复在次优区域徘徊。贝叶斯优化的核心思路完全不同它把超参数和目标函数比如验证集的MAPE看作一个黑箱函数用高斯过程回归逐步拟合这个函数再通过采集函数判断下一个最值得尝试的超参数位置。换句话说它会在探索和利用之间做平衡——既会尝试完全没试过的区域又会倾向于选择预测误差可能更低的区域。常用采集函数有expected-improvement和probability-of-improvementMatlab内置优化默认的是expected-improvement-plus它在常规EI基础上加入了防止过度局部搜索的机制。3.2 Matlab中bayesopt的用法Matlab对这类问题提供了较完整的支持。要使用贝叶斯优化需要先定义一组optimizableVariable它支持整数、实数、分类等不同参数类型也支持log变换模式。对于学习率这类跨越多个数量级的参数设置Transform为’log’非常关键否则优化器大概率只会在某个量级附近反复试。下面是典型的超参数空间定义optimVars [ optimizableVariable(lstmUnits, [50 300], Type, integer) optimizableVariable(dropoutRate, [0.1 0.5], Type, real) optimizableVariable(initialLearnRate, [1e-4 1e-2], Transform, log) optimizableVariable(miniBatchSize, [32 128], Type, integer) ];然后定义一个返回验证集误差的目标函数通过函数句柄传给bayesopt。每次评估一组超参数时目标函数内部需要完成网络构建、训练和验证三个步骤这一步是整个优化过程中的性能瓶颈。提示在目标函数里关闭训练过程的详细输出只保留必要变量能明显减少优化时间。我在实验时会用Verbosefalse并在每次评估结束后用fprintf打印超参数和MAPE方便跟踪优化进度。3.3 训练轮数与早停机制的平衡由于贝叶斯优化要评估超参数每组参数如果都训练到较彻底收敛总耗时将会非常庞大。实际操作中我会先设定一个适中的MaxEpochs比如100然后在训练选项里设置ValidationPatience为10来实现提前停止。但要小心过早停止可能造成欠拟合使超参数比较基数不公平。我的经验是优化阶段MaxEpochs可以适当缩短如果发现效果不佳再手动放开跑最终模型等最优超参数确定后再训练一次更充分的最终模型。这种方法在保证结果稳定的同时可以节省很多GPU或CPU时间。4. Matlab代码落地从数据到多模型对比4.1 第1步加载数据与特征构建这里以一份包含逐小时负荷和气象数据的Excel表为例。数据文件的第一列是时间戳后面依次是负荷、温度、湿度、风速和降雨量字段。读取并构造特征矩阵的代码如下:% 读取历史数据 data readtable(load_weather_data.xlsx); timeVec data.TimeStamp; loadVec data.Load; weatherData [data.Temperature, data.Humidity, data.WindSpeed, data.Precipitation]; % 缺失值处理使用相邻两日同一时刻均值填充 for t 1:height(data) if isnan(weatherData(t, 1)) prevIdx t - 24; nextIdx t 24; if prevIdx 0 nextIdx height(data) weatherData(t, 1) 0.5 * (weatherData(prevIdx, 1) weatherData(nextIdx, 1)); else weatherData(t, 1) 0; end end end上面代码只处理了温度字段的缺失实际项目里需要把其他气象字段也纳入同一套缺失值处理逻辑。更好的做法是写一个函数统一对输入矩阵做填充而不要在主脚本里逐字段复制粘贴。接下来做时间特征编码和归一化hourOfDay hour(timeVec); dayOfWeek weekday(timeVec); isHoliday ismember(datenum(timeVec), holidayList); % holidayList需要提前定义 featureMatrix [weatherData, hourOfDay, dayOfWeek, isHoliday, loadVec]; featureMatrix zscore(featureMatrix); % 或者用自定义min-max归一化注意归一化时不能用全样本的均值方差。正确的做法是只在训练集上计算均值和标准差再分别对验证集和测试集做变换否则会出现信息泄漏测试效果会虚高。这一点在真实项目中很容易被忽略但对结果的可信度影响很大。4.2 第2步构建序列样本把表格型数据转换为序列样本常用的方法是用滑动窗口。窗口长度为seqLen每个样本是seqLen行、N列的特征矩阵目标值是下一时刻负荷值。function [X, Y] makeSequences(featureMatrix, seqLen) numSamples size(featureMatrix, 1) - seqLen; X cell(numSamples, 1); Y zeros(numSamples, 1); for i 1:numSamples X{i} featureMatrix(i:iseqLen-1, :); Y(i) featureMatrix(iseqLen, end); end end上面代码把目标值取为窗口结束后的下一个时刻的负荷注意featureMatrix的最后一列是负荷。转置操作是为了满足Matlab深度学习工具箱对序列输入的维度要求维度顺序是特征数×时间步长。4.3 第3步定义贝叶斯优化的目标函数目标函数接收一组超参数完成网络的建立、训练和验证集误差计算返回一个能够被最小化的标量。我的实现如下function mape lstmObjFcn(params, XTrain, YTrain, XVal, YVal) numFeatures size(XTrain{1}, 1); layers [ sequenceInputLayer(numFeatures) lstmLayer(params.lstmUnits, OutputMode, last) dropoutLayer(params.dropoutRate) fullyConnectedLayer(1) regressionLayer]; options trainingOptions(adam, ... MaxEpochs, 80, ... MiniBatchSize, params.miniBatchSize, ... InitialLearnRate, params.initialLearnRate, ... Verbose, false, ... ValidationData, {XVal, YVal}, ... ValidationFrequency, 10, ... Plots, none); net trainNetwork(XTrain, YTrain, layers, options); YPred predict(net, XVal, MiniBatchSize, params.miniBatchSize); mape mean(abs((YVal - YPred) ./ YVal)) * 100; end在目标函数里我需要特别说明一个细节ValidationData在训练过程中已经提供了早停依据但返回的mape仍是predict之后手工计算的结果这样能使验证集上的评估标准统一不依赖trainingOptions内部的validation指标定义。然后通过bayesopt发起搜索optimVars [ optimizableVariable(lstmUnits, [50 300], Type, integer) optimizableVariable(dropoutRate, [0.1 0.5], Type, real) optimizableVariable(initialLearnRate, [1e-4 1e-2], Transform, log) optimizableVariable(miniBatchSize, [32 128], Type, integer) ]; results bayesopt((params) lstmObjFcn(params, XTrain, YTrain, XVal, YVal), ... optimVars, ... MaxObjectiveEvaluations, 25, ... AcquisitionFunctionName, expected-improvement-plus, ... UseParallel, false);25次评估听起来不多但在深度学习场景下已经需要不少时间所以建议用CPU实验时先把数据集切片缩小。如果有多核或多GPU环境可以把UseParallel设为true把多个超参数组合并行尝试效果非常明显。4.4 第4步用最优超参数训练最终模型bayesopt返回对象中可以从XAtMinObjective获取最优参数组合。用这个参数组合在全量训练集上重新训练然后做测试集验证bestParams results.XAtMinObjective; layersFinal [ sequenceInputLayer(size(XTrain{1}, 1)) lstmLayer(bestParams.lstmUnits, OutputMode, last) dropoutLayer(bestParams.dropoutRate) fullyConnectedLayer(1) regressionLayer]; optionsFinal trainingOptions(adam, ... MaxEpochs, 150, ... MiniBatchSize, bestParams.miniBatchSize, ... InitialLearnRate, bestParams.initialLearnRate, ... Verbose, true); netFinal trainNetwork(XTrain, YTrain, layersFinal, optionsFinal); YPred predict(netFinal, XTest, MiniBatchSize, bestParams.miniBatchSize);这段代码的最终结果是testPredict和testLoad。可以用mapminmax反归一化也可以用一开始保存的sigma和mu手动还原预测值。再计算MAPE和RMSE时应统一使用反归一化后的物理量而不是归一化之后的值。4.5 对比模型的实现差异GRU模型的差异非常小只需要把lstmLayer换成gruLayer其他部分的代码结构几乎不变。CNN-LSTM则需要加一维卷积层。可以是先卷积再输送到LSTMlayers [ sequenceInputLayer(numFeatures) convolution1dLayer(5, 32, Padding, same) reluLayer lstmLayer(params.lstmUnits, OutputMode, last) fullyConnectedLayer(1) regressionLayer];注意卷积层默认在通道维度上工作。对我们的数据形状特征数×时间步长来说这里实际上是对特征维度做卷积。如果你想对时间步长维度提取局部模式可以先转置数据让时间作为行维度再使用卷积。这一个坑我一开始踩过对比试验结果时才发现模型架构设计层面的问题。5. 实验对比与实操经验总结5.1 三模型在测试集上的成绩对比以某地区18个月的负荷和气象数据为例我用同样的特征工程、同样的训练/验证/测试划分方式对三种深度学习模型做了贝叶斯优化后的对比。为避免一次性搜索太慢每个模型评估25组超参数。最终测试集结果大致如下表所示模型结构MAPE%RMSEMW训练耗时相对值LSTM2.6338.21.0GRU2.7139.40.7CNN-LSTM2.4235.61.8说明一下这个结果只是模拟案例的示例数据现实中与你所在地区的数据情况关联较大。但趋势有共性CNN-LSTM通常提升预测精度GRU在训练效率上有优势LSTM表现居中。实际项目里如果企业侧更看重训练周期和硬件成本GRU便是一个很好的备选如果精度目标是第一优先级CNN-LSTM更值得选。5.2 实操中最容易踩的几个坑第一坑测试集数据泄漏。很多人先做全样本归一化再用滑窗切数据导致测试集的均值方差已经“透露”给训练过程。结果是交叉验证分数很高模型上线后预测发飘。正确的流程是先切分时间序列再在训练集上计算归一化参数最后应用到验证集和测试集。第二坑目标函数里使用随机默认初始化。深度学习网络每次初始化的随机种子不完全一致导致同组超参数在不同时间跑出的MAPE波动很大。为解决这个问题我习惯在目标函数和最终训练脚本里固定rng(42)或受控的随机种子。当然如果做正式对比还要做多次重复实验取均值。第三坑把气象预报值当作实测值用。实际部署时电力预测使用未来时段的气象数据来自数值天气预报这个预报本身存在误差。如果在训练时把历史实测天气作为输入而在测试时用预报天气则误差会成倍放大。更稳妥的做法是把天气数据同样拆分为历史实测和未来预报两种份量在训练时设定噪声扰动模拟预报误差代表一种更真实的实验。第四坑窗口长度选择不当。对于小时级负荷预测过短的窗口会让模型无法捕获长的昼夜电价节律过长的窗口则可能导致信息遗忘。可以把这个长度本身也当成超参数与神经网络结构同时优化效果往往能带来额外改善。5.3 贝叶斯优化参数的注意事项数据集规模较小时贝叶斯优化的代理模型很容易过拟合。此时可以考虑把MaxObjectiveEvaluations调小或者增加一个随机基线结果作为代理模型的初始观测值。另外不同深度学习模型的超参数空间设计可以不同——比如GRU没有dropoutRate时其参数搜索空间就应该略去该参数。一定要避免把所有模型塞进同一个搜索空间里去重复计算无意义的组合。还有一个工程层面细节如果你的训练数据跨度超过了一年建议把季节作为特征显式编码而不是让模型自己去“推测”时间关系。由于负荷对季节的依赖性极强把一年分成四个季节标志位甚至加入“是否供暖季”业务标志往往能持续降低误差。这些业务侧知识和深度学习模型叠加往往比单纯调参带来的提升更大。我在多个数据集中都验证了这一点。5.4 我自己的实验习惯与心得每次实验我都会保留四份东西可复现脚本、数据说明文档、超参数搜索日志、预测结果截图。贝叶斯优化的搜索过程看似自动但实际工程中我几乎每次都需要介入。介入点主要有两个一个是当优化结果集中出现在搜索空间的边界时说明给定取值范围设计得不合理需要扩大或移动边界另一个是最优超参数出现后我会选择再围绕该点附近缩小范围做一次二次精搜索用较少代价获得更稳定的配置。这套“粗搜索精搜索”的方式在实际项目中帮我节省了不少调参时间。6. 从研究到实用化的扩展思路6.1 从单步预测到多步预测与区间预测目前的示例只预测下一时刻负荷但实际调度需要未来24小时乃至一周的逐时曲线。多步预测有两种常见处理方式一种是递归式把上一步预测值作为下一步输入误差会沿着时间累积另一种是直接式一次性输出未来N个时刻的负荷向量损失函数同时约束所有预测点。直接式在同步训练上更简洁但输出维度设计需要结合调度粒度来定。除了点预测另一个值得扩展的方向是预测区间。可以用分位数回归的思路让模型输出多个分位数也可以借助多个模型的预测残差来构建置信区间。调度部门如果只知道“明天最大负荷是2000MW”而不知道“有50%的概率落在1940至2060MW之间”决策时会非常被动。把区间信息加进预测结果是用研成果转化成可用工具的关键一步。6.2 特征和模型方向的进一步探索特征层面除了常规气象变量可以考虑加入体感温度指标、0时到当前时刻的累积降雨量、最大风速等这些工程特征对极端天气场景的预测有很大帮助。从模型层面看Transformer类模型近年在时序预测上的势头很猛和LSTM做对比也是值得做的方向。但要把对比做好并不容易必须保证训练设置公平否则很容易被结论误导。贝叶斯优化本身也可以进一步扩展为多目标优化。比如同时优化MAPE和训练耗时或者在“预测精度和模型参数量”之间做取舍用Matlab的paretosearch接口或第三方工具实现。在我个人经验里这些改进虽然会提高实现复杂度但确实能把模型从论文走向落地应用的距离拉近一大截。在做这样一个负荷预测项目的过程中最值得沉淀的往往不是单一模型精度而是一整套“数据→特征→超参数→模型对比→工程部署”的闭环方法论。天气变量让模型学会物理规律贝叶斯优化让模型把参数选到更好的位置而多模型对比让最终选择有了依据。把代码和实验记录都沉淀好后续无论换数据、换地区还是换时间尺度都能用这套链路快速得到可信结果。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑