用XGBoost進行時間序列預測
XGBoost是梯度分類和回歸問題的有效實現。
它既快速又高效,即使在各種預測建模任務上也表現出色,即使不是最好的,也能在數據科學競賽的獲勝者(例如Kaggle的獲獎者)中廣受青睞。
XGBoost也可以用于時間序列預測,盡管它要求將時間序列數據集首先轉換為有監督的學習問題。它還需要使用一種專門的技術來評估模型,稱為前向驗證,因為使用k倍交叉驗證對模型進行評估會導致樂觀的結果。
在本教程中,您將發現如何開發XGBoost模型進行時間序列預測。完成本教程后,您將知道:
1、XGBoost是用于分類和回歸的梯度提升集成算法的實現。
2、可以使用滑動窗口表示將時間序列數據集轉換為監督學習。
3、如何使用XGBoost模型擬合,評估和進行預測,以進行時間序列預測。
教程概述
本教程分為三個部分:他們是:
1、XGBoost集成
2、時間序列數據準備
3、XGBoost用于時間序列預測
XGBoost集成
XGBoost是Extreme Gradient Boosting的縮寫,是隨機梯度提升機器學習算法的有效實現。隨機梯度增強算法(也稱為梯度增強機或樹增強)是一種功能強大的機器學習技術,可在各種具有挑戰性的機器學習問題上表現出色,甚至表現最佳。
它是決策樹算法的集合,其中新樹修復了那些已經屬于模型的樹的錯誤。將添加樹,直到無法對模型進行進一步的改進為止。XGBoost提供了隨機梯度提升算法的高效實現,并提供了一組模型超參數,這些參數旨在提供對模型訓練過程的控制。
XGBoost設計用于表格數據集的分類和回歸,盡管它可以用于時間序列預測。
首先,必須安裝XGBoost庫。您可以使用pip進行安裝,如下所示:
- sudo pip install xgboost
一旦安裝,您可以通過運行以下代碼來確認它已成功安裝,并且您正在使用現代版本:
- # xgboost
- import xgboost
- print("xgboost", xgboost.__version__)
運行代碼,您應該看到以下版本號或更高版本。
- xgboost 1.0.1
盡管XGBoost庫具有自己的Python API,但我們可以通過XGBRegressor包裝器類將XGBoost模型與scikit-learn API結合使用。
可以實例化模型的實例,就像將其用于模型評估的任何其他scikit-learn類一樣使用。例如:
- # define model
- model = XGBRegressor()
現在我們已經熟悉了XGBoost,下面讓我們看一下如何為監督學習準備時間序列數據集。
時間序列數據準備
時間序列數據可以表述為監督學習。給定時間序列數據集的數字序列,我們可以將數據重組為看起來像監督學習的問題。我們可以通過使用以前的時間步長作為輸入變量,并使用下一個時間步長作為輸出變量來做到這一點。讓我們通過一個例子來具體說明。假設我們有一個時間序列,如下所示:
- time, measure
- 1, 100
- 2, 110
- 3, 108
- 4, 115
- 5, 120
通過使用上一個時間步的值來預測下一個時間步的值,我們可以將此時間序列數據集重組為監督學習問題。通過這種方式重組時間序列數據集,數據將如下所示:
- X, y
- ?, 100
- 100, 110
- 110, 108
- 108, 115
- 115, 120
- 120, ?
請注意,時間列已刪除,某些數據行不可用于訓練模型,例如第一和最后一個。
這種表示稱為滑動窗口,因為輸入和預期輸出的窗口會隨著時間向前移動,從而為監督學習模型創建新的“樣本”。
有關準備時間序列預測數據的滑動窗口方法的更多信息。
在給定所需的輸入和輸出序列長度的情況下,我們可以在Pandas中使用shift()函數自動創建時間序列問題的新框架。
這將是一個有用的工具,因為它將允許我們使用機器學習算法探索時間序列問題的不同框架,以查看可能導致性能更好的模型。
下面的函數將一個時間序列作為具有一個或多個列的NumPy數組時間序列,并將其轉換為具有指定數量的輸入和輸出的監督學習問題。
- # transform a time series dataset into a supervised learning dataset
- def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
- n_vars = 1 if type(data) is list else data.shape[1]
- df = DataFrame(data)
- cols = list()
- # input sequence (t-n, ... t-1)
- for i in range(n_in, 0, -1):
- cols.append(df.shift(i))
- # forecast sequence (t, t+1, ... t+n)
- for i in range(0, n_out):
- cols.append(df.shift(-i))
- # put it all together
- agg = concat(cols, axis=1)
- # drop rows with NaN values
- if dropnan:
- agg.dropna(inplace=True)
- return agg.values
我們可以使用此函數為XGBoost準備時間序列數據集。
準備好數據集后,我們必須小心如何使用它來擬合和評估模型。
例如,將模型擬合未來的數據并預測過去是無效的。該模型必須在過去進行訓練并預測未來。這意味著不能使用在評估過程中將數據集隨機化的方法,例如k折交叉驗證。相反,我們必須使用一種稱為前向驗證的技術。在前向驗證中,首先通過選擇一個切點(例如除過去12個月外,所有數據均用于培訓,最近12個月用于測試。
如果我們有興趣進行單步預測,例如一個月后,我們可以通過對訓練數據集進行訓練并預測測試數據集的第一步來評估模型。然后,我們可以將來自測試集的真實觀測值添加到訓練數據集中,重新擬合模型,然后讓模型預測測試數據集中的第二步。對整個測試數據集重復此過程將為整個測試數據集提供一步式預測,可以從中計算出誤差度量以評估模型的技能。
下面的函數執行前向驗證。它使用時間序列數據集的整個監督學習版本以及用作測試集的行數作為參數。然后,它逐步通過測試集,調用xgboost_forecast()函數進行單步預測。計算錯誤度量,并將詳細信息返回以進行分析。
- # walk-forward validation for univariate data
- def walk_forward_validation(data, n_test):
- predictions = list()
- # split dataset
- train, test = train_test_split(data, n_test)
- # seed history with training dataset
- history = [x for x in train]
- # step over each time-step in the test set
- for i in range(len(test)):
- # split test row into input and output columns
- testX, testtesty = test[i, :-1], test[i, -1]
- # fit model on history and make a prediction
- yhat = xgboost_forecast(history, testX)
- # store forecast in list of predictions
- predictions.append(yhat)
- # add actual observation to history for the next loop
- history.append(test[i])
- # summarize progress
- print('>expected=%.1f, predicted=%.1f' % (testy, yhat))
- # estimate prediction error
- error = mean_absolute_error(test[:, -1], predictions)
- return error, test[:, 1], predictions
調用train_test_split()函數可將數據集拆分為訓練集和測試集。我們可以在下面定義此功能。
- # split a univariate dataset into train/test sets
- def train_test_split(data, n_test):
- return data[:-n_test, :], data[-n_test:, :]
我們可以使用XGBRegressor類進行單步預測。下面的xgboost_forecast()函數通過將訓練數據集和測試輸入行作為輸入,擬合模型并進行單步預測來實現此目的。
- # fit an xgboost model and make a one step prediction
- def xgboost_forecast(train, testX):
- # transform list into array
- train = asarray(train)
- # split into input and output columns
- trainX, traintrainy = train[:, :-1], train[:, -1]
- # fit model
- model = XGBRegressor(objective='reg:squarederror', n_estimators=1000)
- model.fit(trainX, trainy)
- # make a one-step prediction
- yhat = model.predict([testX])
- return yhat[0]
現在,我們知道了如何準備時間序列數據以進行預測和評估XGBoost模型,接下來我們可以看看在實際數據集上使用XGBoost的情況。
XGBoost用于時間序列預測
在本節中,我們將探索如何使用XGBoost進行時間序列預測。我們將使用標準的單變量時間序列數據集,以使用該模型進行單步預測。您可以將本節中的代碼用作您自己項目的起點,并輕松地對其進行調整以適應多變量輸入,多變量預測和多步預測。我們將使用每日女性出生數據集,即三年中的每月出生數。
您可以從此處下載數據集,并將其放在文件名“ daily-total-female-births.csv”的當前工作目錄中。
數據集(每天女性出生總數.csv):
- https://raw.githubusercontent.com/jbrownlee/Datasets/master/daily-total-female-births.csv
說明(每日女性出生總數):
- https://raw.githubusercontent.com/jbrownlee/Datasets/master/daily-total-female-births.names
數據集的前幾行如下所示:
- "Date","Births"
- "1959-01-01",35
- "1959-01-02",32
- "1959-01-03",30
- "1959-01-04",31
- "1959-01-05",44
- ...
首先,讓我們加載并繪制數據集。下面列出了完整的示例。
- # load and plot the time series dataset
- from pandas import read_csv
- from matplotlib import pyplot
- # load dataset
- series = read_csv('daily-total-female-births.csv', header=0, index_col=0)
- values = series.values
- # plot dataset
- pyplot.plot(values)
- pyplot.show()
運行示例將創建數據集的折線圖。我們可以看到沒有明顯的趨勢或季節性。
當預測最近的12個月時,持久性模型可以實現約6.7例出生的MAE。這提供了性能基準,在該基準之上可以認為模型是熟練的。
接下來,當對過去12個月的數據進行單步預測時,我們可以評估數據集上的XGBoost模型。
我們將僅使用前6個時間步長作為模型和默認模型超參數的輸入,除了我們將損失更改為'reg:squarederror'(以避免警告消息),并在集合中使用1,000棵樹(以避免學習不足) )。
下面列出了完整的示例。
- # forecast monthly births with xgboost
- from numpy import asarray
- from pandas import read_csv
- from pandas import DataFrame
- from pandas import concat
- from sklearn.metrics import mean_absolute_error
- from xgboost import XGBRegressor
- from matplotlib import pyplot
- # transform a time series dataset into a supervised learning dataset
- def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
- n_vars = 1 if type(data) is list else data.shape[1]
- df = DataFrame(data)
- cols = list()
- # input sequence (t-n, ... t-1)
- for i in range(n_in, 0, -1):
- cols.append(df.shift(i))
- # forecast sequence (t, t+1, ... t+n)
- for i in range(0, n_out):
- cols.append(df.shift(-i))
- # put it all together
- agg = concat(cols, axis=1)
- # drop rows with NaN values
- if dropnan:
- agg.dropna(inplace=True)
- return agg.values
- # split a univariate dataset into train/test sets
- def train_test_split(data, n_test):
- return data[:-n_test, :], data[-n_test:, :]
- # fit an xgboost model and make a one step prediction
- def xgboost_forecast(train, testX):
- # transform list into array
- train = asarray(train)
- # split into input and output columns
- trainX, traintrainy = train[:, :-1], train[:, -1]
- # fit model
- model = XGBRegressor(objective='reg:squarederror', n_estimators=1000)
- model.fit(trainX, trainy)
- # make a one-step prediction
- yhat = model.predict(asarray([testX]))
- return yhat[0]
- # walk-forward validation for univariate data
- def walk_forward_validation(data, n_test):
- predictions = list()
- # split dataset
- train, test = train_test_split(data, n_test)
- # seed history with training dataset
- history = [x for x in train]
- # step over each time-step in the test set
- for i in range(len(test)):
- # split test row into input and output columns
- testX, testtesty = test[i, :-1], test[i, -1]
- # fit model on history and make a prediction
- yhat = xgboost_forecast(history, testX)
- # store forecast in list of predictions
- predictions.append(yhat)
- # add actual observation to history for the next loop
- history.append(test[i])
- # summarize progress
- print('>expected=%.1f, predicted=%.1f' % (testy, yhat))
- # estimate prediction error
- error = mean_absolute_error(test[:, -1], predictions)
- return error, test[:, -1], predictions
- # load the dataset
- series = read_csv('daily-total-female-births.csv', header=0, index_col=0)
- values = series.values
- # transform the time series data into supervised learning
- data = series_to_supervised(values, n_in=6)
- # evaluate
- mae, y, yhat = walk_forward_validation(data, 12)
- print('MAE: %.3f' % mae)
- # plot expected vs preducted
- pyplot.plot(y, label='Expected')
- pyplot.plot(yhat, label='Predicted')
- pyplot.legend()
- pyplot.show()
運行示例將報告測試集中每個步驟的期望值和預測值,然后報告所有預測值的MAE。
注意:由于算法或評估程序的隨機性,或者數值精度的差異,您的結果可能會有所不同。考慮運行該示例幾次并比較平均結果。
我們可以看到,該模型的性能優于持久性模型,MAE約為5.9,而MAE約為6.7
- >expected=42.0, predicted=44.5
- >expected=53.0, predicted=42.5
- >expected=39.0, predicted=40.3
- >expected=40.0, predicted=32.5
- >expected=38.0, predicted=41.1
- >expected=44.0, predicted=45.3
- >expected=34.0, predicted=40.2
- >expected=37.0, predicted=35.0
- >expected=52.0, predicted=32.5
- >expected=48.0, predicted=41.4
- >expected=55.0, predicted=46.6
- >expected=50.0, predicted=47.2
- MAE: 5.957
創建線圖,比較數據集最后12個月的一系列期望值和預測值。這給出了模型在測試集上執行得如何的幾何解釋。
圖2
一旦選擇了最終的XGBoost模型配置,就可以最終確定模型并用于對新數據進行預測。這稱為樣本外預測,例如 超出訓練數據集進行預測。這與在模型評估期間進行預測是相同的:因為我們始終希望使用模型用于對新數據進行預測時所期望使用的相同過程來評估模型。下面的示例演示了在所有可用數據上擬合最終XGBoost模型并在數據集末尾進行單步預測的過程。
- # finalize model and make a prediction for monthly births with xgboost
- from numpy import asarray
- from pandas import read_csv
- from pandas import DataFrame
- from pandas import concat
- from xgboost import XGBRegressor
- # transform a time series dataset into a supervised learning dataset
- def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
- n_vars = 1 if type(data) is list else data.shape[1]
- df = DataFrame(data)
- cols = list()
- # input sequence (t-n, ... t-1)
- for i in range(n_in, 0, -1):
- cols.append(df.shift(i))
- # forecast sequence (t, t+1, ... t+n)
- for i in range(0, n_out):
- cols.append(df.shift(-i))
- # put it all together
- agg = concat(cols, axis=1)
- # drop rows with NaN values
- if dropnan:
- agg.dropna(inplace=True)
- return agg.values
- # load the dataset
- series = read_csv('daily-total-female-births.csv', header=0, index_col=0)
- values = series.values
- # transform the time series data into supervised learning
- train = series_to_supervised(values, n_in=6)
- # split into input and output columns
- trainX, traintrainy = train[:, :-1], train[:, -1]
- # fit model
- model = XGBRegressor(objective='reg:squarederror', n_estimators=1000)
- model.fit(trainX, trainy)
- # construct an input for a new preduction
- row = values[-6:].flatten()
- # make a one-step prediction
- yhat = model.predict(asarray([row]))
- print('Input: %s, Predicted: %.3f' % (row, yhat[0]))
運行示例將XGBoost模型適合所有可用數據。使用最近6個月的已知數據準備新的輸入行,并預測數據集結束后的下個月。
- Input: [34 37 52 48 55 50], Predicted: 42.708