NLP 入門 (2) Text Classification (Sentiment Analysis) — 進階情感分類器 Word2Vec + RNN (GRU)

Gary Hsu
12 min readDec 27, 2020

--

所使用到的函式庫: gensim, scikit-learn, tensorflow (keras), pandas

如果已經知道 Word2Vec 相關的概念,想要直接瞭解怎麼活使用 Word2Vec 來實作文章分類可以直接搜尋並跳到:

Word2Vec Baseline Prediction Model (使用 Support Vector Classifier)

Word2Vec Advanced Prediction Model (使用 GRU 來建立神經網路)

前言:

之前寫了好幾篇關於 NLP 的文章來介紹 Word2Vec,以及怎麼訓練自己的Word2Vec,如果有興趣的話不妨從那兩篇當作學習 Word2Vec 的開頭。

在之前介紹 Bag of word + Naive Bayes 的文章之中,最後將學到的建模技巧應用在IMDB影評的資料集上得到了 0.867 的驗證正確率(validation accuracy)。那接下來就來和大家分享怎麼使用 Word2Vec 來進行遷移學習 (transfer learning) 並應用在相同的資料集上 (IMDB影評),並看看能不能得到更高的正確率(最後得到 0.92 左右的驗證正確率)。

Bag of words簡介

Bag of words的方法會將每個單詞使用一個 one-hot vector 來表示。缺點就是 每個單詞之間的相關性無法被保留。所以當使用 bag of words 時:狗就是狗,貓就是貓,飛機是飛機,無法知道狗和貓都屬於動物。

單詞和單詞之間的距離不能代表相關性。

Bag of words

上圖的例子使用歐幾里得距離 (Euclidean distance)會得到:

Distance (狗, 貓) = sqrt((0–0)² + (1–0)² + (0–1)²) = sqrt 2 ~= 1.414..

Distance (狗, 飛機) = sqrt((1–0)² + (0–0)² + (0–1)²) = sqrt 2 ~= 1.414..

Distance (貓, 飛機) = sqrt((1–0)² + (0–1)² + (0–0)²) = sqrt 2 ~= 1.414..

無法從距離來得出「狗」和「貓」是更為相似的詞。

Word2Vec 簡介

Word2Vec 則是藉由 self-supervised learning 的方式,作者提出了兩種模型架構:

1. Skip-Gram: 使用當前單詞來預測上下文

2. CBOW: 使用上下文來預測當前單詞

原 Word2Vec 論文之中,作者對兩種模型架構進行了詳細的比較,發現在不同的任務上,模型架構各有所長,但差異不大。

如果想要更多有關 Word2Vec 的介紹,請參考我另一篇Blog: NLP專欄(1) — 詳解 Word2vec ,簡而言之,我覺得 Word2Vec 本身最大的優點是:

  1. 使用 Self-supervised Learning 的方式,克服的訓練資料不足的問題,因為不管是 CBOW 或是 Skip-gram,訓練資料都是「文章本身」,不管是給定上下文來預測當前單字 (CBOW),或是給定當前單字來預測上下文(Skip-gram),上下文和當前單詞都不需要再另外人工標注 ,不論是維基百科或者是新聞文章,都可以直接拿來訓練。
  2. 在 Deep Learning 準備大行其道的時候(2013),採用了「Shallow Learning」,使用相對簡單的單層Perceptron 架構可加快訓練速度,在同樣的時間內可以相對地訓練更多資料。

可以想像「狗」和「貓」,相比於「狗」和 「飛機」 會有更類似的上下文,這種模型的架構以及訓練方式,會使得「狗」和「貓」有更相近的 word embeddings。

Word2Vec 範例(非真實訓練結果)

使用歐幾里得距離 (Euclidean distance) 就會得出「狗」和「貓」更為相近的結論。

Distance (狗, 貓) = sqrt((0.9–0.8)² + (0.1–0.2)² + (0.7–1)²) ~= 0.33..

Distance (狗, 飛機) = sqrt((1.7–0.8)² + (1.5–0.2)² + (1.3–1)²) ~= 1.61..

Distance (貓, 飛機) = sqrt((1.7–0.9)² + (1.5–0.1)² + (1.3–0.7)²) ~= 1.72..

此外 Word2Vec 訓練之後,作者還發現了一個特性,vector(“King”) — vector(“man”) + vector(“woman”) ~= vector(“queen”)

https://www.ed.ac.uk/informatics/news-events/stories/2019/king-man-woman-queen-the-hidden-algebraic-struct

有了這些 word embeddings 之後,就可以進行遷移學習 (transfer learning)。

(註: word embeddings, word vector, word representation 都是同義詞)

遷移學習 (transfer learning):

我們從小常常會聽到一句話:「站在巨人的肩膀上」,就是遷移學習最好的詮釋。

當我們手邊有一個任務,但是並沒有很多標注的資料時,往往很難學習出一個高效的模型。

那聰明的機器學習先輩們,就想到了這樣的一個方法:我們先在大量的未標注的資料上完成一個學習任務 (pre-trained),將學習到的知識應用到另外只有少數標注資料的目標任務 (fine-tune)。

這種 pre-trained + fine-tune 的架構可以讓我們在有限的數據集上也得到很不錯的效果。不論是 Word2Vec 還是後來的 ULMFiT,甚至是大名鼎鼎的 BERT 都將遷移學習進一步的發揚光大。

本文接下來的重點就是站在 Word2Vec 這個巨人的肩膀上 (完成了 14 GB 維基百科的訓練), 來預測 IMDB 影評的情感 (sentiment)。

Word2Vec Model 應用:

點擊這裏連結到我的colab 範例

下面例子為這次任務的縮影,我們給機器一段影評,然後它要幫我們分類出這段影片是屬於正面或是負面。

影評範例(中文), colab 的 IMDB dataset 是英文

這邊我想花一點篇幅來介紹 Gesim word2vec Model 的用法:

# 如果你使用 pip 的話
$ pip install gensim
# 如果你使用 conda 的話
$ conda install -c anaconda gensim

如果想還不知道要怎麼訓練自己的word2vec請參考我這篇blog

如果只想使用但自己懶得訓練word2vec,可以直接下載我幫你訓練好的參數

解壓縮我訓練好的參數,然後把檔案放到當前執行程式的資料夾

使用 gensim 讀取 Word2Vec 模型:

from gensim.models import Word2Vecmodel = Word2Vec.load('wiki-lemma-100D-phrase')print(model.wv['machine_learning])
# [-2.929788 , -0.83070016, 2.1403034 , ..., -0.03268742]
# (100D array)

至此,我們就可以藉由 model.wv 來獲得單詞或片語相關的 word embedding.

Word2Vec Baseline Prediction Model: 將每個單詞的詞向量取平均,在使用 Support Vector Classifier 來分類 (0.802 accuracy):

(如果只想得到最好的accuracy,請直接跳到下一章節 Word2Vec Advanced)

libraries: gensim, spaCy, scikit-learn

  1. 將影評之中的 word vector 取平均當成 sentence vector

2. 然後使用傳統的 Machine Learning (Support Vector Classifier)解決:

最後得到了0.802的 validation accuracy。

詳細的 code 再麻煩到我的 colab 查看。

Word2Vec Advanced Prediction Model::直接將每個詞向量當作 Deep Learning (GRU) 的輸入,在使用 Sigmoid當激活函數(0.9 accuracy):

最後在安麗一次:詳細的 code 再麻煩到我的 colab 查看。

# 由於我們在之前下載的 word2vec model 是專門給 gensim 用的,如果我們要給 keras 使用,我們要先將model儲存成不同的格式model.wv.save_word2vec_format('keras_word2vec.txt', binary=False)
讀取 embeddings 到記憶體裡面

因為在訓練 Word2Vec 的時候,我 lammatize 了所有單詞,所以要確保我也對 IMDB 資料集做了相同的預處理(pre-processing):

將預處理完成的資料轉換成 2D tensor:

根據上面 tokenizer 的結果,來建立一個查詢表格 (look up table):

在上面將文字轉換成 2D tensor 的時候,

「This is a great move」 會被轉換成: [11, 3213, 7, 77, 18, 0, 0, 0, …., 0] -> 長度為512 (512 是可調整的參數)的列表,每一個數字就代表該單詞的位置。

因此我們要建立一個這樣的 embedding maxtrix:

embedding_matrix

然後就使用這個 embedding matrix 來初始化 keras embedding layer的參數:

至此,就完成了所有的前置步驟,讓我們來開始訓練 model 吧!

Word2Vec pre-trained

最後我們在第 8 epoch 的時候得到了最高的驗證正確率: 0.9196,但是在第 10 epoch 時驗證正確率又掉到了 0.8408。

在實務的時候可以使用 early stopping,當驗證正確率連續下降幾個 epochs時,我們就停止訓練。這樣就可以讓我們的驗證正確率維持在一個較高的水準。

在進入結論之前我還做了以下兩個小實驗:

實驗一:

如果我們不使用 Word2Vec 訓練的成果來初始化 embedding layer 的參數,轉而使用隨機的參數當作初始值的話:

Random Intialization

我們藉由模型的正確率可以發現,我們採用相同的模型架構之下,有使用 Word2Vec 當作 embedding layer 初始值,比起隨機初始值的模型好上許多。 這也證明了我們成功的站在巨人的肩膀上!

實驗二:

我們直接在 IMDB 的資料集上訓練 embedding layer (細心的你可以找到 code在哪裡不一樣嗎?)

藉由觀察 acc (訓練正確率) 和 val_acc (驗證正確率),可以發現模型在訓練三個 epochs 之後就過度擬合了!依然證明了 Word2Vec 這個巨人的確讓我們看的更高更遠!

結語

我們總共做了四個實驗來證明, 如果我們把 Word2Vec 的結果「使用得當」,它的確可以顯著的提升 IMDB 情感分類器的正確率,但你是否會有幾個疑問:

在實驗二之中我們也得到了不錯的驗證正確率,那我們是否可以不使用 pre-trained ,而直接在 IMDB 資料集上重新訓練呢?

藉由觀察訓練的正確率,可以發現: 直接在 IMDB 重新訓練的模型在訓練 3 個 epochs 之後就開始過度擬合了。 而使用 pre-trained 的例子則是還有繼續訓練的潛力(訓練正確率還沒有偏離驗證正確率太多) ,如果可以的話,先在大量資料上 pre-trained 的效果還是更好。

Word2Vec 有什麼缺陷嗎?

再不論是英文或者中文都有一字多義的情況:

英文:bank 既可以代表銀行,也可以代表河岸。

中文:我幫我的老闆「背黑鍋」。 可以是指真正的黑色鍋子,也可以指承擔別人犯下的錯誤。

但是 Word2Vec 只會將一個單詞產生一個 word embedding,所以這種情況下可能無法很精確的表達一詞多義。

在我之後介紹 BERT/ULMFiT 都會使用語言模型 (Language Model) 來替換 word embedding。

如果覺得我的文章有幫助,再麻煩多幫我拍幾次手囉!有什麼疑問也都歡迎 Email: sfhsu29@gmail.com 來和我討論!

--

--