本教程介紹了使用遞歸神經網絡(RNN)進行時間序列預測的流程。包括兩個部分:首先,預測單變量時間序列,然後預測多變量時間序列。
文章目錄
代碼環境:
python version: 3.7.6
tensorflow version: 2.1.0
導入必要的包
import tensorflow as tf
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
mpl.rcParams['figure.figsize'] = (8, 6)
mpl.rcParams['axes.grid'] = False
1. 數據集介紹
使用 Max Planck Institute for Biogeochemistry 的天氣時間序列數據集
。
該數據集包含14個不同的特徵,例如氣溫,大氣壓力和溼度。從2003年開始,每10分鐘收集一次。爲了提高效率,本文僅使用2009年至2016年之間收集的數據。
1.下載數據集
zip_path = tf.keras.utils.get_file(
origin='https://storage.googleapis.com/tensorflow/tf-keras-datasets/jena_climate_2009_2016.csv.zip',
fname='jena_climate_2009_2016.csv.zip',
extract=True)
csv_path, _ = os.path.splitext(zip_path)
2.加載數據集
df = pd.read_csv(csv_path)
df.head()
如上所示,每10分鐘記錄一次觀測值,一個小時內有6個觀測值,一天有144(6x24)個觀測值。
給定一個特定的時間,假設要預測未來6小時的溫度。爲了做出此預測,選擇使用5天的觀察時間。因此,創建一個包含最後720(5x144)個觀測值的窗口以訓練模型。
下面的函數返回上述時間窗以供模型訓練。參數 history_size
是過去信息的滑動窗口大小。target_size
是模型需要學習預測的未來時間步,也作爲需要被預測的標籤。
下面使用數據的前300,000行當做訓練數據集,其餘的作爲驗證數據集。總計約2100天的訓練數據。
TRAIN_SPLIT = 300000
設置種子以確保可重複性。
tf.random.set_seed(13)
2. 單變量單步輸出預測
首先,使用一個特徵(溫度)訓練模型,並在使用該模型做預測。
1.從數據集中提取溫度
uni_data = df['T (degC)']
uni_data.index = df['Date Time']
uni_data.head()
2.觀察一下這些數據隨時間變化的情況
uni_data.plot(subplots=True)
2.1 數據標準化
在訓練神經網絡之前縮放特徵很重要。標準化是通過減去平均值併除以每個特徵的標準偏差來進行縮放的一種常用方法。也可以使用 tf.keras.utils.normalize
將值縮放到[0,1]範圍內。
uni_data = uni_data.values
uni_train_mean = uni_data[:TRAIN_SPLIT].mean()
uni_train_std = uni_data[:TRAIN_SPLIT].std()
uni_data = (uni_data-uni_train_mean)/uni_train_std
2.2 樣本劃分
現在爲單變量模型創建數據。本部分爲模型提供最後20個溫度觀測值,預測下一個時間步長的溫度。
univariate_past_history = 20
univariate_future_target = 0
# shape 分別爲:(299980, 20, 1);(299980,)
x_train_uni, y_train_uni = univariate_data(uni_data, 0, TRAIN_SPLIT,
univariate_past_history,
univariate_future_target)
x_val_uni, y_val_uni = univariate_data(uni_data, TRAIN_SPLIT, None,
univariate_past_history,
univariate_future_target)
輸出:
Single window of past history
[[-1.99766294]
[-2.04281897]
[-2.05439744]
[-2.0312405 ]
[-2.02660912]
[-2.00113649]
[-1.95134907]
[-1.95134907]
[-1.98492663]
[-2.04513467]
[-2.08334362]
[-2.09723778]
[-2.09376424]
[-2.09144854]
[-2.07176515]
[-2.07176515]
[-2.07639653]
[-2.08913285]
[-2.09260639]
[-2.10418486]]
Target temperature to predict
-2.1041848598100876
2.3 繪製曲線
現在已經創建了數據,看一個例子。提供給網絡的信息以藍色表示,並且它必須預測紅叉處的值。
def create_time_steps(length):
return list(range(-length, 0))
def show_plot(plot_data, delta, title):
labels = ['History', 'True Future', 'Model Prediction']
marker = ['.-', 'rx', 'go']
time_steps = create_time_steps(plot_data[0].shape[0]) # 橫軸刻度
if delta:
future = delta
else:
future = 0
plt.title(title)
for i, x in enumerate(plot_data):
if i:
plt.plot(future, plot_data[i], marker[i], markersize=10,
label=labels[i])
else:
plt.plot(time_steps, plot_data[i].flatten(), marker[i], label=labels[i])
plt.legend()
plt.xlim([time_steps[0], (future+5)*2])
plt.xlabel('Time-Step')
return plt
2.4 基準測試
在繼續訓練模型之前,先設置一個簡單的基準。在給定輸入點的情況下,基線方法將查看所有歷史記錄,並預測下一個點是最近20個觀測值的平均值。
def baseline(history):
return np.mean(history)
show_plot([x_train_uni[0], y_train_uni[0], baseline(x_train_uni[0])], 0,
'Baseline Prediction Example')
2.5 LSTM 建模
遞歸神經網絡(RNN)是一種非常適合時間序列數據的神經網絡。RNN分步處理時間序列,維護內部狀態,以彙總所見信息。
使用 tf.data
來隨機整理,批處理和緩存數據集。
BATCH_SIZE = 256
BUFFER_SIZE = 10000
train_univariate = tf.data.Dataset.from_tensor_slices((x_train_uni, y_train_uni))
train_univariate = train_univariate.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()
val_univariate = tf.data.Dataset.from_tensor_slices((x_val_uni, y_val_uni))
val_univariate = val_univariate.batch(BATCH_SIZE).repeat()
批處理後的數據shape:
simple_lstm_model = tf.keras.models.Sequential([
tf.keras.layers.LSTM(8, input_shape=x_train_uni.shape[-2:]),
tf.keras.layers.Dense(1)
])
simple_lstm_model.compile(optimizer='adam', loss='mae')
讓我們現在訓練模型。由於數據集的大小很大,爲了節省時間,每個epoch將僅使用200個step(batch_size=256,所以一個epoch使用 200 * 256 個數據),而不是像通常那樣遍歷完整的訓練數據。
EVALUATION_INTERVAL = 200
EPOCHS = 10
simple_lstm_model.fit(train_univariate, epochs=EPOCHS,
steps_per_epoch=EVALUATION_INTERVAL,
validation_data=val_univariate, validation_steps=50)
預測:
for x, y in val_univariate.take(3):
plot = show_plot([x[0].numpy(), y[0].numpy(),
simple_lstm_model.predict(x)[0]], 0, 'Simple LSTM model')
plot.show()
由以上可知,看起來比基線更好。既然已經瞭解了基礎知識,繼續第二部分,使用多元時間序列預測。
3. 多變量多步輸出預測
原始數據集包含十四個特徵。爲簡單起見,本節僅考慮原始的十四個中的三個。使用的功能是氣溫,大氣壓力和空氣密度。
要使用更多特徵,將其名稱添加到此列表中即可。
features_considered = ['p (mbar)', 'T (degC)', 'rho (g/m**3)']
查看數據:
features = df[features_considered]
features.index = df['Date Time']
features.head()
查看特徵隨時間的變化
features.plot(subplots=True)
3.1 數據標準化
dataset = features.values
data_mean = dataset[:TRAIN_SPLIT].mean(axis=0)
data_std = dataset[:TRAIN_SPLIT].std(axis=0)
dataset = (dataset-data_mean)/data_std
3.2 單步預測模型
單步預測任務中,模型將根據提供的某些歷史記錄來學習預測未來的單個點。
下面的函數實現了滑動窗口截取數據,它根據給定的窗口寬度對過去的觀測值進行採樣。
def multivariate_data(dataset, target, start_index, end_index, history_size,
target_size, step, single_step=False):
data = []
labels = []
start_index = start_index + history_size
if end_index is None:
end_index = len(dataset) - target_size
for i in range(start_index, end_index):
indices = range(i-history_size, i, step) # step表示間隔採樣步長,6表示每個小時只使用一個採樣值(原數據集每10分鐘採集一次)
data.append(dataset[indices])
if single_step:
labels.append(target[i+target_size])
else:
labels.append(target[i:i+target_size])
return np.array(data), np.array(labels)
在本教程中,輸入給網絡最近五天的數據,即720個觀測值。每隔一小時進行一次重採樣,因爲60分鐘內不會發生劇烈變化。因此,120個重採樣值表示最近五天的溫度狀況。對於單步預測模型,數據點的標籤是未來12小時的溫度。所以,使用之後72(12 * 6)個觀測值作爲標籤。
past_history = 720
future_target = 72
STEP = 6
x_train_single, y_train_single = multivariate_data(dataset, dataset[:, 1], 0,
TRAIN_SPLIT, past_history,
future_target, STEP,
single_step=True)
x_val_single, y_val_single = multivariate_data(dataset, dataset[:, 1],
TRAIN_SPLIT, None, past_history,
future_target, STEP,
single_step=True)
劃分數據集:
train_data_single = tf.data.Dataset.from_tensor_slices((x_train_single, y_train_single))
train_data_single = train_data_single.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()
val_data_single = tf.data.Dataset.from_tensor_slices((x_val_single, y_val_single))
val_data_single = val_data_single.batch(BATCH_SIZE).repeat()
訓練:
single_step_model = tf.keras.models.Sequential()
single_step_model.add(tf.keras.layers.LSTM(32,
input_shape=x_train_single.shape[-2:]))
single_step_model.add(tf.keras.layers.Dense(1))
single_step_model.compile(optimizer=tf.keras.optimizers.RMSprop(), loss='mae')
single_step_history = single_step_model.fit(train_data_single, epochs=EPOCHS,
steps_per_epoch=EVALUATION_INTERVAL,
validation_data=val_data_single,
validation_steps=50)
繪製損失曲線:
def plot_train_history(history, title):
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(loss))
plt.figure()
plt.plot(epochs, loss, 'b', label='Training loss')
plt.plot(epochs, val_loss, 'r', label='Validation loss')
plt.title(title)
plt.legend()
plt.show()
測試
該模型具有每小時過去5天採樣的三個特徵的歷史記錄(120個數據點),因爲目標是預測溫度,所以該圖僅顯示過去的溫度。預測是在未來一天進行的(因此,歷史記錄和預測之間存在差距)。
for x, y in val_data_single.take(3):
plot = show_plot([x[0][:, 1].numpy(), y[0].numpy(),
single_step_model.predict(x)[0]], 12,
'Single Step Prediction')
plot.show()
3.3 多步預測模型
在多步驟預測模型中,給定過去的採樣值,預測未來一系列的值。對於多步驟模型,訓練數據再次包括每小時採樣的過去五天的記錄。但是,這裏的模型需要學習預測接下來12小時的溫度。由於每10分鐘採樣一次數據,因此輸出爲72個預測值。
future_target = 72
x_train_multi, y_train_multi = multivariate_data(dataset, dataset[:, 1], 0,
TRAIN_SPLIT, past_history,
future_target, STEP)
x_val_multi, y_val_multi = multivariate_data(dataset, dataset[:, 1],
TRAIN_SPLIT, None, past_history,
future_target, STEP)
劃分數據集
train_data_multi = tf.data.Dataset.from_tensor_slices((x_train_multi, y_train_multi))
train_data_multi = train_data_multi.cache().shuffle(BUFFER_SIZE).batch(BATCH_SIZE).repeat()
val_data_multi = tf.data.Dataset.from_tensor_slices((x_val_multi, y_val_multi))
val_data_multi = val_data_multi.batch(BATCH_SIZE).repeat()
繪製樣本點數據
def multi_step_plot(history, true_future, prediction):
plt.figure(figsize=(12, 6))
num_in = create_time_steps(len(history))
num_out = len(true_future)
plt.plot(num_in, np.array(history[:, 1]), label='History')
plt.plot(np.arange(num_out)/STEP, np.array(true_future), 'bo',
label='True Future')
if prediction.any():
plt.plot(np.arange(num_out)/STEP, np.array(prediction), 'ro',
label='Predicted Future')
plt.legend(loc='upper left')
plt.show()
for x, y in train_data_multi.take(1):
multi_step_plot(x[0], y[0], np.array([0]))
由於此處的任務比先前的任務複雜一些,因此該模型現在由兩個LSTM層組成。最後,由於需要預測之後12個小時的數據,因此Dense層將輸出爲72。
multi_step_model = tf.keras.models.Sequential()
multi_step_model.add(tf.keras.layers.LSTM(32,
return_sequences=True,
input_shape=x_train_multi.shape[-2:]))
multi_step_model.add(tf.keras.layers.LSTM(16, activation='relu'))
multi_step_model.add(tf.keras.layers.Dense(72))
multi_step_model.compile(optimizer=tf.keras.optimizers.RMSprop(clipvalue=1.0), loss='mae')
訓練:
multi_step_history = multi_step_model.fit(train_data_multi, epochs=EPOCHS,
steps_per_epoch=EVALUATION_INTERVAL,
validation_data=val_data_multi,
validation_steps=50)
訓練日誌:
Epoch 1/10
200/200 [==============================] - 73s 363ms/step - loss: 0.5750 - val_loss: 0.3137
Epoch 2/10
200/200 [==============================] - 73s 367ms/step - loss: 0.4429 - val_loss: 0.2951
Epoch 3/10
200/200 [==============================] - 79s 393ms/step - loss: 0.3839 - val_loss: 0.2793
Epoch 4/10
200/200 [==============================] - 85s 426ms/step - loss: 0.3062 - val_loss: 0.2240
Epoch 5/10
200/200 [==============================] - 98s 488ms/step - loss: 0.2162 - val_loss: 0.2058
Epoch 6/10
200/200 [==============================] - 103s 514ms/step - loss: 0.2185 - val_loss: 0.2108
Epoch 7/10
200/200 [==============================] - 107s 537ms/step - loss: 0.2061 - val_loss: 0.2055
Epoch 8/10
200/200 [==============================] - 108s 539ms/step - loss: 0.2016 - val_loss: 0.1971
Epoch 9/10
200/200 [==============================] - 92s 462ms/step - loss: 0.2047 - val_loss: 0.1969
Epoch 10/10
200/200 [==============================] - 98s 490ms/step - loss: 0.1937 - val_loss: 0.1833
繪製損失曲線:
預測: