使用2D卷積技術進行時間序列預測




本文將展現一種新的時間序列預測方法。python

目標數據集

在這個項目中使用的數據是來自北卡羅來納州夏洛特分校的全球能源預測競賽的數據。您能夠在這裏找到更多信息:http://www.drhongtao.com/gefcom/2017git

你須要知道的是,這些數據是來自能源網絡的各類讀數。咱們的目標是利用這些數據點預測電網的實時能源需求。數據點還包括露點和幹球溫度,由於空調是能源消耗的主力。github

咱們的目標變量是RTDemand(Real Time energy demand):電網的實時能源需求。數據具備清晰的日週期特徵。如下是咱們三天的數據:數組

三天內的每小時數據


在每一個人都在睡覺的半夜裏,咱們的耗電量達到最低。咱們早上醒來,開始工做,當太陽達到峯值強度時,咱們的能量消耗達到了最大值。所以能夠認爲天天的能耗降低與通勤時間相對應。微信

若是咱們再把尺度放大一些,咱們能夠看到清晰的自相關特性和日趨勢。如下是大約3周的數據:網絡


三週內的每小時數據app

咱們還能夠注意到一個更大的季節性趨勢,若是咱們進一步縮小並查看一全年的數據:機器學習


一年內的每小時數據編輯器

由此看見,這是一個至關理想的時間序列數據,能夠對其進行預測。ide

單變量純時間序列預測模型

對於時間序列預測,咱們將須要給定一個目標結果的時間序列。在咱們的例子中,我選擇72小時做爲時間序列的長度。這意味着咱們模型的輸入是72個單獨的數字,表明過去72小時的數據,咱們但願從模型中獲得的目標輸出是它對第73小時的預測。我認爲72小時是一個很好的長度,由於它能夠很好地捕捉當地的趨勢和晝夜循環。

如下是咱們對模型的輸入(連續三天的數據):

 array([
 [12055., 11430., 10966., 10725., 10672., 10852., 11255., 11583.,
         12238., 12877., 13349., 13510., 13492., 13314., 13156., 13364.,
         14632., 15653., 15504., 15088., 14579., 13882., 12931., 11883.,
         10978., 10406., 10089.,  9982., 10031., 10289., 10818., 11444.,
         12346., 13274., 13816., 14103., 14228., 14154., 14055., 14197.,
         15453., 16531., 16410., 15954., 15337., 14347., 13178., 12106.,
         11400., 11059., 10959., 11073., 11485., 12645., 14725., 15863.,
         16076., 16222., 16358., 16362., 16229., 16123., 15976., 16127.,
         17359., 18818., 18724., 18269., 17559., 16383., 14881., 13520.],
       
 [11430., 10966., 10725., 10672., 10852., 11255., 11583., 12238.,
         12877., 13349., 13510., 13492., 13314., 13156., 13364., 14632.,
         15653., 15504., 15088., 14579., 13882., 12931., 11883., 10978.,
         10406., 10089.,  9982., 10031., 10289., 10818., 11444., 12346.,
         13274., 13816., 14103., 14228., 14154., 14055., 14197., 15453.,
         16531., 16410., 15954., 15337., 14347., 13178., 12106., 11400.,
         11059., 10959., 11073., 11485., 12645., 14725., 15863., 16076.,
         16222., 16358., 16362., 16229., 16123., 15976., 16127., 17359.,
         18818., 18724., 18269., 17559., 16383., 14881., 13520., 12630.],
       
 [10966., 10725., 10672., 10852., 11255., 11583., 12238., 12877.,
         13349., 13510., 13492., 13314., 13156., 13364., 14632., 15653.,
         15504., 15088., 14579., 13882., 12931., 11883., 10978., 10406.,
         10089.,  9982., 10031., 10289., 10818., 11444., 12346., 13274.,
         13816., 14103., 14228., 14154., 14055., 14197., 15453., 16531.,
         16410., 15954., 15337., 14347., 13178., 12106., 11400., 11059.,
         10959., 11073., 11485., 12645., 14725., 15863., 16076., 16222.,
         16358., 16362., 16229., 16123., 15976., 16127., 17359., 18818.,
         18724., 18269., 17559., 16383., 14881., 13520., 12630., 12223.]
 ])

輸入數組中的每個數字都是RTDemand的讀數:這個特定的發電站每小時須要多少千瓦的電力。每一個數組中都有72個小時的數據。若是你仔細觀察這3個數組中前8個左右的讀數,你會注意到每個新的數組都是一個向前移動了1小時的序列。所以,這72個長度的輸入數組中的每個數據都表明了最後72小時對這個能源網實時需求的讀數。

咱們須要預測第73小時的需求,因此目標數組格式以下:

 array([[12630.],
        [12223.],
        [12070.]])

須要注意的是,目標數組中的第一個數據是輸入數組中第二個數組的最後一個數據,目標數組中的第二個數據是輸入數組中第三個數組的最後一個數據。也就是說,咱們經過輸入數組中的第一個數組來實現對於目標數組中第一個數據的預測。

數據轉換

一旦咱們加載了數據,接下來咱們須要將其轉換成一個適當的數據集,用於訓練機器學習模型。首先,縮放全部輸入變量。稍後,咱們將討論如何使用數據集的全部12個輸入,但如今將只使用1個變量做爲輸入,以便於介紹本文使用的預測方法。本文不會對目標變量Y進行縮放處理,由於它可使監控模型的進度變得更容易,成本最低。接下來,咱們將把數據分爲一個訓練集和一個測試集:

 from sklearn.preprocessing import StandardScaler
 scaler = StandardScaler()
 X = scaler.fit_transform(X)
 split = int(0.8 * len(X))
 X_train = X[: split - 1]
 X_test = X[split:]
 y_train = y[: split - 1]
 y_test = y[split:]

最後,咱們將使用的模型的輸入是(Samples、Timesteps、Features)。在第一個模型中,咱們只使用時間窗口的目標變量做爲輸入。因此,咱們只有一個輸入特徵Feature。咱們的輸入即爲(Samples,Timesteps)。在進行訓練集和測試集分割以後,咱們如今將對其進行reshape處理:

 X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
 X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))
 X_train.shape
 (61875, 72, 1)

也就是說,第一個模型的輸入數據爲61875個樣本、每一個樣本包含有72小時數據以及1個特徵。

基線模型

首先,咱們創建一個基線模型。咱們的優化函數設置爲均方偏差/均方根偏差。咱們同時也監測R²,不過,若是存在衝突,咱們只使用均方偏差做爲損失函數和優化目標。

對於基線模型,咱們將看到均方偏差和R²的數據狀況。這裏的基準模型實現的功能是猜想時間序列中先前的值。下面是相關代碼:

 # Benchmark model
 prev_val = y_test[0]
 sse = 0
 for n in range(0, len(y_test)-1):
  err = y_test[n] prev_val
  sq_err = err ** 2
  sse = sse + sq_err
  prev_val = y_test[n]
 mse = sse / n
 mse

使用咱們的測試數據集,獲得的平方根偏差是641.54。也就是說,這個基準模型在給定的一小時內會相對於真實狀況相差641.54兆瓦。這是基準模型與實際結果的圖表:


真實數據曲線&基準模型預測曲線

雖然第一個模型很簡單,可是性能上表現出的效果良好。接下來咱們嘗試其它的模型方法。

LSTM預測模型

時間序列數據預測經常使用的模型之一就是LSTM。相對於本文提出的卷積預測模型,它是一個頗有意義的對照模型。LSTM預測的相關代碼以下:

 def basic_LSTM(window_size=5, n_features=1):
  new_model = keras.Sequential()
  new_model.add(tf.keras.layers.LSTM(100,
                input_shape=(window_size, n_features),
                return_sequences=True,
                activation=’relu’))
  new_model.add(tf.keras.layers.Flatten())
  new_model.add(tf.keras.layers.Dense(1500, activation=’relu’))
  new_model.add(tf.keras.layers.Dense(100, activation=’linear’))
  new_model.add(tf.keras.layers.Dense(1))
  new_model.compile(optimizer=」adam」, loss=」mean_squared_error」)
  return new_model
 ls_model = basic_LSTM(window_size=window_size, n_features=X_train.shape[2])
 ls_model.summary()
 Model: "sequential"
 _________________________________________________________________
 Layer (type)                 Output Shape              Param #  
 =================================================================
 lstm (LSTM)                 (None, 72, 100)           40800    
 _________________________________________________________________
 flatten (Flatten)           (None, 7200)              0        
 _________________________________________________________________
 dense (Dense)               (None, 1500)              10801500  
 _________________________________________________________________
 dense_1 (Dense)             (None, 100)               150100    
 _________________________________________________________________
 dense_2 (Dense)             (None, 1)                 101      
 =================================================================
 Total params: 10,992,501
 Trainable params: 10,992,501
 Non-trainable params: 0

經過訓練數據集進行模型訓練,而後經過測試集進行評價:

 ls_model.evaluate(X_test, y_test, verbose=0)
 1174830.0587427279
 from sklearn.metrics import r2_score
 predictions = ls_model.predict(X_test)
 test_r2 = r2_score(y_test, predictions)
 test_r2
 0.8451637094740732

咱們獲得的結果不是很好。具體地說,咱們最終獲得的偏差比以前的基準模型更高。下面的圖表能夠了解它的預測狀況:LSTM預測結果


正如上圖所示,LSTM的預測具備較大的不肯定性。

1D卷積預測方法

另外一種預測時間序列的方法是使用一維卷積模型。1D卷積使用一個過濾窗口並在數據上循環該窗口以產生新的輸出。根據所學的卷積窗參數,它們能夠像移動平均線、方向指示器或模式探測器同樣隨時間變化。


step 1

這裏有一個包含8個元素的數據集,過濾器大小爲4。過濾器中的四個數字是Conv1D層學習的參數。在第一步中,咱們將過濾器的元素乘以輸入數據,並將結果相加以產生卷積輸出。


step 2

在卷積的第二步中,窗口向前移動一個,重複相同的過程以產生第二個輸出。


Last step

這個過程一直持續到窗口到達輸入數據的末尾。在咱們的例子中,一個輸入數據序列是咱們以前設置的72小時的數據。若是咱們添加padding=「same」選項,咱們的輸入數據將在開始和結束處用零進行填充,以保持輸出長度等於輸入長度。上面的演示使用線性激活,這意味着最後一個多色數組是咱們的輸出。可是,咱們能夠在這裏使用一整套激活函數,這些函數將經過一個額外的步驟來運行這個數字。所以,在下面的例子中,將有一個ReLU激活函數應用於最後的輸出,以產生最終的輸出。

下面展現了創建1D卷積模型的相關代碼:

 def basic_conv1D(n_filters=10, fsize=5, window_size=5, n_features=2):
  new_model = keras.Sequential()
  new_model.add(tf.keras.layers.Conv1D(n_filters, fsize, padding=」same」, activation=」relu」, input_shape=(window_size, n_features)))
  # Flatten will take our convolution filters and lay them out end to end so our dense layer can predict based on the outcomes of each
  new_model.add(tf.keras.layers.Flatten())
  new_model.add(tf.keras.layers.Dense(1800, activation=’relu’))
  new_model.add(tf.keras.layers.Dense(100))
  new_model.add(tf.keras.layers.Dense(1))
  new_model.compile(optimizer=」adam」, loss=」mean_squared_error」)
  return new_model

模型狀況以下:

 univar_model = basic_conv1D(n_filters=24, fsize=8, window_size=window_size, n_features=X_train.shape[2])
 univar_model.summary()
 Model: "sequential_1"
 _________________________________________________________________
 Layer (type)                 Output Shape              Param #  
 =================================================================
 conv1d (Conv1D)             (None, 72, 24)            216      
 _________________________________________________________________
 flatten_1 (Flatten)         (None, 1728)              0        
 _________________________________________________________________
 dense_3 (Dense)             (None, 1800)              3112200  
 _________________________________________________________________
 dense_4 (Dense)             (None, 100)               180100    
 _________________________________________________________________
 dense_5 (Dense)             (None, 1)                 101      
 =================================================================
 Total params: 3,292,617
 Trainable params: 3,292,617
 Non-trainable params: 0

注意,這裏有24個卷積窗口,過濾器大小是8。所以,在咱們的例子中,輸入數據將是72小時,這將建立一個大小爲8的窗口,其中將有24個過濾器。由於我使用padding=「same」,每一個過濾器的輸出寬度將是72,就像咱們的輸入數據同樣,而且輸出的數量將是24個卷積數組。最後經過Flatten生成72*24=1728長度的數組。Flatten的工做過程以下圖所示:


Flatten工做示意圖

對比1D卷積模型、LSTM、基線模型的預測損失以下:

顯然1D卷積方法比LSTM更好一些,可是它仍然沒有達到最初的基準模型更好的效果。當咱們看預測效果曲線時,咱們能夠看到這個模型有明顯的誤差:


1D卷積預測效果

添加數據維度

在上面的例子中,咱們只使用咱們想要預測的特性做爲咱們的輸入變量。然而,咱們的數據集有12個可能的輸入變量。咱們能夠將全部的輸入變量疊加起來,而後一塊兒使用它們來進行預測。因爲許多輸入變量與咱們的輸出變量具備中等/較強的相關性,所以使用更多的數據進行更好的預測應該是可能的。


多輸入1D卷積

若是我想把一個不一樣的數據序列疊加到模型中,首先要經過相同的窗口處理過程來生成一組觀測值,每一個觀測值都包含變量的最後72個讀數。例如,若是我想在第1列中添加變量DADemand(日前需求,當前前一天的需求),將對其執行如下操做:

 (DADemand, _) = window_data(gc_df, window_size, 1, 1)
 scaler = StandardScaler()
 DADemand = scaler.fit_transform(DADemand)
 split = int(0.8 * len(X))
 DADemand_train = DADemand[: split 1]
 DADemand_test = DADemand[split:]
 DADemand_test.shape
 (61875, 72, 1)

而後,能夠對全部的12個變量重複這個過程,並將它們堆積成一個單獨的集合,以下所示:

 data_train = np.concatenate((X_train, db_train, dew_train, DADemand_train, DALMP_train, DAEC_train, DACC_train, DAMLC_train, RTLMP_train, RTEC_train, RTCC_train, RTMLC_train), axis=2)
 data_test = np.concatenate((X_test, db_test, dew_test, DADemand_test, DALMP_test, DAEC_test, DACC_test, DAMLC_test, RTLMP_test, RTEC_test, RTCC_test, RTMLC_test), axis=2)
 data_train.shape
 (61875, 72, 12)

至今生成了包含61875個樣本、每個都包含12個不一樣時間序列的72小時單獨讀數的數據集。咱們如今經過一個Conv1D網絡來運行它,看看咱們獲得了什麼結果。若是回顧一下咱們用於建立這些模型的函數,會注意到其中一個變量是特徵feature的數量,所以運行這個新模型的代碼一樣十分簡單。預測偏差結果以下:

模型的性能實際上隨着其餘變量的增長而下降。分析其緣由,多是「模糊」效應(添加更多的數據集每每會「模糊」任何一個特定輸入變化的影響,反而會產生一個不太精確的模型。)。

2D卷積

咱們實際須要的是一個卷積窗口,它能夠查看咱們的模型特徵並找出哪些特徵是有益的。2D卷積能夠實現咱們想要的效果。

在作了一些嘗試以後,本文將使用(1,filter_size)大小的2D卷積窗口,在上圖中,filter_size=3。回到咱們的能源預測問題,咱們有12個特色。爲了讓它進入二維卷積窗口,咱們實際上須要它有4個維度。咱們能夠經過如下方法獲得另外一個維度:

 data_train_wide = data_train.reshape((data_train.shape[0], data_train.shape[1], data_train.shape[2], 1))
 data_test_wide = data_test.reshape((data_test.shape[0], data_test.shape[1], data_test.shape[2], 1))
 data_train_wide.shape
 (61875, 72, 12, 1)

測試了不一樣的窗口尺寸事後,咱們發現一次考慮兩個特徵效果最好:

 def basic_conv2D(n_filters=10, fsize=5, window_size=5, n_features=2):
  new_model = keras.Sequential()
  new_model.add(tf.keras.layers.Conv2D(n_filters, (1,fsize), padding=」same」, activation=」relu」, input_shape=(window_size, n_features, 1)))
  new_model.add(tf.keras.layers.Flatten())
  new_model.add(tf.keras.layers.Dense(1000, activation=’relu’))
  new_model.add(tf.keras.layers.Dense(100))
  new_model.add(tf.keras.layers.Dense(1))
  new_model.compile(optimizer=」adam」, loss=」mean_squared_error」)
  return new_model
 m2 = basic_conv2D(n_filters=24, fsize=2, window_size=window_size, n_features=data_train_wide.shape[2])
 m2.summary()
 Model: "sequential_4"
 _________________________________________________________________
 Layer (type)                 Output Shape              Param #  
 =================================================================
 conv2d (Conv2D)             (None, 72, 12, 24)        72        
 _________________________________________________________________
 flatten_4 (Flatten)         (None, 20736)             0        
 _________________________________________________________________
 dense_12 (Dense)             (None, 1000)              20737000  
 _________________________________________________________________
 dense_13 (Dense)             (None, 100)               100100    
 _________________________________________________________________
 dense_14 (Dense)             (None, 1)                 101      
 =================================================================
 Total params: 20,837,273
 Trainable params: 20,837,273
 Non-trainable params: 0

這個模型至關大。在普通CPU上訓練每個epoch大約須要4分鐘。不過,當它完成後,預測效果以下圖:


與其餘模型對比預測偏差:


能夠看到,2D卷積的效果優於其它全部的預測模型。

補充

若是咱們使用相似的想法,但同時用尺寸爲(8,1)的濾波器進行卷積運算呢?相關代碼以下:

 def deeper_conv2D(n_filters=10, fsize=5, window_size=5, n_features=2, hour_filter=8):
  new_model = keras.Sequential()
  new_model.add(tf.keras.layers.Conv2D(n_filters, (1,fsize), padding=」same」, activation=」linear」, input_shape=(window_size, n_features, 1)))
  new_model.add(tf.keras.layers.Conv2D(n_filters, (hour_filter, 1), padding=」same」, activation=」relu」))
  new_model.add(tf.keras.layers.Flatten())
  new_model.add(tf.keras.layers.Dense(1000, activation=’relu’))
  new_model.add(tf.keras.layers.Dense(100))
  new_model.add(tf.keras.layers.Dense(1))
  new_model.compile(optimizer=」adam」, loss=」mean_squared_error」)
  return new_model

模型預測性能表現很好:


模型預測偏差也進一步下降:


本文全部代碼和數據能夠在這裏直接下載:

https://github.com/walesdata/2Dconv_pub

做者:Johnny Wales

deephub翻譯組:oliver lee


DeepHub

微信號 : deephub-imba

每日大數據和人工智能的重磅乾貨

大廠職位內推信息

長按識別二維碼關注 ->

喜歡就請三連暴擊!    


本文分享自微信公衆號 - DeepHub IMBA(deephub-imba)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索