Keras實現RNN模型

博客作者:凌逆戰

博客地址: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_initializerkernel權重矩陣的初始化器,用於輸入的線性轉換(參見初始化器
  • recurrent_initializerrecurrent_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_initializerkernel權重矩陣的初始化程序,用於輸入的線性變換。(見初始化者
  • recurrent_initializerrecurrent_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後重置狀態,

實現步驟:

  1. 必須將batch_size參數顯式的傳遞給模型的第一層
  2. 在RNN層中設置stateful=True
  3. 在調用fit()時指定shuffle=False,打亂樣本之後,sequence之間就沒有依懶性了。
  4. 訓練完一個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_initializerembeddings矩陣的初始化器(參見初始化器
  • 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都具有相同數目的樣本
  • 如果X1X2是兩個相鄰的batch,那麼對於任何iX2[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某一層狀態

注意,predictfittrain_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)

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章