多步時(shí)間序列預(yù)測(cè)策略實(shí)戰(zhàn)
從數(shù)據(jù)科學(xué)的技術(shù)角度來(lái)看,預(yù)測(cè)單步和預(yù)測(cè)多步有很大區(qū)別,后者更難。預(yù)測(cè)多個(gè)時(shí)期需要全面了解長(zhǎng)期模式和依賴(lài)關(guān)系。它具有前瞻性,需要模型來(lái)捕捉一段時(shí)間內(nèi)錯(cuò)綜復(fù)雜的動(dòng)態(tài)變化。
多步預(yù)測(cè)的策略通常有兩種,即單不預(yù)測(cè)策略和遞歸預(yù)測(cè)策略。時(shí)序基礎(chǔ)模型 ARIMA 是單步預(yù)測(cè)模型。那么如何實(shí)現(xiàn)多步驟預(yù)測(cè)?也許一種方法是遞歸使用同一模型。從模型中得到一個(gè)周期的預(yù)測(cè)結(jié)果,作為預(yù)測(cè)下一個(gè)周期的輸入。然后,將第二期的預(yù)測(cè)作為預(yù)測(cè)第三期的輸入。可以通過(guò)使用前一期的預(yù)測(cè)結(jié)果來(lái)遍歷所有時(shí)期。這正是遞歸預(yù)測(cè)或迭代預(yù)測(cè)策略的作用。圖(A)顯示模型首先產(chǎn) ,然后 成為同一模型的輸入,產(chǎn)
圖(A):遞歸預(yù)測(cè)策略
在"基于樹(shù)的時(shí)間序列預(yù)測(cè)實(shí)戰(zhàn)"中,我們學(xué)會(huì)了將單變量時(shí)間序列表述為基于樹(shù)的建模問(wèn)題。我們可以將一個(gè)時(shí)期的值作為樹(shù)狀模型的目標(biāo),得出一個(gè)無(wú)偏值。若我們建立n個(gè)模型,每個(gè)模型都能預(yù)測(cè)第 n 個(gè)時(shí)期,我們可以將它們的預(yù)測(cè)結(jié)果結(jié)合起來(lái),這就是直接預(yù)測(cè)策略。這個(gè)方法成功的原因在于,針對(duì)單一目標(biāo)的樹(shù)狀模型通常是有效的,而且不同的模型可以捕捉到隨時(shí)間變化的復(fù)雜動(dòng)態(tài)。其他替代策略也存在,但主要是這兩種方法的衍生。
圖(B):直接預(yù)測(cè)策略
兩種主要策略是:
- 遞歸預(yù)測(cè)
- n步直接預(yù)測(cè)
這兩種方法需要耗費(fèi)大量時(shí)間進(jìn)行數(shù)據(jù)重組和模型迭代。遞歸策略已經(jīng)納入經(jīng)典 Python 庫(kù) "statsmodels"中,而我將采用開(kāi)源 Python 庫(kù) "Sktime",該庫(kù)使遞歸和直接預(yù)測(cè)方法變得更加簡(jiǎn)單。Sktime 封裝了多種工具,包括 "statsmodels",并提供了統(tǒng)一的 API,可用于時(shí)間序列預(yù)測(cè)、分類(lèi)、聚類(lèi)和異常檢測(cè)(Markus等人,2019,2020)
接下來(lái)云朵君和大家一起學(xué)習(xí)如何思考產(chǎn)生多步預(yù)測(cè)的策略、多步預(yù)測(cè)的代碼以及評(píng)估。
- 多步預(yù)測(cè)的遞歸策略
- n 個(gè)周期、n 個(gè)模型的直接預(yù)測(cè)策略
- 使用 ARIMA 的遞歸策略
安裝 sktime 庫(kù)和 lightGBM 庫(kù)。
!pip install sktime
!pip install lightgbm
遞歸預(yù)測(cè)
遞歸策略中,先對(duì)前一步進(jìn)行預(yù)測(cè),然后用這些預(yù)測(cè)作為輸入,對(duì)未來(lái)的時(shí)間步驟進(jìn)行迭代預(yù)測(cè)。整個(gè)過(guò)程中只使用一個(gè)模型,生成一個(gè)預(yù)測(cè),并將其輸入到模型中生成下一個(gè)預(yù)測(cè),如此循環(huán)。步驟如下:
- 建模:訓(xùn)練一個(gè)時(shí)間序列預(yù)測(cè)模型,預(yù)測(cè)一步前瞻??梢允褂脗鹘y(tǒng)的時(shí)間序列模型(如ARIMA)、指數(shù)平滑模型或機(jī)器學(xué)習(xí)模型(如lightGBM)。
- 生成第一次預(yù)測(cè):利用歷史數(shù)據(jù),使用已訓(xùn)練的模型預(yù)測(cè)下一個(gè)時(shí)間步驟。
- 將預(yù)測(cè)值作為下一次預(yù)測(cè)模型的輸入:將預(yù)測(cè)值添加到歷史數(shù)據(jù)中,創(chuàng)建更新的時(shí)間序列。
- 迭代預(yù)測(cè):使用更新后的時(shí)間序列作為模型的輸入數(shù)據(jù),重復(fù)上述過(guò)程。在每次迭代中,模型考慮之前的預(yù)測(cè)值,進(jìn)行多步驟預(yù)測(cè)。繼續(xù)迭代預(yù)測(cè)過(guò)程,直到達(dá)到期望的未來(lái)步數(shù)。
一個(gè)可以發(fā)現(xiàn)的問(wèn)題是,隨著時(shí)間推移,預(yù)測(cè)的準(zhǔn)確性會(huì)下降,初期預(yù)測(cè)的誤差會(huì)在后期積累。只要模型足夠復(fù)雜,能夠捕捉到錯(cuò)綜復(fù)雜的模式,這種情況似乎是可以接受的。
加載電力消耗數(shù)據(jù),數(shù)據(jù)說(shuō)明可以在"基于樹(shù)模型的時(shí)間序列預(yù)測(cè)實(shí)戰(zhàn)"中找到。
%matplotlib inline
from matplotlib import pyplot as plt
import pandas as pd
import numpy as np
data = pd.read_csv('/electric_consumption.csv', index_col='Date')
data.index = pd.to_datetime(data.index)
data = data.sort_index() # Make sure the data are sorted
data.index = pd.PeriodIndex(data.index, freq='H') # Make sure the Index is sktime format
data.index
我唯一想指出的是使用 pd.PeriodIndex() 使索引符合 sktime 格式。
圖片
從 Pandas DataFrame 中提取一個(gè)序列。Pandas 系列保留了 sktime 所需的索引。
y = data['Consumption']
# Train and test split
cuttime = int(len(y)*0.9) # Take 90% for training
train = y[0:cuttime]
test = y[cuttime::]
y
圖片
我們計(jì)劃使用遞歸預(yù)測(cè)方法并建立一個(gè) lightGBM 模型,使用與"基于樹(shù)的時(shí)間序列預(yù)測(cè)教程"相同的超參數(shù)。此外,Python Notebook 中還有一個(gè)未顯示的 GBM 模型示例供你嘗試其他模型。
import lightgbm as lgb
from sktime.forecasting.compose import make_reduction
lgb_regressor = lgb.LGBMRegressor(num_leaves = 10,
learning_rate = 0.02,
feature_fraction = 0.8,
max_depth = 5,
verbose = 0,
num_boost_round = 15000,
nthread = -1
)
lgb_forecaster = make_reduction(lgb_regressor, window_length=30, strategy="recursive")
lgb_forecaster.fit(train)
我們只指定了 "遞歸" 作為策略超參數(shù)值,稍后會(huì)采用 "直接" 預(yù)測(cè)方法。
下一步,我們需要生成一個(gè)未來(lái)周期的數(shù)字列表。我設(shè)置未來(lái)期限 (fh) 為 100 期,即 [1,2,...,100]。然后生成這 100 期的預(yù)測(cè)。
vlen = 100
fh = list(range(1, vlen,1))
y_test_pred = lgb_forecaster.predict(fh= fh)
y_test_pred
與測(cè)試數(shù)據(jù)中的實(shí)際值相比,預(yù)測(cè)結(jié)果如何?讓我們將 100 期的實(shí)際值和預(yù)測(cè)值進(jìn)行匹配并繪制成圖。
from sktime.utils.plotting import plot_series
actual = test[test.index<=y_test_pred.index.max()]
plot_series(actual, y_test_pred, labels=['Actual', 'Predicted'])
plt.show()
我使用的函數(shù) plot_series() 只是 matplotlib 函數(shù)的封裝。
圖 (A):使用 LightGBM 的遞歸策略
常見(jiàn)的評(píng)估指標(biāo)包括平均絕對(duì)百分比誤差(MAPE)和對(duì)稱(chēng)MAPE。在sktime中,可以通過(guò)控制超參數(shù)來(lái)簡(jiǎn)化這一操作。
from sktime.performance_metrics.forecasting import mean_absolute_percentage_error
print('%.16f' % mean_absolute_percentage_error(actual, y_test_pred, symmetric=False))
print('%.16f' % mean_absolute_percentage_error(actual, y_test_pred, symmetric=True))
結(jié)果如下。稍后我們將把這兩個(gè)數(shù)字與直接預(yù)測(cè)法中的數(shù)字進(jìn)行比較。
- MAPE: 0.0546382493115653
- sMAPE: 0.0547477326419614
太好了,我們成功地使用遞歸法建立了多重預(yù)測(cè)。接下來(lái),讓我們學(xué)習(xí)直接預(yù)測(cè)策略。
n步直接預(yù)測(cè)
了解了遞歸預(yù)測(cè)策略后,我們來(lái)考慮一下直接預(yù)測(cè)策略。我們將建立多個(gè)單獨(dú)的模型,每個(gè)模型負(fù)責(zé)預(yù)測(cè)特定的未來(lái)時(shí)段。盡管建立許多模型會(huì)耗費(fèi)一些時(shí)間,但這是機(jī)器的事情,而不是我的大腦時(shí)間。在利用 CPU 能力的同時(shí)。
接下來(lái)是整個(gè)過(guò)程的步驟:
- 模型訓(xùn)練:為每個(gè)未來(lái)時(shí)間步訓(xùn)練一個(gè)獨(dú)立的模型。例如,如果要預(yù)測(cè)未來(lái) 100 個(gè)時(shí)間段,就需要訓(xùn)練 100 個(gè)單獨(dú)的模型,每個(gè)模型負(fù)責(zé)預(yù)測(cè)各自時(shí)間步的值。
- 預(yù)測(cè):使用每個(gè)訓(xùn)練好的模型獨(dú)立生成特定時(shí)間的預(yù)測(cè)值。這些模型可以并行運(yùn)行,因?yàn)樗鼈兊念A(yù)測(cè)并不相互依賴(lài)。
- 合并預(yù)測(cè):只需將這些預(yù)測(cè)連接起來(lái)即可。
這種直接預(yù)測(cè)策略的優(yōu)勢(shì)之一是能夠捕捉每個(gè)預(yù)測(cè)范圍內(nèi)的特定模式。每個(gè)模型都可以針對(duì)其負(fù)責(zé)的時(shí)間步長(zhǎng)進(jìn)行優(yōu)化,從而提高準(zhǔn)確性。
我們將使用與回歸器相同的 LightGBM,并使用 make_reduction(),唯一的區(qū)別是超參數(shù)是 direct 而不是 recursive。
from sktime.forecasting.compose import make_reduction
import lightgbm as lgb
lgb_regressor = lgb.LGBMRegressor(num_leaves = 10,
learning_rate = 0.02,
feature_fraction = 0.8,
max_depth = 5,
verbose = 0,
num_boost_round = 15000,
nthread = -1
)
lgb_forecaster = make_reduction(lgb_regressor, window_length=30, strategy="direct")
我們將使用 100 個(gè)周期[1,2,...,100]的列表來(lái)預(yù)測(cè)范圍 (fh)。每個(gè)周期會(huì)建立一個(gè) LightGBM 模型,總共會(huì)有 100 個(gè)模型。盡管構(gòu)建這么多 LightGBM 模型可能會(huì)花費(fèi)很多時(shí)間,但我只演示 100 個(gè)周期的原因。
vlen = 100
fh=list(range(1, vlen,1))
lgb_forecaster.fit(train, fh = fh)
y_test_pred = lgb_forecaster.predict(fh=fh)
y_test_pred
將繪制測(cè)試數(shù)據(jù)中的實(shí)際值與預(yù)測(cè)值的對(duì)比圖。
from sktime.utils.plotting import plot_series
plot_series(actual, y_test_pred, labels=["Actual", "Prediction"], x_label='Date', y_label='Consumption');
這是因?yàn)槊總€(gè)預(yù)測(cè)都來(lái)自一個(gè)獨(dú)立的模型,有自己的特點(diǎn)。
圖 (B):使用 LightGBM 的直接預(yù)測(cè)策略
回顧一下評(píng)估指標(biāo):
from sktime.performance_metrics.forecasting import mean_absolute_percentage_error
print('%.16f' % mean_absolute_percentage_error(actual, y_test_pred, symmetric=False))
print('%.16f' % mean_absolute_percentage_error(actual, y_test_pred, symmetric=True))
MAPE 和 sMAPE 與遞歸法的結(jié)果非常接近。
- MAPE: 0.0556899099509884
- sMAPE: 0.0564747643400997
Make_reduction()
LightGBM模型是一個(gè)監(jiān)督學(xué)習(xí)模型,需要包含x和y的數(shù)據(jù)幀來(lái)進(jìn)行模型訓(xùn)練。make_reduction()函數(shù)可以將單變量時(shí)間序列轉(zhuǎn)化為數(shù)據(jù)幀。該函數(shù)有兩個(gè)主要參數(shù),即strategy("遞歸"或"直接")和window_length(滑動(dòng)窗口長(zhǎng)度)?;瑒?dòng)窗口與單變量時(shí)間序列一起移動(dòng),創(chuàng)建樣本,窗口中的值就是x值。遞歸策略和直接策略將在接下來(lái)進(jìn)行解釋。
遞歸策略
遞歸策略中,滑動(dòng)窗口前的值即為目標(biāo)值,圖(D)滑動(dòng) 14 窗口,生成了 6 個(gè)樣本的數(shù)據(jù)幀,其中藍(lán)色的 y 值為目標(biāo)值,該數(shù)據(jù)幀用于訓(xùn)練模型。
圖 (D):遞歸策略的 Make_reduction()
直接預(yù)測(cè)策略
直接預(yù)測(cè)策略為每個(gè)未來(lái)目標(biāo)期建立一個(gè)模型。假設(shè)目標(biāo)值是 t+3 的值。圖(D)滑動(dòng) 14 窗口,生成一個(gè)包含 4 個(gè)樣本的數(shù)據(jù)幀。目標(biāo)值是 t+3 中的 y 值。該數(shù)據(jù)幀用于訓(xùn)練預(yù)測(cè) t+3 的 y 值的模型。
圖 (E):針對(duì) y_t+3 的直接策略 Make_reduction()
目標(biāo)是預(yù)測(cè) t+4 中的值。圖 (D) 滑動(dòng)了 14 個(gè)窗口并生成了一個(gè)包含 3 個(gè)樣本的數(shù)據(jù)幀,用于訓(xùn)練預(yù)測(cè) t+4 中 y 值的模型。
圖(F):針對(duì) y_t+4 的直接策略 Mak
使用 ARIMA 進(jìn)行多步預(yù)測(cè)
Sktime可以使用Python庫(kù)pmdarima進(jìn)行ARIMA并提供多步預(yù)測(cè)。Sktime本身不提供多步預(yù)測(cè),但是pmdarima庫(kù)可以進(jìn)行多步預(yù)測(cè)。一旦建立了ARIMA模型,它會(huì)對(duì)預(yù)測(cè)范圍內(nèi)的每個(gè)時(shí)間點(diǎn)進(jìn)行提前一步預(yù)測(cè),并且采用遞歸策略生成預(yù)測(cè)值。
!pip install pmdarima
函數(shù) temporal_train_test_split()。
首先將數(shù)據(jù)分成訓(xùn)練數(shù)據(jù)和測(cè)試數(shù)據(jù)。
from sktime.forecasting.model_selection import temporal_train_test_split
train, test = temporal_train_test_split(y, train_size = 0.9)
以下代碼將建立模型并提供預(yù)測(cè)。注意該代碼沒(méi)有使用 Sktime 的 make_reduction() 函數(shù)。這是因?yàn)槎嗖筋A(yù)測(cè)是由 AutoARIMA 模型提供的。
from sktime.forecasting.arima import AutoARIMA
arima_model = AutoARIMA(sp=12, suppress_warnings=True)
arima_model.fit(train)
# Future horizon
vlen = 100
fh=list(range(1, vlen,1))
# Predictions
pred = arima_model.predict(fh)
from sktime.performance_metrics.forecasting import mean_absolute_percentage_error
print('%.16f' % mean_absolute_percentage_error(test, pred, symmetric=False))
Sktime 簡(jiǎn)介
Sktime是一個(gè)開(kāi)源的Python庫(kù),集成了許多預(yù)測(cè)工具,包括時(shí)間序列預(yù)測(cè)、分類(lèi)、聚類(lèi)和異常檢測(cè)的工具和算法。它提供了一系列主要功能,包括時(shí)間序列數(shù)據(jù)預(yù)處理、時(shí)間序列預(yù)測(cè)、時(shí)間序列分類(lèi)和聚類(lèi),以及時(shí)間序列注釋。
- 時(shí)間序列數(shù)據(jù)預(yù)處理:包括缺失值處理、歸因和轉(zhuǎn)換。
- 時(shí)間序列預(yù)測(cè):它包括常見(jiàn)的時(shí)間序列建模算法,我將在下一段列出。
- 時(shí)間序列分類(lèi)和聚類(lèi):它包括時(shí)間序列 k-nearest neighbors (k-NN) 等分類(lèi)模型和時(shí)間序列 k-means 等聚類(lèi)模型。
- 時(shí)間序列注釋?zhuān)核试S對(duì)時(shí)間序列數(shù)據(jù)進(jìn)行標(biāo)注和注釋?zhuān)@對(duì)異常檢測(cè)和事件檢測(cè)等任務(wù)非常有用。
Sktime包括一些常見(jiàn)的時(shí)間序列建模算法,如指數(shù)平滑 (ES)、經(jīng)典自回歸綜合移動(dòng)平均 (ARIMA) 和季節(jié)性 ARIMA (SARIMA) 模型,以及向量自回歸(VAR)、向量誤差修正模型(VECM)、結(jié)構(gòu)時(shí)間序列(STS)模型等結(jié)構(gòu)模型。此外,它還可以處理神經(jīng)網(wǎng)絡(luò)模型,包括時(shí)間卷積神經(jīng)網(wǎng)絡(luò)(CNN)、全連接神經(jīng)網(wǎng)絡(luò)(FCN)、長(zhǎng)短期記憶全卷積網(wǎng)絡(luò)(LSTM-FCN)、多尺度注意力卷積神經(jīng)網(wǎng)絡(luò)(MACNN)、時(shí)間遞歸神經(jīng)網(wǎng)絡(luò)(RNN)和時(shí)間卷積神經(jīng)網(wǎng)絡(luò)(CNN)。
結(jié)論
本章介紹了單步預(yù)測(cè)到多步預(yù)測(cè)的建模策略,包括遞歸預(yù)測(cè)和 n 期直接預(yù)測(cè)兩種方法。我們還學(xué)習(xí)了 Python 軟件包 "sktime",它支持輕松執(zhí)行這兩種策略。除了演示的 LightGBM 模型外,我們也可以使用其他模型,如 ARIMA、線性回歸、GBM 或 XGB 作為回歸因子。