本文介紹了LSTM網絡中的TimeDistributed包裝層,代碼演示了具有TimeDistributed層的LSTM網絡配置方法。
演示了一對一,多對一,多對多,三種不同的預測方法如何配置。
在對多對一預測中用了不配置TimeDistributed的方法,在多對多預測中使用了TimeDistributed層。
對代碼的解析在代碼注釋中
源碼地址:
https://github.com/yangwohenmai/LSTM/tree/master/%E9%95%BF%E7%9F%AD%E6%9C%9F%E8%AE%B0%E5%BF%86(LSTM)/LSTM%E7%9A%84%E7%89%B9%E6%80%A7/%E8%BE%93%E5%85%A5%E8%BE%93%E5%87%BA%E5%AF%B9%E5%92%8CTimeDistributed
正文:
長期短期網絡或LSTM是一種流行且功能強大的遞歸神經網絡或RNN。
它們可能很難配置並應用於任意序列預測問題,即使使用定義良好且“易於使用”的接口,如Python中的Keras深度學習庫中提供的那些接口也是如此。
在Keras中遇到這種困難的一個原因是使用了TimeDistributed包裝層,並且需要一些LSTM層來返回序列而不是單個值。
在本教程中,您將發現為序列預測配置LSTM網絡的不同方法,TimeDistributed層所扮演的角色以及如何使用它。
TimeDistributed 包裝層
LSTM功能強大,但難以使用且難以配置。
你應該如何以及何時使用LSTM的這個包裝器?
本教程旨在消除使用帶有LSTM的TimeDistributed包裝器的混亂,以及可以檢查,運行和使用的工作示例,以幫助您進行具體的理解。
序列學習問題
我們將使用一個簡單的序列學習問題來演示TimeDistributed層。
在這個問題中,序列[0.0,0.2,0.4,0.6,0.8]將一次作為輸入一個項目給出,並且必須依次作為輸出返回,一次一個項目。
可以把它想象成一個簡單的回聲程序。我們給出0.0作為輸入,我們期望看到0.0作為輸出,對序列中的每個項重復。
我們可以直接生成這個序列如下:
from numpy import array length = 5 seq = array([i/float(length) for i in range(length)]) print(seq)
運行此示例將打印生成的序列:
[ 0. 0.2 0.4 0.6 0.8]
該示例是可配置的,如果您願意,您可以稍后自己玩更長/更短的序列。請在評論中告訴我您的結果。
用於序列預測的一對一LSTM
在我們深入研究之前,重要的是要表明這種序列學習問題可以分段學習。
也就是說,我們可以將問題重新構造為序列中每個項目的輸入 - 輸出對的數據集。給定0,網絡應輸出0,給定0.2,網絡必須輸出0.2,依此類推。
這是問題的最簡單的表述,並且要求將序列分成輸入 - 輸出對,並且序列一次一步地預測並聚集在網絡外部。
輸入輸出對如下:
X, y
0.0, 0.0
0.2, 0.2
0.4, 0.4
0.6, 0.6
0.8, 0.8
LSTM的輸入必須是三維的。我們可以將2D序列重新整形為具有5個樣本,1個時間步長和1個特征的3D序列。我們將輸出定義為具有1個特征的5個樣本。
X = seq.reshape(5, 1, 1)
y = seq.reshape(5, 1)
我們將網絡模型定義為具有1個輸入和1個時間步長。第一個隱藏層將是一個有5個單位的LSTM。輸出層是一個帶有1個輸出的全連接層。
該模型將符合有效的ADAM優化算法和均方誤差損失函數。
批量大小設置為時期中的樣本數量,以避免必須使LSTM有狀態並手動管理狀態重置,盡管這可以很容易地完成,以便在每個樣本顯示到網絡后更新權重。
完整的代碼清單如下:
from numpy import array from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM # prepare sequence length = 5 seq = array([i/float(length) for i in range(length)]) # 定義輸入為:5個樣本,一個步長,一個特征值 """ [[[0. ]] [[0.2]] [[0.4]] [[0.6]] [[0.8]]] """ X = seq.reshape(5, 1, 1) print(X) # 定義輸出為:5個樣本,每個樣本1個特征值 """ [[0. ] [0.2] [0.4] [0.6] [0.8]] """ y = seq.reshape(5, 1) print(y) # define LSTM configuration n_neurons = 5 n_batch = 5 # create LSTM model = Sequential() # 輸入類型為1個步長和1個特征值 model.add(LSTM(5, input_shape=(1, 1))) model.add(Dense(1)) model.compile(loss='mean_squared_error', optimizer='adam') print(model.summary()) # train LSTM model.fit(X, y, epochs=1000, batch_size=n_batch, verbose=2) # evaluate result = model.predict(X, batch_size=n_batch, verbose=0) for value in result: print('%.1f' % value)
首先運行該示例將打印已配置網絡的結構。
我們可以看到LSTM層有140個參數。這是根據輸入數量(1)和輸出數量(隱藏層中5個單位為5)計算的,如下所示:
n = 4 * ((inputs + 1) * outputs + outputs^2) n = 4 * ((1 + 1) * 5 + 5^2) n = 4 * 35 n = 140
我們還可以看到,完全連接的層只有6個參數用於輸入數量(5個用於前一層的5個輸入),輸出數量(1個用於圖層中的1個神經元)和偏差。
n = inputs * outputs + outputs n = 5 * 1 + 1 n = 6 _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= lstm_1 (LSTM) (None, 1, 5) 140 _________________________________________________________________ dense_1 (Dense) (None, 1, 1) 6 ================================================================= Total params: 146.0 Trainable params: 146 Non-trainable params: 0.0 _________________________________________________________________
網絡正確地學習了預測問題。
0.0 0.2 0.4 0.6 0.8
用於序列預測的多對一LSTM(沒有TimeDistributed)
在本節中,我們開發了一個LSTM來同時輸出序列,盡管沒有TimeDistributed包裝層。
LSTM的輸入必須是三維的。我們可以將2D序列重塑為具有1個樣本,5個時間步長和1個特征的3D序列。我們將輸出定義為具有5個特征的1個樣本。
X = seq.reshape(1, 5, 1)
y = seq.reshape(1, 5)
您可以立即看到,必須稍微調整問題定義,以便在沒有TimeDistributed包裝器的情況下支持網絡進行序列預測。具體來說,輸出一個向量而不是一次一步地構建輸出序列。差異可能聽起來很微妙,但了解TimeDistributed包裝器的作用非常重要。
我們將模型定義為具有5個時間步長的一個輸入。第一個隱藏層將是一個有5個單位的LSTM。輸出層是一個完全連接的層,有5個神經元。
# create LSTM model = Sequential() model.add(LSTM(5, input_shape=(5, 1))) model.add(Dense(length)) model.compile(loss='mean_squared_error', optimizer='adam') print(model.summary())
接下來,我們將模型僅適用於訓練數據集中的單個樣本的500個歷元和批量大小為1。
# train LSTM model.fit(X, y, epochs=500, batch_size=1, verbose=2)
綜合這些,下面提供了完整的代碼清單。
from numpy import array from keras.models import Sequential from keras.layers import Dense from keras.layers import LSTM # prepare sequence # 輸入為1個樣本,5個步長,1個特征值 length = 5 seq = array([i/float(length) for i in range(length)]) """ [[[0. ] [0.2] [0.4] [0.6] [0.8]]] """ X = seq.reshape(1, 5, 1) print(X) # 輸出為五個特征值的一個樣本 """ [[0. 0.2 0.4 0.6 0.8]] """ y = seq.reshape(1, 5) print(y) # define LSTM configuration n_neurons = length n_batch = 1 # create LSTM model = Sequential() # 輸入類型為5個步長,1個特征值 model.add(LSTM(5, input_shape=(5, 1))) model.add(Dense(5)) model.compile(loss='mean_squared_error', optimizer='adam') print(model.summary()) # train LSTM model.fit(X, y, epochs=500, batch_size=n_batch, verbose=2) # evaluate result = model.predict(X, batch_size=n_batch, verbose=0) for value in result[0,:]: print('%.1f' % value)
首先運行該示例將打印已配置網絡的摘要。
我們可以看到LSTM層有140個參數,如上一節所述。
LSTM單元已經癱瘓,每個單元都輸出一個值,提供5個值的矢量作為完全連接層的輸入。時間維度或序列信息已被丟棄並折疊成5個值的向量。
我們可以看到完全連接的輸出層有5個輸入,預計輸出5個值。我們可以按如下方式計算要學習的30個權重:
n = inputs * outputs + outputs n = 5 * 5 + 5 n = 30
網絡摘要報告如下:
________________________________________________________________ Layer (type) Output Shape Param # ================================================================= lstm_1 (LSTM) (None, 5) 140 _________________________________________________________________ dense_1 (Dense) (None, 5) 30 ================================================================= Total params: 170.0 Trainable params: 170 Non-trainable params: 0.0 _________________________________________________________________
該模型適合,在最終確定和打印預測序列之前打印損失信息。
序列被正確再現,但是作為單個部分而不是逐步通過輸入數據。我們可能使用Dense層作為第一個隱藏層而不是LSTM,因為LSTM的這種使用並沒有充分利用它們完整的序列學習和處理能力。
0.0 0.2 0.4 0.6 0.8
用於序列預測的多對多LSTM(具有TimeDistributed)
在本節中,我們將使用TimeDistributed層來處理LSTM隱藏層的輸出。
使用TimeDistributed包裝層時需要記住兩個關鍵點:
- 輸入必須(至少)為3D。這通常意味着您需要在TimeDistributed wrapped Dense層之前配置最后一個LSTM層以返回序列(例如,將“return_sequences”參數設置為“True”)。
- 輸出將是3D。這意味着如果TimeDistributed wrapped Dense圖層是輸出圖層並且您正在預測序列,則需要將y數組的大小調整為3D矢量。
我們可以將輸出的形狀定義為具有1個樣本,5個時間步長和1個特征,就像輸入序列一樣,如下所示:
y = seq.reshape(1, length, 1)
我們可以通過將“ return_sequences ”參數設置為true 來定義LSTM隱藏層以返回序列而不是單個值。
model.add(LSTM(n_neurons, input_shape=(length, 1), return_sequences=True))
這具有每個LSTM單元返回5個輸出序列的效果,輸出數據中的每個時間步長一個,而不是如前一示例中的單個輸出值。
我們還可以使用輸出層上的TimeDistributed來將完全連接的Dense圖層包裝為單個輸出。
model.add(TimeDistributed(Dense(1)))
輸出層中的單個輸出值是關鍵。它強調我們打算從輸入中的每個時間步的序列輸出一個時間步。碰巧我們將一次處理輸入序列的5個時間步。
TimeDistributed通過一次一個步驟將相同的Dense層(相同的權重)應用於LSTM輸出來實現此技巧。這樣,輸出層只需要一個連接到每個LSTM單元(加上一個偏置)。
因此,需要增加訓練時期的數量以考慮較小的網絡容量。我將它從500加倍到1000,以匹配第一個一對一的例子。
將它們放在一起,下面提供了完整的代碼清單。
from numpy import array from keras.models import Sequential from keras.layers import Dense from keras.layers import TimeDistributed from keras.layers import LSTM # prepare sequence length = 5 seq = array([i/float(length) for i in range(length)]) # 輸入為1個樣本,5個步長,1個特征值 X = seq.reshape(1, 5, 1) # 輸出為1個樣本,5個步長,1個特征值 y = seq.reshape(1, 5, 1) # define LSTM configuration n_batch = 1 # create LSTM model = Sequential() # 輸入類型為5個步長和1個特征值,return_sequences=True返回整個序列 model.add(LSTM(5, input_shape=(5, 1), return_sequences=True)) model.add(TimeDistributed(Dense(1))) model.compile(loss='mean_squared_error', optimizer='adam') print(model.summary()) # train LSTM model.fit(X, y, epochs=1000, batch_size=n_batch, verbose=2) # evaluate result = model.predict(X, batch_size=n_batch, verbose=0) for value in result[0,:,0]: print('%.1f' % value)
運行該示例,我們可以看到已配置網絡的結構。
我們可以看到,與前面的示例一樣,LSTM隱藏層中有140個參數。
完全連接的輸出層是一個非常不同的故事。實際上,它完全符合一對一的例子。一個神經元,對於前一層中的每個LSTM單元具有一個權重,加上一個用於偏置輸入。
這有兩個重要的事情:
- 允許問題在定義時被構建和學習,即一個輸出到一個輸出,保持每個時間步的內部過程分開。
- 通過要求更少的權重來簡化網絡,使得一次只處理一個時間步長。
將一個更簡單的完全連接層應用於從前一層提供的序列中的每個時間步,以構建輸出序列。
_________________________________________________________________ Layer (type) Output Shape Param # ================================================================= lstm_1 (LSTM) (None, 5, 5) 140 _________________________________________________________________ time_distributed_1 (TimeDist (None, 5, 1) 6 ================================================================= Total params: 146.0 Trainable params: 146 Non-trainable params: 0.0 _________________________________________________________________
同樣,網絡學習序列。
0.0 0.2 0.4 0.6 0.8
我們可以將時間步長問題框架和TimeDistributed層視為在第一個示例中實現一對一網絡的更緊湊方式。它甚至可能在更大規模上更有效(空間或時間)。
參考:
https://blog.csdn.net/yangwohenmai1/article/details/84981645
https://machinelearningmastery.com/timedistributed-layer-for-long-short-term-memory-networks-in-python/