使用Catboost從RNN、ARIMA和Prophet模型中提取信號進行預測。
集成各種弱學習器可以提高預測精度,但是如果我們的模型已經(jīng)很強大了,集成學習往往也能夠起到錦上添花的作用。流行的機器學習庫scikit-learn提供了一個StackingRegressor,可以用于時間序列任務。但是StackingRegressor有一個局限性;它只接受其他scikit-learn模型類和api。所以像ARIMA這樣在scikit-learn中不可用的模型,或者來自深度神經(jīng)網(wǎng)絡的模型都無法使用。在這篇文章中,我將展示如何堆疊我們能見到的模型的預測。

我們將用到下面的包:
pip install --upgrade scalecast
conda install tensorflow
conda install shap
conda install -c conda-forge cmdstanpy
pip install prophet
數(shù)據(jù)集
數(shù)據(jù)集每小時一次,分為訓練集(700個觀測值)和測試集(48個觀測值)。下面代碼是讀取數(shù)據(jù)并將其存儲在Forecaster對象中:
import pandas as pd
import numpy as np
from scalecast.Forecaster import Forecaster
from scalecast.util import metrics
import matplotlib.pyplot as plt
import seaborn as sns
def read_data(idx = 'H1', cis = True, metrics = ['smape']):
info = pd.read_csv(
'M4-info.csv',
index_col=0,
parse_dates=['StartingDate'],
dayfirst=True,
)
train = pd.read_csv(
f'Hourly-train.csv',
index_col=0,
).loc[idx]
test = pd.read_csv(
f'Hourly-test.csv',
index_col=0,
).loc[idx]
y = train.values
sd = info.loc[idx,'StartingDate']
fcst_horizon = info.loc[idx,'Horizon']
cd = pd.date_range(
start = sd,
freq = 'H',
periods = len(y),
)
f = Forecaster(
y = y, # observed values
current_dates = cd, # current dates
future_dates = fcst_horizon, # forecast length
test_length = fcst_horizon, # test-set length
cis = cis, # whether to evaluate intervals for each model
metrics = metrics, # what metrics to evaluate
)
return f, test.values
f, test_set = read_data()
f # display the Forecaster object
結(jié)果是這樣的:

模型
在我們開始構(gòu)建模型之前,我們需要從中生成最簡單的預測,naive方法就是向前傳播最近24個觀測值。
f.set_estimator('naive')
f.manual_forecast(seasonal=True)
然后使用ARIMA、LSTM和Prophet作為基準。
ARIMA
Autoregressive Integrated Moving Average 是一種流行而簡單的時間序列技術(shù),它利用序列的滯后和誤差以線性方式預測其未來。通過EDA,我們確定這個系列是高度季節(jié)性的。所以最終選擇了應用order (5,1,4) x(1,1,1,24)的季節(jié)性ARIMA模型。
f.set_estimator('arima')
f.manual_forecast(
order = (5,1,4),
seasonal_order = (1,1,1,24),
call_me = 'manual_arima',
)
LSTM
如果說ARIMA是時間序列模型中比較簡單的一種,那么LSTM就是比較先進的方法之一。它是一種具有許多參數(shù)的深度學習技術(shù),其中包括一種在順序數(shù)據(jù)中發(fā)現(xiàn)長期和短期模式的機制,這在理論上使其成為時間序列的理想選擇。這里使用tensorflow建立這個模型
f.set_estimator('rnn')
f.manual_forecast(
lags = 48,
layers_struct=[
('LSTM',{'units':100,'activation':'tanh'}),
('LSTM',{'units':100,'activation':'tanh'}),
('LSTM',{'units':100,'activation':'tanh'}),
],
optimizer = 'Adam',
epochs = 15,
plot_loss = True,
validation_split=0.2,
call_me = 'rnn_tanh_activation',
)
f.manual_forecast(
lags = 48,
layers_struct=[
('LSTM',{'units':100,'activation':'relu'}),
('LSTM',{'units':100,'activation':'relu'}),
('LSTM',{'units':100,'activation':'relu'}),
],
optimizer = 'Adam',
epochs = 15,
plot_loss = True,
validation_split=0.2,
call_me = 'rnn_relu_activation',
)
Prophet
盡管它非常受歡迎,但有人聲稱它的準確性并不令人印象深刻,主要是因為它對趨勢的推斷有時候很不切實際,而且它沒有通過自回歸建模來考慮局部模式。但是它也有自己的特點。1,它會自動將節(jié)日效果應用到模型身上,并且還考慮了幾種類型的季節(jié)性??梢砸杂脩羲璧淖畹托枨髞硗瓿蛇@一切,所以我喜歡把它用作信號,而不是最終的預測結(jié)果。
f.set_estimator('prophet')
f.manual_forecast()
比較結(jié)果
現(xiàn)在我們已經(jīng)為每個模型生成了預測,讓我們看看它們在驗證集上的表現(xiàn)如何,驗證集是我們訓練集中的最后48個觀察結(jié)果。
results = f.export(determine_best_by='TestSetSMAPE')
ms = results['model_summaries']
ms[
[
'ModelNickname',
'TestSetLength',
'TestSetSMAPE',
'InSampleSMAPE',
]
]

每個模型的表現(xiàn)都優(yōu)于naive方法。ARIMA模型表現(xiàn)最好,百分比誤差為4.7%,其次是Prophet模型。讓我們看看所有的預測與驗證集的關(guān)系:
f.plot(order_by="TestSetSMAPE",ci=True)
plt.show()

所有這些模型在這個時間序列上的表現(xiàn)都很合理,它們之間沒有很大的偏差。下面讓我們把它們堆起來!
堆疊模型
每個堆疊模型都需要一個最終估計器,它將過濾其他模型的各種估計,創(chuàng)建一組新的預測。我們將把之前結(jié)果與Catboost估計器疊加在一起。Catboost是一個強大的程序,希望它能從每個已經(jīng)應用的模型中充實出最好的信號。
f.add_signals(
f.history.keys(), # add signals from all previously evaluated models
)
f.add_ar_terms(48)
f.set_estimator('catboost')
上面的代碼將來自每個評估模型的預測添加到Forecaster對象中。它稱這些預測為“信號”。 它們的處理方式與存儲在同一對象中的任何其他協(xié)變量相同。 這里還添加了最后 48 個系列的滯后作為 Catboost 模型可以用來進行預測的附加回歸變量。 現(xiàn)在讓我們調(diào)用三種 Catboost 模型:一種使用所有可用信號和滯后,一種僅使用信號,一種僅使用滯后。
f.manual_forecast(
Xvars='all',
call_me='catboost_all_reg',
verbose = False,
)
f.manual_forecast(
Xvars=[x for x in f.get_regressor_names() if x.startswith('AR')],
call_me = 'catboost_lags_only',
verbose = False,
)
f.manual_forecast(
Xvars=[x for x in f.get_regressor_names() if not x.startswith('AR')],
call_me = 'catboost_signals_only',
verbose = False,
)
下面可以比較所有模型的結(jié)果。我們將研究兩個度量:SMAPE和平均絕對比例誤差(MASE)。這是實際M4比賽中使用的兩個指標。
test_results = pd.DataFrame(index = f.history.keys(),columns = ['smape','mase'])
for k, v in f.history.items():
test_results.loc[k,['smape','mase']] = [
metrics.smape(test_set,v['Forecast']),
metrics.mase(test_set,v['Forecast'],m=24,obs=f.y),
]
test_results.sort_values('smape')

可以看到,通過組合來自不同類型模型的信號生成了兩個優(yōu)于其他估計器的估計器:使用所有信號訓練的Catboost模型和只使用信號的Catboost模型。這兩種方法的樣本誤差都在2.8%左右。下面是對比圖:
fig, ax = plt.subplots(figsize=(12,6))
f.plot(
models = ['catboost_all_reg','catboost_signals_only'],
ci=True,
ax = ax
)
sns.lineplot(
x = f.future_dates,
y = test_set,
ax = ax,
label = 'held out actuals',
color = 'darkblue',
alpha = .75,
)
plt.show()

哪些信號最重要?
為了完善分析,我們可以使用shapley評分來確定哪些信號是最重要的。Shapley評分被認為是確定給定機器學習模型中輸入的預測能力的最先進的方法之一。得分越高,意味著輸入在特定模型中越重要。
f.export_feature_importance('catboost_all_reg')

上面的圖只顯示了前幾個最重要的預測因子,但我們可以從中看出,ARIMA信號是最重要的,其次是序列的第一個滯后,然后是Prophet。RNN模型的得分也高于許多滯后模型。如果我們想在未來訓練一個更輕量的模型,這可能是一個很好的起點。
總結(jié)
在這篇文章中,我展示了在時間序列上下文中集成模型的力量,以及如何使用不同的模型在時間序列上獲得更高的精度。這里我們使用scalecast包,這個包的功能還是很強大的,如果你喜歡,可以去它的主頁看看:https://github.com/mikekeith52/scalecast
本文的數(shù)據(jù)集是M4的時序競賽:https://github.com/Mcompetitions/M4-methods
使用代碼在這里:https://scalecast-examples.readthedocs.io/en/latest/misc/stacking/custom_stacking.html