【深度學習】圖文並茂!用Keras LSTM構建編碼器-解碼器模型

2021-02-23 機器學習初學者

編譯 | VK 

來源 | Towards Data Science

基礎知識:了解本文之前最好擁有關於循環神經網絡(RNN)和編解碼器的知識。

本文是關於如何使用Python和Keras開發一個編解碼器模型的實用教程,更精確地說是一個序列到序列(Seq2Seq)。在上一個教程中,我們開發了一個多對多翻譯模型,如下圖所示:

這種結構有一個重要的限制,即序列長度。正如我們在圖像中看到的,輸入序列和輸出序列的長度必須相同。如果我們需要不同的長度呢?

例如,我們想實現一個接受不同序列長度的模型,它接收一個單詞序列並輸出一個數字,或者是圖像字幕模型,其中輸入是一個圖像,輸出是一個單詞序列。

如果我們要開發的模型是輸入和輸出長度不同,我們需要開發一個編解碼器模型。通過本教程,我們將了解如何開發模型,並將其應用於翻譯練習。模型的表示如下所示。

我們將模型分成兩部分,首先,我們有一個編碼器,輸入西班牙語句子並產生一個隱向量。編碼器是用一個嵌入層將單詞轉換成一個向量然後用一個循環神經網絡(RNN)來計算隱藏狀態,這裡我們將使用長短期記憶(LSTM)層。

然後編碼器的輸出將被用作解碼器的輸入。對於解碼器,我們將再次使用LSTM層,以及預測英語單詞的全連接層。

實現

示例數據來自manythings.org。它是由語言的句子對組成的。在我們的案例中,我們將使用西班牙語-英語對。

建立模型首先需要對數據進行預處理,得到西班牙語和英語句子的最大長度。

1-預處理

先決條件:了解Keras中的類「tokenizer」和「pad_sequences」。如果你想詳細回顧一下,我們在上一個教程中討論過這個主題。

首先,我們將導入庫,然後讀取下載的數據。

import string
import numpy as np

from 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):
    # 創建 tokenizer
    text_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

獲取本站知識星球優惠券,複製連結直接打開:

https://t.zsxq.com/y7uvZF6

本站qq群704220115。

加入微信群請掃碼:

相關焦點

  • 用Keras LSTM構建編碼器-解碼器模型
    編碼器是用一個嵌入層將單詞轉換成一個向量然後用一個循環神經網絡(RNN)來計算隱藏狀態,這裡我們將使用長短期記憶(LSTM)層。然後編碼器的輸出將被用作解碼器的輸入。對於解碼器,我們將再次使用LSTM層,以及預測英語單詞的全連接層。實現示例數據來自manythings.org。
  • 【乾貨】seq2seq模型實例:用Keras實現機器翻譯
    model使用編碼解碼器模型實現從英文翻譯成法語 這篇文章受啟發於keras的例子和關於編碼器- 解碼器網絡的論文。本文中我自己關於這個例子的實現可以在我個人的GitHub中找到 keras的例子連結:https://github.com/keras-team/keras/blob/master/examples/lstm_seq2seq.py編碼器- 解碼器網絡論文:https://arxiv.org/abs/1409.3215
  • 深度學習筆記 | 第17講:深度生成模型之自編碼器(AutoEncoder)
    所謂自編碼器(Autoencoder,AE),就是一種利用反向傳播算法使得輸出值等於輸入值的神經網絡,它現將輸入壓縮成潛在空間表徵,然後將這種表徵重構為輸出。所以,從本質上來講,自編碼器是一種數據壓縮算法,其壓縮和解壓縮算法都是通過神經網絡來實現的。自編碼器有如下三個特點:數據相關性。就是指自編碼器只能壓縮與自己此前訓練數據類似的數據,比如說我們使用mnist訓練出來的自編碼器用來壓縮人臉圖片,效果肯定會很差。數據有損性。自編碼器在解壓時得到的輸出與原始輸入相比會有信息損失,所以自編碼器是一種數據有損的壓縮算法。
  • python用於NLP的seq2seq模型實例:用Keras實現神經機器翻譯
    對於標記化,可以使用庫中的Tokenizer類keras.preprocessing.text。本tokenizer類執行兩個任務:這是非常重要的,因為深度學習和機器學習算法可以處理數字。  除了標記化和整數轉換外,該類的word_index屬性還Tokenizer返回一個單詞索引字典,其中單詞是鍵,而相應的整數是值。
  • 深度學習第51講:自編碼器(AutoEncoder)及其keras實現
    從現有情況來看,無監督學習很有可能是一把決定深度學習未來發展方向的鑰匙,在缺乏高質量打標數據的監督機器學習時代,若是能在無監督學習方向上有所突破對於未來深度學習的發展意義重大。從自編碼器到生成對抗網絡,筆者將和大家一起來探索深度學習中的無監督學習。
  • 十分鐘掌握Keras實現RNN的seq2seq學習
    這需要一個更高級的設置,這就是人們在「序列到序列模型」時經常提及的沒有上下文。下面是它的工作原理:有一個RNN層(或其堆疊)作為「編碼器」:它負責處理輸入序列並返回其自身的內部狀態。注意,我們將丟棄編碼器RNN的輸出,只恢復狀態。該狀態將在下一步驟中用作解碼器的「上下文」或「環境」。
  • 入門 | 十分鐘搞定Keras序列到序列學習(附代碼實現)
    GitHub:https://github.com/fchollet/keras/blob/master/examples/lstm_seq2seq.py什麼是序列到序列學習?序列到序列學習(Seq2Seq)是指訓練模型從而把一個域的序列(比如英語語句)轉化為另一個域的序列(比如法語中的對應語句)。
  • 更深的編碼器+更淺的解碼器=更快的自回歸模型
    從另一個方面看,如果解碼器更小(即層數更少),那麼即使是自回歸模型,是不是也可以顯著提高生成效率呢?這就是本文要回答的問題:當模型解碼器變淺、編碼器變深時,模型是否還能在保持原來效果的前提下,提升生成效率。這可以用下面的圖表示:
  • CICC科普欄目|十分鐘搞定Keras序列到序列學習(附代碼實現)
    GitHub:https://github.com/fchollet/keras/blob/master/examples/lstm_seq2seq.py什麼是序列到序列學習?序列到序列學習(Seq2Seq)是指訓練模型從而把一個域的序列(比如英語語句)轉化為另一個域的序列(比如法語中的對應語句)。
  • 如何Keras自動編碼器給極端罕見事件分類
    近來,深度學習被廣泛應用於分類中。然而正樣本數太少不利於深度學習的應用。不論數據總量多大,深度學習的使用都會受制於陽性數據的數量。2. 為什麼要使用深度學習?這個問題很合理。為什麼不考慮使用其他機器學習方法呢?答案很主觀。我們總是可以採用某種機器學習方法來達到目的。
  • 用 Keras 功能 API 進行深度學習
    Keras Python庫可快速輕鬆地創建深度學習模型。順序API允許您針對大多數問題逐層創建模型。它的局限性在於它不允許您創建共享圖層或具有多個輸入或輸出的模型。Keras中的功能性API是創建模型的替代方法,它提供了更大的靈活性,包括創建更複雜的模型。在本教程中,您將發現如何在Keras中使用更靈活的功能API來定義深度學習模型。
  • 【Keras教程】用Encoder-Decoder模型自動撰寫文本摘要
    【導讀】這篇博文介紹了如何在深度學習框架Keras上實現文本摘要問題,探討了如何使用編碼器-解碼器遞歸神經網絡體系結構來解決文本摘要問題,如何實現文本摘要問題的不同的編碼器和解碼器,博文通俗易懂,專知內容組整理出來,希望大家喜歡。
  • 手把手教你用 Keras 實現 LSTM 預測英語單詞發音
    出於這個原因,我們將創建一個帶有編碼器 (encoder) 和解碼器 (decoder) 兩個部分的 seq2seq [博客連結](https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html) 模型。
  • 用Python將Keras深度學習模型部署為Web應用程式
    我們想要的是將深度學習模型部署為世界上任何人都可以訪問的Web應用程式。在本文中,我們將看到如何編寫一個Web應用程式,該應用程式使用經過訓練的Keras遞歸神經網絡,並允許用戶生成新的專利文摘。這個項目建立在遞歸神經網絡的基礎上,但是了解如何創建RNN是不必要的。現在我們將其視為黑匣子:我們按一個開始的順序進行操作,它輸出一個全新的專利文摘,可以在瀏覽器中顯示!
  • 獨家 | Python利用深度學習進行文本摘要的綜合指南(附教程)
    我們使用深度學習完成的文本摘要結果如何呢?非常出色。因此,在本文中,我們將逐步介紹使用深度學習構建文本摘要器的過程,其中包含構建它所需的全部概念。然後將用Python實現我們的第一個文本摘要模型!注意:本文要求對一些深度學習概念有基本的了解。 我建議閱讀以下文章。
  • Keras 深度學習模型可視化
    深度學習可視化深度學習的過程是一個黑盒子,模型通過大量的權重去學習擬合輸入的數據和學習目標,模型的性能很大程度上取決於模型的輸入的數據;深度學習的擬合效果往往出乎我們的的想像,但是模型如何擬合數據和學習目標之間的關係,我們知之甚少。
  • 為文本摘要模型添加注意力機制:採用LSTM的編解碼器模型實現
    摘要文本摘要算法創建新的短語和句子,從原始文本中傳遞最有用的信息——就像人類一樣。在本文中,我們將重點研究抽象摘要技術,並將利用編解碼器架構來解決這一問題。什麼是編碼器-解碼器架構?常用的序列-序列模型(編碼器-解碼器)的整體結構如下圖所示該模型由編碼器、中間矢量和解碼器三部分組成。編碼器該編碼器基本上由一系列LSTM/GRU單元組成(請查看LSTM/GRU文檔以更好地理解架構)。
  • 在Keras中可視化LSTM
    在本文中,我們不僅將在Keras中構建文本生成模型,還將可視化生成文本時某些單元格正在查看的內容。就像CNN一樣,它學習圖像的一般特徵,例如水平和垂直邊緣,線條,斑塊等。類似,在「文本生成」中,LSTM則學習特徵(例如空格,大寫字母,標點符號等)。 LSTM層學習每個單元中的特徵。
  • 最適合初學者的深度學習框架 -- Keras官方中文版文檔正式發布
    Keras 相對於其它深度學習庫非常容易構建:首先它提供一致和簡單的 API;其次,它提供獨立的、完全可配置的模塊構成序列或圖表以完成模型;最後,作為新的類和函數,新的模塊很容易擴展。這樣說可能比較抽象,但正如文檔中所描述的,我們甚至在 30 秒就能快速上手 Keras。所以在坑外徘徊或準備入坑 Keras 的小夥伴可以開心地開始你們的 30 秒。
  • Keras構建模型的一般流程
    首先引入 tensorflow 和 keras。import tensorflow as tfimport tensorflow.keras as kerasKeras 是深度學習框架,裡面有各種深度學習模型,介紹它之前讓我們先回憶下它的好兄弟 - 機器學習框架 Scikit-Learn。