十分鐘告訴你——何爲Keras中的序列到序列學習

做者 | Francois Chollet
編譯 | CDA數據分析師
原文 |  A ten-minute introduction to sequence-to-sequence learning in Keras

什麼是順序學習?

序列到序列學習(Seq2Seq)是關於將模型從一個域(例如英語中的句子)轉換爲另外一域(例如將相同句子翻譯爲法語的序列)的訓練模型。html

「貓坐在墊子上」 -> [ Seq2Seq 模型] -> 「在小吃中聊天小程序

這可用於機器翻譯或免費問答(在給定天然語言問題的狀況下生成天然語言答案)-一般,它可在須要生成文本的任什麼時候間使用。數組

有多種處理此任務的方法,可使用RNN或使用一維卷積網絡。在這裏,咱們將重點介紹RNN。網絡

普通狀況:輸入和輸出序列的長度相同

當輸入序列和輸出序列的長度相同時,您能夠簡單地使用Keras LSTM或GRU層(或其堆棧)來實現此類模型。在此示例腳本 中就是這種狀況, 該腳本顯示瞭如何教RNN學習加編碼爲字符串的數字:函數

該方法的一個警告是,它假定能夠生成target[...t]給定input[...t]。在某些狀況下(例如添加數字字符串),該方法有效,但在大多數用例中,則無效。在通常狀況下,有關整個輸入序列的信息是必需的,以便開始生成目標序列。oop

通常狀況:規範序列間

在通常狀況下,輸入序列和輸出序列具備不一樣的長度(例如,機器翻譯),而且須要整個輸入序列才能開始預測目標。這須要更高級的設置,這是人們在沒有其餘上下文的狀況下提到「序列模型的序列」時一般所指的東西。運做方式以下:學習

RNN層(或其堆棧)充當「編碼器」:它處理輸入序列並返回其本身的內部狀態。請注意,咱們放棄了編碼器RNN的輸出,僅恢復 了狀態。在下一步中,此狀態將用做解碼器的「上下文」或「條件」。測試

另外一個RNN層(或其堆棧)充當「解碼器」:在給定目標序列的先前字符的狀況下,對其進行訓練以預測目標序列的下一個字符。具體而言,它通過訓練以將目標序列變成相同序列,但在未來會偏移一個時間步,在這種狀況下,該訓練過程稱爲「教師強迫」。重要的是,編碼器使用來自編碼器的狀態向量做爲初始狀態,這就是解碼器如何獲取有關應該生成的信息的方式。有效地,解碼器學會產生targets[t+1...] 給定的targets[...t],調節所述輸入序列。編碼

在推斷模式下,即當咱們想解碼未知的輸入序列時,咱們會經歷一個略有不一樣的過程:spa

  • 將輸入序列編碼爲狀態向量。
  • 從大小爲1的目標序列開始(僅是序列開始字符)。
  • 將狀態向量和1個字符的目標序列饋送到解碼器,以生成下一個字符的預測。
  • 使用這些預測來採樣下一個字符(咱們僅使用argmax)。
  • 將採樣的字符追加到目標序列
  • 重複直到生成序列結束字符或達到字符數限制。

一樣的過程也能夠用於訓練Seq2Seq網絡,而無需 「教師強制」,即經過將解碼器的預測從新注入到解碼器中。

一個Keras例子

  • 將句子翻譯成3個numpy的陣列,encoderinputdata,decoderinputdata,decodertargetdata:
  • encoderinputdata是一個3D形狀的數組,(numpairs, maxenglishsentencelength, numenglishcharacters) 其中包含英語句子的一鍵向量化。
  • decoderinputdata是一個3D形狀的數組,(numpairs, maxfrenchsentencelength, numfrenchcharacters) 其中包含法語句子的一鍵矢量化。
  • decodertargetdata與相同,decoderinputdata但相差一個時間步長。 decodertargetdata[:, t, :]將與相同decoderinputdata[:, t + 1, :]。
  • 訓練基於LSTM的基本Seq2Seq模型,以預測decodertargetdata 給定encoderinputdata和decoderinputdata。咱們的模型使用教師強迫。
  • 解碼一些句子以檢查模型是否正常運行(即,將的樣本從encoderinputdata 轉換爲的對應樣本decodertargetdata)。

由於訓練過程和推理過程(解碼句子)有很大的不一樣,因此咱們對二者使用不一樣的模型,儘管它們都利用相同的內部層。

這是咱們的訓練模型。它利用Keras RNN的三個關鍵功能:

return_state構造器參數,配置RNN層返回一個列表,其中,第一項是輸出與下一個條目是內部RNN狀態。這用於恢復編碼器的狀態。

inital_state呼叫參數,指定一個RNN的初始狀態(S)。這用於將編碼器狀態做爲初始狀態傳遞給解碼器。

return_sequences構造函數的參數,配置RNN返回其輸出全序列(而不僅是最後的輸出,其默認行爲)。在解碼器中使用。

from keras.models import Model
from keras.layers import Input, LSTM, Dense

Define an input sequence and process it.

encoder_inputs = Input(shape=(None, num_encoder_tokens))
encoder = LSTM(latent_dim, return_state=True)
encoder_outputs, state_h, state_c = encoder(encoder_inputs)

We discard encoder_outputs and only keep the states.

encoder_states = [state_h, state_c]

Set up the decoder, using encoder_states as initial state.

decoder_inputs = Input(shape=(None, num_decoder_tokens))

We set up our decoder to return full output sequences,

and to return internal states as well. We don't use the 

return states in the training model, but we will use them in inference.

decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True)
decoder_outputs, , = decoder_lstm(decoder_inputs,
initial_state=encoder_states)
decoder_dense = Dense(num_decoder_tokens, activation='softmax')
decoder_outputs = decoder_dense(decoder_outputs)

Define the model that will turn

encoder_input_data & decoder_input_data into decoder_target_data

model = Model([encoder_inputs, decoder_inputs], decoder_outputs)

咱們分兩行訓練咱們的模型,同時監視20%的保留樣本中的損失。

Run training

model.compile(optimizer='rmsprop', loss='categorical_crossentropy')
model.fit([encoder_input_data, decoder_input_data], decoder_target_data,
batch_size=batch_size,
epochs=epochs,
validation_split=0.2)

在MacBook CPU上運行大約一個小時後,咱們就能夠進行推斷了。爲了解碼測試語句,咱們將反覆:

  • 對輸入語句進行編碼並檢索初始解碼器狀態
  • 以該初始狀態和「序列開始」令牌爲目標,運行解碼器的一步。輸出將是下一個目標字符。
  • 添加預測的目標字符並重復。

這是咱們的推理設置:

encoder_model = Model(encoder_inputs, encoder_states)

decoder_state_input_h = Input(shape=(latent_dim,))
decoder_state_input_c = Input(shape=(latent_dim,))
decoder_states_inputs = [decoder_state_input_h, decoder_state_input_c]
decoder_outputs, state_h, state_c = decoder_lstm(
decoder_inputs, initial_state=decoder_states_inputs)
decoder_states = [state_h, state_c]
decoder_outputs = decoder_dense(decoder_outputs)
decoder_model = Model(
[decoder_inputs] + decoder_states_inputs,
[decoder_outputs] + decoder_states)

咱們使用它來實現上述推理循環:

def decode_sequence(input_seq):

Encode the input as state vectors.

states_value = encoder_model.predict(input_seq)

Generate empty target sequence of length 1.

target_seq = np.zeros((1, 1, num_decoder_tokens))

Populate the first character of target sequence with the start character.

target_seq[0, 0, target_token_index['t']] = 1.

Sampling loop for a batch of sequences

(to simplify, here we assume a batch of size 1).

stop_condition = False
decoded_sentence = ''
while not stop_condition:
output_tokens, h, c = decoder_model.predict(
[target_seq] + states_value)

Sample a token

sampled_token_index = np.argmax(output_tokens[0, -1, :])
sampled_char = reverse_target_char_index[sampled_token_index]
decoded_sentence += sampled_char

Exit condition: either hit max length

or find stop character.

if (sampled_char == 'n' or
len(decoded_sentence) > max_decoder_seq_length):
stop_condition = True

Update the target sequence (of length 1).

target_seq = np.zeros((1, 1, num_decoder_tokens))
target_seq[0, 0, sampled_token_index] = 1.

Update states

states_value = [h, c]

return decoded_sentence

咱們獲得了一些不錯的結果-絕不奇怪,由於咱們正在解碼從訓練測試中提取的樣本

Input sentence: Be nice.
Decoded sentence: Soyez gentil !
-
Input sentence: Drop it!
Decoded sentence: Laissez tomber !
-
Input sentence: Get out!
Decoded sentence: Sortez !

到此,咱們結束了對Keras中序列到序列模型的十分鐘介紹。提醒:此腳本的完整代碼能夠在GitHub上找到。

掃碼進入CDA官方小程序,解鎖更多新鮮資訊和優質內容,還有免費試聽課程,不要錯過喲!

相關文章
相關標籤/搜索