時序預測的三種方式:統計學模型、機器學習、循環(huán)神經網絡
作者 | luanhz來源 | 小數志
導讀
時序預測是一類經典的問題,在學術界和工業(yè)界都有著廣泛的研究和應用。甚至說,世間萬物加上時間維度后都可抽象為時間序列問題,例如股****價格、天氣變化等等。關于時序預測問題的相關理論也極為廣泛,除了經典的各種統計學模型外,當下火熱的機器學習以及深度學習中的循環(huán)神經網絡也都可以用于時序預測問題的建模。今天,本文就來介紹三種方式的簡單應用,并在一個真實的時序數據集上加以驗證。
時間序列預測,其主要任務是基于某一指標的歷史數據來預測其在未來的取值,例如上圖中的曲線記錄了1949年至1960年共12年144個月份的每月航班乘客數(具體單位未經考證),那么時序預測要解決的問題就是:給定前9年的歷史數據,例如1949-1957,那么能否預測出1958-1960兩年間的乘客數量的問題。
為了解決這一問題,大概當前主流的解決方式有4種:
統計學模型,較為經典的AR系列,包括AR、MA、ARMA以及ARIMA等,另外Facebook(準確的講,現在應該叫Meta了)推出的Prophet模型,其實本質上也是一種統計學模型,只不過是傳統的趨勢、周期性成分的基礎上,進一步細化考慮了節(jié)假日、時序拐點等因素的影響,以期帶來更為精準的時序規(guī)律刻畫;
機器學習模型,在有監(jiān)督機器學習中,回歸問題主要解決的是基于一系列Feature來預測某一Label的可能取值的問題,那么當以歷史數據作為Feature時其實自然也就可以將時序預測問題抽象為回歸問題,從這一角度講,所有回歸模型都可用于解決時序預測。關于用機器學習抽象時序預測,推薦查看這篇論文《Machine Learning Strategies for Time Series Forecasting》;
深度學習模型,深度學習主流的應用場景當屬CV和NLP兩大領域,其中后者就是專門用于解決序列問題建模的問題,而時間序列當然屬于序列數據的一種特殊形式,所以自然可以運用循環(huán)神經網絡來建模時序預測;
隱馬爾科夫模型,馬爾科夫模型是用于刻畫相鄰狀態(tài)轉換間的經典抽象,而隱馬爾科夫模型則在其基礎上進一步增加了隱藏狀態(tài),來以此豐富模型的表達能力。但其一大假設條件是未來狀態(tài)僅與當前狀態(tài)有關,而不利于利用多個歷史狀態(tài)來共同參與預測,較為常用的可能就是天氣預報的例子了。
本文主要考慮前三種時序預測建模方法,并分別選取:1)Prophet模型,2)RandomForest回歸模型,3)LSTM三種方案加以測試。
首先在這個航班乘客真實數據集上進行測試,依次對比三個所選模型的預測精度。該數據集共有12年間每個月的乘客數量,以1958年1月作為切分界面劃分訓練集和測試集,即前9年的數據作為訓練集,后3年的數據作為測試集驗證模型效果。數據集切分后的示意圖如下:
df = pd.read_csv("AirPassengers.csv", parse_dates=["date"]).rename(columns={"date":"ds", "value":"y"})X_train = df[df.ds<"19580101"]X_test = df[df.ds>="19580101"]
plt.plot(X_train['ds'], X_train['y'])plt.plot(X_test['ds'], X_test['y'])
1.Prophet模型預測。Prophet是一個高度封裝好的時序預測模型,接受一個DataFrame作為訓練集(要求有ds和y兩個字段列),在預測時也接受一個DataFrame,但此時只需有ds列即可,關于模型的詳細介紹可參考其官方文檔:https://facebook.github.io/prophet/。模型訓練及預測部分核心代碼如下:
from prophet import Prophetpro = Prophet()pro.fit(X_train)pred = pro.predict(X_test)
pro.plot(pred)
訓練后的結果示意圖如下:
當然,這是通過Prophet內置的可視化函數給出的結果,也可通過手動繪制測試集真實標簽與預測結果間的對比:
易見,雖然序列的整體****上具有良好的擬合結果,但在具體取值上其實差距還是比較大的。
2.機器學習模型,這里選用常常用作各種baseline的RandomForest模型。在使用機器學習實現時序預測時,通常需要通過滑動窗口的方式來提取特征和標簽,而后在實現預測時實際上也需滑動的截取測試集特征實現單步預測,參考論文《Machine Learning Strategies for Time Series Forecasting》中的做法,該問題可大致描述如下:
據此,設置特征提取窗口長度為12,構建訓練集和測試集的方式如下:
data = df.copy()n = 12for i in range(1, n+1): data['ypre_'+str(i)] = data['y'].shift(i)data = data[['ds']+['ypre_'+str(i) for i in range(n, 0, -1)]+['y']]
# 提取訓練集和測試集X_train = data[data['ds']<"19580101"].dropna()[['ypre_'+str(i) for i in range(n, 0, -1)]]y_train = data[data['ds']<"19580101"].dropna()[['y']]X_test = data[data['ds']>="19580101"].dropna()[['ypre_'+str(i) for i in range(n, 0, -1)]]y_test = data[data['ds']>="19580101"].dropna()[['y']]
# 模型訓練和預測rf = RandomForestRegressor(n_estimators=10, max_depth=5)rf.fit(X_train, y_train)y_pred = rf.predict(X_test)
# 結果對比繪圖y_test.assign(yhat=y_pred).plot()
可見,預測效果也較為一般,尤其是對于最后兩年的預測結果,與真實值差距還是比較大的。用機器學習模型的思維很容易解釋這一現象:隨機森林模型實際上是在根據訓練數據集來學習曲線之間的規(guī)律,由于該時序整體呈現隨時間增長的趨勢,所以歷史數據中的最高點也不足以cover住未來的較大值,因而在測試集中超過歷史數據的所有標簽其實都是無法擬合的。
3.深度學習中的循環(huán)神經網絡,其實深度學習一般要求數據集較大時才能發(fā)揮其優(yōu)勢,而這里的數據集顯然是非常小的,所以僅設計一個最為簡單的模型:1層LSTM+1層Linear。模型搭建如下:
class Model(nn.Module): def __init__(self): super().__init__() self.rnn = nn.LSTM(input_size=1, hidden_size=10, batch_first=True) self.linear = nn.Linear(10, 1)
def forward(self, x): x, _ = self.rnn(x) x = x[:, -1, :] x = self.linear(x) return x
數據集構建思路整體同前述的機器學習部分,而后,按照進行模型訓練煉丹,部分結果如下:
# 數據集轉化為3DX_train_3d = torch.Tensor(X_train.values).reshape(*X_train.shape, 1)y_train_2d = torch.Tensor(y_train.values).reshape(*y_train.shape, 1)X_test_3d = torch.Tensor(X_test.values).reshape(*X_test.shape, 1)y_test_2d = torch.Tensor(y_test.values).reshape(*y_test.shape, 1)
# 模型、優(yōu)化器、評估準則model = Model()creterion = nn.MSELoss()optimizer = optim.Adam(model.parameters())
# 訓練過程for i in range(1000): out = model(X_train_3d) loss = creterion(out, y_train_2d) optimizer.zero_grad() loss.backward() optimizer.step()
if (i+1)%100 == 0: y_pred = model(X_test_3d) loss_test = creterion(y_pred, y_test_2d) print(i, loss.item(), loss_test.item())
# 訓練結果99 65492.08984375 188633.796875199 64814.4375 187436.4375299 64462.09765625 186815.5399 64142.70703125 186251.125499 63835.5 185707.46875599 63535.15234375 185175.1875699 63239.39453125 184650.46875799 62947.08203125 184131.21875899 62657.484375 183616.203125999 62370.171875 183104.671875
通過上述1000個epoch,大體可以推斷該模型不會很好的擬合了,所以果斷放棄吧!
當然必須指出的是,上述測試效果只能說明3種方案在該數據集上的表現,而不能代表這一類模型在用于時序預測問題時的性能。實際上,時序預測問題本身就是一個需要具體問題具體分析的場景,沒有放之四海而皆準的好模型,就像“No Free Lunch”一樣!
本文僅是作為時序預測系列推文的一個牛刀小試,后續(xù)將不定期更新其他相關心得和總結。
*博客內容為網友個人發(fā)布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。