背景

指数平滑是在 20 世纪 50 年代后期提出的,并激发了一些十分成功的预测方法。使用指数平滑方法生成的预测是过去观测值的加权平均值,并且随着过去观测值离预测值距离的增大,权重呈指数型衰减。换句话说,观察值越近,相应的权重越高。该框架能够快速生成可靠的预测结果,并且适用于广泛的时间序列,这是一个巨大的优势并且对于工业应用来说非常重要。

本文主要学习四种常见的指数平滑方法:

  • Exponential smoothing:针对没有趋势和季节性的序列

    一次指数平滑,从最邻近到最早的数据点的权重呈现指数型下降的规律。

  • Holt exponential smoothing:针对有趋势但没有季节性的序列

    二次指数平滑,通过引入一个额外的系数来解决指数平滑无法应用于具有趋势性数据的问题。

  • Holt-Winters exponential smoothing:针对有趋势且有季节性的序列

    三次指数平滑,通过再次引入一个新系数的方式同时解决了 Holt exponential smoothing 无法解决具有季节性变化数据的不足。

所有的指数平滑法需要更新上一时间点的计算结果,并使用当前时间点的数据中包含的新信息。它们通过”混合“新信息和旧信息来实现,而相关的新旧信息的权重由一个可调整的参数来控制。

Exponential smoothing

一次指数平滑的递推公式如下

si=αxi+(1α)si1,0α1s_{i}=\alpha x_{i}+(1-\alpha) s_{i-1}, 0 \leq \alpha \leq 1

其中xix_i 表示时间序列第ii 时间点对应的观测值,sis_i 表示第ii 个时间点平滑后的值;衰减因子α\alpha 可以控制对历史数据的遗忘程度,当α\alpha 接近 1 时表示只保留当前时间点的值。

递归公式展开可以得到以下形式:

si=αxi+(1α)si1=αxi+(1α)[αxi1+(1α)si2]=αxi+(1α)[αxi1+(1α)[αxi2+(1α)si3]]=α[xi+(1α)xi1+(1α)2xi2+(1α)3si3]==αj=0i(1α)jxij\begin{aligned} s_{i} &=\alpha x_{i}+(1-\alpha) s_{i-1} \\ &=\alpha x_{i}+(1-\alpha)\left[\alpha x_{i-1}+(1-\alpha) s_{i-2}\right] \\ &=\alpha x_{i}+(1-\alpha)\left[\alpha x_{i-1}+(1-\alpha)\left[\alpha x_{i-2}+(1-\alpha) s_{i-3}\right]\right] \\ &=\alpha\left[x_{i}+(1-\alpha) x_{i-1}+(1-\alpha)^{2} x_{i-2}+(1-\alpha)^{3} s_{i-3}\right] \\ &=\ldots \\ &=\alpha \sum_{j=0}^{i}(1-\alpha)^{j} x_{i-j} \end{aligned}

从上式即可看出指数平滑考虑所有历史观测值对当前值的影响,但影响程度随时间增长而减小。对应的时间序列预测公式:

x^i+h=si\hat{x}_{i+h}=s_{i}

其中sis_i 表示最后一个时间点对应的值;h=1h=1 表示下个预测值。

Holt exponential smoothing

指数平滑考虑的是数据的 baseline,二次指数平滑在此基础上将趋势作为一个分量考虑。

趋势,即斜率定义:b=Δy/Δxb=Δy/Δx,其中ΔxΔxxx 坐标轴的变化值,b=Δy/Δxb=\Delta y/\Delta x 表示Δy\Delta y 值变换。

二次指数平滑保留并更新两个量的状态:平滑后的信号平滑后的趋势。公式如下:

si=αxi+(1α)(si1+ti1)ti=β(sisi1)+(1β)ti1\begin{aligned} s_{i} &=\alpha x_{i}+(1-\alpha)\left(s_{i-1}+t_{i-1}\right) \\ t_{i} &=\beta\left(s_{i}-s_{i-1}\right)+(1-\beta) t_{i-1} \end{aligned}

从上式可以看出仅是在一次指数平滑的基础上添加了趋势项,趋势也使用一次指数平滑进行处理。对应的时间序列预测公式:

x^i+h=si+hti\hat{x}_{i+h}=s_{i}+h t_{i}

Holt-Winters exponential smoothing

二次指数平滑考虑了序列的 baseline 和趋势性,三次指数平滑在此基础上引入季节性分量考虑时间序列周期性模式。

季节性(周期性)是指一个序列在每个固定的时间间隔中都出现某种重复的模式,令kk 表示周期长度。

Holt-Winters 指数平滑方法有两种不同的季节性组成部分:

  • 当季节变化在该时间序列中大致保持不变时,通常选择加法模型;
  • 当季节变化与时间序列的水平成比例变化时,通常选择乘法模型;

加法模型

计算公式如下:

si=α(xipik)+(1α)(si1+ti1)ti=β(sisi1)+(1β)ti1pi=γ(xist)+(1γ)pik\begin{aligned} s_{i} &=\alpha\left(x_{i}-p_{i-k}\right)+(1-\alpha)\left(s_{i-1}+t_{i-1}\right) \\ t_{i} &=\beta\left(s_{i}-s_{i-1}\right)+(1-\beta) t_{i-1} \\ p_{i} &=\gamma\left(x_{i}-s_t\right)+(1-\gamma) p_{i-k} \end{aligned}

其中pikp_{i-k} 表示周期性分量,第三个公式中sts_t 表示si1+ti1s_{i-1} + t_{i-1} 。对应的预测公式如下:

x^i+h=si+hti+pik+h\hat{x}_{i+h}=s_{i}+h t_{i}+p_{i-k+h}

乘法模型

计算公式如下:

si=αxipik+(1α)(si1+ti+1)ti=β(sisi1)+(1β)ti1pi=γxisi1+pi1+(1γ)pik\begin{aligned} s_{i} &=\alpha \frac{x_{i}}{p_{i-k}}+(1-\alpha)(s_{i-1}+t_{i+1})\\ t_{i} &=\beta \left(s_{i}-s_{i-1}\right)+\left(1-\beta\right) t_{i-1} \\ p_{i} &=\gamma \frac{x_{i}}{s_{i-1}+p_{i-1}}+(1-\gamma)p_{i-k} \end{aligned}

对应的预测公式为

x^i+h=(si+hti)pik+h\hat{x}_{i+h}=(s_{i}+h t_{i})p_{i-k+h}

Python 实践

为了节约时间,本文中直接采用 statsmodels 库中实现的 Holter-Winters 方法进行测试。

采用一周的时间序列数据为例,如下图所示

raw data

先将时间序列进行分解看下趋势项和周期项:

1
2
3
4
from statsmodels.tsa.seasonal import seasonal_decompose
from statsmodels.tsa.holtwinters import SimpleExpSmoothing, ExponentialSmoothing

decompose_result = seasonal_decompose(data, model="multiplicative", period=288)

可视化结果如下

时间序列分解

一次指数平滑

📢 需要注意参数α\alpha 选择问题。

1
2
## 一次指数平滑
data["1exp"] = SimpleExpSmoothing(data["value"]).fit(smoothing_level=alpha).fittedvalues

可视化结果如下

一次指数平滑

二次指数平滑

1
2
data["2exp_add"] = ExponentialSmoothing(data["value"], trend="add", seasonal=None).fit().fittedvalues
data["2exp_mul"] = ExponentialSmoothing(data["value"], trend="mul", seasonal=None).fit().fittedvalues

可视化结果如下

二次指数平滑

三次指数平滑

1
2
data["3exp_add"] = ExponentialSmoothing(data["value"], trend="add", seasonal="add", seasonal_periods=288).fit().fittedvalues
data["3exp_mul"] = ExponentialSmoothing(data["value"], trend="mul", seasonal="mul", seasonal_periods=288).fit().fittedvalues

可视化结果如下

三次指数平滑

序列预测

取一天数据进行预测,周期为 1D = 288 * 5min;

1
2
3
## 预测
model = ExponentialSmoothing(data["value"], trend="add", seasonal="add", seasonal_periods=288).fit()
pred = model.forecast(288)

可视化结果如下

序列预测

时间序列的整体预测效果还是不错的,大体上符合趋势 ~