利用Keras開發用於分類問題的雙向LSTM及與LSTM效能的比較
雙向LSTM是傳統LSTM的擴充套件,可以提高序列分類問題的模型效能。在輸入序列為時間問題的分類資料上,雙向LSTM在輸入序列上訓練的模型是兩個而不是一個LSTM。輸入序列中的第一個是原始樣本,第二個是輸入序列的反向樣本。這可以為網路提供額外的上下文,並且可以更快,更全面地學習該問題。
環境 本教程假設您已安裝Python SciPy環境。您可以在此示例中使用Python 2或3。 本教程假設您使用TensorFlow(v1.1.0 +)或Theano(v0.9 +)後端安裝了Keras(v2.0.4 +)。 本教程還假設您安裝了scikit-learn,Pandas,NumPy和Matplotlib。
雙向LSTM演變 其實雙向迴歸神經網路(RNN)的想法很簡單。但是RNN不具有記憶功能為了克服常規RNN的侷限性,我們提出了一種雙向遞迴神經網路(BRNN),可以使用特定時間幀過去和未來的所有可用輸入資訊進行訓練...這個想法是將常規RNN的狀態神經元分成負責正時間方向的部分(前向狀態)和負時間方向的部分(後向狀態)。- Mike Schuster和Kuldip K. Paliwal,雙向遞迴神經網路,1997 這種方法已被用於長期短期記憶(LSTM)迴歸神經網路的巨大效果。最初在語音識別領域中使用雙向提供序列是有道理的,因為有證據表明整個話語的語境用於解釋所說的內容而不是線性解釋...依靠對未來的瞭解似乎乍一看違反了因果關係。我們怎樣才能將我們所聽到的內容的理解基於尚未說過的內容?然而,人類聽眾正是這樣做的。根據未來的背景,聽起來,單詞甚至整個句子最初都意味著沒有任何意義。我們必須記住的是真正線上的任務之間的區別 - 在每次輸入後需要輸出 - 以及僅在某些輸入段結束時需要輸出的任務。 雙向LSTM的使用對於某些序列預測問題可能沒有意義,但是對於那些適當的域問題可以提供一些益處。我們發現雙向網路比單向網路明顯更有效......需要說明的是,輸入序列中的時間步長仍然是一次處理一次,只是網路同時在兩個方向上逐步通過輸入序列。Keras中的雙向LSTM
序列分類問題
我們將定義一個簡單的序列分類問題來探索雙向LSTM。
該問題被定義為0和1之間的隨機值序列。該序列被作為問題的輸入,每個時間步提供一個數字。
二進位制標籤(0或1)與每個輸入相關聯。輸出值均為0.一旦序列中輸入值的累積和超過閾值,則輸出值從0翻轉為1。
使用序列長度的1/4的閾值。
例如,下面是10個輸入時間步長(X)的序列
1 |
0.63144003 0.29414551 0.91587952 0.95189228 0.32195638 0.60742236 0.83895793 0.18023048 0.84762691 0.29165514 |
相應的分類輸出(y)將是:
1 |
0 0 0 1 1 1 1 1 1 1 |
我們可以用Python實現它。
第一步是生成一系列隨機值。我們可以使用隨機模組中的random()函式。
1 2 |
# create a sequence of random numbers in [0,1] X = array([random() for _ in range(10)]) |
我們可以將閾值定義為輸入序列長度的四分之一。
1 2 |
# calculate cut-off value to change class values limit = 10/4.0 |
可以使用cumsum()NumPy函式計算輸入序列的累積和。此函式返回一系列累積和值,例如:
1 |
pos1, pos1+pos2, pos1+pos2+pos3, ... |
然後,我們可以計算輸出序列,以確定每個累積和值是否超過閾值。
1 2 |
# determine the class outcome for each item in cumulative sequence y = array([0 if x < limit else 1 for x in cumsum(X)]) |
下面的函式名為get_sequence(),將所有這些結合在一起,將序列的長度作為輸入,並返回新問題案例的X和y分量。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
from random import random from numpy import array from numpy import cumsum # create a sequence classification instance def get_sequence(n_timesteps): # create a sequence of random numbers in [0,1] X = array([random() for _ in range(n_timesteps)]) # calculate cut-off value to change class values limit = n_timesteps/4.0 # determine the class outcome for each item in cumulative sequence y = array([0 if x < limit else 1 for x in cumsum(X)]) return X, y |
我們可以使用新的10個步驟序列測試此函式,如下所示:
1 2 3 |
X, y = get_sequence(10) print(X) print(y) |
首先執行示例列印生成的輸入序列,然後輸出匹配的輸出序列。
1 2 3 |
[ 0.22228819 0.26882207 0.069623 0.91477783 0.02095862 0.71322527 0.90159654 0.65000306 0.88845226 0.4037031 ] [0 0 0 0 0 0 1 1 1 1] |
LSTM用於序列分類
我們可以從為序列分類問題開發傳統的LSTM開始。
首先,我們必須更新get_sequence()函式以將輸入和輸出序列重新整形為3維以滿足LSTM的期望。預期結構具有尺寸[樣本,時間步長,特徵]。分類問題具有1個樣本(例如,一個序列),可配置的時間步長,以及每個時間步長一個特徵。
分類問題具有1個樣本(例如,一個序列),可配置的時間步長,以及每個時間步長一個特徵。
因此,我們可以如下重塑序列。
1 2 3 |
# reshape input and output data to be suitable for LSTMs X = X.reshape(1, n_timesteps, 1) y = y.reshape(1, n_timesteps, 1) |
更新後的get_sequence()函式如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 |
# create a sequence classification instance def get_sequence(n_timesteps): # create a sequence of random numbers in [0,1] X = array([random() for _ in range(n_timesteps)]) # calculate cut-off value to change class values limit = n_timesteps/4.0 # determine the class outcome for each item in cumulative sequence y = array([0 if x < limit else 1 for x in cumsum(X)]) # reshape input and output data to be suitable for LSTMs X = X.reshape(1, n_timesteps, 1) y = y.reshape(1, n_timesteps, 1) return X, y |
我們將序列定義為具有10個時間步長。
接下來,我們可以為問題定義LSTM。輸入圖層將有10個時間步長,1個特徵是一個片段,input_shape =(10,1)。
第一個隱藏層將有20個記憶體單元,輸出層將是一個完全連線的層,每個時間步輸出一個值。在輸出上使用sigmoid啟用函式來預測二進位制值。
在輸出層周圍使用TimeDistributed包裝層,以便在給定作為輸入提供的完整序列的情況下,可以預測每個時間步長一個值。這要求LSTM隱藏層返回一系列值(每個時間步長一個),而不是整個輸入序列的單個值。
最後,因為這是二進位制分類問題,所以使用二進位制日誌丟失(Keras中的binary_crossentropy)。使用有效的ADAM優化演算法來找到權重,並且計算每個時期的精度度量並報告。
1 2 3 4 5 |
# define LSTM model = Sequential() model.add(LSTM(20, input_shape=(10, 1), return_sequences=True)) model.add(TimeDistributed(Dense(1, activation='sigmoid'))) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['acc']) |
LSTM將接受1000個時期的培訓。將在每個時期生成新的隨機輸入序列以使網路適合。這可以確保模型不記住單個序列,而是可以推廣解決方案以解決此問題的所有可能的隨機輸入序列。
1 2 3 4 5 6 |
# train LSTM for epoch in range(1000): # generate new random sequence X,y = get_sequence(n_timesteps) # fit model for one epoch on this sequence model.fit(X, y, epochs=1, batch_size=1, verbose=2) |
一旦經過訓練,網路將在另一個隨機序列上進行評估。然後將預測與預期輸出序列進行比較,以提供系統技能的具體示例。
1 2 3 4 5 |
# evaluate LSTM X,y = get_sequence(n_timesteps) yhat = model.predict_classes(X, verbose=0) for i in range(n_timesteps): print('Expected:', y[0, i], 'Predicted', yhat[0, i]) |
下面列出了完整的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
from random import random from numpy import array from numpy import cumsum from keras.models import Sequential from keras.layers import LSTM from keras.layers import Dense from keras.layers import TimeDistributed # create a sequence classification instance def get_sequence(n_timesteps): # create a sequence of random numbers in [0,1] X = array([random() for _ in range(n_timesteps)]) # calculate cut-off value to change class values limit = n_timesteps/4.0 # determine the class outcome for each item in cumulative sequence y = array([0 if x < limit else 1 for x in cumsum(X)]) # reshape input and output data to be suitable for LSTMs X = X.reshape(1, n_timesteps, 1) y = y.reshape(1, n_timesteps, 1) return X, y # define problem properties n_timesteps = 10 # define LSTM model = Sequential() model.add(LSTM(20, input_shape=(n_timesteps, 1), return_sequences=True)) model.add(TimeDistributed(Dense(1, activation='sigmoid'))) model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['acc']) # train LSTM for epoch in range(1000): # generate new random sequence X,y = get_sequence(n_timesteps) # fit model for one epoch on this sequence model.fit(X, y, epochs=1, batch_size=1, verbose=2) # evaluate LSTM X,y = get_sequence(n_timesteps) yhat = model.predict_classes(X, verbose=0) for i in range(n_timesteps): print('Expected:', y[0, i], 'Predicted', yhat[0, i]) |
執行該示例在每個時期的隨機序列上列印日誌丟失和分類準確性。
這清楚地表明瞭模型對序列分類問題的解決方案的概括性。
我們可以看到該模型表現良好,達到最終準確度,徘徊在90%左右,準確率達到100%。不完美,但對我們的目的有好處。
將新隨機序列的預測與預期值進行比較,顯示出具有單個錯誤的大多數正確結果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
... Epoch 1/1 0s - loss: 0.2039 - acc: 0.9000 Epoch 1/1 0s - loss: 0.2985 - acc: 0.9000 Epoch 1/1 0s - loss: 0.1219 - acc: 1.0000 Epoch 1/1 0s - loss: 0.2031 - acc: 0.9000 Epoch 1/1 0s - loss: 0.1698 - acc: 0.9000 Expected: [0] Predicted [0] Expected: [0] Predicted [0] Expected: [0] Predicted [0] Expected: [0] Predicted [0] Expected: [0] Predicted [0] Expected: [0] Predicted [1] Expected: [1] Predicted [1] Expected: [1] Predicted [1] Expected: [1] Predicted [1] Expected: [1] Predicted [1] |
用於序列分類的雙向LSTM
現在我們知道如何為序列分類問題開發LSTM,我們可以擴充套件該示例以演示雙向LSTM。
我們可以通過使用雙向圖層包裝LSTM隱藏層來完成此操作,如下所示:
1 |
model.add(Bidirectional(LSTM(20, return_sequences=True), input_shape=(n_timesteps, 1))) |
這將建立隱藏層的兩個副本,一個適合輸入序列,一個適合輸入序列的反向副本。預設情況下,將連線這些LSTM的輸出值。
這意味著,而不是TimeDistributed層接收10個時間段的20個輸出,它現在將接收10個時間段的40(20個單位+ 20個單位)輸出。
下面列出了完整的示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|