LSTM網絡是一種循環神經網絡。循環神經網絡是一種神經網絡,它試圖對依賴於時間或順序的行爲(如語言、股價、電力需求等)進行建模。這是通過將神經網絡層在t時刻的輸出反饋給同一網絡層在t + 1時刻的輸入來實現的。它是這樣的:
vanilla 循環神經網絡(由常規神經網絡節點構造)的問題在於,當我們嘗試對由大量其他單詞分隔的單詞或序列值之間的依賴關係建模時,我們體驗到了梯度消失問題(有時也是梯度爆炸問題)。這是因爲小梯度或權重(小於 1 的值)在多個時間步長中乘以多次,並且梯度以不相通方式收縮爲零。這意味着這些早期圖層的權重不會顯著更改,因此網絡不會學習長期依賴關係。
1.結構
LSTM網絡是一個循環神經網絡,它用LSTM細胞塊來代替我們的標準神經網絡層。這些單元格有不同的組成部分,稱爲輸入門、遺忘門和輸出門——這些將在稍後詳細解釋。以下是LSTM單元格的圖形表示:
首先,在左側,我們將新單詞/序列值xt連接到單元格ht−1的前一個輸出。這個組合輸入的第一步是通過tanh層進行壓縮。第二步是這個輸入通過一個輸入門。輸入門是一層sigmoid激活節點,其輸出乘以壓扁的輸入。這些輸入門sigmoid可以“殺死”輸入向量中不需要的任何元素。一個sigmoid函數輸出0到1之間的值,因此可以將連接到這些節點的輸入的權重訓練爲接近於零的輸出值來“關閉”某些輸入值(或者相反,接近於1的輸出值來“傳遞”其他值)。
通過此單元格的數據流的下一步是內部狀態/遺忘門循環。LSTM細胞有一個內部狀態變量st。這個變量,滯後一個時間步長,即st−1被添加到輸入數據中,創建一個有效的遞歸層。這個加法運算,而不是乘法運算,有助於降低梯度消失的風險。 然而,這個遞歸循環是由遺忘門控制的——它的工作原理與輸入門相同,但是它幫助網絡學習哪些狀態變量應該“記住”或“忘記”。
最後,我們有一個輸出層tanh壓扁函數,它的輸出由一個輸出門控制。這個門決定哪些值實際上可以作爲單元格ht的輸出。
2. Embedding
我們將設置所謂的嵌入層,將每個單詞轉換爲有意義的單詞向量。我們必須指定嵌入層的大小——這是每個單詞所表示的向量的長度——通常在100-500之間。換句話說,如果嵌入層的大小爲250,那麼每個單詞將由一個250長度的向量表示,i.e.[x1 ,x2 ,…,x250 ],那麼LSTM單元中有250個隱藏層。
**我們通常將嵌入層輸出的大小與LSTM單元中隱藏層的數量匹配起來。**您可能想知道LSTM單元格中的隱藏層來自何處。在我的LSTM概覽圖中,我簡單地顯示了輸入數據流經的“data rails”。然而,單元格中的每個sigmoid、tanh或隱藏狀態層實際上是一組節點,其數量等於隱藏層的大小。因此,LSTM細胞中的每個“節點”實際上是一組正常的神經網絡節點,就像密集連接的神經網絡中的每一層一樣。
3.Keras LSTM
步驟:generator - model - fit - evaluate
generator
class KerasBatchGenerator(object):
def __init__(self, data, num_steps, batch_size, vocabulary, skip_step=5):
self.data = data
self.num_steps = num_steps
self.batch_size = batch_size
self.vocabulary = vocabulary
# this will track the progress of the batches sequentially through the
# data set - once the data reaches the end of the data set it will reset
# back to zero
self.current_idx = 0
# skip_step is the number of words which will be skipped before the next
# batch is skimmed from the data set
self.skip_step = skip_step
def generate(self):
x = np.zeros((self.batch_size, self.num_steps))
y = np.zeros((self.batch_size, self.num_steps, self.vocabulary))
while True:
for i in range(self.batch_size):
if self.current_idx + self.num_steps >= len(self.data):
# reset the index back to the start of the data set
self.current_idx = 0
x[i, :] = self.data[self.current_idx:self.current_idx + self.num_steps]
temp_y = self.data[self.current_idx + 1:self.current_idx + self.num_steps + 1]
# convert all of temp_y into a one hot representation
y[i, :, :] = to_categorical(temp_y, num_classes=self.vocabulary)
self.current_idx += self.skip_step
yield x, y
train_data_generator = KerasBatchGenerator(train_data, num_steps, batch_size, vocabulary,
skip_step=num_steps)
valid_data_generator = KerasBatchGenerator(valid_data, num_steps, batch_size, vocabulary,
skip_step=num_steps)
model
model = Sequential()
model.add(Embedding(vocabulary, hidden_size, input_length=num_steps))
model.add(LSTM(hidden_size, return_sequences=True))
model.add(LSTM(hidden_size, return_sequences=True))
if use_dropout:
model.add(Dropout(0.5))
model.add(TimeDistributed(Dense(vocabulary)))
model.add(Activation('softmax'))
第一步是使用sequence()構造函數創建Keras模型。網絡中的第一層,正如前面顯示的架構圖所示,是一個單詞嵌入層。這將把我們的單詞(由數據中的整數引用)轉換成有意義的嵌入向量。這個Embedding() 層將詞彙表的大小作爲它的第一個參數,然後將生成的嵌入向量的大小作爲下一個參數。最後,由於這一層是網絡的第一層,我們必須指定輸入的“長度”,即每個樣本中的步驟/單詞的數量。
跟蹤網絡中的張量形狀是值得的——在本例中,嵌入層的輸入是(batch_size, num_steps),輸出是(batch_size, num_steps, hidden_size)。注意,在順序模型中,Keras始終將batch大小保持爲第一個維度。它從Keras擬合函數(即本例中的fit_generator)接收batch大小,因此它很少包含在序列模型層的定義中,一般設置爲函數接受batch參數。
下一層是兩個LSTM層中的第一個。要指定LSTM層,首先必須提供LSTM單元中隱藏層中的節點數,例如忘卻門層中的單元數、tanh壓縮輸入層中的單元數等等。
代碼中指定的下一個參數是return_sequences=True參數。這樣做的目的是確保LSTM單元始終返回展開的LSTM單元的所有輸出。如果省略了這個參數,LSTM單元將只提供來自最後一個時間步驟的LSTM單元的輸出(最後一層LSTM)。當return_sequences=False時,只有一個輸出。但是,當return_sequences=True時,將返回LSTM單元格的所有展開輸出h0… ht。在這種情況下,後者是適當的。爲什麼? 在這個例子中,我們試着預測序列中的下一個單詞(數值)。然而,如果我們試圖訓練模型,最好是能夠比較LSTM細胞輸出在每個時間步長序列中的下一個詞——通過這種方式,我們獲得了num_steps糾正模型中的誤差(BP),而不是隻針對每個示例返回一個誤差。
Keras LSTM網絡的下一層是dropout層,以防止過擬合。然後,有一個特殊的Keras層用於循環神經網絡稱爲TimeDistributed。該函數爲遞歸模型中的每個時間步添加一個獨立的層。例如,如果我們在一個模型中有10個時間步長,一個TimeDistributed層在一個Dense層上運行,會產生10個獨立的Dense層,每個時間步長對應一個。在Keras LSTM模型的最後一層,這些Dense層的激活被設置爲softmax。
30s上手Keras
Keras 的核心數據結構是 model,一種組織網絡層的方式。最簡單的模型是 Sequential 順序模型,它由多個網絡層線性堆疊。對於更復雜的結構,你應該使用 Keras 函數式 API,它允許構建任意的神經網絡圖。
可以簡單地使用 .add() 來堆疊模型。在完成了模型的構建後, 可以使用 .compile() 來配置學習過程。
from keras.models import Sequential
from keras.layers import Dense
model = Sequential()
model.add(Dense(units=64, activation='relu', input_dim=100))
model.add(Dense(units=10, activation='softmax'))
model.compile(loss='categorical_crossentropy',
optimizer='sgd',
metrics=['accuracy'])
# 編譯模型時必須指明損失函數和優化器,如果你需要的話,也可以自己定製損失函數。Keras的一個核心理念就是簡明易用同時,保證用戶對Keras的絕對控制力度,用戶可以根據自己的需要定製自己的模型、網絡層,甚至修改源代碼
#model.compile(loss=keras.losses.categorical_crossentropy,optimizer=keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True))
- Sequential()
模型需要知道輸入數據的shape,因此,Sequential的第一層需要接受一個關於輸入數據shape的參數,後面的各個層則可以自動的推導出中間數據的shape,因此不需要爲每個層都指定這個參數。有幾種方法來爲第一層指定輸入數據的shape:
-
傳遞一個input_shape的關鍵字參數給第一層,input_shape是一個tuple類型的數據,其中也可以填入None,** 如果填入None則表示此位置可能是任何正整數 ** 。數據的batch大小不應包含在其中。
-
有些2D層,如Dense,支持通過指定其輸入維度input_dim來隱含的指定輸入數據shape,是一個Int類型的數據。一些3D的時域層支持通過參數input_dim和input_length來指定輸入shape。
-
如果你需要爲輸入指定一個固定大小的batch_size(常用於stateful RNN網絡),可以傳遞batch_size參數到一個層中,例如你想指定輸入張量的batch大小是32,數據shape是(6,8),則你需要傳遞batch_size=32和input_shape=(6,8)。
- Complie()
compile接收三個參數:
-
優化器optimizer:該參數可指定爲已預定義的優化器名,如rmsprop、adagrad,或一個Optimizer類的對象,詳情見optimizers
-
損失函數loss:該參數爲模型試圖最小化的目標函數,它可爲預定義的損失函數名,如categorical_crossentropy、mse,也可以爲一個損失函數。詳情見losses
-
指標列表metrics:對分類問題,我們一般將該列表設置爲metrics=[‘accuracy’]。指標可以是一個預定義指標的名字,也可以是一個用戶定製的函數.指標函數應該返回單個張量,或一個完成metric_name - > metric_value映射的字典.請參考性能評估
Keras以Numpy數組作爲輸入數據和標籤的數據類型。訓練模型一般使用fit函數。現在,你可以批量地在訓練數據上進行迭代了:
# x_train 和 y_train 是 Numpy 數組 -- 就像在 Scikit-Learn API 中一樣。
model.fit(x_train, y_train, epochs=5, batch_size=32)
# 只需一行代碼就能評估模型性能:
loss_and_metrics = model.evaluate(x_test, y_test, batch_size=128)
# 或者對新的數據生成預測:
classes = model.predict(x_test, batch_size=128)
對於Dense(), 注意定義第一層的時候需要制定數據輸入的形狀,即input_dim,這樣才能讓數據正常喂進網絡!
Embedding層只能作爲模型的第一層,單詞嵌入是使用密集的矢量表示來表示單詞和文檔的一類方法。
Flatten層用來將輸入“壓平”,即把多維的輸入一維化,常用在從卷積層到全連接層的過渡。Flatten不影響batch的大小。
常用方法
- fit_generator
fit_generator(self, generator, steps_per_epoch, epochs=1,
verbose=1, callbacks=None,
validation_data=None,
validation_steps=None,
class_weight=None, max_q_size=10,
workers=1, pickle_safe=False,
initial_epoch=0)
-
generator:生成器函數,生成器的輸出應該爲:
一個形如(inputs,targets)的tuple
一個形如(inputs, targets,sample_weight)的tuple。所有的返回值都應該包含相同數目的樣本。生成器將無限在數據集上循環。 -
steps_per_epoch:整數,當生成器返回steps_per_epoch次數據時計一個epoch結束,執行下一個epoch。
-
validation_data:具有以下三種形式之一
生成驗證集的生成器
一個形如(inputs,targets)的tuple
一個形如(inputs,targets,sample_weights)的tuple
- Dense()
定義第一層的時候需要制定數據輸入的形狀,即input_dim
- units:該層有幾個神經元
- activation:該層使用的激活函數
- use_bias:是否添加偏置項
keras.layers.Dense(units,
activation=None,
use_bias=True,
kernel_initializer='glorot_uniform',
bias_initializer='zeros',
kernel_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
kernel_constraint=None,
bias_constraint=None)
- Dropout()
- Hinton-2012-Improving neural networks by preventing co-adaptation of feature detectors提出:在每次訓練前向傳導的時候,讓某個神經元的激活值以一定的概率p停止工作(Bernoulli以概率p,隨機生成一個0\1向量),可以讓一個神經元的出現不應該依賴於另外一個神經元,提高網絡的泛化能力,稱之爲dropout。
- 如果採用dropout,訓練時間大大延長,但是對測試階段沒影響。
- dropout-rescale:通常爲了提高測試的性能(減少測試時的運算時間),可以將縮放的工作轉移到訓練階段,而測試階段與不使用dropout時相同。將前向傳播dropout時保留下來的神經元的權重除p(期望: p*a+(1-p)*0=pa,測試時不用再縮小權重),這一改動僅會影響訓練過程,而並不影響測試過程。
- 我們在LSTM循環層和全連接層之間使用Dropout正則化。還有一種方法可以將Dropout與LSTM之類的循環層一起使用。LSTM可以將相同的Dropout掩碼用於所有的輸入中。這個方法也可用於跨樣本時間步長的循環輸入連接。這種使用遞歸模型進行Dropout正則化則稱爲變分循環神經網絡(Variational RNN)。
model.add(Dropout(0.5))
- Embedding
嵌入層被定義爲網絡的第一個隱藏層。它必須指定3個參數:
input_dim:這是文本數據中詞彙的取值可能數。例如,如果您的數據是整數編碼爲0-9之間的值,那麼詞彙的大小就是10個單詞;
output_dim:這是嵌入單詞的向量空間的大小。它爲每個單詞定義了這個層的輸出向量的大小。例如,它可能是32或100甚至更大,可以視爲具體問題的超參數;
input_length:這是輸入序列的長度,就像您爲Keras模型的任何輸入層所定義的一樣,也就是一次輸入帶有的詞彙個數。例如,如果您的所有輸入文檔都由1000個字組成,那麼input_length就是1000。
例:embedding層將(1, 20)的一個輸入sample(最長爲20個單詞的句子,其中每個單詞表示爲一個int數字),嵌入爲一個(1, 20, 8)的向量,即將每個單詞embed爲一個8維的向量,而整個embedding層的參數就由神經網絡學習得到,數據經過embedding層之後就方便地轉換爲了可以由CNN或者RNN進一步處理的格式。
- Callback()
keras.callbacks.Callback()
params: 字典:verbosity, batch size, number of epochs…
model: keras.models.Model 的實例,指代被訓練模型。
被回調函數作爲參數的 logs 字典,它會含有於當前批量或訓練輪相關數據的鍵。目前,Sequential 模型類的 .fit() 方法會在傳入到回調函數的 logs 裏面包含以下的數據:
- on_epoch_end: 包括 acc 和 loss 的日誌, 也可以選擇性的包括 val_loss(如果在 fit 中啓用驗證),和 val_acc(如果啓用驗證和監測精確值。
- on_batch_begin: 包括 size 的日誌,在當前批量內的樣本數量。
- on_batch_end: 包括 loss 的日誌,也可以選擇性的包括 acc(如果啓用監測精確值。
可自定義Callback,例如繪製神經網絡訓練iteration_loss曲線,參考:使用keras繪製實時的loss與acc曲線