首页
/ ML-For-Beginners 时间序列预测实战指南:用经典机器学习预测全球电力负荷

ML-For-Beginners 时间序列预测实战指南:用经典机器学习预测全球电力负荷

2026-09-07 13:41:10作者:段琳惟

本篇指南以 ML-For-Beginners 课程中第 7 大章节"时间序列预测(Time Series Forecasting)"为主题,围绕其区域主题"全球电力消费"展开:你将理解什么是时间序列及其典型特征,学会用 GEFCom2014 竞赛的真实电力负荷数据完成加载、清洗与可视化,并掌握两条经典机器学习建模路径——统计线性模型 ARIMA/SARIMA 与非线性模型 SVR(支持向量回归),最终用 MAPE 客观评估预测精度。学完后,你具备独立完成"单变量时间序列一步/多步预测"的完整实操能力。

本章节属于 ML-For-Beginners 课程(12 周、26 课)的一部分。整章包含 3 篇子课程:入门与数据可视化(1-Introduction)、ARIMA 建模(2-ARIMA)、SVR 建模(3-SVR)。章节内容最初由 Francesca Lazzeri 与 Jen Looper 撰写,notebook 最早出自 Azure "Deep Learning For Time Series" 仓库,SVR 一课由 Anirban Mukherjee 编写,后融入本开源课程并同步了多语言翻译。

电力电网主题图

什么是时间序列预测

时间序列预测(Time Series Forecasting)是通过分析过去发生的趋势来预测未来事件的过程。你可以把它理解为一种"水晶球":基于某个变量(例如价格、用电负荷)的过往表现,推断它在未来的可能取值。

  • 时间序列(Time series):按时间顺序索引、且通常以等间隔采样的一系列数据点,例如道琼斯工业平均指数的每日收盘价。
  • 时间序列分析(Time series analysis):对上述数据的分析,用于探测模式,例如检测某个"打断事件"前后序列演变规律的"受扰时间序列"(interrupted time series)。
  • 时间序列预测(Time series forecasting):用模型基于历史数据表现出的模式预测未来值。

关键区别在于:普通线性回归的数据点相互独立,而时间序列数据是有序观测值的列表,因此最好用专门的时间序列模型来分析。最常见的一种就是 ARIMA——"自回归积分滑动平均"(Autoregressive Integrated Moving Average)模型的缩写,ARIMA 模型将序列的当前值与过去的值及过去的预测误差联系起来。

时间序列预测对工业与商业应用价值极高,直接服务于定价、库存、供应链等场景。虽然神经网络也可以用来增强预测效果,但本章节刻意放在经典机器学习语境下学习——模型基于过去即可预测未来表现。

区域主题:全球电力消费与 GEFCom2014 数据集

章节选择"全球电力消费"作为区域主题,目的就是用一个直观且商业价值显著的例子来学习时间序列预测:依据历史上负荷(load)的变化模式,预测未来的电力消耗。

在仓库中,配套数据集位于 7-TimeSeries/data/energy.csv,来源于 GEFCom2014 全球能源预测竞赛,包含 2012 至 2014 三年间逐小时的电力负荷与温度值(共约 2.6 万余条小时级记录,2012-01-01 起每整点一条)。其原始竞赛论文为 Tao Hong 等人于 2016 年发表的 Probabilistic energy forecasting: Global Energy Forecasting Competition 2014 and beyond。数据仅含三列:

timestamp load temp
2012-01-01 00:00:00 2698.0 32.0
2012-01-01 01:00:00 2558.0 32.67
2012-01-01 02:00:00 2444.0 30.0

若想复现数据的原始提取流程,可查看 1-Introduction/working/common/extract_data.py:它读取 GEFCom2014 的 zip 与 Excel 原始文件,把日期与小时合成 timestamp 列,只保留 2012-01-01 之后有负荷记录的时段并导出 energy.csv

从源码看数据加载与评估工具

章节内所有子课程 notebook 都复用同一套公共工具 7-TimeSeries/common/utils.py,理解它有助于你顺利跑通课程代码:

  • load_data(data_dir)utils.py):用 pandas 读取 data_dir/energy.csv 并将 timestamp 解析为日期;随后以小时频率 freq='H'reindex,在最小与最大时间戳之间补齐所有时间点。这样能显式暴露数据中缺失的时段(该数据集中没有缺失),返回以时间为索引、仅含数值列的 DataFrame。
  • mape(predictions, actuals)utils.py):计算平均绝对百分比误差,即 (|预测值 - 实际值| / 实际值).mean(),作为全章统一的精度评估口径。

此外,notebook 还依赖同目录 common/ 下的 environment.yaml(Conda 环境定义,环境名为 dlts)。仓库中该文件将 Python 固定为 3.6.6 并安装了 pandas、statsmodels、scikit-learn、matplotlib 等依赖。需要提醒的是,这些版本号较旧,现代环境建议按需放宽(例如使用 Python 3.8+ 与新版 statsmodels 安装时,直接 conda env create -f environment.yaml 可能出现版本兼容问题,可手动升级相关包后再运行)。

时间序列数据的六类特征

拿到时间序列后,先要识别数据中需要留意并加以处理的"特征"。若把数据当作待分析的"信号",这些特征常表现为"噪声",往往需要用统计手段抵消。入门课归纳了以下概念:

  1. 趋势(Trends):随时间可测量的上升或下降。
  2. 季节性(Seasonality):周期性波动,例如节假日带来的销量高峰。
  3. 离群点(Outliers):远离标准数据方差的数据点。
  4. 长期周期(Long-run cycle):独立于季节性、跨度超过一年的长期波动(如经济下行)。
  5. 恒定方差(Constant variance):部分数据长期呈现稳定幅度波动,如日夜间的能源使用差异。
  6. 突变(Abrupt changes):需进一步分析的突变事件,例如疫情导致商业活动骤停所引发的数据变化。

电力负荷数据同时表现出多种特征:明显的季节性(日/年周期)、负荷随时间的长期变化与离群点等。要看清这些特征,最直接的手段就是可视化——这也是入门课的第一步实操。

实操 1:加载并可视化电力负荷

7-TimeSeries/1-Introduction/working/notebook.ipynb(对应 working 目录)中可逐步完成以下步骤:

  1. 引入绘图与数据加载库:
import os
import matplotlib.pyplot as plt
from common.utils import load_data
%matplotlib inline
  1. load_data() 载入数据并查看前几行:
data_dir = './data'
energy = load_data(data_dir)[['load']]
energy.head()
  1. 绘制 2012 至 2014 全量负荷曲线:
energy.plot(y='load', subplots=True, figsize=(15, 8), fontsize=12)
plt.xlabel('timestamp', fontsize=12)
plt.ylabel('load', fontsize=12)
plt.show()
  1. ['起始日期':'结束日期'] 切片,单独观察 2014 年 7 月第一周:
energy['2014-07-01':'2014-07-07'].plot(y='load', subplots=True, figsize=(15, 8), fontsize=12)
plt.xlabel('timestamp', fontsize=12)
plt.ylabel('load', fontsize=12)
plt.show()

电力负荷日周期可视化

观察 2014-07-01 至 07-07 一周的曲线,可以清楚看到负荷按天呈现的周期性起伏——即典型的季节性。请结合上面的特征清单,思考还能从全量曲线中发现哪些模式。课后挑战(assignment)要求你另找三个适合时间序列预测的真实数据集(例如 Kaggle 或 Azure Open Datasets),在 notebook 中可视化并标注它们的季节性、突变等特征。

实操 2:用 ARIMA / SARIMA 做统计线性预测

掌握数据形态后,2-ARIMA 教你构建第一个真正的时间序列预测模型。

前置概念:平稳性与差分

  • 平稳性(Stationarity):统计上指数据的分布不随时间平移而改变。非平稳数据会因趋势产生波动,必须先做变换才能分析,例如用"季节性差分"消除季节性引入的波动。
  • 差分(Differencing):把非平稳数据转换为平稳数据的过程,通过移除数据中非常数趋势来达成——"差分移除时间序列水平的变化,消除趋势与季节性,从而稳定序列的均值"。

拆解 ARIMA 的 AR / I / MA

  • AR(自回归 AutoRegressive):模型"回头看"过去的值(称为滞后项 lags)来推断当前。例如月销量序列,各月销量被视为随时间演化的变量,被对其自身的滞后值做回归。
  • I(积分 Integrated):区别于 ARMA 模型的关键,指对数据施加差分步骤以消除非平稳性的"积分"过程。
  • MA(滑动平均 Moving Average):输出变量由当前及过去的滞后预测误差决定。

简言之,ARIMA 让模型尽可能贴合时间序列数据的特殊形态。除了 pdq 三个常规参数,若数据存在季节性(本数据集就有),则使用季节性 ARIMA(SARIMA),还需指定季节分量参数 PDQ。参数含义如下:

参数 对应部件 作用
p / P 自回归部分 纳入过去值(滞后项)的数量
d / D 积分部分 对序列施加的差分阶数
q / Q 滑动平均部分 纳入过去预测误差的数量

常规参数与季节参数含义一一对应,差异仅在于后者作用在季节分量上。手动挑选这些参数比较主观耗时,实践中可借助 auto_arima() 自动搜索,本课程则以手动尝试为主。

训练/测试集划分

为保证模型不从"未来"偷看信息,必须让测试集覆盖晚于训练集的时段。ARIMA 与 SVR 两课一致地按以下时间窗切分(train_start_dttest_start_dt 之间为训练,之后为测试):

train_start_dt = '2014-11-01 00:00:00'
test_start_dt = '2014-12-30 00:00:00'

其中训练集为 2014-11-01 至 2014-12-30 期间共 1416 小时,测试集为 2014-12-30 起的 48 小时。由于该数据反映每日能耗、近期消费形态最相似,用相对较短的训练窗口即可。

数据准备:过滤与 MinMax 缩放

过滤出所需时间窗与 load 列后,用 sklearn.preprocessing.MinMaxScaler 将数据缩放到 (0, 1) 区间:

scaler = MinMaxScaler()
train['load'] = scaler.fit_transform(train)
test['load'] = scaler.transform(test)

缩放有两个目的:消除量纲影响、使梯度类/距离类算法更稳定。注意必须先 fit 训练集、再 transform 测试集,避免测试集信息泄漏到缩放器中。

构建 SARIMA 模型

statsmodelsSARIMAX()(支持季节分量,位于 statsmodels.tsa.statespace.sarimax)实现。以 3 小时为预测视界(horizon),手工尝试一组参数:

HORIZON = 3

order = (4, 1, 0)
seasonal_order = (1, 1, 0, 24)   # 最后一位 24 表示按小时数据的日季节周期

model = SARIMAX(endog=train, order=order, seasonal_order=seasonal_order)
results = model.fit()
print(results.summary())

order=(4,1,0) 表示自回归阶数 4、差分 1、滑动平均 0;seasonal_order=(1,1,0,24) 表示季节周期为 24 小时(一天)的 SARIMA 结构。fit() 会输出一张含系数、显著性等信息的摘要表。

使用 walk-forward 前向滚动验证评估模型

时间序列模型在实践中往往每来一条新数据就重新训练一次,以在每个时间步做出最佳预测。Walk-forward 验证因此被视作时间序列模型评估的"金标准":从序列起点开始,用训练集训练模型 → 预测下一个时间步 → 与已知值比对 → 把该已知值并入训练集(为控制训练窗口固定长度,同时从窗口头部移除最旧观测)→ 重复。它给出的精度估计更贴近真实使用,代价是需要反复训练大量模型;数据量小或模型简单时可接受,规模化时会成为瓶颈。

先按视界步长生成滞后目标列,将测试数据水平移位以便逐一比对:

test_shifted = test.copy()
for t in range(1, HORIZON+1):
    test_shifted['load+'+str(t)] = test_shifted['load'].shift(-t, freq='H')
test_shifted = test_shifted.dropna(how='any')

随后用固定 720 小时(30 天)的滑动训练窗口循环预测:

%%time
training_window = 720  # 用最近 30 天(720 小时)训练
history = [x for x in train['load']][(-training_window):]
predictions = []
order = (2, 1, 0)
seasonal_order = (1, 1, 0, 24)

for t in range(test_shifted.shape[0]):
    model = SARIMAX(endog=history, order=order, seasonal_order=seasonal_order)
    model_fit = model.fit()
    yhat = model_fit.forecast(steps=HORIZON)
    predictions.append(yhat)
    obs = list(test_shifted.iloc[t])
    history.append(obs[0])   # 加入真实观测
    history.pop(0)           # 移出窗口头部最旧值

用 MAPE 量化预测误差

平均绝对百分比误差(MAPE)用比例形式表达预测精度:将每个预测点的 (实际值 - 预测值) 绝对值除以实际值,再对全部拟合点求平均。对应地,课程中的评估代码为:

eval_df['APE'] = (eval_df['prediction'] - eval_df['actual']).abs() / eval_df['actual']
print(eval_df.groupby('h')['APE'].mean())

本案例运行结果:单步(t+1)预测 MAPE 约 0.56%,多步预测整体 MAPE 约 1.15%——MAPE 越小越好(如 10 表示平均偏差 10%)。将真实值与多步预测绘制在同一时间轴上(红色为实际、蓝色为预测,线宽/透明度随步数衰减),可直观看到模型贴合度很高。

ARIMA 模型预测精度

课后挑战(assignment)可进一步研究 MAE、MSE、RMSE 等其它时间序列精度度量方式。ARIMA 课配套 notebook 位于 7-TimeSeries/2-ARIMA/working/notebook.ipynb,含完整可运行代码与答案(solution 目录)。

实操 3:用 SVR 处理非线性时间序列

ARIMA 是处理时间序列的经典统计线性方法,但很多真实时间序列含有非线性,线性模型难以刻画。此时,3-SVR 借助支持向量机在回归任务中对非线性数据的处理能力来解决这一短板。

概念铺垫

  • 回归(Regression):监督学习的一种,从给定输入预测连续值,思想是在特征空间中拟合一条覆盖最多数据点的曲线(或直线)。
  • 支持向量机(SVM):用于分类、回归与离群点检测的监督模型。在特征空间中它是一个超平面:分类时充当边界,回归时充当最佳拟合线。SVM 通常用核函数把数据变换到更高维空间,使其更易分离。
  • 支持向量回归(SVR):SVM 家族用于回归的变体,目标是找到拥有最多数据点的最佳拟合线(超平面)。

构建 SVR 的时间步样本

SVR 不像 ARIMA 天然消费"有序序列",需要先把数据改造成 [batch, timesteps] 形态的监督学习样本。本课取 timesteps = 5,即用前 4 个时间步预测第 5 个时间步:

from sklearn.svm import SVR
from sklearn.preprocessing import MinMaxScaler
from common.utils import load_data, mape

energy = load_data('../../data')[['load']]   # 相对 notebook 所在子目录的路径

train_data = train.values
test_data = test.values
timesteps = 5

# 用嵌套列表推导把序列切分成重叠窗口,得到二维张量
train_data_timesteps = np.array([[j for j in train_data[i:i+timesteps]]
                                 for i in range(0, len(train_data)-timesteps+1)])[:,:,0]
test_data_timesteps  = np.array([[j for j in test_data[i:i+timesteps]]
                                 for i in range(0, len(test_data)-timesteps+1)])[:,:,0]

# 前 timesteps-1 步作输入,第 timesteps 步作输出
x_train, y_train = train_data_timesteps[:,:timesteps-1], train_data_timesteps[:,[timesteps-1]]
x_test,  y_test  = test_data_timesteps[:,:timesteps-1],  test_data_timesteps[:,[timesteps-1]]

运行后训练张量形状为 (1412, 5)(输入 1412×4、输出 1412×1),测试张量为 (44, 5)

训练 SVR 并评估

本课使用 RBF 核,并设置三个关键超参数:核宽 gamma=0.5、正则化 C=10、不敏感带 epsilon=0.05

model = SVR(kernel='rbf', gamma=0.5, C=10, epsilon=0.05)
model.fit(x_train, y_train[:,0])

y_train_pred = model.predict(x_train).reshape(-1,1)
y_test_pred  = model.predict(x_test).reshape(-1,1)

评估前先把预测值与真实值经 scaler.inverse_transform() 还原到原始量纲,再与对应的 timesteps-1 之后的时间戳配对绘图并计算 MAPE。课程运行的实测结果:

  • 训练集 MAPE ≈ 1.72%
  • 测试集 MAPE ≈ 1.26%(对未见过的未来数据表现良好);
  • 全量数据集(26300 个窗口)MAPE ≈ 2.06%

SVR 全量数据预测

课后挑战(assignment)值得逐一尝试:调整 gammaCepsilon 观察测试集指标变化;更换其它核函数(如线性核、多项式核)对比表现;或改变 timesteps 的回看窗口长度。完整代码见 7-TimeSeries/3-SVR/working/notebook.ipynb

三条预测路径如何取舍

维度 ARIMA / SARIMA SVR
模型本质 统计线性模型 基于核的机器学习回归模型
数据结构 直接消费有序序列 需先切分为 [batch, timesteps] 窗口样本
适用场景 线性、含趋势与季节性的数据 存在非线性关系的数据
关键参数 p,d,q 与季节 P,D,Q、季节周期 核函数、gammaCepsilontimesteps
主要评估手段 walk-forward + MAPE 训练/测试/全量 MAPE 与曲线对比

对本章节的电力负荷数据,两者都拿到了很低的 MAPE(1%~2% 量级):ARIMA 展示了经典统计模型在含强季节性数据上的精准,SVR 则证明了非线性回归器同样胜任。实际项目中常以此类误差指标结合可视化决定取舍,也可把两者作为基准线再引入深度学习等更复杂模型。

实践建议与继续探索

  • 运行环境:每个子课程的 working/ 目录都自带 common/environment.yamlcommon/utils.pydata/energy.csv,可将其视为一个自包含的迷你项目;notebook 与工具代码在同一目录层级下,按 README 内相对路径导入 common.utils 即可。仓库还提供了 solution/ 目录存放参考答案。
  • 逐课推进顺序:先看 入门课 完成数据可视化,再依次阅读 ARIMA 课SVR 课,每课末尾都配有课后小测、挑战题与扩展阅读。
  • 章节导航:本节是第 7 大章节"时间序列"的入口,本文件即其索引页;课程全貌(共 12 周)见仓库根目录 README。需要系统化学习时,可结合章节内的手绘示意图 ml-timeseries.png 快速建立整体概念。
  • 多语言对照:本主题页在 translations/ 下还有包括保加利亚语、中文(zh-CN)等数十种语言的同步翻译,例如本页对应的保加利亚语版即位于 translations/bg/7-TimeSeries/README.md,可作语言对照学习。

至此,你已经走通了时间序列预测的完整链路:理解概念与数据特征 → 加载真实 GEFCom2014 电力数据 → 可视化识别模式 → 用 SARIMA 做统计预测并用 walk-forward 严格验证 → 用 SVR 处理非线性并比较核方法与统计模型——这套方法论可直接迁移到销量、库存、金融与能源等任意有序时间数据的预测任务上。

登录后查看全文
热门项目推荐
相关项目推荐