Keras實現RNN模型
- 2020 年 3 月 31 日
- 筆記
博客作者:凌逆戰
博客地址:https://www.cnblogs.com/LXP-Never/p/10940123.html
這篇文章主要介紹使用Keras框架來實現RNN家族模型,TensorFlow實現RNN的代碼可以參考我的另外一篇博客:TensorFlow中實現RNN,徹底弄懂time_step
Keras實現RNN模型
SimpleRNN層
keras.layers.GRU(units, activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0, implementation=1, return_sequences=False, return_state=False, go_backwards=False, stateful=False, unroll=False)
參數:
- units:輸出維度
- activation:激活函數(參考激活函數)
- use_bias: 布爾值,是否使用偏置項
- kernel_initializer:
kernel
權重矩陣的初始化器,用於輸入的線性轉換(參見初始化器) - recurrent_initializer:
recurrent_kernel
權重矩陣的初始化程序,用於循環狀態的線性轉換(參見初始化程序) - bias_initializer:偏置向量的初始化器(參見初始化器)
- kernel_regularizer:應用於
kernel
權重矩陣的正則化函數(參見正則化器) - bias_regularizer:應用於偏置向量的正則化函數(參見正則化器)
- recurrent_regularizer:應用於
recurrent_kernel
權重矩陣的正則化函數(參見正則化器) - activity_regularizer:應用於圖層輸出的正則化函數(其“激活”)。(見規範者)
- kernel_constraints:應用於
kernel
權重矩陣的約束函數(請參閱約束) - recurrent_constraints:應用於
recurrent_kernel
權重矩陣的約束函數(請參閱約束) - bias_constraints:應用於偏置向量的約束函數(請參閱約束)
- dropout:0~1之間的浮點數,控制輸入線性變換的神經元斷開比例
- recurrent_dropout:0~1之間的浮點數,控制循環狀態的線性變換的神經元斷開比例
- 其他參數參考Recurrent的說明
GRU層
門限循環單元
keras.layers.recurrent.GRU(units, activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0)
參數:
- units:正整數,輸出空間的維數
- 激活:要使用的激活功能(請參閱激活)
- recurrent_activation:用於循環步驟的激活功能(參見激活)。
- use_bias:Boolean,該層是否使用偏向量
- kernel_initializer:
kernel
權重矩陣的初始化器,用於輸入的線性轉換(參見初始化器) - recurrent_initializer:
recurrent_kernel
權重矩陣的初始化程序,用於循環狀態的線性轉換(參見初始化程序) - bias_initializer:偏置向量的初始化器(參見初始化器)
- kernel_regularizer:應用於
kernel
權重矩陣的正則化函數(參見正則化器) - recurrent_regularizer:應用於
recurrent_kernel
權重矩陣的正則化函數(參見正則化器) - bias_regularizer:應用於偏置向量的正則化函數(參見正則化器)
- activity_regularizer:應用於圖層輸出的正則化函數(其“激活”)。(見規範者)
- kernel_constraint:應用於
kernel
權重矩陣的約束函數(請參閱約束) - recurrent_constraint:應用於
recurrent_kernel
權重矩陣的約束函數(請參閱約束) - bias_constraint:應用於偏置向量的約束函數(請參閱約束)
- dropout:浮點數介於0和1之間。為輸入的線性變換而下降的單位的分數
- recurrent_dropout:浮點數在0和1之間。對於循環狀態的線性變換,單位的分數下降
- implementation:實現模式,1或2.模式1將其操作構造為更大數量的較小點產品和添加,而模式2將其分組為更少,更大的操作。這些模式將在不同硬件和不同應用程序上具有不同的性能配置文件。
- return_sequences:布爾值。是返回輸出序列中的最後一個輸出,還是返回完整序列。
- return_state:布爾值。是否返回除輸出之外的最後一個狀態。
- go_backwards:Boolean(默認為False)。如果為True,則向後處理輸入序列並返回相反的序列。
- stateful:Boolean(默認為False)。如果為True,則批次中索引i處的每個樣本的最後狀態將用作後續批次中索引i的樣本的初始狀態。
- unroll:Boolean(默認為False)。如果為True,則將展開網絡,否則將使用符號循環。展開可以加速RNN,儘管它往往會佔用大量內存。展開僅適用於短序列。
- reset_after:GRU約定(是否在矩陣乘法之前或之前應用複位門)。False =“之前”(默認),True =“之後”(CuDNN兼容)。
LSTM
長短期記憶網絡
keras.layers.LSTM(units, activation='tanh', recurrent_activation='hard_sigmoid', use_bias=True, kernel_initializer='glorot_uniform', recurrent_initializer='orthogonal', bias_initializer='zeros', unit_forget_bias=True, kernel_regularizer=None, recurrent_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, recurrent_constraint=None, bias_constraint=None, dropout=0.0, recurrent_dropout=0.0, implementation=1, return_sequences=False, return_state=False, go_backwards=False, stateful=False, unroll=False)
參數:
- units:正整數,輸出空間的維數
- 激活:要使用的激活功能(請參閱激活)
- recurrent_activation:用於循環步驟的激活功能(參見激活)
- use_bias:Boolean,該層是否使用偏向量
- kernel_initializer:
kernel
權重矩陣的初始化程序,用於輸入的線性變換。(見初始化者) - recurrent_initializer:
recurrent_kernel
權重矩陣的初始化程序,用於循環狀態的線性轉換。(見初始化者) - bias_initializer:偏置向量的初始化器(參見初始化器)
- unit_forget_bias:布爾值。如果為True,則在初始化時將忘記門的偏差加1。將其設置為true也會強制執行
bias_initializer="zeros"
- kernel_regularizer:應用於
kernel
權重矩陣的正則化函數(參見正則化器) - recurrent_regularizer:應用於
recurrent_kernel
權重矩陣的正則化函數(參見正則化器) - bias_regularizer:應用於偏置向量的正則化函數(參見正則化器)
- activity_regularizer:應用於圖層輸出的正則化函數(其“激活”)。(見規範者)
- kernel_constraint:應用於
kernel
權重矩陣的約束函數(請參閱約束) - recurrent_constraint:應用於
recurrent_kernel
權重矩陣的約束函數(請參閱約束) - bias_constraint:應用於偏置向量的約束函數(請參閱約束)
- dropout:浮點數介於0和1之間。為輸入的線性變換而下降的單位的分數
- recurrent_dropout:浮點數在0和1之間。對於循環狀態的線性變換,單位的分數下降
- 實現:實現模式,1或2.模式1將其操作構造為更大數量的較小點產品和添加,而模式2將其分組為更少,更大的操作。這些模式將在不同硬件和不同應用程序上具有不同的性能配置文件
- return_sequences:布爾值。默認
False
。若為True,
在輸出序列中,返回全部 hidden state值;若為False,返回單個time step 的 hidden state值。 - return_state:布爾值。默認False,True:返回hidden state之外,還要返回最後一個cell state狀態
- go_backwards:Boolean(默認為False)。如果為True,則向後處理輸入序列並返回相反的序列
- stateful:Boolean(默認為False)。如果為True,則批次中索引i處的每個樣本的最後狀態將用作後續批次中索引i的樣本的初始狀態
- unroll:Boolean(默認為False)。如果為True,則將展開網絡,否則將使用符號循環。展開可以加速RNN,儘管它往往會佔用大量內存。展開僅適用於短序列
還有許多ConvLSTM2D:卷積LSTM。它類似於LSTM層,但輸入轉換和循環轉換都是卷積的。
在這裡我們細講一下return_sequences和return_state,這部分主要參考EastWR的CSDN博客。
首先我們需要先了解一下cell state和hidden state。在LSTM網絡中,直接根據當前input數據,得到的輸出稱為hidden state。還有一種數據是不僅僅依賴於當前輸入數據,而是一種伴隨整個網絡過程中用來記憶,遺忘,選擇並最終影響 hidden state 結果的東西,稱為 cell state。cell state 就是實現 long short memory 的關鍵。cell state 是不輸出的,它僅對輸出 hidden state 產生影響。通常情況,我們不需要訪問 cell state,除非想設計複雜的網絡結構。
h = LSTM(X)
return_sequences和return_state默認就是false。此時只會返回一個hidden state 值。如果input 數據包含多個時間步,則這個hidden state 是最後一個時間步的結果
LSTM(1, return_sequences=True)
return_sequences=True,return_state=False。輸出的hidden state 包含全部時間步的結果。
lstm1, state_h, state_c = LSTM(1, return_state=True)
return_sequences=False,return_state=True。lstm1 和 state_h 結果都是 hidden state。在這種參數設定下,它們倆的值相同。都是最後一個時間步的 hidden state。 state_c 是最後一個時間步 cell state結果。
lstm1, state_h, state_c = LSTM(1, return_sequences=True, return_state=True)
此時,我們既要輸出全部時間步的 hidden state ,又要輸出 cell state。lstm1 存放的就是全部時間步的 hidden state。state_h 存放的是最後一個時間步的 hidden state,state_c 存放的是最後一個時間步的 cell state。
有狀態RNN和無狀態RNN
而stateless指的只是樣本內的信息傳遞。
timestep時間步長,也可以理解為展開的rnn或者lstm的block的個數,(batch_size, time_steps, input_size)
舉個例子來講解一下timestep、batch、batchsize、input_size在LSTM中的關係,假如我們有一篇文章X,其中每個句子X[i]作為一個訓練對象(sequence)。一句話裏面每個字代表一個timestep時間步,一個epoch裏面分batch的訓練數據,每一個batch的一個樣本裏面,分timestep的訓練句子中的依賴關係。
stateless LSTM
和DNN、CNN神經網絡一樣訓練
stateful LSTM
有狀態的RNN能夠在訓練中維護跨批次的有狀態信息,即當前批次的訓練數據計算的狀態值,可以用作下一批次訓練數據的初始隱藏狀態。stateful代表除了每個樣本內的時間步內傳遞,而且每個樣本之間會有信息(c,h)傳遞,
優點:更小的網絡,或者更少的訓練時間
缺點:需要數據batchsize來訓練網絡,並在每個訓練epoch後重置狀態,
實現步驟:
- 必須將batch_size參數顯式的傳遞給模型的第一層
- 在RNN層中設置stateful=True
- 在調用fit()時指定shuffle=False,打亂樣本之後,sequence之間就沒有依懶性了。
- 訓練完一個epoch後,要重置狀態
- 使用 model.reset_states()來重置模型中所有層的狀態。
- 使用layer.reset_states()來重置指定有狀態 RNN 層的狀態
順序模型,方式一:LSTM(hidden_size, stateful=True, batch_input_shape=(batch_size, timestep, input_dim))
函數式模型,方式一:如果是帶有 1 個或多個 Input 層的函數式模型,為你的模型的所有第一層傳遞一個 batch_shape=(...)
。 這是你的輸入的預期尺寸,包括批量維度。 它應該是整數的元組,例如 (32, 10, 100)
。
方式二:LSTM(hidden_size, stateful=True, input_shape=(data[1], data[2]), batch_size)
如果下一層還是LSTM層的話,需要把隱藏層狀態全部返回給下一層LSTM層,設置return_sequences=True:
LSTM(hidden_size, stateful=True, batch_input_shape=(batch_size, timestep, input_dim), return_sequences=True)
訓練階段:
for i in range(epochs):
model.fit(x_train, y_train, batch_size, epochs=1, validation_data=(x_test, y_test), shuffle=False)
model.reset_states()
from keras.models import Sequential from keras.layers import LSTM, Dense import numpy as np import math data_dim = 16 timesteps = 8 num_classes = 10 batch_size = 32 num_epochs = 800 # 期望輸入數據尺寸: (batch_size, timesteps, data_dim) # 請注意,我們必須提供完整的 batch_input_shape,因為網絡是有狀態的。 # 第 k 批數據的第 i 個樣本是第 k-1 批數據的第 i 個樣本的後續。 model = Sequential() model.add(LSTM(32, return_sequences=True, stateful=True, batch_input_shape=(batch_size, timesteps, data_dim))) model.add(LSTM(32, return_sequences=True, stateful=True)) model.add(LSTM(32, stateful=True)) model.add(Dense(10, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy']) # 生成虛擬訓練數據 x_train = np.random.random((batch_size * 10, timesteps, data_dim)) # (320, 8, 16) y_train = np.random.random((batch_size * 10, num_classes)) # (320, 10) # 生成虛擬驗證數據 x_val = np.random.random((batch_size * 3, timesteps, data_dim)) # (96, 8, 16) y_val = np.random.random((batch_size * 3, num_classes)) for i in range(num_epochs): print("Epoch {:d}/{:d}".format(i+1, num_epochs)) model.fit(x_train, y_train, batch_size=batch_size, epochs=1, validation_data=(x_val, y_val), shuffle=False) model.reset_states() score, _ = model.evaluate(x_val, y_val, batch_size=batch_size) # 返回誤差值和度量值 rmse = math.sqrt(score) print("nMSE: {:.3f}, RMSE: {:.3f}".format(score, rmse)) pre = model.predict(x_val, batch_size=batch_size)
Concatenate
keras.layers.Concatenate([input1, input2 ...], axis=-1)
張量串聯,它將一個張量的列表作為輸入,除了要連接的軸shape值之外其他軸的shape值都要相同,並返回單個張量,即所有輸入張量的串聯。
嵌入層Embedding
keras.layers.embeddings.Embedding(input_dim, output_dim, embeddings_initializer='uniform', embeddings_regularizer=None, activity_regularizer=None, embeddings_constraint=None, mask_zero=False, input_length=None)
嵌入層將正整數(下標)轉換為具有固定大小的向量,如[[4],[20]]–>[[0.25,0.1],[0.6,-0.2]]
該層支持對具有可變時間步長的輸入數據進行masking。如果想將輸入數據的一部分屏蔽掉,請使用Embedding層並將參數mask_zero
設為True
。
Embedding層只能作為模型的第一層
參數:
- input_dim:int> 0.詞彙表的大小,即最大整數索引+ 1。
- output_dim:int> = 0.Dense嵌入的維度。
- embeddings_initializer:
embeddings
矩陣的初始化器(參見初始化器) - embeddings_regularizer:應用於
embeddings
矩陣的正則化函數(參見正則化器) - activity_regularizer:應用於圖層輸出的正則化函數(其“激活”)(見規範者)
- embeddings_constraint:應用於
embeddings
矩陣的約束函數(請參閱約束) - mask_zero:輸入值0是否是應屏蔽的特殊“padding”值。這在使用可能需要可變長度輸入的循環層時很有用。如果mask_zero設置為True,則結果是索引0不能在詞彙表
- input_length:輸入序列的長度,當其為常量時。如果要在上游連接“Flatten”和“Dense”,則需要此參數。
輸入形狀:2D張量形狀:(batch_size, sequence_length)
輸出形狀:3D張量與形狀:(batch_size, sequence_length, output_dim)
import numpy as np model = Sequential() # 詞彙表的大小為999+1,Dense嵌入的維度為64,輸入序列長度為10 model.add(Embedding(1000, 64, input_length=10)) # 模型輸入 (batch, input_length) # 輸入中的最大整數(即字索引)應為 不大於999(詞彙大小)。 # 現在model.output_shape==(none,10,64),其中none是batch dimension. input_array = np.random.randint(1000, size=(32, 10)) # shape=(32,10) model.compile('rmsprop', 'mse') output_array = model.predict(input_array) assert output_array.shape == (32, 10, 64)
Embedding層還不是很熟悉,要學會和LSTM相結合。
Masking層
keras.layers.core.Masking(mask_value=0.0)
使用給定的mask_value值對輸入的序列信號進行“屏蔽”,用以定位需要跳過的時間步
對於輸入張量的時間步,即輸入張量的第1維度(維度從0開始算,見例子),如果輸入張量在該時間步上都等於mask_value
,則該時間步將在模型接下來的所有層(只要支持masking)被跳過(屏蔽)。
如果模型接下來的一些層不支持masking,卻接受到masking過的數據,則拋出異常。
例子
考慮輸入數據x
是一個形如(batch_size,timesteps,features)的張量,現將其送入LSTM層。因為你缺少時間步為3和5的信號,所以你希望將其掩蓋。這時候應該:
- 賦值
x[:,3,:] = 0.
,x[:,5,:] = 0.
- 在LSTM層之前插入
mask_value=0.
的Masking
層
model = Sequential() model.add(Masking(mask_value=0., input_shape=(timesteps, features))) model.add(LSTM(32))
參數:
msak_value:None或要跳過的掩碼值
model.summary() # 在模型編譯之後,打印網絡結構
print(model.output_shape) # 打印模型輸出
使用LSTM序列分類
from keras.models import Sequential from keras.layers import Dense, Dropout from keras.layers import Embedding from keras.layers import LSTM model = Sequential() model.add(Embedding(max_features, output_dim=256)) model.add(LSTM(128)) model.add(Dropout(0.5)) model.add(Dense(1, activation='sigmoid')) model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy']) model.fit(x_train, y_train, batch_size=16, epochs=10) score = model.evaluate(x_test, y_test, batch_size=16)
用於序列分類的棧式LSTM
在該模型中,我們將三個LSTM堆疊在一起,使該模型能夠學習更深層次的時域特徵表示。
開始的兩層LSTM返回其全部時間步的hidden state,而第三層LSTM只返回最後一個時間步的hidden state,從而其時域維度降低(即將輸入序列轉換為單個向量)
from keras.models import Sequential from keras.layers import LSTM, Dense import numpy as np data_dim = 16 timesteps = 8 num_classes = 10 # 預期輸入數據shape: (batch_size, timesteps, data_dim) model = Sequential() model.add(LSTM(32, return_sequences=True, input_shape=(timesteps, data_dim))) # (None, 8, 32) model.add(LSTM(32, return_sequences=True)) # (None, 8, 32) model.add(LSTM(32)) # (None, 32) model.add(Dense(10, activation='softmax')) # (None, 10) model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy']) print(model.summary()) # 生成虛擬訓練數據 x_train = np.random.random((1000, timesteps, data_dim)) # (1000, 8, 16) y_train = np.random.random((1000, num_classes)) # 生成虛擬驗證數據 x_val = np.random.random((100, timesteps, data_dim)) y_val = np.random.random((100, num_classes)) model.fit(x_train, y_train, batch_size=64, epochs=5, validation_data=(x_val, y_val))
採用stateful LSTM的相同模型
一個RNN是狀態RNN,意味着訓練時每個batch的狀態都會被用於初始化下一個batch的初始狀態。
當使用狀態RNN時,有如下假設
- 所有的batch都具有相同數目的樣本
- 如果
X1
和X2
是兩個相鄰的batch,那麼對於任何i
,X2[i]
都是X1[i]
的後續序列
要使用狀態RNN,我們需要在實例化層對象時指定參數stateful=True
- 顯式的指定每個batch的大小。可以通過模型的首層參數。
batch_input_shape
是一個整數tuple。例如(32,10,16)代表一個具有10個時間步,每步向量長為16,每32個樣本構成一個batch的輸入數據格式。 - 在函數式模型中,對所有的輸入都要指定相同的
batch_size
。
要重置循環網絡的狀態,使用:
model.reset_states()
來重置網絡中所有層的狀態layer.reset_states()
來重置指定層的狀態
from keras.models import Sequential from keras.layers import Dense, recurrent import numpy as np X = np.ones(shape=(32, 21, 16)) # 輸入數據的shape(32, 21, 16) # 我們將把它按長度10的順序輸入我們的模型 model = Sequential() model.add(recurrent.LSTM(32, input_shape=(10, 16), batch_size=32, stateful=True)) print(model.output_shape) # (32, 32) model.add(Dense(16, activation='softmax')) print(model.output_shape) # (32, 16) model.compile(optimizer='rmsprop', loss='categorical_crossentropy') # 我們訓練網絡預測前10個時間點的第11個時間點: print(X[:, :10, :].shape) # (32, 10, 16) print(X[:, 10, :].shape) # (32, 16) model.train_on_batch(X[:, :10, :], np.reshape(X[:, 10, :], (32, 16))) # 網絡狀態已更改。我們可以輸入後續序列: model.train_on_batch(X[:, 10:20, :], np.reshape(X[:, 20, :], (32, 16))) model.reset_states() # 讓我們重置LSTM層的狀態: model.layers[0].reset_states() # 重置LSTM某一層狀態
注意,predict
,fit
,train_on_batch
,predict_classes
等方法都會更新模型中狀態層的狀態。這使得你不但可以進行狀態網絡的訓練,也可以進行狀態網絡的預測。
stateful LSTM的特點是,在處理過一個batch的訓練數據後,其內部狀態(記憶)會被作為下一個batch的訓練數據的初始狀態。狀態LSTM使得我們可以在合理的計算複雜度內處理較長序列
from keras.models import Sequential from keras.layers import LSTM, Dense import numpy as np data_dim = 16 timesteps = 8 num_classes = 10 batch_size = 32 # 預期的 input batch shape: (batch_size, timesteps, data_dim) # 注意,由於網絡是有狀態的,所以我們必須提供完整的batch_input_shape # 索引為 I 的 第k個batch的樣本是k-1 batch 樣本 後續跟進 model = Sequential() model.add(LSTM(32, return_sequences=True, stateful=True, batch_input_shape=(batch_size, timesteps, data_dim))) model.add(LSTM(32, return_sequences=True, stateful=True)) model.add(LSTM(32, stateful=True)) model.add(Dense(10, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['accuracy']) # 生成虛擬訓練數據 x_train = np.random.random((batch_size * 10, timesteps, data_dim)) y_train = np.random.random((batch_size * 10, num_classes)) # 生成虛擬驗證數據 x_val = np.random.random((batch_size * 3, timesteps, data_dim)) y_val = np.random.random((batch_size * 3, num_classes)) model.fit(x_train, y_train, batch_size=batch_size, epochs=5, shuffle=False, validation_data=(x_val, y_val))
將兩個LSTM合併作為編碼端來處理兩路序列的分類
在本模型中,兩路輸入序列通過兩個LSTM被編碼為特徵向量
兩路特徵向量被串連在一起,然後通過一個全連接網絡得到結果,示意圖如下:
也就是用Concatenate,把上面兩個輸出串聯起來了。
參考文獻
Keras_LSTM中的return_sequence和return_state參數
Keras 之 LSTM 有狀態模型(stateful LSTM)和無狀態模型(stateless LSTM)