【深度学习】图文并茂!用Keras LSTM构建编码器-解码器模型
作者 | Nechu BM?
編譯 | VK?
來源 | Towards Data Science
基礎(chǔ)知識(shí):了解本文之前最好擁有關(guān)于循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)和編解碼器的知識(shí)。
本文是關(guān)于如何使用Python和Keras開發(fā)一個(gè)編解碼器模型的實(shí)用教程,更精確地說是一個(gè)序列到序列(Seq2Seq)。在上一個(gè)教程中,我們開發(fā)了一個(gè)多對(duì)多翻譯模型,如下圖所示:
這種結(jié)構(gòu)有一個(gè)重要的限制,即序列長(zhǎng)度。正如我們?cè)趫D像中看到的,輸入序列和輸出序列的長(zhǎng)度必須相同。如果我們需要不同的長(zhǎng)度呢?
例如,我們想實(shí)現(xiàn)一個(gè)接受不同序列長(zhǎng)度的模型,它接收一個(gè)單詞序列并輸出一個(gè)數(shù)字,或者是圖像字幕模型,其中輸入是一個(gè)圖像,輸出是一個(gè)單詞序列。
如果我們要開發(fā)的模型是輸入和輸出長(zhǎng)度不同,我們需要開發(fā)一個(gè)編解碼器模型。通過本教程,我們將了解如何開發(fā)模型,并將其應(yīng)用于翻譯練習(xí)。模型的表示如下所示。
我們將模型分成兩部分,首先,我們有一個(gè)編碼器,輸入西班牙語句子并產(chǎn)生一個(gè)隱向量。編碼器是用一個(gè)嵌入層將單詞轉(zhuǎn)換成一個(gè)向量然后用一個(gè)循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)來計(jì)算隱藏狀態(tài),這里我們將使用長(zhǎng)短期記憶(LSTM)層。
然后編碼器的輸出將被用作解碼器的輸入。對(duì)于解碼器,我們將再次使用LSTM層,以及預(yù)測(cè)英語單詞的全連接層。
實(shí)現(xiàn)
示例數(shù)據(jù)來自manythings.org。它是由語言的句子對(duì)組成的。在我們的案例中,我們將使用西班牙語-英語對(duì)。
建立模型首先需要對(duì)數(shù)據(jù)進(jìn)行預(yù)處理,得到西班牙語和英語句子的最大長(zhǎng)度。
1-預(yù)處理
先決條件:了解Keras中的類“tokenizer”和“pad_sequences”。如果你想詳細(xì)回顧一下,我們?cè)谏弦粋€(gè)教程中討論過這個(gè)主題。
首先,我們將導(dǎo)入庫,然后讀取下載的數(shù)據(jù)。
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()#?解析數(shù)據(jù) raw_data?=?raw_data.split('\n') pairs?=?[sentence.split('\t')?for?sentence?in??raw_data] pairs?=?pairs[1000:20000]一旦我們閱讀了數(shù)據(jù),我們將保留第一個(gè)例子,以便更快地進(jìn)行訓(xùn)練。如果我們想開發(fā)更高性能的模型,我們需要使用完整的數(shù)據(jù)集。然后我們必須通過刪除大寫字母和標(biāo)點(diǎn)符號(hào)來清理數(shù)據(jù)。
def?clean_sentence(sentence):#?把這個(gè)句子小寫lower_case_sent?=?sentence.lower()#?刪除標(biāo)點(diǎn)string_punctuation?=?string.punctuation?+?"?"?+?'?'clean_sentence?=?lower_case_sent.translate(str.maketrans('',?'',?string_punctuation))return?clean_sentence接下來,我們將句子標(biāo)識(shí)化并分析數(shù)據(jù)。
def?tokenize(sentences):#?創(chuàng)建?tokenizertext_tokenizer?=?Tokenizer()#?應(yīng)用到文本上text_tokenizer.fit_on_texts(sentences)return?text_tokenizer.texts_to_sequences(sentences),?text_tokenizer創(chuàng)建完函數(shù)后,我們可以進(jìn)行預(yù)處理:
#?清理句子 english_sentences?=?[clean_sentence(pair[0])?for?pair?in?pairs] spanish_sentences?=?[clean_sentence(pair[1])?for?pair?in?pairs]#?標(biāo)識(shí)化單詞 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))))#?檢查長(zhǎng)度 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))上面的代碼打印以下結(jié)果
根據(jù)之前的代碼,西班牙語句子的最大長(zhǎng)度為12個(gè)單詞,英語句子的最大長(zhǎng)度為6個(gè)單詞。在這里我們可以看到使用編解碼器模型的優(yōu)勢(shì)。以前我們處理等長(zhǎng)句子有局限性,所以我們需要對(duì)英語句子應(yīng)用填充到12,現(xiàn)在只需要一半。因此,更重要的是,它還減少了LSTM時(shí)間步數(shù),減少了計(jì)算需求和復(fù)雜性。
我們使用填充來使每種語言中句子的最大長(zhǎng)度相等。
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)現(xiàn)在我們已經(jīng)準(zhǔn)備好了數(shù)據(jù),讓我們構(gòu)建模型。
2.模型開發(fā)
在下一節(jié)中,我們將創(chuàng)建模型,并在python代碼中解釋添加的每一層。
2.1-編碼器
我們定義的第一層是圖像的嵌入層。為此,我們首先必須添加一個(gè)輸入層,這里唯一要考慮的參數(shù)是“shape”,這是西班牙語句子的最大長(zhǎng)度,在我們的例子中是12。
然后我們將其連接到嵌入層,這里要考慮的參數(shù)是“input_dim”(西班牙語詞匯表的長(zhǎng)度)和“output_dim”(嵌入向量的形狀)。此層將把西班牙語單詞轉(zhuǎn)換為輸出維度形狀的向量。
這背后的概念是以空間表示的形式提取單詞的含義,其中每個(gè)維度都是定義單詞的特征。例如,“sol”將轉(zhuǎn)換為形狀為128的向量。輸出維越高,從每個(gè)單詞中提取的語義意義就越多,但所需的計(jì)算和處理時(shí)間也就越高。我們也需要在速度和性能之間找到平衡。
input_sequence?=?Input(shape=(max_spanish_len,)) embedding?=?Embedding(input_dim=spanish_vocab,?output_dim=128,)(input_sequence)接下來,我們將添加大小為64的LSTM層。即使LSTM的每一個(gè)時(shí)間步都輸出一個(gè)隱藏向量,我們會(huì)把注意力集中在最后一個(gè),因此參數(shù)「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)當(dāng)返回序列為'False'時(shí),輸出是最后一個(gè)隱藏狀態(tài)。
2.2-解碼器
編碼器層的輸出將是最后一個(gè)時(shí)間步的隱藏狀態(tài)。然后我們需要把這個(gè)向量輸入解碼器。讓我們更精確地看一下解碼器部分,并了解它是如何工作的。
正如我們?cè)趫D像中看到的,隱藏向量被重復(fù)n次,因此LSTM的每個(gè)時(shí)間步都接收相同的向量。為了使每個(gè)時(shí)間步都有相同的向量,我們需要使用層RepeatVector,因?yàn)樗拿忠馕吨淖饔檬侵貜?fù)它接收的向量,我們需要定義的唯一參數(shù)是n,重復(fù)次數(shù)。這個(gè)數(shù)字等于譯碼器部分的時(shí)間步數(shù),換句話說就是英語句子的最大長(zhǎng)度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)一旦我們準(zhǔn)備好輸入,我們將繼續(xù)解碼器。這也是用LSTM層構(gòu)建的,區(qū)別在于參數(shù)return_sequences,在本例中為'True'。這個(gè)參數(shù)是用來做什么的?在編碼器部分,我們只期望在最后一個(gè)時(shí)間步中有一個(gè)向量,而忽略了其他所有的向量,這里我們期望每個(gè)時(shí)間步都有一個(gè)輸出向量,這樣全連接層就可以進(jìn)行預(yù)測(cè)。
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)我們還有最后一步,預(yù)測(cè)翻譯的單詞。為此,我們需要使用全連接層。我們需要定義的參數(shù)是單元數(shù),這個(gè)單元數(shù)是輸出向量的形狀,它需要與英語詞匯的長(zhǎng)度相同。為什么?這個(gè)向量的值都接近于零,除了其中一個(gè)單位接近于1。然后我們需要將輸出1的單元的索引映射到字典中,在字典中我們將每個(gè)單元映射到一個(gè)單詞。
例如,如果輸入是單詞‘sun’,而輸出是一個(gè)向量,其中所有都是零,然后單元472是1,那么我們將該索引映射到包含英語單詞的字典上,并得到值‘sun’。
我們剛剛看到了如何應(yīng)用全連接層來預(yù)測(cè)一個(gè)單詞,但是我們?nèi)绾螌?duì)整個(gè)句子進(jìn)行預(yù)測(cè)呢?因?yàn)槲覀兪褂胷eturn_sequence=True,所以LSTM層在每個(gè)時(shí)間步輸出一個(gè)向量,所以我們需要在每個(gè)時(shí)間步應(yīng)用前面解釋過的全連接層層,讓其每次預(yù)測(cè)一個(gè)單詞。
為此,Keras開發(fā)了一個(gè)稱為TimeDistributed的特定層,它將相同的全連接層應(yīng)用于每個(gè)時(shí)間步。
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)最后,我們創(chuàng)建模型并添加一個(gè)損失函數(shù)。
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()一旦我們定義了模型,我們就可以訓(xùn)練它了。
model_results?=?enc_dec_model.fit(spa_pad_sentence,?eng_pad_sentence,?batch_size=30,?epochs=100)當(dāng)模型訓(xùn)練好后,我們就可以進(jìn)行第一次翻譯了。你還可以找到函數(shù)“l(fā)ogits_to_sentence”,它將全連接層的輸出與英語詞匯進(jìn)行映射。
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))結(jié)論
編解碼器結(jié)構(gòu)允許不同的輸入和輸出序列長(zhǎng)度。首先,我們使用嵌入層來創(chuàng)建單詞的空間表示,并將其輸入LSTM層,因?yàn)槲覀冎魂P(guān)注最后一個(gè)時(shí)間步的輸出,我們使用return_sequences=False。
這個(gè)輸出向量需要重復(fù)的次數(shù)與解碼器部分的時(shí)間步數(shù)相同,為此我們使用RepeatVector層。解碼器將使用LSTM,參數(shù)return_sequences=True,因此每個(gè)時(shí)間步的輸出都會(huì)傳遞到全連接層。
盡管這個(gè)模型已經(jīng)是上一個(gè)教程的一個(gè)很好的改進(jìn),我們?nèi)匀豢梢蕴岣邷?zhǔn)確性。我們可以在一層的編碼器和解碼器中增加一層。我們也可以使用預(yù)訓(xùn)練的嵌入層,比如word2vec或Glove。最后,我們可以使用注意機(jī)制,這是自然語言處理領(lǐng)域的一個(gè)主要改進(jìn)。我們將在下一個(gè)教程中介紹這個(gè)概念。
附錄:不使用重復(fù)向量的編解碼器
在本教程中,我們了解了如何使用RepeatVector層構(gòu)建編碼器-解碼器。還有第二個(gè)選項(xiàng),我們使用模型的輸出作為下一個(gè)時(shí)間步驟的輸入,而不是重復(fù)隱藏的向量,如圖所示。
實(shí)現(xiàn)這個(gè)模型的代碼可以在Keras文檔中找到,它需要對(duì)Keras庫有更深入的理解,并且開發(fā)要復(fù)雜得多: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
往期精彩回顧適合初學(xué)者入門人工智能的路線及資料下載機(jī)器學(xué)習(xí)及深度學(xué)習(xí)筆記等資料打印機(jī)器學(xué)習(xí)在線手冊(cè)深度學(xué)習(xí)筆記專輯《統(tǒng)計(jì)學(xué)習(xí)方法》的代碼復(fù)現(xiàn)專輯 AI基礎(chǔ)下載機(jī)器學(xué)習(xí)的數(shù)學(xué)基礎(chǔ)專輯 獲取本站知識(shí)星球優(yōu)惠券,復(fù)制鏈接直接打開: https://t.zsxq.com/y7uvZF6 本站qq群704220115。加入微信群請(qǐng)掃碼:總結(jié)
以上是生活随笔為你收集整理的【深度学习】图文并茂!用Keras LSTM构建编码器-解码器模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【机器学习基础】算法工程师必备的机器学习
- 下一篇: 创造属于你自己的深度学习框架,就在这2天