1. 程式人生 > >(Stanford CS224d) Deep Learning and NLP課程筆記(二):word2vec

(Stanford CS224d) Deep Learning and NLP課程筆記(二):word2vec

本節課將開始學習Deep NLP的基礎——詞向量模型。

背景

word vector是一種在計算機中表達word meaning的方式。在Webster詞典中,關於meaning有三種定義:

  • the idea that is represented by a word, phrase, etc.
  • the idea that a person wants to express by using words, signs, etc.
  • the idea that is expressed in a word of writing, art, etc.

這三種定義由具象到抽象,但對於Deep NLP而言,處理的難度卻是由難入易。

傳統的NLP領域裡主要是針對第一種定義——word representation——進行研究。例如,通過在一張描述語義關係的拓撲圖(WordNet)上定義的上義詞(Hypernyms)和同義詞(Synonym),進而給出word在計算機中的表達。但這種方式存在很多問題。

首先,由於大多數詞都有著屬於自己獨特的語境,例如,我們會說某人在某個領域是一個專家(expert),也會稱讚某人的語言很流利(proficient)。雖然在WordNet中expert和proficient被定義為是一對同義詞,但顯然我們不能說某人在某個領域很proficient。這種一刀切的定義上義詞和同義詞的方式顯然不夠精細。

其次,語言的變化日新月異。而這種人工定義WordNet的方式顯然跟不上變化的步伐。此外,人類對自然語言理解的主觀性,以及在標註和維護WordNet時消耗的大量人力,都成為了制約WordNet發展的瓶頸。

最後,也是最糟糕的問題是,對於計算機而言,這種基於WordNet的詞義定義方式很難對兩個word之間的語義相似度進行準確的計算。我們只能粗劣地對兩個詞是否同義給出一個二分類的結果。

WordNet的這些問題是詞的離散化表達(discrete representation)所帶來的通病。除了WordNet,還有很多基於規則或是統計的NLP模型將word視為一個獨立的原子單元(atomic symbols)進行處理。例如,在經典的VSM模型裡,每一個word都被表示為一個one-hot向量(除了一個索引下標對應的位置是\(1\)

,其他位置上的元素都是\(0\))。顯然,在這種表達方式下,對任意兩個word計算出來的相似度都是\(0\)。此外,one-hot向量表達還存在著資料稀疏性和維度災難的問題。

Distributional Representation

於是,人們想出了一種新的word meaning的定義方式:通過一個word相鄰的詞來定義這個word的meaning。這個定義來源於一個古老的idea(J.R.Firth, 1957):

You shall know a word by the company it keeps

併成為了包括傳統NLP模型和今天將要介紹的word2vec模型在內的大部分word representation模型的基礎。

具體來說,我們通過從大量的語料文字中構建一個co-occurrence矩陣來定義word representation。矩陣的構造通常有兩種方式:基於document和基於windows。

通過統計word與document共現的次數得到的矩陣被稱為word-document矩陣。這個矩陣一般被用於主題模型。相同主題的word之間往往有著較高的相似度。但該矩陣很難描述word的語法資訊(例如POS tag)。

我們在課堂上主要講授的是第二類矩陣:word-context矩陣。通過統計一個事先指定大小的視窗內的word共現次數,不僅可以刻畫word的語義資訊,還在一定程度上反應了word的語法結構資訊。

舉一個簡單的例子。假設我們的語料庫由三句話構成:

  1. I like deep learning.
  2. I like NLP.
  3. I enjoy flying.

設定統計視窗的大小為\(1\),並採用對稱視窗(與之相對的是非對稱視窗,即僅考慮目標詞左側或右側的上下文),則可以得到word-context矩陣如下:
word-context matrix

矩陣裡的元素是列向量所代表的word出現在行向量所代表的word的上下文裡的次數。(注意,我們並沒有對句首或句尾的詞做任何特殊的處理:比如增加一個S或E的標記Token作為句首或句尾的padding。)

現在,我們可以用矩陣的行向量來計算word之間的相似度了。

然而, 這種定義方式得到的詞向量的維度等於詞典的大小。這意味著,我們需要大量的空間來儲存這些高維的詞向量。同時,伴隨著高維向量出現的資料稀疏性問題,也使得基於這些詞向量的機器學習模型的訓練變得異常困難。

簡單來說,co-occurrence矩陣定義的詞向量在一定程度上緩解了one-hot向量相似度為\(0\)的問題,但沒有解決資料稀疏性和維度災難的問題。

SVD分解:低維詞向量的間接學習

既然基於co-occurrence矩陣得到的離散詞向量存在著高維和稀疏性的問題,一個自然而然的解決思路是對原始詞向量進行降維,從而得到一個稠密的連續詞向量。

第一個出場的對原始矩陣進行降維的方法是奇異值分解(SVD)。SVD的基本思想是,通過將原co-occurrence矩陣\(X\)分解為一個正交矩陣\(U\),一個對角矩陣\(S\),和另一個正交矩陣\(V\)乘積的形式,並提取\(U\)\(k\)個主成分(按\(S\)裡對角元的大小排序)構造低維詞向量。(關於SVD更多的介紹可以參考這篇部落格:機器學習中的數學(5)-強大的矩陣奇異值分解(SVD)及其應用

除此之外,在對原始矩陣\(X\)的處理上,還有很多簡單但很好用的Hacks。比如對原始矩陣中高頻詞的降頻處理;帶權重的統計視窗(距離越近的詞對詞義的貢獻越大);用Pearson相關性係數替代簡單的詞頻統計等。包括我們後面要學習到的word2vec模型,也屬於這一類Hacks。

即便是簡單的對co-occurrence矩陣進行SVD分解得到的稠密詞向量,也具有很多優美的性質。語義相近的詞(比如"wrist"和"ankle")可以通過用詞向量內積定義的相似度聚類到一起;同一動詞的不同時態也往往出現在向量空間的同一片區域。詞向量甚至可以一定程度上反應word之間的線性聯絡。

然而,高昂的計算複雜度(\(O(mn^2)\))是SVD演算法的效能瓶頸。這對於現在動輒上百萬甚至上億資料量的語料庫而言,是一筆巨大的計算開銷。更不用說每一個新詞的加入都需要在一個新的矩陣上重新執行一遍SVD分解。此外,後面我們會看到,SVD演算法和其他Deep Learning模型本質上是屬於兩類不同的學習演算法。

儘管SVD分解存在著這樣或那樣的問題,但是其將word表示為一個稠密的低維連續向量的思想,成為了包括Deep NLP在內的眾多NLP模型的基礎。

From now on, every word will be a dense vector.

word2vec:低維詞向量的直接學習

接下來,我們來看下Deep Learning是如何從原始的語料庫中直接學習到低維詞向量的表達。這也是我們學習的第一個Deep NLP模型——word2vec模型。

與直接從co-occurrence矩陣裡提取詞向量的SVD演算法不同,word2vec模型背後的基本思想是對出現在上下文環境裡的詞進行預測(事實上,後面會看到,這種對上下文環境的預測本質上也是一種對co-occurrence統計特徵的學習)。對於每一條輸入文字,我們選取一個上下文視窗和一箇中心詞,並基於這個中心詞去預測窗口裡其他詞出現的概率。因此,word2vec模型可以方便地從新增語料中學習到新增詞的向量表達,是一種高效的線上學習演算法(online learning)。

對於一個長度為\(T\)的語料庫,假設我們為每一個詞選取的上下文視窗的大小是\(m\)(指的是上下文各\(m\)個詞),則我們的目標函式是最大化訓練語料的對數似然概率:
\[J(\theta)=\frac{1}{T}\sum_{t=1}^T\sum_{-m \leq j \leq m, j \neq 0}{\log p(w_{t+j}|w_t)}\]
其中,\(\theta\)是模型的所有引數。

一個簡單的計算條件概率\(p(w_{t+j}|w_t)\)的公式如下:
\[p(o|c)=\frac{exp(u_o^Tv_c)}{\sum_{w=1}^W{exp(u_w^Tv_c)}}\]
其中,\(o\)為輸出的目標詞的id,\(c\)為輸入的中心詞的id。每一個詞都有兩套向量:作為中心詞的"center"向量\(v\),和作為目標詞的"outside"向量\(u\)\(v\)\(u\)都是模型要學習的引數。(事實上,我們也可以用一套向量去描述每一個word。之所以用兩套向量只是為了後面計算的簡單。)

對機器學習有一定基礎的同學會發現,這裡的條件概率其實是一個Softmax分類函式,而目標函式對應著這個分類函式的交叉熵。

優化這個目標函式的演算法是SGD——隨機梯度下降法。為此,我們要求解這個目標函式的一階導數。

首先,我們引入兩個重要的求導法則:

  1. 向量求導法則:\(\frac{\partial x^Ta}{\partial x}=\frac{\partial a^Tx}{\partial x}=a\)
  2. 鏈式求導法則:\(\frac{dy}{dx}=\frac{dy}{du} \frac{du}{dx}\)

有線性代數和微積分基礎的同學可以輕鬆證明這兩個法則的正確性。

接下來,我們首先對"center"向量\(v_c\)進行求導:
\[ \begin{aligned} \frac{\partial \log p(o|c)}{\partial v_c} &= \frac{\partial}{\partial v_c}(u_o^Tv_c-\log \sum_{w=1}^W{exp(u_w^Tv_c)}) \\ &= u_o - \frac{\partial}{\partial v_c}{\log \sum_{w=1}^W{exp(u_w^Tv_c)}} \\ &= u_o - \frac{1}{\sum_{w=1}^W{exp(u_w^Tv_c)}} \frac{\partial}{\partial v_c}\sum_{w'=1}^W{exp(u_{w'}^Tv_c)} \\ &= u_o - \sum_{w'=1}^W \frac{1}{\sum_{w=1}^W{exp(u_w^Tv_c)}} \frac{\partial}{\partial v_c}{exp(u_{w'}^Tv_c)} \\ &= u_o - \sum_{w'=1}^W \frac{exp(u_{w'}^Tv_c)}{\sum_{w=1}^W{exp(u_w^Tv_c)}} \frac{\partial}{\partial v_c}{u_{w'}^Tv_c} \\ &= u_o - \sum_{w'=1}^W {p(w'|c)u_{w'}} \end{aligned} \]

類似地,我們可以求出"outside"向量\(u\)的導數:
\[\frac{\partial \log p(o|c)}{\partial u_o} = v_c - v_c p(o|c)\]
\[\frac{\partial \log p(o|c)}{\partial u_{o',o' \neq o}} = - v_c p(o'|c)\]

(如果從神經網路的角度出發,可以藉助反向傳播演算法輕鬆寫出這兩個變數的求導公式。參見我的ProblemSet1的作業筆記。)

顯然,這種定義下的梯度計算需要對每個上下文視窗都計算出訓練集中所有單詞的條件概率。這對於動輒上百萬的詞典而言,幾乎是一件不可能完成的任務。因此,Mikolov在他2013年發表的論文裡提出了一些效能優化的Hacks,包括近似歸一化的層次Softmax,和避免歸一化的負取樣技術。這些技術的細節在我的另一篇博文word2vec前世今生裡有詳細的討論,這裡就不再贅述。但是,word2vec基本的思想和背後的數學已全部展示在這裡了。

word2vec得到的詞向量很好地編碼了詞與詞在語義和語法上的聯絡(不只是相似性)。例如單數與複數,子類和父類。一個著名的例子是:\(v_{king}-v_{man}+v_{woman} \approx v_{queen}\)。曾經只能用WordNet人工定義的聯絡,如今從co-occurrence的統計特徵自動湧現了出來!

Count Based vs Direct Prediction

我們學習了基於co-occurrence矩陣和SVD分解的向量降維技術,也學習了直接對co-occurrence進行預測的word2vec技術。那麼,這兩類詞向量生成的技術在計算效率和結果上又有哪些差異呢?

與每次只處理部分語料的word2vec模型相比,基於SVD的矩陣模型充分地利用了資料集的統計特徵。同時,SVD分解的計算複雜度只依賴於語料的詞典大小,而word2vec模型的計算複雜度與整個語料庫的大小線性相關。因此,在某些情況下,前者的訓練更加高效。

不過,在評估最終得到的詞向量的效果時,word2vec模型要遠超SVD矩陣模型。前者不僅可以刻畫詞在語義或語法上的相似性,更可以描述詞與詞之間的線性關係。在一些複雜任務上,word2vec向量的效果也更為突出。

在下一節課,我們將學習一種結合二者優勢的新的詞向量模型——Glove模型。