【NLP CS224N筆記】Lecture 2 - Word Vector Representations: word2vec
I. Word meaning
Meaning的定義有很多種,其中有:
- the idea that is represented by a word,phrase,etc.
- the idea that a person wants to express by using words, signs, etc.
1.Discrete representation
那麼在計算機中是如何獲取一個word的meaning的呢?常見的解決辦法是使用像WordNet之類的資料集,它包含了同義詞(synonym)組和上位詞(hypernyms)組。這種表示方法屬於Discrete representation
上位詞(hypernym),指概念上外延更廣的主題詞。 例如:”花”是”鮮花”的上位詞,”植物”是”花”的上位詞,”音樂”是”mp3”的上位詞。上位詞是相對某主題詞的,也有它自己的等同詞、上位詞、下位詞、同類詞。
但是類似於WordNet的資料集存在如下缺點:
- 儘管儲存的詞條較為豐富,但是詞與詞之間缺少細微的差別。例如proficient只是good的同義詞,但是二者卻存在一些差別。
- 缺少新的詞彙,例如Dama(大媽)這種非常fashion的詞彙很難及時地更新。
- 對詞的定義較為主觀,因為都需要人工提前設定。因此也需要大量的人力去維護這個資料集。
- 很難計算詞之間的相似性。
2.將words表示為離散符號(discrete symbols)
如何將單詞量化成計算機能讀懂的資料呢?常見的一種方法是one-hot編碼。
例如假設我們的資料集一共有6個單詞,其中有motel(汽車旅館) 和 hotel。
那麼我們可以作如下表示:
- motel=[0 0 0 0 1 0]
- hotel=[0 0 0 0 0 1]
但是這有一個很明顯的缺點就是詞與詞之間沒有關聯性,也就是說我們無法從one-hot編碼中得知不同詞之間的相似性,因為各個詞編碼後得到的向量是正交的。
所以我們還需要找到一種能夠計算單詞相似性,相關性的編碼方式。
3. Distributed similarity based representation
一個很自然,很直觀的方法就是根據某個單詞的上下文對該單詞的含義進行編碼。該方法的核心思想是:A word’s meaning is given by the words that frequently appear close-by,由J.R.Firth在1957年提出。
下圖給出示例,假如我們需要對banking的含義進行編碼,那麼我們可以根據預先設定的固定大小的視窗對banking前後出現的單詞進行統計,banking的含義可以有這些單詞表示。
II. Word2vec Indtroduction
1. 學習神經網路word embeddings的基本思路
我們先定義這樣一個模型,該模型是在word vectors編碼的基礎上能夠預測出中心詞\(w_t\)的上下文:
\[p(context|w_t)\]
該模型的損失函式為
\[J=1-p(w_{-t}|w_t)\]
\(w_{-t}\)表示出了t以外的其他詞,即\(w_t\)的上下文。
2. word2vec的核心思想
word2vec的核心思想是predict between every word and its context words!
兩個演算法:
- Skip-grams (SG):給定目標詞彙去預測它的上下文。簡單地說就是預測上下文
- Continuous Bag of Words (CBOW):從bag-ofwords上下文去預測目標詞彙。
兩個稍微高效一點的訓練方法:
- Hierarchical softmax
- Negative sampling
該課程主要集中講解Naive softmax,上面的兩個演算法和訓練方法可以參考word2vec原理推導與程式碼分析。
3. Skip-gram prediction
下圖給出了skip-gram示意圖。中心詞banking位置為\(t\),用\(w_t\)表示。視窗大小為\(m\)
需要注意的是雖然banking的上下文可以分為前面和後面,但是概率分佈只有一種,即假設banking左邊第三個單詞是as,右邊第二個是as,這兩個as概率分佈是要合併計算的,不存在說分前後兩種概率分佈。
4. Word2vec細節
1)目標函式
介紹完模型後下面需要介紹一下目標函式,函式形式如下:
\[ \begin{align} max\,\,J'(\theta)=\prod_{t=1}^T\,\,\,\prod_{-m≤j≤m,\,\,j≠0}p(w_{t+j}|w_t;\theta) \end{align}\]
- \(J'\)表示最優值
- \(t\)表示當前中心詞所在位置,\(T\)表示詞庫的總長度
- \(m\)表示視窗的大小,\(j\)表示從視窗最左邊移動到最右邊,不等於0是因為不用計算中心詞本身
- \(\theta\)表示word representation模型本身
上式表示儘可能地預測出每個中心詞的上下文,即最大化所有概率的乘積。
通常為了方便計算會將上式化為log的形式,即
\[ \begin{align} min \,\,\,J(\theta)=-\frac{1}{T}\sum_{t=1}^{T}\,\,\,\sum_{-m≤j≤m,\,\,j≠0}log\,\,p(w_{t+j}|w_t;\theta) \end{align} \]
2)引入softmax
那麼如何計算\(p(w_{t+j}|w_t)\)呢?為方便說明先做如下定義:
- \(v_w\)表示w是一箇中心詞
- \(u_w\)表示w是一個上下文詞
所以\(p(w_{t+j}|w_t)\)簡寫成\(p(o|c)\),且有
\[ \begin{align} p(o|c)=\frac{exp(u_o^Tv_c)}{\sum_{w=1}^Vexp(u_w^Tv_c)} \end{align} \]
- \(o\)表示output或outside之意,即上下文。\(u_o\)用來表示某個需要計算上下文詞彙
- \(c\)表示center,\(v_c\)就表示中心詞
- \(V\)表示詞庫的長度,\(w\)從1開始遍歷到\(V\)
公式(3)中的分子的點積運算,他可以用於計算中心詞和上下文詞的相似性,值越大表示越相似。
3)流程示意圖
上圖雖然乍一看很凌亂,但是很清晰地展示了skipgram的計算過程。
從左往右看:
- 1.首先是維度為\(V×1\)的向量\(w_t\),用來表示此時中心詞所在的位置,用one-hot形式進行編碼,其中\(V\)表示需要遍歷計算的中心詞的個數,即總共的詞數量。
- 2.之後是維度為\(d×V\)的單詞矩陣\(W\),該矩陣儲存了所有中心詞(center word)的向量表達,\(d\)表示用於表示詞的向量的長度。
- 3.\(w_t\)與\(W\)做矩陣運算後便可得到當前的中心詞的representation,即\(v_c=W·w_t∈R^{d×1}\)
- 4.下一步就是中心詞向量\(v_c\)與上下文矩陣\(W'\)相乘(\(u_o^Tv_c\))求得各個詞之間的相似性。
- 5.接下來根據上一步求出的相似性值,使用softmax將相似性大小轉化為概率,選擇概率最大的index作為輸出。
需要注意的是 \(W'\)並不是\(W\)的轉置 ,他們是兩個完全不同的矩陣,只不過維度恰好是對方的轉置矩陣維度而已,一般將\(W∈R^{d×V}\)稱為input vector,\(W'∈R^{V×d}\)稱為output vector。所以每個單詞由兩個詞向量表示,那麼那個作為最終的表示呢?有兩種策略,一種是將兩個詞向量加起來,另一種是將兩個詞向量拼接起來,即得到\(R^{2d×1}\)詞向量。
這裡有個不明白的地方是為什麼單詞矩陣\(W∈R^{d×V}\)不止一個?希望明白的朋友能在評論區或者發郵件([email protected])解釋一下,謝謝!
下圖是官方給的整理後的示意圖,意思與上圖一樣不再贅述。
如果上面的解釋還不能讓你明白,可以參考Word2Vec介紹:直觀理解skip-gram模型。
III. Research Highlight
期間老師讓一箇中國學生做了一個關於一篇論文的報告,具體內容不作贅述,可參考CS224n研究熱點1 一個簡單但很難超越的Sentence Embedding基線方法。
IV. Word2vec objective function gradients
目前為止,目標函式和流程圖都已經清楚了,那麼接下來我們需要計算出模型的引數\(\theta\)了。在上面內容中已經介紹了每個單詞由兩個維度為\(d×1\)的向量表示,常見的辦法是將二者拼接,這樣我們就可以得到一個非常龐大的向量引數,即
\[ \begin{align} \theta&=\left[ \begin{matrix} v_a \\ v_{abandon}\\ \vdots \\ v_{zoo}\\ i_a \\ u_{abandon}\\ \vdots \\ u_{zoo}\\ \end{matrix} \right] ∈R^{2dV} \end{align} \]
那麼如何優化這個引數呢?很自然地是梯度下降啦。
首先回顧一下目標函式:
\[ \begin{align} min \,\,\,J(\theta)&=-\frac{1}{T}\sum_{t=1}^{T}\,\,\,\sum_{-m≤j≤m,\,\,j≠0}log\,\,p(w_{t+j}|w_t;\theta) \\ &=-\frac{1}{T}\sum_{t=1}^{T}\,\,\,\sum_{-m≤j≤m,\,\,j≠0}log\,\,p(u_o|v_c;\theta) \\ &=-\frac{1}{T}\sum_{t=1}^{T}\,\,\,\sum_{-m≤j≤m,\,\,j≠0}log\,\,\frac{exp(u_o^Tv_c)}{\sum_{w=1}^Vexp(u_w^Tv_c)} \end{align} \]
要想計算\(J(\theta)\)最小值,首先需要計算\(\frac{\partial{J(\theta)}}{\partial{v_c}}\)。仔細觀察公式(7)知道我可以先對右邊的log項求微分,具體推導過程如下:
- 首先將log項中的除法拆分成減法,得到兩項:①和②,接著分別對這兩項進行偏微分求導
- ①和②都包含log和指數,所以為了求導方便令\(log=ln\),所以①很容易就求出來了。需要注意的是\(\frac{\partial{u_o^Tv_c}}{\partial{v_c}}=u_o\)而不是\(u_o^T\)。
- ②的偏微分稍微複雜一點,需要使用鏈式法則進行求解。
- 首先我們想log右邊的求和項看成一個整體,即為\(z=g(v_c)\),那麼log整體可以看成是\(f(z)=f(g(v_c))\)注意不能把log和∑換位!!!看視訊的時候我就是這麼想的。。。
- 同樣為了計算方便令\(log=ln\),那麼\(\frac{f(g(v_c)}{\partial{v_c}}=\frac{1}{g(v_c)}\)
- 最後只需要再求\(\frac{\partial{g_c}}{\partial{v_c}}\)即可,需要用到指數求導公式,\(d{e^x}/dx={e^x}\),後面的步驟很簡單了,不再贅述。
由上面的步驟可以得到
\[ \begin{align} \frac{\partial{J(\theta)}}{\partial{v_c}}=-\frac{1}{T}\sum_{t=1}^T\sum_{-m≤j≤m,\,\,j≠0}(u_o-\sum_{x=1}^Vp(u_x|v_c)·u_x) \end{align} \]
計算出梯度後,就能夠開始優化引數了:
\[ \begin{align} \theta^{new} &=\theta^{old}-\alpha\frac{\partial{J(\theta)}}{\partial{\theta^{old}}} \notag\\ &=\theta^{old}-\alpha \nabla_\theta J(\theta) \end{align} \]
上面的公式在實際應用中還存在一些問題,因為通常一個詞庫是包含非常多單詞的,如果對整個詞庫進行梯度更新的話會非常緩慢。所以可以引入SGD演算法,即每次只對視窗大小的資料進行梯度更新。程式碼流程如下:
while True:
window = sample_window(corpus)
theta_grad = evaluate_gradient(J, window, theta)
theta = theta - alpha * theta_grad