最近在做一个配电网的短期负荷预测项目,数据预处理、特征工程折腾了一圈,最后在预测模型上卡住了。试过传统的ARIMA、支持向量回归,效果怎么说呢,能用,但总觉得差点意思。后来把LSTM和Adaboost结合起来搞了一套混合模型,效果出乎意料地稳,尤其是在峰值和突变段的拟合上,比单一模型强了不少。这篇文章就把这套基于Matlab的实现思路、核心代码逻辑和踩过的坑完整记录下来,给正在做负荷预测的朋友一个参考。
既然是做电力负荷预测,先说清楚这套方案解决什么问题。电网侧的短期负荷预测(通常指未来24小时到168小时)直接关系到发电计划的制定、机组启停安排以及电力市场的交易策略。预测误差每降低一个百分点,带来的经济效益都是很可观的。LSTM擅长捕捉时间序列中的长期依赖关系,对负荷这种强周期性的数据很友好;但单个LSTM在泛化能力上有限,容易出现对某些特殊日(比如节假日、极端天气)预测偏差较大的问题。Adaboost作为一种经典的集成学习方法,核心思想是串行训练多个弱学习器,重点关注前一轮预测误差大的样本,最终加权组合成一个强学习器。把这两者结合,逻辑很清晰:先用LSTM作为基学习器提取时序特征,再用Adaboost的框架对LSTM的预测结果进行纠偏和集成,相当于给LSTM配了一个“差生补习班”,专门攻克难预测的样本点。
从实际效果看,这套组合在RMSE(均方根误差)上相比单一LSTM能下降8%到15%,在MAPE(平均绝对百分比误差)上也有明显改善。当然提升幅度跟数据质量、训练周期长短有关系,但方向是没问题的。
1. 内容整体设计与思路拆解
1.1 为什么是LSTM加Adaboost,而不是其他组合
先聊聊模型选型。电力负荷数据有两个很明显的特征:周期性和趋势性。每天的负荷曲线呈现早高峰、晚高峰的“双峰”形态,同时受工作日、周末、季节更替影响,整体趋势也在变化。LSTM通过门控机制(输入门、遗忘门、输出门)能够自主决定记住哪些历史信息、遗忘哪些信息,这个特性和负荷预测的需求是很匹配的。
但LSTM有个问题,对极端样本的拟合能力不足。比如夏季高温日,空调负荷激增,负荷曲线会出现一个明显的尖峰。这种样本在历史数据里占比不高,但恰恰是预测的难点。普通LSTM训练时,MSE损失函数对所有样本一视同仁,模型会把更多的“精力”放在拟合大多数普通样本上,对尖峰样本反而拟合不到位。
Adaboost解决这个问题的思路很有意思。它在每一轮训练结束后,会计算所有样本的误差,然后提高那些被预测错的样本的权重,降低预测正确样本的权重,下一轮的弱学习器就会更关注这些“难啃的骨头”。这个机制叫“样本权重自适应更新”。当LSTM作为弱学习器嵌入Adaboost框架时,每个LSTM的侧重点是不同的:第一个LSTM学的是负荷的整体规律,第二个LSTM在第一个的基础上重点学第一个LSTM预测不好的时段,以此类推。最终把多个LSTM的预测结果按权重组合起来,相当于多个视角互补,比单个模型“硬扛”要稳健得多。
1.2 基学习器数量怎么定,为什么不是越多越好
Adaboost里一个关键参数是弱学习器的数量(迭代轮数,记为T)。我一开始想当然地设了20轮,结果训练时间翻了快三倍,效果反而比10轮的差。原因在于Adaboost每轮会更新样本权重,如果某个样本在连续多轮里都被预测错,它的权重会被放大到很高,导致后续的弱学习器“死磕”这个样本,反而忽略了整体规律,这就是过拟合的表现——在训练集上看得挺好,一到验证集就露馅。
经过GridSearchCV网格搜索试验,我最终把T定在8到12之间。具体做法是在验证集上画出T从1到20的误差曲线,选择误差最小的那一点。对于大多数电力负荷数据,这个区间是比较合理的。如果数据量特别大(超过两三年),可以适当增加到15轮左右;如果数据只有几个月,4到6轮就够了。
注意:Adaboost的弱学习器数量不是越多越好,这是一个常见的误区。每增加一轮训练都会重新计算样本权重,迭代轮数过高,后期基本就是在拟合噪声,泛化能力反而下降。采用早停策略(在验证集误差开始上升时停止训练)是最稳妥的做法。
1.3 整体方案的技术路线
整套方案分五个模块:数据预处理、特征工程、LSTM基学习器构建、Adaboost集成、结果评估与可视化。
数据预处理做的是缺失值填补、异常值修正和归一化。负荷数据因为采集设备故障、通信干扰等原因,出现空值和尖峰毛刺是很常见的,直接丢进模型会带偏训练方向。特征工程环节构建了历史负荷特征(前24小时、前168小时的同时刻负荷)、时间特征(小时、星期、是否节假日)和气象特征(温度、湿度,如果数据充足的话)。基学习器是多个结构相同但侧重点不同的LSTM网络,每个网络单独训练并输出预测结果。Adaboost集成环节计算每个LSTM的误差,更新样本权重,给每个LSTM分配一个权重系数,最终预测值是所有LSTM预测值的加权和。结果评估阶段用RMSE、MAPE、R²三个指标来量化模型效果,同时画出预测值与真实值的对比曲线。
2. 核心细节解析与实操要点
2.1 数据预处理:别小看缺失值和异常值
电力负荷数据最常见的两个问题是:一是有零散的缺失点,二是负载数据中混有瞬时的尖峰毛刺。刚开始做这个项目的时候,我对缺失值采用“直接删除”的策略,后来发现不行,删除会导致时间序列不连续,LSTM的时序学习能力直接受影响。后来改成线性插值,如果缺失区间较长(超过三个点),则采用同一天前后同时刻的平均值来填充。
异常值的处理比缺失值更麻烦。比如某个时刻负荷突然跳到正常值的两倍,然后又恢复,这很可能不是真实的负荷变化,而是采集或传输环节的问题。我采用的方法是滑动窗口法:以当前点为中心,取前后三个点的负荷平均值,如果当前点偏离平均值超过三倍标准差,就判定为异常值,用该平均值替代。
还有归一化。LSTM内部使用的是sigmoid和tanh激活函数,输入数据的尺度直接影响收敛速度和最终的精度。我用的是Min-Max归一化,将数据压缩到0到1之间。这里有个细节要特别注意:一定要先对训练集做归一化,然后用训练集的最小值和最大值去归一化测试集,而不是对全部数据统一归一化。如果混在一起做,等于把测试集的信息泄漏到了训练过程中,最终评估出来的误差是偏乐观的,实际部署时效果会打折扣。
2.2 特征工程:预测24小时,到底该喂什么特征
特征工程直接决定了模型的上限。一开始我只用了历史负荷序列——也就是用前168小时的负荷去预测下一个时刻,效果一般。后来逐步加入了时间特征和气象特征,提升非常明显。
时间特征包含三块:小时(0到23)、星期(1到7)、是否节假日(0或1)。这三个特征的意义在于:工作日的早高峰出现在8到9点,晚高峰出现在18到20点;周末的负荷整体偏低且曲线更平缓;节假日(比如春节、国庆)的负荷模式跟普通工作日完全不同,需要模型能“识别出”这些特殊日期。时间和星期都做了one-hot编码,这样喂给LSTM时不会因为数值大小关系误导模型。是否节假日这个特征尤其重要,凡是遇到节假日而模型预测偏差大的情况,往往就是特征里缺少这个标记。
气象特征方面,温度对负荷的影响非常大,尤其是夏季和冬季。我加入了当天气温、前一天气温以及体感温度(根据温湿度计算)。注意,做预测时用的是气象预报数据,但训练时用的是历史实际数据。这里有存在偏差的风险——气象预报本身有误差,这个误差会传导到负荷预测里。所以在训练时,我会给温度特征增加少量高斯噪声(标准差在0.5到1度之间),模拟预报误差,这样模型在部署时对温度的不准确性有更强的容忍度。
2.3 滑动窗口:样本怎么构造
LSTM的输入需要一个三维张量,格式是(样本数, 时间步长, 特征维度)。实施时,我用的是一个长度为L的滑动窗口,窗口内的序列作为输入,窗口后一个时刻的负荷作为输出。L的取值要做实验来确定——窗口太短,信息量不足;窗口太长,模型复杂度上升,而且早期的信息可能已经被遗忘门“丢弃”了,起不到作用。
在24小时预测这个场景里,我测试了L等于24、48、72、168四种配置。结果是在这个数据集上,L=72的效果最好,再增加窗口长度收益非常有限,训练时间却大幅上升。需要注意的是窗口内部的序列频率:原始数据是15分钟一个点,但预测粒度是小时,那就先聚合成小时均值再构造窗口,而不是直接用15分钟的窗口去预测小时负荷,这两个粒度混在一起会让模型训练过程不稳定。
2.4 LSTM网络结构设计
LSTM网络结构方面,我用的是三层LSTM加一层全连接输出的结构。第一层LSTM有64个隐藏单元,返回完整序列给第二层;第二层LSTM有32个隐藏单元,也返回完整序列;第三层LSTM有16个隐藏单元,只返回最后一个时间步的输出;最后接一个全连接层,输出维度为1,就是预测的负荷值。层数再多,试验下来容易过拟合,训练时间也明显变长,而且对精度的提升已经微乎其微。
关于正则化和优化器,我来分享几个实操里积累的经验。LSTM层之间加了Dropout层,失活概率设在0.2到0.3之间。这个可以理解为:每次训练时随机让20%到30%的神经元“失活”,强迫其他神经元学习到更鲁棒的特征表示。优化器用Adam,初始学习率设为1e-3,训练过程中每5轮损失不再下降时,学习率自动衰减一半,类似于训练后期步子迈得更小一点,精细搜索最优位置。批量大小(batch size)设为64,训练轮数(epoch)设为50,配合早停策略(基于验证集损失最小为准则)。
2.5 Adaboost集成的核心逻辑
现在说到最关键的Adaboost集成环节。在标准Adaboost分类器中,弱学习器通常是深度很小的决策树(树桩),但在回归任务上,我们需要做一些调整。
首先是样本权重的初始化。设训练集有N个样本,每个样本的初始权重设为1/N。将训练集输入第一个LSTM,训练结束后,计算每个样本的预测误差。误差的计算方式有多种,我这里使用的是相对误差: e_i = |y_i - ŷ_i| / y_i。然后计算整个模型的加权误差率:E = Σ(w_i * e_i)。根据加权误差率E计算这个LSTM的权重系数:α = 0.5 * log((1-E) / E)。如果E大于0.5,这个弱学习器还不如随机猜测,需要重新调整。最后更新样本权重:w_i = w_i * exp(α * e_i),然后归一化所有样本权重,让总和为1。
这个更新逻辑很直观:上一轮预测误差大的样本,权重会变大,下一轮训练时,LSTM的损失函数会更多地考虑这些样本,相当于模型把精力转移到“差生”身上。
这里有一个Adaboost回归和分类的重要区别。在分类任务里,弱学习器的误差率阈值是0.5(因为随机猜测的正确率是50%);但在回归任务里,阈值很难确定,一般通过交叉验证来选取。如果发现亏损比较严重,可以适当降低学习率,相当于每个弱学习器的“纠偏力度”小一点,但稳定性更高。
3. 实操过程与核心环节实现
3.1 第一版Matlab代码框架
我最早写的第一版代码框架大概是这样的:加载数据、数据预处理、特征工程、窗口构造、LSTM训练、Adaboost集成。为了让你更容易理解,下面是简化的主流程结构,把关键函数都列出来了。
%% ================== 主程序入口 ================== clear; clc; close all; rng(42); % 固定随机种子,保证实验可复现 %% 1. 加载与预处理 data = load('load_data.mat'); % load_data.mat 中包含 load_seq, temp_seq 等变量 load_seq = data.load_seq; temp_seq = data.temp_seq; [load_filled] = preprocess_load(load_seq); % 缺失值插补 + 异常值修正 %% 2. 特征构造(时间特征 + 滑窗 + 气象特征) % 构造特征矩阵 X 和目标向量 Y [X, Y, feat_names] = build_features(load_filled, temp_seq); %% 3. 训练/测试划分(按时间顺序前70%为训练,后30%为测试) train_ratio = 0.7; num_train = round(size(X,1) * train_ratio); X_train = X(1:num_train, :); Y_train = Y(1:num_train); X_test = X(num_train+1:end, :); Y_test = Y(num_train+1:end); % 归一化(只在训练集上计算min/max) [X_train_n, X_test_n, Y_train_n, Y_test_n, ps_X, ps_Y] = normalize_data(... X_train, X_test, Y_train, Y_test); %% 4. 训练 Adaboost-LSTM 模型 T = 10; % 弱学习器数量 models = cell(T,1); % 保存每个 LSTM 模型 alphas = zeros(T,1); % 保存每个模型权重 sample_weights = ones(size(Y_train_n)) / length(Y_train_n); for t = 1:T fprintf('训练第 %d 个 LSTM 弱学习器...\n', t); % 关键:训练时每个样本的权重通过 “sample_weights” 体现 mdl = train_lstm_weighted(X_train_n, Y_train_n, sample_weights); % 在当前训练集上的预测(用于计算误差) y_pred_n = predict_lstm(mdl, X_train_n); % 反归一化到原始负荷量纲 y_pred = mapminmax('reverse', y_pred_n', ps_Y); y_true = mapminmax('reverse', Y_train_n', ps_Y); % 计算每个样本的相对误差 e = abs(y_true - y_pred) ./ max(y_true, 1); % 计算加权误差率 E = sum(sample_weights .* e'); % 避免除零与极端情况 E = min(max(E, 1e-10), 1-1e-10); % 计算模型权重 alpha alphas(t) = 0.5 * log((1 - E) / E); % 更新样本权重 sample_weights = sample_weights .* exp(alphas(t) * e'); sample_weights = sample_weights / sum(sample_weights); % 归一化 models{t} = mdl; end %% 5. 测试集预测(加权集成) pred_n = zeros(size(Y_test_n)); for t = 1:T pred_n = pred_n + alphas(t) * predict_lstm(models{t}, X_test_n); end pred_n = pred_n / sum(alphas); % 反归一化 pred_final = mapminmax('reverse', pred_n', ps_Y); Y_test_real = mapminmax('reverse', Y_test_n', ps_Y); %% 6. 误差评估与作图 rmse_val = sqrt(mean((pred_final - Y_test_real).^2)); mape_val = mean(abs((pred_final - Y_test_real)./ Y_test_real)) * 100; fprintf('RMSE: %.2f MW\n', rmse_val); fprintf('MAPE: %.2f%%\n', mape_val); plot_forecast(Y_test_real, pred_final);3.2 关键函数实现详解
train_lstm_weighted这个函数是重点。Matlab的深度学习工具箱支持自定义损失函数吗?严格说,trainNetwork函数不支持传入样本权重。这里有一个很重要的处理技巧:我们不能直接在LSTM的loss函数里引入权重,所以要在输入数据层面做处理。具体做法是对输入数据做“重采样”,让权重大的样本以更高的概率出现在训练集中。操作方式是:先把样本权重转换成概率分布,然后从原始训练集中重复采样出同样数量的新训练集。这意味着权重大的样本会被重复采样多次,而权重小的样本被采样的次数少。这样,LSTM在训练时天然会更关注到那些重要的样本。
function mdl = train_lstm_weighted(X_train_n, Y_train_n, sample_weights) % 基于样本权重重采样得到新的训练集 n = size(X_train_n, 1); % 根据sample_weights构造累积概率分布 cum_dist = cumsum(sample_weights) / sum(sample_weights); % 从均匀分布中采样索引,生成与原始样本等量的新样本集 idx = zeros(n,1); for i = 1:n r = rand(); idx(i) = find(cum_dist >= r, 1, 'first'); end X_resampled = X_train_n(idx, :, :); Y_resampled = Y_train_n(idx); % 使用重采样后的数据训练LSTM numFeatures = size(X_resampled, 2); numResponses = 1; numHiddenUnits1 = 64; numHiddenUnits2 = 32; numHiddenUnits3 = 16; layers = [ sequenceInputLayer(numFeatures) lstmLayer(numHiddenUnits1, 'OutputMode', 'sequence') dropoutLayer(0.2) lstmLayer(numHiddenUnits2, 'OutputMode', 'sequence') dropoutLayer(0.2) lstmLayer(numHiddenUnits3, 'OutputMode', 'last') fullyConnectedLayer(numResponses) regressionLayer]; options = trainingOptions('adam', ... 'MaxEpochs', 50, ... 'InitialLearnRate', 0.001, ... 'MiniBatchSize', 64, ... 'Verbose', 0, ... 'Plots', 'none', ... 'LearnRateSchedule', 'piecewise', ... 'LearnRateDropPeriod', 10, ... 'LearnRateDropFactor', 0.5); mdl = trainNetwork(X_resampled, Y_resampled, layers, options); end3.3 为什么用重采样而不是在损失函数上加权重
我估计有人要问:权重信息明明可以在LSTM的损失函数上直接做乘法,为什么要绕一圈做重采样呢?
核心原因在于Matlab的trainNetwork不支持样本权重作为输入。如果你用Python的PyTorch或TensorFlow,完全可以在损失函数上乘以样本权重。但在Matlab里,类似这种“组合模型”的设计,重采样是最实用、最不容易出错的办法。而且重采样本身也带来了一个额外的好处:每一次重采样相当于对训练集做了轻微的扰动,这本身就带了点“Bagging”的味道,和Adaboost的多样性需求是契合的。
需要注意的一点是:重采样操作要保留随机种子,否则每次运行得到的结果不同,不利于实验对比。我建议固定随机数生成器的种子,保证可复现性。
3.4 训练细节和初始实验
第一轮实验跑下来,我记录的数据是这样的:单一LSTM的测试集RMSE是32.5MW,MAPE是4.8%;引入Adaboost(T=10)后,RMSE降到28.9MW,MAPE降到3.9%。从误差曲线看,日峰值附近的预测精度提升最明显。但T再增加,比如15轮,RMSE反而回升到30.1MW,大概率就是上面提到的过拟合前兆了。
第二个现象是:随着迭代轮数增加,样本权重的分布越来越“尖锐”。到第8轮时,最大样本权重已经是最小样本权重的15倍以上,这意味着模型几乎把所有注意力都集中在了极小部分“异常样本”上。这是Adaboost的典型行为,也是需要我们警惕的地方。如果权重分布过于尖锐,建议降低学习率,或者采用“软早停”:当最大权重和最小权重的比值超过某阈值(比如20)时,停止增加新的弱学习器。
3.5 预测结果可视化
训练完之后,要把结果画出来看看。单看RMSE和MAPE容易“自我感觉良好”,画图才能真正发现哪些时段出了问题。
我通常画三张图:
第一张是连续7天的负荷预测对比图,包含真实值、LSTM单一预测、LSTM-Adaboost预测三条曲线,重点看峰值段的贴合度。
第二张是误差分布直方图,横轴是预测误差(可以换算成百分比误差),纵轴是样本数量。如果直方图呈现钟形分布且均值在0附近,说明模型没有系统性偏移;如果明显向右偏,说明模型整体偏低估了负荷。
第三张是典型日(比如某工作日和某休息日)的负荷曲线对比,单独放大看早晚高峰的预测情况。我踩过的一个坑是:模型在凌晨低谷段的表现其实还不错,但一眼看去曲线很“平缓”;到了早高峰段,曲线跟真实值差了半拍——真实负荷是8点冲到顶点,模型预测的峰值却出现在9点。这种“相位偏移”在RMSE和MAPE里是能反映出来的,但只有画图才能一眼发现。出现这种偏移,说明时间特征(小时)没有起到足够的约束作用,需要在特征里加“当前时刻是否处于上升段”之类的辅助标记。
4. 常见问题与排查技巧实录
4.1 预测曲线存在滞后性,怎么办
这是我做负荷预测遇到最多的问题。表现为预测曲线的整体位置没问题,但拐点比真实曲线慢一到两个时刻。在电力负荷预测里,这个问题特别容易出现在早上6到9点,和下午16到20点的“爬坡段”。
排查思路是这样的:如果滞后不多(一两个时刻),大概率是LSTM本身的结构特性导致的——LSTM倾向于输出接近前一个时刻的数值,因为历史序列中大部分样本都是平稳相关的。解决办法是强化时间特征,比如增加“当前时刻距离今天用电高峰时刻还有几个小时”这种相对量特征。
如果滞后特别严重,那就要检查LSTM窗口是否太短了——一个判断方法,观察权重:如果LSTM最后一个时间步的输入特征权重过高,说明模型过度依赖“上一刻负荷”,对长时段趋势的感知不够。改进办法是加宽窗口到L=168(一周),让模型看到完整的周期形态。我在实际项目中,把窗口从24调大到72后,滞后问题有明显缓解。
4.2 节假日预测偏差特别大,怎么办
节假日负荷模式和普通工作日差异很大。端午、中秋这种三天小长假,负荷整体比正常工作日低8%到15%,但又不完全等同于周末——很多人会出门旅游,工业负荷下降,商业和居民负荷的形态又不同。
如果数据中历史节假日的样本数量太少,任何模型都难以学好。我做过一个“节假日样本加权”的操作:在训练集中,把这些“节假日样本”的基准权重人为提高到普通样本的1.5到2倍。注意初始权重调整后,权重归一化会自动修正,不会影响Adaboost的训练逻辑。
还有一个更实用的小技巧:建模时把节假日单独建一个模型,平时用日常模型,节假日切换到节假日模型。代价是要维护两个模型,但精度提升非常明显。如果节假日样本实在太少(比如某个一年只有一次的长假),退而求其次,在节假日期间给预测结果加上人工修正,根据当天的气象情况手动调整模型输出。
4.3 训练过程出现NaN值,怎么办
这个问题出现的原因很明确:学习率太大了,或者数据处理有误(比如归一化后有零值)。学习率过大会让权重的更新步长过大,直接越过最优区域,进入梯度爆炸区,最后导致梯度为NaN。
排查步骤很简单:先检查归一化后的数据里有没有NaN或Inf;然后检查网络结构里有没有除以0的情况(比如Adaboost里计算alpha时E如果为0或1,会导致log里面出现无穷大);如果都没问题,就把初始学习率从1e-3降到3e-4,把批量大小从64调大到128。批量大小变大,相当于降低了每个mini-batch梯度的随机抖动,训练过程会稳不少。
4.4 测试集效果好,但新数据上线后效果变差,怎么办
这是所有预测模型都会面临的痛:训练集和测试集都来自同一个时间段,它们的时间分布是连续的;但真正上线预测的是未来的数据,环境可能已经变了(比如某个大型工厂迁走了,或者新增了一个商业综合体)。这种“分布漂移”问题会让模型的MAE开始爬升。
一个实用的方案是“滚动训练”。不要训练好一个模型就一直用,而是每过去一个月,就将最近的三个月数据作为新的训练集,重新训练一次。我通常的做法是:用LSTM-Adaboost预测未来的7天,同时保留一个滑动窗口的近期训练数据,每天凌晨自动用最近30天的数据增量微调模型(增量训练的epoch数设为5到10),既保证模型能跟上最新的负荷变化,又不会因为数据量太小而失去长期规律的学习能力。
4.5 常见问题速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 预测曲线整体偏低 | 训练集中夏季/冬季高负荷样本占比不足 | 提高高负荷样本的权重;或增加高负荷时段的特征 |
| 峰值预测偏低、谷值预测偏高 | 损失函数对中等误差样本不敏感 | 使用带权重的损失函数;在Adaboost中调整权重更新公式 |
| 预测值变化过于平缓,没有波动 | 输入特征中时间特征不足,或网络过度正则化 | 增加小时、星期特征;降低Dropout比例到0.1 |
| 训练集误差极低,验证集误差高 | 过拟合,模型学到的更多是记忆而非规律 | 增加训练数据量;增加Dropout比例;减小LSTM隐藏单元数 |
| 多个弱学习器预测结果几乎一样 | 重采样没有生效,或各轮样本权重分布过于均匀 | 检查重采样算法;手动放大权重差异,增大alpha的初始值 |
| 温度特征怎么调都没有作用 | 温度与负荷的关系不是线性的,尤其在温和天气下 | 构造温度的分段特征,或加入制冷/制热度日数作为特征 |
5. 模型评估与指标深度解读
5.1 三个指标怎么配合使用
评估负荷预测模型,我一般同时看RMSE、MAPE和R²。RMSE(均方根误差)对大误差更敏感,如果某个时刻预测偏差很大,RMSE会被明显拉高,适合用来发现“极端错误”。MAPE(平均绝对百分比误差)直观性好,2%、3%、5%,对不同量级的数据都可以比较。R²(决定系数)反映的是模型对真实负荷变化的解释能力,越接近1越好。
只看一个指标会得出有偏颇的结论。比如RMSE很低但MAPE偏高,说明少数中低负荷时段预测偏差相对较大;MAPE很低但RMSE偏高,说明模型在峰值段可能存在“接近却不完全贴合”的偏差。
5.2 基准对比实验
以我最近做的某地区真实负荷数据为例,采用前70%数据训练、后30%数据测试:
| 模型 | RMSE(MW) | MAPE(%) | R² |
|---|---|---|---|
| ARIMA | 38.6 | 5.7 | 0.91 |
| SVR(RBF核) | 34.2 | 5.1 | 0.93 |
| 单LSTM | 30.5 | 4.6 | 0.95 |
| LSTM-Adaboost(T=8) | 28.1 | 4.1 | 0.96 |
| LSTM-Adaboost(T=12) | 27.7 | 3.9 | 0.96 |
| LSTM-Adaboost(T=20) | 29.4 | 4.4 | 0.95 |
这个趋势已经说得很清楚了:T=12时达到一个最佳平衡点,T=20反而退化。你可能会想,那T=8和T=12之间怎么选?从实用角度出发,如果训练时间紧张,T=8已经可以用;如果想在发布前多榨取一点精度,T=12会更稳。但没有必要追求更大的T。
注意:即使我已经做了归一化、早停、网格搜索这些操作,不同时间段的负荷数据仍然会跑出不一样的最佳T。每次换数据,建议都把T从1到15扫一遍,画出误差曲线,再定参数,不要直接沿用之前数据上的经验值。
5.3 误差在哪一段时间最大
做了误差的分时段统计,发现一个共同的规律:晚间高峰时段(18:00—21:00)的MAPE普遍在5%以上,远高于凌晨低谷时段的3%左右。原因是晚高峰叠加了居民用电、商业用电、路灯照明等多个用电成分,而且受气温回落、用户行为随机性等因素影响,负荷变化本身的方差就比较大。针对这个时段,我尝试过单独训练一个“晚高峰专用模型”,输入特征增加“日落时间”、“晚间温度”,预测精度提升比较明显。
6. 扩展思路与后续优化方向
6.1 多尺度特征融合
单个LSTM加Adaboost框架已经把时序特征利用得比较充分了,但还可以进一步引入多尺度信息。具体做法是:在特征工程阶段,不仅使用小时的负荷序列,同时计算日级和星期级的统计值作为额外特征输入,例如过去24小时的峰谷差、过去7天的平均负荷、前一天同时刻的负荷变化率。这些多尺度特征能够为模型补充不同时间维度的信息,对预测精度的提升有帮助。
6.2 模型集成层次加深
目前的Adaboost框架中,弱学习器都是结构相同的LSTM。可以进一步引入异构弱学习器——比如同时使用LSTM、GRU、TCN作为基学习器,让不同结构的时序模型在Adaboost框架下互补。GRU结构比LSTM更轻量,训练速度更快;TCN的使用可以捕捉更长的时序依赖。混合基学习器的集成一般比单一同构模型的集成效果更好。
6.3 实时修正模块
模型部署后,可以增加一个在线修正模块,实时对比最近几小时的预测值和实际值。若误差持续超过阈值,触发修正逻辑:用最近24小时的误差分布对后续预测值做动态补偿。这个模块不需要重新训练模型,计算开销较小,能有效应对突发性负荷变化(比如某个区域突然断电又恢复,负荷曲线出现“V字型”反弹)。
7. 实操中积累的几点经验
最后分享几条我在反复实验和调试中积累下来的经验,都来自实际操作。
第一,数据的质量远比模型结构更重要。我试过用非常复杂的LSTM网络结构去拟合一组含有大量毛刺的数据,效果还不如把数据清洗干净后用最简单的单层LSTM。做负荷预测,超过一半的时间应该花在数据检查和特征工程上,花在调模型上的时间真的不用太多。
第二,Adaboost的样本权重更新和LSTM的随机性有一定的耦合关系。LSTM的初始化权重、mini-batch内的样本顺序都带有随机性,导致每一轮重采样后的训练结果也会有波动。最后做集成时,建议跑3次实验,取预测结果的平均值,这样能抵消一部分随机性带来的偏差。
第三,Matlab的trainNetwork在数据处理上有局限性,样本权重的实现需要通过重采样完成,这一点要想清楚,不要试图强行修改工具箱内部的损失函数来实现,容易把问题复杂化。
第四,预测曲线和真实曲线画在同一张图上的时候,别只关注平均误差,要特别留意峰谷时刻的对应关系。有时候RMSE和MAPE都很漂亮,但峰值时刻出现了偏移,这在电力调度场景下是会有问题的——调度员需要知道准确的峰值出现时间段来安排备用容量,而不是只知道一个“大致差不多”的峰值大小。
这套LSTM-Adaboost方法,优势在于实现思路清晰、代码结构直观,而且效果在多个数据集上是稳定的。如果你手头刚好也有负荷预测相关的需求,不妨按照这篇文章的框架自己跑一遍,尤其是Adaboost的T参数,务必多试几组多看曲线,你会对模型的“脾气”了解得更透彻。用Matlab实现的好处就是可视化方便,调参方便,尤其适合做方案对比和学术验证,上手门槛相对友好。希望这篇文章能帮你少走一些弯路。