使用Keras和LSTM實現對於長期趨勢記憶的時間序列預測-LSTNet

各位好,好久不見。我終於忙完論文和答辯啦。
今天我們來實現長期趨勢的多維度時間序列預測
同時會提供一個完整的預測流程以及相關的評價指標,用於評價預測精確度。
算法來源於一篇經典的論文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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章