去年年中我接了一个活:给某区域电网做一个短期负荷预测模块。当时手头的数据很朴素——近三年逐日的气象观测记录、一张节假日历表,以及一整年的电网负荷采集值。团队里有人提议直接上深度模型,但我最后坚持用了支持向量机,把"基于日特征气象因素的支持向量机负荷预测"这条路完整走了一遍。这篇博文就是那次旅程的复盘,写给正在传统算法和深度学习之间犹豫的人。如果你手头的数据量不大、又想把气象因素真正用进预测模型里,SVR的路子可能是性价比最高的选择。
1. 为什么是SVM:在时序模型和深度模型之间的一次理性选择
1.1 负荷预测到底在解决什么问题
电力调度不是"等到当天再看用电量"这么简单。发电机组开机需要提前几小时甚至一天安排,联络线功率要提前计划,新能源并网后电网的波动性又变大了,预测不准意味着要么多开机浪费燃料,要么少开机面临拉闸限电风险。所以日负荷预测一直是电力系统调度运行的刚需,预测对象通常是一天24点或96点的负荷曲线,也可以浓缩成两个数——日峰值负荷和日总电量。我当时定的目标就是预测日最大负荷,调度员最关心的是"明天最高会到多少,我顶不顶得住"。
1.2 为什么没选ARIMA和深度学习
这是个很现实的问题。传统时间序列模型比如ARIMA,核心假设是"历史规律会重演",它只能吃负荷序列本身,很难把明天的温度、湿度塞进去。你可以做外生变量的ARIMAX,但到手的数据里天气因素和负荷的关系明显是非线性的,ARIMA这类线性模型表现很勉强。
反过来看深度学习,LSTM和Transformer在长序列预测上确实强,但有个前提是数据量要够大。我这里能用的有效日样本就几百个,一个几百维的深度网络进去简直是"杀鸡用牛刀",而且大概率过拟合。我见过不止一个项目,深度学习模型的测试集指标还没一个认真调过参的SVR好看。
支持向量回归(SVR)站在中间:核函数可以隐式地把数据映射到高维空间,拟合非线性关系;结构风险最小化的设计让它在小样本下不容易乱来;而且加入气象特征、日历特征这种外生变量非常自然,不像ARIMA那样还得专门改造。三者的取舍我整理成了表:
| 模型 | 是否吃气象特征 | 小样本表现 | 训练成本 | 可解释性 |
|---|---|---|---|---|
| ARIMA | 弱,需改造 | 中等 | 低 | 中 |
| 线性回归 | 强 | 中等 | 极低 | 高 |
| LSTM/Transformer | 强 | 差,易过拟合 | 高 | 差 |
| SVR | 强 | 好 | 低 | 中 |
1.3 样本量决定了算法上限
当时的样本是前一年10月到今年9月逐日数据,一共三百多个日样本。对深度学习来说,三百个样本连验证集都分不痛快;但对SVR来说,这个规模恰恰是最舒服的区间。机器学习圈有个经验说法:样本量在上千以内,SVM/树模型这类"传统算法"往往比深度学习更可靠。真实业务里不是每个场景都有海量数据,承认数据规模的限制,选一个匹配的模型,是工程判断力的一部分。
2. 气象因素与负荷之间的隐藏关系:特征工程的第一步
2.1 先看相关系数,别急着建模
我拿到数据第一件事不是跑模型,而是画相关性热力图。把日最大负荷和各个气象因子挨个做Pearson相关系数,结果和预料中差不多:平均温度相关性最高,能到0.75左右;相对湿度呈负相关,约-0.4;平均风速也是负相关,约-0.3;降雨量相关性很弱,只有0.1上下。这一步的目的不是"筛掉弱相关特征",而是让你心里有数:温度和负荷的关系是最主要的矛盾,其他变量都是锦上添花。
2.2 温度与负荷不是直线关系,是一条U型曲线
如果只算一个线性相关系数,你会以为温度越高负荷越大,但把散点图画出来就露馅了:负荷和温度的关系更像一条U型曲线。温度在18到24度这个区间时,负荷最低,因为不需要制冷也不需要取暖;低于这个区间,每降一度负荷明显上升;高于这个区间,每升一度负荷也明显上升。空调和取暖设备是这个区域负荷的大头,所以才有这种"两头翘"的形状。
这个U型关系恰恰是线性模型很难处理的。SVR的RBF核函数可以拟合这种非线性曲线,这也是我坚持选它的理由之一。你想想看,一个人对温度的舒适感受是存在一个"舒适区"的,偏离越多,需要额外消耗的能量就越多,这种实际物理过程天然就是非线性的,强行用直线拟合肯定失真。
2.3 湿度、风速、降雨这些"边角料"特征也别浪费
湿度对负荷的影响是间接的但很真实:夏天湿度大,体感温度比实际温度高,空调就得开大一档;冬天湿度大,湿冷感往骨头里钻,取暖负荷也跟着上去。风速主要影响建筑散热,风大天觉得凉快,制冷负荷就小一点。降雨的效应更微妙,降雨天光照弱、气温下降,商业区的空调负荷会降,但可能因为人们减少外出,居民负荷会有些变化。这些特征的相关系数虽然不高,但在极端天气场景里往往能救你一把,我当时把湿度、风速、降雨量都保留了下来。
2.4 容易被所有人忽略的"滞后温度效应"
这是我第一次建模时漏掉、后来又补回来的重要发现。建筑物有热惯性,墙体白天吸热晚上放热,连续两天高温,第二天人会觉得更热,负荷也会更高——即使第二天实测温度和第一天一样。处理办法很简单:把前一天的平均温度作为额外的特征加进去,甚至可以加前两天的。加了这个滞后温度特征之后,夏季高温时段的预测误差立刻降了一截。你看,真正的信息增量往往不在"当天",而在"过去一天到今天"的累积状态里。
3. 构造"日特征"数据集的具体方法
3.1 日历特征:星期、节假日、季节分工明确
"日特征"这个词,我理解成"以一天为粒度构造的特征向量"。每一天的样本包含三块信息。第一块是日历信息:星期几、是否节假日、是否工作日、所在月份。星期几要用One-Hot编码,因为星期一是从周末低谷爬升的过渡日,星期二到星期四负荷平稳,星期五受下班后活动影响有波动,周末整体下沉。节假日是完全独立的模式,尤其是长假期间,工厂停工、商业活动变化,负荷水平可能比普通周末还低。
3.2 气象特征:不能只用一个平均温度糊弄
第二块是气象信息。我当时选的组合是:当日平均温度、日最高温度、日最低温度、相对湿度、平均风速、降雨量,再加上前一天的日平均温度。有人可能会贪多,把十几个气象因子全塞进去,但SVR在高维小样本下反而容易泛化变差。特征不是越多越好,而是"每一列都有独立的信息增量"。温度取平均和最高最低,是因为一天内的温度曲线形态对负荷影响很大:一个大太阳的晴天和阴沉沉的雨天,即使平均温度一样,负荷差很多。
3.3 目标值设定与历史负荷特征
第三块是历史负荷信息。我加了过去两天同时间尺度的负荷值,相当于给模型一个"昨天是多少"的锚点。目标值则是日最大负荷,归一化之后作为y。这样构造出来的数据集,一行就是一个样本,大概长这样(字段示意):
date, is_weekend, is_holiday, weekday_onehot(7列), month, avg_temp, max_temp, min_temp, humidity, wind_speed, rainfall, prev1_avg_temp, prev1_load, prev2_load, target_load3.4 数据清洗和归一化:细节决定成败
气象站偶尔会缺测,负荷采集也有异常尖峰,处理不当模型分分钟被带偏。缺测值我用前后两天的线性插补;异常尖峰用中位数平滑修正。然后就是归一化,SVR基于距离计算,温度和负荷根本不在一个量级上,不归一化,温差的几十个单位会彻底淹没负荷的几千个单位。这里有个坑必须提醒:归一化的统计量只能从训练集计算,再用同一套参数去转测试集。如果拿全量数据的均值最小值做归一化,测试集的信息就提前泄露进了预处理环节,模型评价会虚高。
4. SVR参数调优实战:核函数、C、epsilon与网格搜索
4.1 RBF核为什么成了默认首选
SVR的核函数有线性核、多项式核、RBF核几种。线性核只能处理直线关系,显然不适合U型温度曲线;多项式核对参数敏感,阶数一高数值计算就容易不稳定;RBF核相当于在无穷维空间里干活,能拟合任意光滑的非线性函数,参数也只有一个gamma,实践中最省心。我做对比时,线性核MAPE接近4.2%,RBF核能到3%左右,差距很明显。多数情况下,你可以无脑从RBF开始。
4.2 三个核心参数的含义与调节方向
SVR有三个绕不开的参数。C是误差惩罚系数,C越大越不愿意容忍训练集上的误差,容易过拟合,C越小模型越平滑但可能欠拟合。epsilon表示回归管道的不敏感宽度,管道内的误差忽略不计,epsilon越大预测曲线越平滑。gamma是RBF核的宽度参数,决定单个样本影响半径的大小,gamma太大决策边界弯弯曲曲,太小则欠拟合。我自己的搜索范围用表格整理如下:
| 参数 | 作用 | 调大影响 | 本次搜索范围 |
|---|---|---|---|
| C | 误差惩罚力度 | 过拟合 | 1 / 10 / 50 / 100 / 200 |
| gamma | RBF核宽度 | 边界复杂化 | 0.001 / 0.01 / 0.1 / 0.5 |
| epsilon | 不敏感管道宽度 | 预测更平滑 | 0.01 / 0.05 / 0.1 |
4.3 时间序列的交叉验证,千万不能随机打乱
这是最容易被坑的一步。普通K折交叉验证会把样本随机切分,但负荷数据是强时间序列,用未来的数据去训练、用过去的数据验证,相当于作弊,得到的精度完全不可信。正确做法是滚动前向验证:比如按时间顺序,第一次用第1到200天训练,预测第201到220天;第二次用第1到220天训练,预测第221到240天,以此类推。我在代码里用一个常用的机器学习库实现SVR,核心训练逻辑也就十几行:
from sklearn.svm import SVR from sklearn.preprocessing import StandardScaler scaler = StandardScaler() X_train_scaled = scaler.fit_transform(X_train) X_test_scaled = scaler.transform(X_test) model = SVR(kernel='rbf', C=50, gamma=0.01, epsilon=0.05) model.fit(X_train_scaled, y_train) y_pred = model.predict(X_test_scaled)参数组合用网格搜索加滚动验证来选,最后锁定的参数是C=50、gamma=0.01、epsilon=0.05。搜索过程很枯燥,但结果值得,参数固定之后模型的稳定性明显上了个台阶。
4.4 训练/验证/测试的划分策略
我把前10个月的数据作为训练集,11月作为验证集用来调参,12月作为最终测试集。这里有个细节:验证集和测试集必须是连续时间段里完全没参与训练的数据。有人喜欢随机抽10%做测试,在时间序列里这么搞就是灾难。我当时还刻意验证了一下,把测试集的月份变换成夏季和冬季分别测试,因为不同季节的负荷特性差别太大,只看一个月的指标说明不了问题。
5. 训练结果与评估:指标、基准与典型日复盘
5.1 评价指标怎么选
负荷预测行业最常用的指标是MAPE(平均绝对百分比误差),因为调度员关心的是"偏了多少个百分点"。我同时记录RMSE和R2。MAPE和RMSE各有利弊,MAPE对低负荷时段的误差很敏感,RMSE会放大个别大误差的影响。所以我做结论时主要看MAPE,但调参时会兼顾RMSE,避免模型在某些极端日疯狂踩雷。
5.2 和基准模型的PK结果
光看SVR自己的指标没有意义,得有对比。我设了两个基准:一个是持久性模型(直接用前一天负荷作为预测值),这是负荷预测的"底线",连这个都打不过的模型可以直接扔了;另一个是加了相同特征的多元线性回归。测试集上的结果如下:
| 模型 | MAPE | RMSE(万千瓦) | R2 |
|---|---|---|---|
| 持久性模型 | 4.8% | 42.3 | 0.87 |
| 线性回归 | 3.9% | 36.1 | 0.91 |
| SVR | 3.1% | 29.5 | 0.94 |
SVR比线性回归强一个多百分点,优势来自非线性拟合和异常样本的鲁棒性。说实话,这个差距没有想象中"惊艳",但在工程上已经意味着一个夏季高峰日少误判几十万千瓦的负荷,对调度安排机组的意义非常大了。
5.3 典型场景的误差复盘
我专门挑了几个典型日子看预测曲线。35度以上高温日,模型预测误差在2.8%左右,表现很好,因为夏季模式在训练集里出现过很多次。深秋平稳日最舒服,误差只有1.6%,温度在舒适区,负荷波动小。最难看的是寒潮突袭日,24小时内温度骤降10度,负荷一天内从秋季模式跳到冬季模式,模型明显低估,误差拉到4.5%。这说明模型对"剧烈变化"的响应天生滞后,这种日子你必须靠人工修正或者加突变特征去兜底。
5.4 误差分布里藏着的小规律
把预测误差按小时统计,峰值时段附近的误差明显大于夜间低谷时段。夜间负荷基数低,MAPE容易虚高,但其实绝对值很小;白天高峰负荷大,一旦误判就是大几十万千瓦的偏差。另外我观察到周末的误差系统性偏大,原因很简单:周末样本只有全年的五分之一,模式学习不充分。后来我把"是否周末"和"是否节假日"两个特征的权重在特征工程阶段做了强化,误差又小了一点。
6. 这趟旅程踩过的坑和复盘
6.1 训练用实测温度,预测用预报温度,这是个系统偏差
我在第一次做Demo时,训练集用的气象站实测温度,等到真正预测"明天"时才发现,第二天还没到来的时候,你根本没有实测值,只有数值天气预报的预报值。预报和实测之间是有误差的,模型在训练时没适应过这个噪声,线上表现就比测试集差。后来我把训练集中的温度特征人为叠加了服从正态分布的随机噪声,模拟"预报不准"的情况,模型反而变得更稳。这是个很隐蔽的坑,网上很少有人说。
6.2 节假日样本太少,过拟合防不胜防
春节、国庆这类长假,一年就那么几天,样本少到模型几乎学不到规律。硬训练的结果是,模型把节假日日期当作噪声忽略掉,预测值明显偏高。我的解法是两步走:第一,把节假日做明显的标签特征而不是让它混在星期特征里;第二,把节假日的负荷曲线往"相似的周末曲线"上靠,用周末样本的规律作为先验去修正。效果比直接硬预测好不少。
6.3 跨季节统一建模的隐疾
用全年数据训练一个模型,夏季样本多,冬季样本少,模型实际上被夏季主导了。到了冬季预测时,虽然整体MAPE能看,但寒潮日的偏差异常大。后来我在训练集里做了季节加权,冬季样本权重提高一些,寒潮日的误差从4.5%降到了3.7%。你也可以直接给春夏秋冬各训练一个模型,但那样做要注意各季节接缝处的样本量分配,我这次采用权重方案更简洁。
6.4 数据质量是最大的隐形敌人
这个项目里最让我头疼的其实不是算法,是终端数据质量。个别变电站的遥测终端故障,某天峰值负荷直接少记了一截,模型还傻乎乎地认为那天负荷就是这么低,把相邻天的预测也带偏了。后来我加了"前后三小时负荷平滑校验"逻辑,同时仔细核对了时区切换那几天的数据。送大家一句话:预测模型的精度上限,往往不是由算法决定的,而是由数据质量决定的。模型再强,喂给它的数据是脏的,输出就不可能干净。
写这个项目复盘,我最想表达的是,不是所有预测问题都非要上深度学习堆算力。支持向量机配合扎实的日特征构造和气象因素挖掘,在中小样本的负荷预测场景里依然有很强的实用价值,而且它训练快、解释起来也不费劲,调度同事问起来你还能讲清楚"为什么预测这个数"。最后分享一个我压箱底的小技巧:如果你手头有多年历史数据,除了训练全局模型,还可以按"同一季节+相似气象条件"动态筛选训练子集,给预测日构造一个局部模型。我试过之后,在极端天气日的预测精度能再上个台阶,你们不妨也试试。