Python 時間序列預測:Hot-Winters
本文轉載自微信公眾號「Python中文社區」,作者wedo實驗君。轉載本文請聯系Python中文社區公眾號。
1. 什么是Holt-Winters
時間序列是非常常見的數據格式,以[時間,觀測值]形式表現,如下圖。
現實場景中如股票走勢圖,國家GDP歷年數據,機器cpu利用率,內存數據等都是時間序列。對未來時間的觀測值進行預測是有意義的工作,提前預知未來的數據的走勢,可以提前做出行動,如預測cpu使用率,如果cpu飆高,可以及早進行調整,避免機器負載過高而宕機,這個在AIOPS是很常見的一個應用場景。
今天要說到Holt-Winters是利用三次指數平滑來做時間序列預測的方法。Holt-Winters是綜合了1957年Holt和1960年Winters兩個人的思路的一種方法。
一次指數平滑
我們來看下,一次指數平滑如下圖:
可知,si表示第i時刻的平滑估計,si可以表示為當前實際值xi和上一時刻平滑估計值得加權組合,權重由alpha來決定。那為什么稱為指數平滑呢?我們來把式子展開,如下:
有點類似泰勒展開式的味道
alpha 屬于[0, 1], 越大表示近期的數據影響更大
二次指數平滑:加上趨勢的因素
一次指數平滑,沒有考慮時間序列的趨勢和季節性,二次指數平滑加上趨勢因素。
從公式可知,一個時間序列的時刻值分解為baseline部分和趨勢部分,t表示趨勢,可以表示為連續兩個時刻的差值;可知,ti也是一次的指數平滑。
Holt-Winters三次指數平滑:加上季節性因素
在二次指數平滑基礎上,考慮季節性因素,就是三次指數平滑,也就是Holt-Winters。由此,一個時間序列的時刻值分解為baseline部分和趨勢部分以及季節部分。由于季節性,存在周期,比如按周,按月等。pi季節性為當前季節性值和上一個周期季節性估計值的加權組合,周期在公式中以k來表示。如下:
2. Holt-Winters的實現
從第一部分可知,要實現Holt-Winters,只要知道:
- 初始值:s0,t0和p0
- 合適的參數:alpha,beta, gamma
- 套入公式即可完成預測
三個重要參數:alpha,beta, gamma都屬于[0, 1]之間,要么人為的搜索,要么通過數據來估計,通常采用L-BFGS優化算法來擬合數據。優化算法來自包scipy.optimize的fmin_l_bfgs_b。
- from __future__ import division
- from sys importexit
- from math import sqrt
- from numpy import array
- from scipy.optimize import fmin_l_bfgs_b
- # 優化算法的loss function,即判斷擬合效果,由RMSE MAE等
- def RMSE(params, *args):
- Y = args[0]
- type = args[1]
- rmse = 0
- alpha, beta, gamma = params
- m = args[2]
- a = [sum(Y[0:m]) / float(m)]
- b = [(sum(Y[m:2* m]) - sum(Y[0:m])) / m ** 2]
- if type == 'additive':
- s = [Y[i] - a[0] for i in range(m)]
- y = [a[0] + b[0] + s[0]]
- for i in range(len(Y)):
- a.append(alpha * (Y[i] - s[i]) + (1- alpha) * (a[i] + b[i]))
- b.append(beta * (a[i + 1] - a[i]) + (1- beta) * b[i])
- s.append(gamma * (Y[i] - a[i] - b[i]) + (1- gamma) * s[i])
- y.append(a[i + 1] + b[i + 1] + s[i + 1])
- rmse = sqrt(sum([(m - n) ** 2for m, n in zip(Y, y[:-1])]) / len(Y))
- return rmse
- # 加性的時間序列
- def additive(x, m, fc, alpha = None, beta = None, gamma = None):
- Y = x[:]
- # 利用fmin_l_bfgs_b來估計參數alpha beta和gamma
- if(alpha == Noneor beta == Noneor gamma == None):
- initial_values = array([0.3, 0.1, 0.1])
- boundaries = [(0, 1), (0, 1), (0, 1)]
- type = 'additive'
- parameters = fmin_l_bfgs_b(RMSE, x0 = initial_values, args = (Y, type, m), bounds = boundaries, approx_grad = True)
- alpha, beta, gamma = parameters[0]
- # 初始值 a表示baseline, b表示趨勢,s表示季節性,y表示預測值, 分別取第一個周期的統計數據為初始值
- a = [sum(Y[0:m]) / float(m)]
- b = [(sum(Y[m:2* m]) - sum(Y[0:m])) / m ** 2]
- s = [Y[i] - a[0] for i in range(m)]
- y = [a[0] + b[0] + s[0]]
- rmse = 0
- # 套用上面公式,從0開始,fc表示預測的數量,如已知前7天,預測接下來的一個小時的數據,如果數據粒度是5分鐘,fc為12。
- for i in range(len(Y) + fc):
- if i == len(Y):
- # 預測值為
- Y.append(a[-1] + b[-1] + s[-m])
- a.append(alpha * (Y[i] - s[i]) + (1- alpha) * (a[i] + b[i]))
- b.append(beta * (a[i + 1] - a[i]) + (1- beta) * b[i])
- s.append(gamma * (Y[i] - a[i] - b[i]) + (1- gamma) * s[i])
- y.append(a[i + 1] + b[i + 1] + s[i + 1])
- # 計算rmse值
- rmse = sqrt(sum([(m - n) ** 2for m, n in zip(Y[:-fc], y[:-fc - 1])]) / len(Y[:-fc]))
- return y[-fc:], alpha, beta, gamma, rmse
另外,statsmodels包中也提供的實現的方法
- from statsmodels.tsa.holtwinters importExponentialSmoothing
3. Holt-Winters參數
從上面實現可知,holt-winters通過預估alpha,beta和gamma來預測。算法的關鍵就是這三個參數和初始化值。三個參數可以通過優化算法來預估,但有可能并不是最優的。初始值的設置除了上面統計值外,還可以通過時序的分解的趨勢和季節部分來初始。
- import numpy as np
- from pandas import read_csv
- import matplotlib.pyplot as plt
- from statsmodels.tsa.seasonal import seasonal_decompose
- decomposition = seasonal_decompose(df_clean.bw, model='additive', period=288)
- decomposition.plot()
Holt-Winters針對波形比較穩定,沒有突刺的情況下,效果會比較好。
對于存在突刺,統一的alpha,beta,gamma不能很好擬合,預測可能會滯后。
4. 總結
本文分享了時間序列預測算法Holt-Winters以及重要參數的選擇,希望對你有幫助??偨Y如下:
- Holt-Winters是三次指數平滑,分別為baseline,趨勢和季節性;
- alpha、beta和gamma分別為baseline,趨勢和季節性的指數加權參數,一般通過優化算法L-BFGS估計
- 初始化可通過平均值,也可通過時間序列分解得到
- 周期m或者k的選擇要根據實際數據來選擇
- Holt-Winters針對波形比較穩定,沒有突刺的情況下,效果會比較好
作者簡介:wedo實驗君, 數據分析師;熱愛生活,熱愛寫作