Python基于LightGBM進(jìn)行時(shí)間序列預(yù)測(cè)
前言
當(dāng)我們考慮時(shí)間序列的增強(qiáng)樹(shù)時(shí),通常會(huì)想到 M5 比賽,其中前十名中有很大一部分使用了 LightGBM。但是當(dāng)在單變量情況下使用增強(qiáng)樹(shù)時(shí),由于沒(méi)有大量的外生特征可以利用,它的性能非常的糟糕。
首先需要明確的是M4 比賽的亞軍 DID 使用了增強(qiáng)樹(shù)。但是它作為一個(gè)元模型來(lái)集成其他更傳統(tǒng)的時(shí)間序列方法。在 M4 上公開(kāi)的代碼中,所有標(biāo)準(zhǔn)增強(qiáng)樹(shù)的基準(zhǔn)測(cè)試都相當(dāng)糟糕,有時(shí)甚至還達(dá)不到傳統(tǒng)的預(yù)測(cè)方法。下面是Sktime 包和他們的論文所做的出色工作[1]:

任何帶有“XGB”或“RF”的模型都使用基于樹(shù)的集成。在上面的列表中 Xgboost 在每小時(shí)數(shù)據(jù)集中提供了 10.9 的最佳結(jié)果!然后,但是這些模型只是Sktime 在他們框架中做過(guò)的簡(jiǎn)單嘗試,而 M4 的獲勝者在同一數(shù)據(jù)集上的得分是 9.3 分……。在該圖表中我們需要記住一些數(shù)字,例如來(lái)自 XGB-s 的每小時(shí)數(shù)據(jù)集的 10.9 和每周數(shù)據(jù)集中的樹(shù)性模型的“最佳”結(jié)果:來(lái)自 RF-t-s 的 9.0。
從上圖中就引出了我們的目標(biāo):創(chuàng)建一個(gè)基于LightGBM并且適合個(gè)人使用的時(shí)間序列的快速建模程序,并且能夠絕對(duì)超越這些數(shù)字,而且在速度方面可與傳統(tǒng)的統(tǒng)計(jì)方法相媲美。
聽(tīng)起來(lái)很困難,并且我們的第一個(gè)想法可能是必須優(yōu)化我們的樹(shù)。但是提升樹(shù)非常復(fù)雜,改動(dòng)非常費(fèi)時(shí),并且結(jié)果并不一定有效。但是有一點(diǎn)好處是我們正在擬合是單個(gè)數(shù)據(jù)集,是不是可從特征下手呢?
特征
在查看單變量空間中樹(shù)的其他實(shí)現(xiàn)時(shí)都會(huì)看到一些特征工程,例如分箱、使用目標(biāo)的滯后值、簡(jiǎn)單的計(jì)數(shù)器、季節(jié)性虛擬變量,也許還有傅里葉函數(shù)。這對(duì)于使用傳統(tǒng)的指數(shù)平滑等方法是非常棒的。但是我們今天目的是必須對(duì)時(shí)間元素進(jìn)行特征化并將其表示為表格數(shù)據(jù)以提供給樹(shù)型模型,LazyProphet這時(shí)候就出現(xiàn)了。除此以外,LazyProphet還包含一個(gè)額外的特征工程元素:將點(diǎn)”連接”起來(lái)。
很簡(jiǎn)單,將時(shí)間序列的第一個(gè)點(diǎn)連接起來(lái),并將一條線連接到中途的另一個(gè)點(diǎn),然后將中途的點(diǎn)連接到最后一個(gè)點(diǎn)。重復(fù)幾次,同時(shí)更改將哪個(gè)點(diǎn)用作“kink”(中間節(jié)點(diǎn)),這就是我們所說(shuō)的“連接”。
下面張圖能很好地說(shuō)明這一點(diǎn)。藍(lán)線是時(shí)間序列,其他線只是“連接點(diǎn)”:

事實(shí)證明,這些只是加權(quán)分段線性基函數(shù)。這樣做的一個(gè)缺點(diǎn)是這些線的外推可能會(huì)出現(xiàn)偏差。為了解決這個(gè)問(wèn)題,引入一個(gè)懲罰從中點(diǎn)到最后點(diǎn)的每條線的斜率的“衰減”因子。
在這個(gè)基礎(chǔ)上加滯后的目標(biāo)值和傅里葉基函數(shù),在某些問(wèn)題上就能夠接近最先進(jìn)的性能。因?yàn)橐蠛苌?,因因此我們把它稱作“LazyProphet”。
下面我們看看實(shí)際的應(yīng)用結(jié)果。
代碼
這里使用的數(shù)據(jù)集都是開(kāi)源的,并在M-competitions github上發(fā)布。數(shù)據(jù)已經(jīng)被分割為訓(xùn)練和測(cè)試集,我們直接使用訓(xùn)練csv進(jìn)行擬合,而測(cè)試csv用于使用SMAPE進(jìn)行評(píng)估?,F(xiàn)在導(dǎo)入LazyProphet:
pip install LazyProphet
安裝后,開(kāi)始編碼:
import matplotlib.pyplot as plt
import numpy as np
from tqdm import tqdm
import pandas as pd
from LazyProphet import LazyProphet as lp
train_df = pd.read_csv(r'm4-weekly-train.csv')
test_df = pd.read_csv(r'm4-weekly-test.csv')
train_df.index = train_df['V1']
train_df = train_df.drop('V1', axis = 1)
test_df.index = test_df['V1']
test_df = test_df.drop('V1', axis = 1)
導(dǎo)入所有必要的包后將讀入每周數(shù)據(jù)。創(chuàng)建 SMAPE 函數(shù),它將返回給定預(yù)測(cè)和實(shí)際值的 SMAPE:
def smape(A, F): return 100/len(A) * np.sum(2 * np.abs(F - A) / (np.abs(A) + np.abs(F)))
對(duì)于這個(gè)實(shí)驗(yàn)將取所有時(shí)間序列的平均值與其他模型進(jìn)行比較。為了進(jìn)行健全性檢查,我們還將獲得的平均 SMAPE,這樣可以確保所做的與比賽中所做的一致。
smapes = []
naive_smape = []
j = tqdm(range(len(train_df)))
for row in j:
y = train_df.iloc[row, :].dropna()
y_test = test_df.iloc[row, :].dropna()
j.set_description(f'{np.mean(smapes)}, {np.mean(naive_smape)}')
lp_model = LazyProphet(scale=True,
seasonal_period=52,
n_basis=10,
fourier_order=10,
ar=list(range(1, 53)),
decay=.99,
linear_trend=None,
decay_average=False)
fitted = lp_model.fit(y)
predictions = lp_model.predict(len(y_test)).reshape(-1)
smapes.append(smape(y_test.values, pd.Series(predictions).clip(lower=0)))
naive_smape.append(smape(y_test.values, np.tile(y.iloc[-1], len(y_test))))
print(np.mean(smapes))
print(np.mean(naive_smape))
在查看結(jié)果之前,快速介紹一下 LazyProphet 參數(shù)。
scale:這個(gè)很簡(jiǎn)單,只是是否對(duì)數(shù)據(jù)進(jìn)行縮放。默認(rèn)值為 True 。
seasonal_period:此參數(shù)控制季節(jié)性的傅立葉基函數(shù),因?yàn)檫@是我們使用 52 的每周頻率。
n_basis:此參數(shù)控制加權(quán)分段線性基函數(shù)。這只是要使用的函數(shù)數(shù)量的整數(shù)。
Fourier_order:用于季節(jié)性的正弦和余弦對(duì)的數(shù)量。
ar:要使用的滯后目標(biāo)變量值??梢垣@取多個(gè)列表 1-52 。
decay:衰減因子用于懲罰我們的基函數(shù)的“右側(cè)”。設(shè)置為 0.99 表示斜率乘以 (1- 0.99) 或 0.01。
linear_trend:樹(shù)的一個(gè)主要缺點(diǎn)是它們無(wú)法推斷出后續(xù)數(shù)據(jù)的范圍。為了克服這個(gè)問(wèn)題,有一些針對(duì)多項(xiàng)式趨勢(shì)的現(xiàn)成測(cè)試將擬合線性回歸以消除趨勢(shì)。None 表示有測(cè)試,通過(guò) True 表示總是去趨勢(shì),通過(guò) False 表示不測(cè)試并且不使用線性趨勢(shì)。
decay_average:在使用衰減率時(shí)不是一個(gè)有用的參數(shù)。這是一個(gè)trick但不要使用它。傳遞 True 只是平均基函數(shù)的所有未來(lái)值。這在與 elasticnet 程序擬合時(shí)很有用,但在測(cè)試中對(duì) LightGBM 的用處不大。
下面繼續(xù)處理數(shù)據(jù):
train_df = pd.read_csv(r'm4-hourly-train.csv')
test_df = pd.read_csv(r'm4-hourly-test.csv')
train_df.index = train_df['V1']
train_df = train_df.drop('V1', axis = 1)
test_df.index = test_df['V1']
test_df = test_df.drop('V1', axis = 1)
smapes = []
naive_smape = []
j = tqdm(range(len(train_df)))
for row in j:
y = train_df.iloc[row, :].dropna()
y_test = test_df.iloc[row, :].dropna()
j.set_description(f'{np.mean(smapes)}, {np.mean(naive_smape)}')
lp_model = LazyProphet(seasonal_period=[24,168],
n_basis=10,
fourier_order=10,
ar=list(range(1, 25)),
decay=.99)
fitted = lp_model.fit(y)
predictions = lp_model.predict(len(y_test)).reshape(-1)
smapes.append(smape(y_test.values, pd.Series(predictions).clip(lower=0)))
naive_smape.append(smape(y_test.values, np.tile(y.iloc[-1], len(y_test))))
print(np.mean(smapes))
print(np.mean(naive_smape))
所以真正需要修改是seasonal_period 和ar 參數(shù)。將list傳遞給seasonal_period 時(shí),它將為列表中的所有內(nèi)容構(gòu)建季節(jié)性基函數(shù)。ar 進(jìn)行了調(diào)整以適應(yīng)新的主要季節(jié) 24。
結(jié)果
對(duì)于上面的 Sktime 結(jié)果,表格如下:

LazyProphet 擊敗了 Sktime 最好的模型,其中包括幾種不同的基于樹(shù)的方法。在每小時(shí)數(shù)據(jù)集上輸給給了 M4 的獲勝者,但平均而言總體上優(yōu)于 ES-RNN。這里要意識(shí)到的重要一點(diǎn)是,只使用默認(rèn)參數(shù)進(jìn)行了此操作……
boosting_params = {
"objective": "regression",
"metric": "rmse",
"verbosity": -1,
"boosting_type": "gbdt",
"seed": 42,
'linear_tree': False,
'learning_rate': .15,
'min_child_samples': 5,
'num_leaves': 31,
'num_iterations': 50
}
可以在創(chuàng)建 LazyProphet 類時(shí)傳遞你參數(shù)的字典,可以針對(duì)每個(gè)時(shí)間序列進(jìn)行優(yōu)化,以獲得更多收益。
對(duì)比一下我們的結(jié)果和上面提到的目標(biāo):
進(jìn)行了零參數(shù)優(yōu)化(針對(duì)不同的季節(jié)性稍作修改)
分別擬合每個(gè)時(shí)間序列
在我的本地機(jī)器上在一分鐘內(nèi)“懶惰地”生成了預(yù)測(cè)。
在基準(zhǔn)測(cè)試中擊敗了所有其他樹(shù)方法
目前看是非常成功的,但是成功可能無(wú)法完全的復(fù)制,因?yàn)樗麛?shù)據(jù)集的數(shù)據(jù)量要少得多,因此我們的方法往往會(huì)顯著降低性能。根據(jù)測(cè)試LazyProphet 在高頻率和大量數(shù)據(jù)量上表現(xiàn)的更好,但是LazyProphet還是一個(gè)時(shí)間序列建模的很好選擇,我們不需要花多長(zhǎng)時(shí)間進(jìn)行編碼就能夠測(cè)試,這點(diǎn)時(shí)間還是很值得。
到此這篇關(guān)于Python基于LightGBM進(jìn)行時(shí)間序列預(yù)測(cè)的文章就介紹到這了,更多相關(guān)Python LightGBM時(shí)間序列預(yù)測(cè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決安裝tensorflow遇到無(wú)法卸載numpy 1.8.0rc1的問(wèn)題
今天小編就為大家分享一篇解決安裝tensorflow遇到無(wú)法卸載numpy 1.8.0rc1的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
Python爬蟲(chóng)基礎(chǔ)之簡(jiǎn)單說(shuō)一下scrapy的框架結(jié)構(gòu)
今天給大家?guī)?lái)的是關(guān)于Python爬蟲(chóng)的相關(guān)知識(shí),文章圍繞著scrapy的框架結(jié)構(gòu)展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
Python2和Python3中print的用法示例總結(jié)
在Python 3中接觸的第一個(gè)很大的差異就是縮進(jìn)是作為語(yǔ)法的一部分,這和C++等其他語(yǔ)言確實(shí)很不一樣,所以要小心,其中python3和python2中print的用法有很多不同,這篇文章主要給大家介紹了關(guān)于Python2和Python3中print用法的相關(guān)資料,需要的朋友可以參考下。2017-10-10
python中round函數(shù)保留兩位小數(shù)的方法
在本篇內(nèi)容里小編給各位分享的是一篇關(guān)于python中round函數(shù)保留兩位小數(shù)的方法及相關(guān)知識(shí)點(diǎn),有興趣的朋友們可以學(xué)習(xí)下。2020-12-12
Python使用MD5加密算法對(duì)字符串進(jìn)行加密操作示例
這篇文章主要介紹了Python使用MD5加密算法對(duì)字符串進(jìn)行加密操作,結(jié)合實(shí)例形式分析了Python實(shí)現(xiàn)md5加密相關(guān)操作技巧,需要的朋友可以參考下2018-03-03
使用python requests模塊發(fā)送http請(qǐng)求及接收響應(yīng)的方法
用 python 編寫(xiě) http request 消息代碼時(shí),建議用requests庫(kù),因?yàn)閞equests比urllib內(nèi)置庫(kù)更為簡(jiǎn)捷,requests可以直接構(gòu)造get,post請(qǐng)求并發(fā)送,本文給大家介紹了使用python requests模塊發(fā)送http請(qǐng)求及接收響應(yīng)的方法,需要的朋友可以參考下2024-03-03
關(guān)于Python中對(duì)變量賦值過(guò)程的理解
在Python中對(duì)變量賦值過(guò)程的理解,有助于學(xué)習(xí)者對(duì)Python的變量和所指向的對(duì)象之間的指向關(guān)系深刻理解,避免編程中多個(gè)變量賦值后,對(duì)變量結(jié)果的不確定,,需要的朋友可以參考下2023-05-05
python 實(shí)現(xiàn)將txt文件多行合并為一行并將中間的空格去掉方法
今天小編就為大家分享一篇python 實(shí)現(xiàn)將txt文件多行合并為一行并將中間的空格去掉方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12

