當前位置:
首頁 > 知識 > 如何用 seq2seq 模型來應對 NLP 任務

如何用 seq2seq 模型來應對 NLP 任務

本文為 AI 研習社編譯的技術博客,原標題 :

Solving NLP task using Sequence2Sequence model: from Zero to Hero

作者 |Dima Shulga

翻譯 | 鄧普斯?傑弗、Zedom0 編輯 | 王立魚

https://towardsdatascience.com/solving-nlp-task-using-sequence2sequence-model-from-zero-to-hero-c193c1bd03d1

今天我想要解決一個非常流行的NLP任務,它叫做命名實體識別(NER)。簡單來說,NER是從單詞序列(一個句子)中抽取命名實體的任務。例如,給出下列句子:

"Jim在2006年買了Acme公司的300股"

我們會說:"Jim"是一個人,"Acme"是一個組織,"2006"是時間。

為了完成命名實體識別任務,我會使用公開的Kaggle數據集(dataset),跳過所有的數據處理代碼,專註於實際的問題和它的解決方案。你可以在這個notebook中看到完整代碼。 在這個數據集中有很多的實體類型,如個人(PER),組織(ORG)等等,每個實體類型都有兩種標籤:"B-SOMETAG" 和 "I-SOMETAG". B代表實體名的開始,I代表這個實體的延續。如果我們有一個實體:世界衛生組織",對應的標籤就是: [B-ORG, I-ORG, I-ORG]

這有個從數據集中獲取的樣例:

由此我們得到了一些單詞序列(一個句子),然後我們想要預測每個詞的類別。這跟分類、回歸這樣的平常的機器學習任務不同,我們得到了一個序列,輸出的也將是同樣長度的序列。

有很多方法來解決命名實體識別問題,以下是我的策略:

把這個任務視作給每個句子每個詞的分類任務,以此構建一個非常簡單的模型,並把這個模型作為基準。

用Keras構建一個 序列到序列(Seq2Seq)的模型。

找到如何正確衡量與比較結果的方法。

在Seq2Seq模型中使用Glove預訓練的詞嵌入(embedding)。

你可以隨意選擇任何部分。

詞袋和多分類

如我先前提及的那樣,我們的輸出是一個類別序列。首先,我想要嘗試一個樸素的方法:一個簡單的多分類模型。我想要把每個句子中的每個詞看作是一個單獨的實例,然後對於每個詞來預測它的類別,類別可能是O,B-ORG, I-ORG, B-PER 等等。 這當然不是對這個問題建模的最佳方法,但我想這樣做有兩個原因:首先,我想要創建一個儘可能簡單的基準模型。其次,我想要表明序列對序列模型在我們處理序列時表現要優秀得多。 很多時候,當我們想要對現實生活中的問題進行建模的時候,我們通常不清楚我們到底要解決什麼樣的問題。有時我們嘗試把這些問題建模成簡單的分類任務,但實際上用序列模型或許會更好。

如我所說,我把這個方法作為基準,並把問題儘可能簡化,所以對每個詞(實例),我的特徵會是詞向量(詞袋)以及同個句子中的所有其它詞。我的目標變數是17個類標籤中的一個。

因此給定如下句子:

「The World Health Organization says 227 people have died from bird flu」

我們得到了12個實例:

現在我們的任務是,給定一個句子中的一個詞,預測它的類別

我們的數據集中有47958個句子,我們把它們劃分到訓練集和測試集中:

我們用上面的方法把所有的句子轉換成很多個詞的實例。在訓練集中我們有839214個單詞實例。

我們的X有50892維,其中包括: 當前詞的獨熱(one hot)向量,同個句子中所有其它詞的詞袋(Bag of words)向量。

我們用梯度上升分類器作為預測器:

由此得到:

它表現地好嗎?難說,但起碼看起來不差。 我們可能想到好幾種改進模型的方法,但這不是本文的目的,如我所說,我想要讓它成為一個非常簡單的基準。

但我們現在面臨一個問題:不能正確地衡量我們地模型。我們得到了每個詞的精確率\召回率,但它沒有告訴我們關於真實實體的任何東西,舉個簡單的例子,還是那個句子:

「The World Health Organization says 227 people have died from bird flu」

我們有3個類是ORG,如果只正確地預測出其中兩個,會得到66%的正確率,但實際上我們並沒有把"World Health Organization" 這個實體正確抽取出來,所以實際的正確率應該是0!

之後我會談到更好的衡量命名實體識別的方法,但首先,構建我們的 "序列到序列"(Seq2Seq)模型吧。

序列到序列模型

前面的方法的一個主要缺點在於我們丟失了詞之間的依賴信息。給定一個句子中的一個詞,如果我們知道這個詞的左邊(或右邊)的詞是一個實體,那會有益於我們進行預測。如果我們為每個詞構建實例的話,就很難做到這點,我們在預測的時候也無法獲取這個信息。這是我們把整個句子作為一個樣本實例的原因。

我們能夠用很多不同的模型做到序列預測,如隱馬爾科夫模型(HMM)、條件隨機場(CRF)或許做的不錯,但在這我想要用Keras實現一個循環神經網路(RNN)。

為了使用Keras,我們需要把我們的句子轉換成數字序列,每個數字代表一個詞,而後我們需要讓所有數字序列等長,對此我們可以用Keras里的util函數來實現。

首先,我們構建一個分詞器(Tokenizer)來幫我們把詞轉換成數字,值得注意的是,我們只能在訓練集中構建分詞器。

然後,我們可以用分詞器來創建數字序列並把它們填充成等長的序列:

我們可以看到訓練集有38366個序列,測試集中有9592個序列,每個序列有75個詞。

對標籤也進行相同的操作,我就跳過這部分的代碼了,你可以在原文里看到這部分的代碼。

可以看到,訓練集中有38366個序列,測試集中有9592個序列,每個序列有17個標籤。

現在我們準備來構建模型了,我們將使用已被證明在這類任務中十分有效的雙向長短時記憶(LSTM)層:

來看一下上面的代碼。

我們的第一層是 Input, 它接受維度是 (75,)的向量,這跟X變數匹配(我們訓練集和測試集的序列長度為75)。

然後就是Embedding層,這個層會獲取每個詞並把它們轉換成300維的稠密向量。我們可以把它看作是一個巨大的查詢表(詞典),詞的id是鍵(key),而實際的向量是值(value)。這個查詢表是可訓練的,在模型每輪訓練中,我們將會更新這些向量以此來匹配輸入。

在經過Embedding層之後,我們的輸入從長為75的向量變為維度為(75,300)的矩陣,75個詞現在每個都有300維的向量。

一旦我們有了這個,我們就可以用雙向LSTM層來讓每個詞看到它前後方向的詞,並返回在之後有助於我們分類這個詞的狀態。默認來說,LSTM層會返回一個向量(最後一個),但在這個例子中,我們想要每個詞一個向量,所以我們設置 return_sequences=True。

雙向LSTM看起來如下圖:

該層的輸出是一個(75, 128)維的矩陣,75個詞,兩個方向每個64個數字。

最終我們用Time Distributed Dense層(當設置return_sequences=True時它由Dense 變為 Time Distributed Dense)

它將LSTM層的(75,128)維輸出作為輸入並返回需要的(75,18)維矩陣,75個詞,每個詞分別有17個標籤的概率,最後一個為__PADDING__標籤。

調用model.summary()方法就可以非常清晰地看到發生了什麼:

你可以看到所有的層以及它們的輸入輸出維度。此外,我們還可以看到模型的參數數目。你可能會注意到embedding層有最多的參數,原因在於我們有很多詞,每個詞都需要學習300個數字。後文中,我會用預訓練的embedding來改進我們的模型。

我們來訓練這個模型吧:

我們在測試集上的準確率是98.6%。鑒於我們大多數的標籤都是0(其它),因此這個準確率並沒有告訴我們很多信息。我們想要看到先前那樣的每個類的精確率和召回率,但如同我在前面小節提到的那樣,這不是衡量我們的模型最好的方式。我們想要的是一種,能夠知道我們可以正確預測多少不同類型實體的方法。

序列到序列模型的評估

在處理序列時,我們的標籤/實體也可能是序列。如前所述,如果把「World Health Organisation」作為真正的實體,那麼預測「World Organisation」或「World Health」可能會使我們在辭彙層面上的準確率達到66%,但兩者都是錯誤的預測。我們想要將每個句子中的所有實體包裝起來,並將它們與預測的實體進行比較。

我們可以使用優秀的Seqeval庫來實現這個點。對於每個句子,它查找所有不同的標記並構造實體。通過同時對真標記和預測標記進行操作,我們可以比較真實的實體值,而不僅僅是單詞。在這種情況下,沒有「b-」或「i-」標記,我們比較實體的實際類型,而不是單詞類。

使用我們的預測值,這是一個概率矩陣,我們想為每個句子構建一個標籤序列,其原始長度(而不是我們所做的75),這樣我們就可以將它們與真實值進行比較。我們將為我們的LSTM模型和我們的詞袋模型執行此操作:

現在我們準備用seqeval 庫來對模型進行評估:

得到:

這裡很不同。你可以看到bow模型幾乎不能正確預測任何結果,而lstm模型做得更好。

當然,我們可以在BOW模型上做更多的工作,取得更好的效果,但整體上是清楚的,在這種情況下,序列到序列模型效果更好。

預訓練的詞嵌入(詞表達)

正如之前看到的,我們的大多數模型參數是用於嵌入層的。因為有大量單詞,而且訓練數據有限,所以訓練這一層非常困難。使用預先訓練過的嵌入層是非常常見的情況。目前大多數嵌入模型都使用所謂的「分布假設」,即在相似的上下文中,單詞具有相似的含義。通過構建一個模型來預測給定上下文的單詞(或相反的方式),它們可以生成對單詞含義有良好表示的單詞向量。雖然它與我們的任務沒有直接關係,但是使用這些嵌入可能有助於我們的模型更好地表示其目標的單詞。

還有其他方法來構建單詞嵌入,從簡單的共現矩陣到更複雜的語言模型。在這篇文章中,我嘗試使用圖像構建單詞嵌入。

這裡我們將使用流行的Glove嵌入。word2vec或任何其他實現也可以實現。

我們需要下載glove,載入單詞向量並創建嵌入矩陣。我們將使用此矩陣作為嵌入層的不可訓練權重:

現在我們的模型:

一切都和以前一樣。唯一的區別是現在我們的嵌入層具有恆定的不可訓練權重。您可以看到,總參數的數量沒有改變,而可訓練的參數數量要低得多。

讓我們訓練模型:

準確度變化不大,但正如我們之前看到的,準確度並不是正確的衡量標準。讓我們以正確的方式評估它,並與以前的模型進行比較:

非常好!我們的F1分數從76提高到80!

結論:

序列到序列模型對於許多任務來說都是非常強大的模型,比如命名實體識別(NER)、詞性(POS)標註、解析等等。有許多技術和方法可以訓練它們,但最重要的是知道何時使用它們,以及如何正確地模擬我們的問題。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 AI研習社 的精彩文章:

IEEE標準協會中國籍董事袁昱回應華為禁令
20分鐘了解TensorFlow基礎

TAG:AI研習社 |