【深度学习】图文并茂!用Keras LSTM构建编码器-解码器模型
作者 | Nechu BM?
編譯 | VK?
來源 | Towards Data Science
基礎知識:了解本文之前最好擁有關于循環神經網絡(RNN)和編解碼器的知識。
本文是關于如何使用Python和Keras開發一個編解碼器模型的實用教程,更精確地說是一個序列到序列(Seq2Seq)。在上一個教程中,我們開發了一個多對多翻譯模型,如下圖所示:
這種結構有一個重要的限制,即序列長度。正如我們在圖像中看到的,輸入序列和輸出序列的長度必須相同。如果我們需要不同的長度呢?
例如,我們想實現一個接受不同序列長度的模型,它接收一個單詞序列并輸出一個數字,或者是圖像字幕模型,其中輸入是一個圖像,輸出是一個單詞序列。
如果我們要開發的模型是輸入和輸出長度不同,我們需要開發一個編解碼器模型。通過本教程,我們將了解如何開發模型,并將其應用于翻譯練習。模型的表示如下所示。
我們將模型分成兩部分,首先,我們有一個編碼器,輸入西班牙語句子并產生一個隱向量。編碼器是用一個嵌入層將單詞轉換成一個向量然后用一個循環神經網絡(RNN)來計算隱藏狀態,這里我們將使用長短期記憶(LSTM)層。
然后編碼器的輸出將被用作解碼器的輸入。對于解碼器,我們將再次使用LSTM層,以及預測英語單詞的全連接層。
實現
示例數據來自manythings.org。它是由語言的句子對組成的。在我們的案例中,我們將使用西班牙語-英語對。
建立模型首先需要對數據進行預處理,得到西班牙語和英語句子的最大長度。
1-預處理
先決條件:了解Keras中的類“tokenizer”和“pad_sequences”。如果你想詳細回顧一下,我們在上一個教程中討論過這個主題。
首先,我們將導入庫,然后讀取下載的數據。
import?string import?numpy?as?npfrom?keras.preprocessing.text?import?Tokenizer from?keras.preprocessing.sequence?import?pad_sequences from?keras.models?import?Model from?keras.layers?import?LSTM,?Input,?TimeDistributed,?Dense,?Activation,?RepeatVector,?Embedding from?keras.optimizers?import?Adam from?keras.losses?import?sparse_categorical_crossentropy#?翻譯文件的路徑 path_to_data?=?'data/spa.txt'#?讀文件 translation_file?=?open(path_to_data,"r",?encoding='utf-8')? raw_data?=?translation_file.read() translation_file.close()#?解析數據 raw_data?=?raw_data.split('\n') pairs?=?[sentence.split('\t')?for?sentence?in??raw_data] pairs?=?pairs[1000:20000]一旦我們閱讀了數據,我們將保留第一個例子,以便更快地進行訓練。如果我們想開發更高性能的模型,我們需要使用完整的數據集。然后我們必須通過刪除大寫字母和標點符號來清理數據。
def?clean_sentence(sentence):#?把這個句子小寫lower_case_sent?=?sentence.lower()#?刪除標點string_punctuation?=?string.punctuation?+?"?"?+?'?'clean_sentence?=?lower_case_sent.translate(str.maketrans('',?'',?string_punctuation))return?clean_sentence接下來,我們將句子標識化并分析數據。
def?tokenize(sentences):#?創建?tokenizertext_tokenizer?=?Tokenizer()#?應用到文本上text_tokenizer.fit_on_texts(sentences)return?text_tokenizer.texts_to_sequences(sentences),?text_tokenizer創建完函數后,我們可以進行預處理:
#?清理句子 english_sentences?=?[clean_sentence(pair[0])?for?pair?in?pairs] spanish_sentences?=?[clean_sentence(pair[1])?for?pair?in?pairs]#?標識化單詞 spa_text_tokenized,?spa_text_tokenizer?=?tokenize(spanish_sentences) eng_text_tokenized,?eng_text_tokenizer?=?tokenize(english_sentences)print('Maximum?length?spanish?sentence:?{}'.format(len(max(spa_text_tokenized,key=len)))) print('Maximum?length?english?sentence:?{}'.format(len(max(eng_text_tokenized,key=len))))#?檢查長度 spanish_vocab?=?len(spa_text_tokenizer.word_index)?+?1 english_vocab?=?len(eng_text_tokenizer.word_index)?+?1 print("Spanish?vocabulary?is?of?{}?unique?words".format(spanish_vocab)) print("English?vocabulary?is?of?{}?unique?words".format(english_vocab))上面的代碼打印以下結果
根據之前的代碼,西班牙語句子的最大長度為12個單詞,英語句子的最大長度為6個單詞。在這里我們可以看到使用編解碼器模型的優勢。以前我們處理等長句子有局限性,所以我們需要對英語句子應用填充到12,現在只需要一半。因此,更重要的是,它還減少了LSTM時間步數,減少了計算需求和復雜性。
我們使用填充來使每種語言中句子的最大長度相等。
max_spanish_len?=?int(len(max(spa_text_tokenized,key=len))) max_english_len?=?int(len(max(eng_text_tokenized,key=len)))spa_pad_sentence?=?pad_sequences(spa_text_tokenized,?max_spanish_len,?padding?=?"post") eng_pad_sentence?=?pad_sequences(eng_text_tokenized,?max_english_len,?padding?=?"post")#?重塑 spa_pad_sentence?=?spa_pad_sentence.reshape(*spa_pad_sentence.shape,?1) eng_pad_sentence?=?eng_pad_sentence.reshape(*eng_pad_sentence.shape,?1)現在我們已經準備好了數據,讓我們構建模型。
2.模型開發
在下一節中,我們將創建模型,并在python代碼中解釋添加的每一層。
2.1-編碼器
我們定義的第一層是圖像的嵌入層。為此,我們首先必須添加一個輸入層,這里唯一要考慮的參數是“shape”,這是西班牙語句子的最大長度,在我們的例子中是12。
然后我們將其連接到嵌入層,這里要考慮的參數是“input_dim”(西班牙語詞匯表的長度)和“output_dim”(嵌入向量的形狀)。此層將把西班牙語單詞轉換為輸出維度形狀的向量。
這背后的概念是以空間表示的形式提取單詞的含義,其中每個維度都是定義單詞的特征。例如,“sol”將轉換為形狀為128的向量。輸出維越高,從每個單詞中提取的語義意義就越多,但所需的計算和處理時間也就越高。我們也需要在速度和性能之間找到平衡。
input_sequence?=?Input(shape=(max_spanish_len,)) embedding?=?Embedding(input_dim=spanish_vocab,?output_dim=128,)(input_sequence)接下來,我們將添加大小為64的LSTM層。即使LSTM的每一個時間步都輸出一個隱藏向量,我們會把注意力集中在最后一個,因此參數「return_sequences」 是'False'。我們將看到LSTM層如何在解碼器的return_sequences=True的情況下工作。
input_sequence?=?Input(shape=(max_spanish_len,)) embedding?=?Embedding(input_dim=spanish_vocab,?output_dim=128,)(input_sequence) encoder?=?LSTM(64,?return_sequences=False)(embedding)當返回序列為'False'時,輸出是最后一個隱藏狀態。
2.2-解碼器
編碼器層的輸出將是最后一個時間步的隱藏狀態。然后我們需要把這個向量輸入解碼器。讓我們更精確地看一下解碼器部分,并了解它是如何工作的。
正如我們在圖像中看到的,隱藏向量被重復n次,因此LSTM的每個時間步都接收相同的向量。為了使每個時間步都有相同的向量,我們需要使用層RepeatVector,因為它的名字意味著它的作用是重復它接收的向量,我們需要定義的唯一參數是n,重復次數。這個數字等于譯碼器部分的時間步數,換句話說就是英語句子的最大長度6。
input_sequence?=?Input(shape=(max_spanish_len,)) embedding?=?Embedding(input_dim=spanish_vocab,?output_dim=128,)(input_sequence) encoder?=?LSTM(64,?return_sequences=False)(embedding) r_vec?=?RepeatVector(max_english_len)(encoder)一旦我們準備好輸入,我們將繼續解碼器。這也是用LSTM層構建的,區別在于參數return_sequences,在本例中為'True'。這個參數是用來做什么的?在編碼器部分,我們只期望在最后一個時間步中有一個向量,而忽略了其他所有的向量,這里我們期望每個時間步都有一個輸出向量,這樣全連接層就可以進行預測。
input_sequence?=?Input(shape=(max_spanish_len,)) embedding?=?Embedding(input_dim=spanish_vocab,?output_dim=128,)(input_sequence) encoder?=?LSTM(64,?return_sequences=False)(embedding) r_vec?=?RepeatVector(max_english_len)(encoder) decoder?=?LSTM(64,?return_sequences=True,?dropout=0.2)(r_vec)我們還有最后一步,預測翻譯的單詞。為此,我們需要使用全連接層。我們需要定義的參數是單元數,這個單元數是輸出向量的形狀,它需要與英語詞匯的長度相同。為什么?這個向量的值都接近于零,除了其中一個單位接近于1。然后我們需要將輸出1的單元的索引映射到字典中,在字典中我們將每個單元映射到一個單詞。
例如,如果輸入是單詞‘sun’,而輸出是一個向量,其中所有都是零,然后單元472是1,那么我們將該索引映射到包含英語單詞的字典上,并得到值‘sun’。
我們剛剛看到了如何應用全連接層來預測一個單詞,但是我們如何對整個句子進行預測呢?因為我們使用return_sequence=True,所以LSTM層在每個時間步輸出一個向量,所以我們需要在每個時間步應用前面解釋過的全連接層層,讓其每次預測一個單詞。
為此,Keras開發了一個稱為TimeDistributed的特定層,它將相同的全連接層應用于每個時間步。
input_sequence?=?Input(shape=(max_spanish_len,)) embedding?=?Embedding(input_dim=spanish_vocab,?output_dim=128,)(input_sequence) encoder?=?LSTM(64,?return_sequences=False)(embedding) r_vec?=?RepeatVector(max_english_len)(encoder) decoder?=?LSTM(64,?return_sequences=True,?dropout=0.2)(r_vec) logits?=?TimeDistributed(Dense(english_vocab))(decoder)最后,我們創建模型并添加一個損失函數。
enc_dec_model?=?Model(input_sequence,?Activation('softmax')(logits)) enc_dec_model.compile(loss=sparse_categorical_crossentropy,optimizer=Adam(1e-3),metrics=['accuracy']) enc_dec_model.summary()一旦我們定義了模型,我們就可以訓練它了。
model_results?=?enc_dec_model.fit(spa_pad_sentence,?eng_pad_sentence,?batch_size=30,?epochs=100)當模型訓練好后,我們就可以進行第一次翻譯了。你還可以找到函數“logits_to_sentence”,它將全連接層的輸出與英語詞匯進行映射。
def?logits_to_sentence(logits,?tokenizer):index_to_words?=?{idx:?word?for?word,?idx?in?tokenizer.word_index.items()}index_to_words[0]?=?'<empty>'?return?'?'.join([index_to_words[prediction]?for?prediction?in?np.argmax(logits,?1)])index?=?14 print("The?english?sentence?is:?{}".format(english_sentences[index])) print("The?spanish?sentence?is:?{}".format(spanish_sentences[index])) print('The?predicted?sentence?is?:') print(logits_to_sentence(enc_dec_model.predict(spa_pad_sentence[index:index+1])[0],?eng_text_tokenizer))結論
編解碼器結構允許不同的輸入和輸出序列長度。首先,我們使用嵌入層來創建單詞的空間表示,并將其輸入LSTM層,因為我們只關注最后一個時間步的輸出,我們使用return_sequences=False。
這個輸出向量需要重復的次數與解碼器部分的時間步數相同,為此我們使用RepeatVector層。解碼器將使用LSTM,參數return_sequences=True,因此每個時間步的輸出都會傳遞到全連接層。
盡管這個模型已經是上一個教程的一個很好的改進,我們仍然可以提高準確性。我們可以在一層的編碼器和解碼器中增加一層。我們也可以使用預訓練的嵌入層,比如word2vec或Glove。最后,我們可以使用注意機制,這是自然語言處理領域的一個主要改進。我們將在下一個教程中介紹這個概念。
附錄:不使用重復向量的編解碼器
在本教程中,我們了解了如何使用RepeatVector層構建編碼器-解碼器。還有第二個選項,我們使用模型的輸出作為下一個時間步驟的輸入,而不是重復隱藏的向量,如圖所示。
實現這個模型的代碼可以在Keras文檔中找到,它需要對Keras庫有更深入的理解,并且開發要復雜得多:https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html
原文鏈接:https://towardsdatascience.com/how-to-build-an-encoder-decoder-translation-model-using-lstm-with-python-and-keras-a31e9d864b9b
往期精彩回顧適合初學者入門人工智能的路線及資料下載機器學習及深度學習筆記等資料打印機器學習在線手冊深度學習筆記專輯《統計學習方法》的代碼復現專輯 AI基礎下載機器學習的數學基礎專輯 獲取本站知識星球優惠券,復制鏈接直接打開: https://t.zsxq.com/y7uvZF6 本站qq群704220115。加入微信群請掃碼:總結
以上是生活随笔為你收集整理的【深度学习】图文并茂!用Keras LSTM构建编码器-解码器模型的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【机器学习基础】算法工程师必备的机器学习
- 下一篇: 创造属于你自己的深度学习框架,就在这2天