1. 程式人生 > >機器學習 | 八大步驟解決90%的NLP問題

機器學習 | 八大步驟解決90%的NLP問題

本文將分八大步驟來介紹如何用機器學習處理文字資料。從最簡單的方法開始,逐一講解,然後分析更具體的方案細節,如特徵工程、詞向量和深度學習。你可以把本文看作是標準方法的高度概括。

程式碼連結:

https://github.com/hundredblocks/concrete_NLP_tutorial/blob/master/NLP_notebook.ipynb

一、收集資料

每一個機器學習問題都始於資料,比如一組郵件、帖子或是推文。文字資訊的常見來源包括:

  • 商品評價(來自 Amazon、Yelp 以及其他 App 商城)

  • 使用者產出的內容(推文、Facebook 的帖子、StackOverflow 的提問等)

  • 問題解決(客戶請求、技術支援、聊天記錄)

 “社交媒體中的災難”資料集

在這篇文章中,我們將使用 CrowdFlower 提供的一個數據集,名為“社交媒體中的災難(Disasters on Social Media)”。

貢獻者們查看了超過 10000 條具有類似“著火”、“隔離”、“混亂”等搜尋關鍵詞的推文,然後標記這個推文是否和災難事件有關(與之相反的是一些玩笑、電影點評或是一些非災難性的事件)。

我們的任務是分辨出哪些推文是真正和災難事件相關的,而不是一些類似電影描述的不相關話題。為什麼呢?一個潛在的應用是針對突發事件對執法人員進行專門的提醒,而不會被其他無關資訊,比如 Adam Sandler 新上映的電影所幹擾。這項任務中一個特別的挑戰是這兩種情況在搜尋推文的時候都用到了相同的檢索詞,所以我們只能通過細微的差別去區分他們。

在下面的文章中,我們將把與災難事件相關的推文稱為“災難”,將其他推文稱為“不相關的”。

標籤

我們已經標註過資料,所以知道推文是如何分類的。比起優化一個複雜的無監督學習方法,尋找和標記足夠多的資料來訓練模型會更加快捷、簡單和廉價。

二、資料清洗

資料科學家的一個必備技能是知道自己的下一步操作是處理模型還是資料。有一個好的經驗法則是先觀察資料然後進行資料清洗。一個乾淨的資料集能使模型學習到有意義的特徵而不會被一些不相關的噪聲影響。

可以借鑑下方的列表來進行資料清洗:(檢視程式碼獲取更多資訊)

  • 去除一切不相關的字元,比如任何非字母數字的字元

  • 標記你的文字,將他們拆分為獨立的單詞

  • 去除不相關的詞語,比如 @這類提醒或是 url 連結

  • 將所有字母轉換成小寫,這樣“hello”,“Hello”,“HELLO”就會被當做同樣的單詞處理

  • 將拼錯的單詞或是多種拼法的單詞與某個特定的表達繫結(比如:“cool”/“kewl”/“cooool”)

  • 考慮詞形還原(比如將“am”,“are”,“is”都看做“be”)

完成這些步驟並檢查完其他錯誤後,我們就可以使用這些乾淨的、標記過的資料進行模型訓練了!

程式碼:https://github.com/hundredblocks/concrete_NLP_tutorial/blob/master/NLP_notebook.ipynb:

三、找到一種好的資料表達方式

機器學習模型通常以數值作為輸入。我們這裡的資料集是句子列表,為了讓模型可以從資料中學到句子的特徵模式,我們首先要找到一種方法來把它轉換成模型能理解的形式,即數字列表。

獨熱編碼(One-hot encoding)- 詞袋模型(Bag of Words)

通常為計算機解釋文字的方法是將每一個字元都編為一個獨立的數字(例如 ASCII 碼)。如果使用這種簡單的表達來做分類器,需要我們的資料從頭開始學習詞語的結構,這對大多數資料集來說是很難實現的。所以我們需要一種更上層的方法。

例如,我們可以為資料集中的所有單詞製作一張詞表,然後將每個單詞和一個唯一的索引關聯。每個句子都是由一串數字組成,這串數字是詞表中的獨立單詞對應的個數。通過列表中的索引,我們可以統計出句子中某個單詞出現的次數。這種方法叫做 詞袋模型,它完全忽略了句子中單詞的順序。如下圖所示:

640?wx_fmt=jpeg

用詞袋模型表示句子。句子在左邊,模型表達在右邊。向量中的每一個索引代表了一個特定的單詞。


嵌入視覺化

在“社交媒體中的災難”樣本詞表中大概會有 20000 個單詞,這意味著每句句子都會用一個長度為 20000 的向量來表示。向量的 大部分會被 0 填充,因為每句話只包含了詞表中很小的一個子集。

為了看出嵌入的工作是否真正抓住了和問題相關的資訊(比如推文是否與災難相關),有一個好方法是將它們視覺化,然後觀察結果是否有很好的分佈。考慮到詞表通常很大,而且用 20000 維的資料做視覺化是基本不可能的,所以我們使用了 PCA 這種技術將資料降到二維。繪製如下:

640?wx_fmt=jpeg

詞袋嵌入模型的視覺化結果

兩個分類看起來沒有很好的分離,這可能是我們選擇的嵌入方法的特徵或是單純因為維度的減少引起的。為了瞭解詞袋模型的特徵是否會起一些作用,我們可以試著基於它訓練一個分類器。

四、分類

當初次接觸一個問題,通常來說最好的方法是先挑選一個能解決問題的最簡單的工具。當提到資料分類時,一般最受歡迎的是通用性和可解釋性兼具的邏輯迴歸演算法。這種演算法很容易訓練而且結果也是可解釋的,你可以很輕鬆地從模型中提取出最重要的一些係數。

我們將資料分為兩個集合,訓練集用於匹配模型,測試集用於觀察應用在未知資料上的效果。訓練後我們得到了 75.4% 的精確度。結果還不錯!推測出現最多的類(“不相關”)只能達到 57%。但是,即使是 75% 的精確度也已經足夠好了,我們決不能在還沒有理解模型的情況下就開始應用它。

五、檢驗混淆矩陣

理解模型的第一步,是瞭解模型產生的錯誤分類,以及最不應該出現的錯誤。在我們的例子中,“誤報”是指將不相關的推文分類為“災難事件”,“漏報”是指將與災難有關的推文歸類為“與災難無關的事件”。如果要優先處理潛在的災難事件,那就要降低“漏報”。而如果資源受限,就要優先降低“誤報”,減少錯誤的提醒。使用混淆矩陣可以很好地視覺化這些資訊,並將模型預測的結果與資料的真是標籤進行比較。理想情況下,模型的預測結果與真實情況(人工標註)完全相符,這時候混淆矩陣是一條從左上角到右下角的對角矩陣。

640?wx_fmt=jpeg

混淆矩陣(綠色部分所佔比例較高,藍色部分的比例較低)

相比假陽性結果,我們的分類器產生了更多的假陰性結果。換句話說,模型中最常見的錯誤是將災難性推文錯誤歸類為不相關推文。如果假陽性結果的執法成本很高的話,那麼我們分類器的這種偏差就是良性的。

解釋和說明模型

為了驗證模型並解釋它的預測結果,我們需要明確模型用以進行判斷的那些詞彙。如果我們的資料有偏差,而分類器在樣本資料中卻能做出準確預測,那這樣的模型就無法在現實世界中很好地推廣。

在這裡,我們可以用圖表來表示災難性推文與不相關推文兩類預測中最重要的詞彙。由於我們可以對模型的預測係數進行提取和排序,用詞袋模型(bag-of-words)和Logistic迴歸模型很容易就能計算出單詞的重要性。

640?wx_fmt=jpeg

詞袋模型(bag-of-words):單詞的重要性

我們的分類器能夠正確識別出一些模式(如廣島、大屠殺等),但在一些毫無意義的詞彙(如heyoo、x1392等)上還是出現了過擬合。詞袋模型(bag-of-words)僅能處理龐大詞彙表內的不同詞彙,並對所有的詞彙分配相同的權重。然而,其中一些詞彙出現得非常頻繁,但卻只是預測結果的噪音資料。接下來,我們將試著找到一種能夠表示詞彙在句子中出現頻率的方法,儘量讓模型從資料中獲取更多的訊號。

六、詞彙結構的統計

TF-IDF嵌入模型

為了讓模型專注於學習更有意義的詞彙,我們可以在詞袋模型上面使用TF-IDF評分(術語頻率,逆文件頻率)。TF-IDF通過詞彙在資料集中的稀有程度來評估它的重要性,適度削弱出現過於頻繁的單詞。下圖是TF-IDF嵌入模型的PCA對映:

640?wx_fmt=jpeg

視覺化TF-IDF嵌入模型

從中可以看出,兩種顏色之間有了更清晰的區分,使這兩類資料更易於被分類器分開。在新模型上訓練Logistic迴歸,我們得到了76.2%的準確度,說明TF-IDF確實有助於提高識別效能。

儘管只是非常微小的改進,但我們的模型能否就此學到更重要的詞彙呢?如果能得到更好的結果,同時還能避免模型在無關詞彙上的過擬合,那TF-IDF嵌入模型就可以被認為是真正的“升級版”模型。

640?wx_fmt=jpeg

TF-IDF嵌入模型:單詞的重要性

可以看到,新模型學到的詞彙看起來相關度更高!儘管測試集的指標只是略有增加,但是我們對模型的識別效能更有把握,因此部署新模型的互動系統會讓使用者體驗更為舒適。

七、語義資訊的利用Word2Vec

TF-IDF嵌入模型能夠學習到訊號更高頻的詞彙。然而,如果部署該模型後,我們很可能會遇到一些訓練集中從未出現過的詞彙。先前的模型均無法正確分類這樣的新資料,即便其中的詞彙與訓練集非常相似。

要解決這個問題,我們就要捕捉詞彙的語義,這就意味著模型需要理解“好”與“積極”在語義上的距離要比“杏”和“大陸”更接近。這裡的工具就是Word2Vec。

使用預訓練的嵌入模型

Word2Vec是一種為單詞查詢連續嵌入的技術。通過閱讀大量的文字,它能夠學習並記憶那些傾向於在相似語境中出現的詞彙。經過足夠的資料訓練之後,它會為詞彙表中的每個單詞都生成一個300維的向量,用以記錄語義相近的詞彙。

Word2Vec作者在一個非常大的語料庫上預訓練並開源了該模型。利用這一語料庫,我們可以將一些語義知識納入到我們的模型內。預訓練好的詞向量可以在本文的GitHub程式碼庫中找到。

GitHub地址:https://github.com/hundredblocks/concrete_NLP_tutorial

句子分級表示

讓分類器快速得到句子嵌入的方法,是先將句中所有詞彙Word2Vec得分的平均化。這與此前詞袋模型的做法類似,但這裡我們在保留語義資訊的同時只丟棄句法。

640?wx_fmt=jpeg

Word2vec模型的句子嵌入

利用前面的視覺化技術對新模型繪圖,結果如下:

640?wx_fmt=jpeg

Word2Vc嵌入模型的視覺化結果

在這裡,兩組顏色的分離程度更大一些,這就意味著Word2Vec能夠幫助分類器更好地分離這兩種類別。再一次使用Logistic迴歸,得到77.7%的準確率,是我們迄今最好的結果!

複雜性/可解釋性權衡取捨

與先前的模型不同,新模型無法將每個單詞都表示成一維向量,因此很難看出哪些詞彙與我們的分類結果相關度最高。儘管我們仍可使用Logistic迴歸的係數,但它們僅與嵌入的300個維度相關,而與詞彙索引值並不相關。

模型準確率確實提高了,但完全做不了可解釋性分析就有點得不償失了。不過,對於更復雜的模型,我們可以利用LIME這樣的“黑盒直譯器”來稍微解釋一下分類器具體是如何工作的。

LIME

LIME是Github上的一個開源軟體包,它允許使用者通過觀察輸入的擾動(比如在我們的例子中,從句中移除單詞)來分析一個特定分類器的預測結果是如何變化的。

從下圖來看它對我們資料集中幾個句子的解釋:

640?wx_fmt=jpeg

正確分類的災難性詞彙被歸類為“相關”

640?wx_fmt=jpeg

這個詞對分類的影響似乎不太明顯

不過,我們沒有時間去逐一探索資料集中的數千個樣本。我們要做的是在代表性的測試樣本上執行LIME,以此來分析哪些詞彙對於分類預測的影響更大。這樣,我們就可以像前面一樣獲取到單詞的重要性分數,以驗證模型的預測結果。

640?wx_fmt=jpeg

Word2Vec:單詞的重要性

模型能夠提取高度相關的詞,這意味著它做出了可解釋的決定。這些詞彙的相關度是最高的,因此我們更願意在實際生產中部署這樣的模型。

八、使用端到端的方式訓練語法特徵

我們已經介紹過如何用快速有效的辦法來生成緊湊的句子嵌入。然而,通過省略詞彙的順序,我們也放棄了語句的所有句法資訊。如果簡單的方法給不出令人滿意的結果,那我們就用更為複雜的模型:將整個句子作為輸入並預測標籤,同時無需建立中間表示。一種常見的做法是把句子視為詞向量的序列,如使用Word2Vec,或是GloVe、CoVe等更先進的方法。接下來我們詳細討論。

640?wx_fmt=jpeg

高效的端到端的訓練體系結構(源)

用於句子分類的卷積神經網路(https://arxiv.org/abs/1408.5882)訓練速度很快。它作為一種入門級的深度學習架構,能夠很好地解決分類問題。儘管CNN聲名主要源自它在影象處理方面的出色能力,但在文字相關任務上,它所提供的結果也相當優異。且相比多數複雜的NLP方法(如LSTM、Encoder/Decoder架構等),CNN訓練速度也更快。它能夠保留單詞的順序,很好地學習單詞的序列特徵以及其他有用資訊。相對於先前的模型,它可以區分出“Alex eats plants”與“Plants eat Alex”之間差異。

相比先前的方法,該模型的訓練不需更多的工作,但效果卻好得多,準確率高達79.5%!與前面的步驟一樣,下一步也要繼續探索並可視化該模型的預測結果,以驗證它是否為最佳模型。做到這一步,你應該能自己完成這裡的操作。

寫在最後

簡單回顧一下,我們在各個步驟中所用的方法是這樣的:

  • 從一個簡單的模型快速開始

  • 解釋模型的預測

  • 理解模型分類中的錯誤樣本

  • 使用這些知識來決定下一步的部署。

上述八大步驟所用的模型是我們處理短文字時的幾個特定例項,但其背後的解決方法已經廣泛被用在各類NLP問題的實際處理上。

原文連結:

https://blog.insightdatascience.com/how-to-solve-90-of-nlp-problems-a-step-by-step-guide-fda605278e4e

640?wx_fmt=gif

640?wx_fmt=png

640?wx_fmt=png