各位好,好久不見。我終於忙完論文和答辯啦。
今天我們來實現長期趨勢的多維度時間序列預測
同時會提供一個完整的預測流程以及相關的評價指標,用於評價預測精確度。
算法來源於一篇經典的論文LSTNet,相關的介紹可以見 LSTNet詳解-知乎
開源代碼來源於 LSTNet_keras ,做了替換數據集和簡化處理。’
LSTNet是一個專門爲多變量時間序列預測所建立的模型,在交通流量,電力消耗和匯率等數據上進行了實驗,取得了不錯的結果。發表於2018年的ACM SIGIR會議上。
數據介紹
該數據集是一個污染數據集,我們需要用該多維時間序列去預測pollution這個維度,採用80%作爲訓練集,20%作爲測試集。
pollution數據趨勢如下:
模型介紹
LSTNet的網絡結構如圖所示
我們可以看到使用了一個卷積層 兩層循環神經網絡(論文中使用了RNN或GRU,本文中使用了LSTM),可以看到第二層圖上實現了一個稱爲"跳過層"的結構,用於實現對於非常長期趨勢的記憶。但是其實是進行了數據變換而非LSTM結構的更改。
對於跳過層,例如輸入數據[1,2,3,4,5,6,7,8,9,10,11,12],會進行一系列的數據變換變換爲
[[1,7] , [2,8] , [3,9] , [4,10] , [5,11] , [6,12]],然後輸入到LSTM之中,實現對於長期趨勢的記憶。之後綜合兩層LSTM的結果,輸入到全連接層之中。
對於Autogressive,是使用了全連接層模擬的自迴歸機制,會截取進幾個時間步的數據,輸入到全連接層的機制。得到結果。論文中稱爲"爲模型添加了線性成分",實際在預測某些峯值時有很好的效果。
模型實現
對於原模型而言 對於第二個跳過層的實現需要大量的數據切片,會非常耗時
但是本文參考了LSTNet_keras 將輸入分解爲(1)短期時間序列,如(t-3, t-2, t-1, t)和(2)長期跳躍時間序列,如(t-2xskip, t-skip, t)。結果和原來的一樣好,但是快得多。
數據構造
數據構造採用瞭如下的代碼,具體如何使用見文末的github源代碼
def create_dataset(dataset, look_back,skip):
'''
對數據進行處理
'''
dataX,dataX2,dataY = [],[],[]
#len(dataset)-1 不必要 但是可以避免某些狀況下的bug
for i in range(look_back*skip,len(dataset)-1):
dataX.append(dataset[(i-look_back):i,:])
dataY.append(dataset[i, :])
temp=[]
for j in range(i-look_back*skip,i,skip):
temp.append(dataset[j,:])
dataX2.append(temp)
TrainX = np.array(dataX)
TrainX2 = np.array(dataX2)
TrainY = np.array(dataY)
return TrainX, TrainX2 , TrainY
模型代碼
對於初始的LSTNet而言,只使用單個一維卷積對於數據進行處理,之後再進行數據變換。但是對於本簡化版而言,在構造數據時進行了數據變換。所以需要兩個一維卷積,然後對他們賦予了相同的權重。
模型中的z指的是AR模型的實現
def LSTNet(trainX1,trainX2,trainY,config):
input1 = Input(shape=(trainX1.shape[1], trainX1.shape[2]))
conv1 = Conv1D(filters=48, kernel_size=6, strides=1, activation='relu') # for input1
# It's a probelm that I can't find any way to use the same Conv1D layer to train the two inputs,
conv2 = Conv1D(filters=48, kernel_size=6 , strides=1, activation='relu') # for input2
conv2.set_weights(conv1.get_weights()) # at least use same weight
conv1out = conv1(input1)
lstm1out = CuDNNLSTM(64)(conv1out)
lstm1out = Dropout(config.dropout)(lstm1out)
input2 = Input(shape=(trainX2.shape[1], trainX2.shape[2]))
conv2out = conv2(input2)
lstm2out = CuDNNLSTM(64)(conv2out)
lstm2out = Dropout(config.dropout)(lstm2out)
lstm_out = concatenate([lstm1out,lstm2out])
output = Dense(trainY.shape[1])(lstm_out)
#highway 使用Dense模擬AR自迴歸過程,爲預測添加線性成份,同時使輸出可以響應輸入的尺度變化。
highway_window = config.highway_window
#截取近3個窗口的時間維 保留了所有的輸入維度
z = Lambda(lambda k: k[:, -highway_window:, :])(input1)
z = Lambda(lambda k: K.permute_dimensions(k, (0, 2, 1)))(z)
z = Lambda(lambda k: K.reshape(k, (-1, highway_window*trainX1.shape[2])))(z)
z = Dense(trainY.shape[1])(z)
output = add([output,z])
output = Activation('sigmoid')(output)
model = Model(inputs=[input1,input2], outputs=output)
return model
模型結構如圖,
進行預測
我們選取前80%的數據進行訓練,後20%的數據進行預測,預測下一時刻的pollution數據。
data = pd.read_csv("./pollution.csv")
#注:爲了演示方便故不使用wnd_dir,其實可以通過代碼將其轉換爲數字序列
data = data.drop(['wnd_dir'], axis = 1)
data = data.iloc[:int(0.8*data.shape[0]),:]
print("長度爲",data.shape[0])
評價指標
選取的評價指標爲RMSE,MAE,MAPE
import numpy as np
from sklearn import metrics
def GetRMSE(y_hat,y_test):
sum = np.sqrt(metrics.mean_squared_error(y_test, y_hat))
return sum
def GetMAE(y_hat,y_test):
sum = metrics.mean_absolute_error(y_test, y_hat)
return sum
def GetMAPE(y_hat,y_test):
sum = np.mean(np.abs((y_hat - y_test) / y_test)) * 100
return sum
預測結果:
由於y_test有爲0的元素,所以我們將其刪除再求MAPE
得到結果如下:
RMSE爲 26.184022062997542
MAE爲 13.882745963353731
MAPE爲 22.928112428670353
總結
在本博客中,提供了一套完整的建模-預測-評價方法,是現成可用的
實現了一種對於長期趨勢記憶的方法
預測精度仍然有進步空間(有許多原因,筆者在大量數據上使用該方法預測效果很好)
注:
代碼已上傳到我的github
如果覺得不錯的話可以去github點個星星(看在我租服務器跑實驗的份上)
參考:
LSTNet_keras
LSTNet