1. 程式人生 > >【NLP CS224N筆記】Lecture 2 - Word Vector Representations: word2vec

【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概率分佈是要合併計算的,不存在說分前後兩種概率分佈。

image.png

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)流程示意圖

image.png

上圖雖然乍一看很凌亂,但是很清晰地展示了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])解釋一下,謝謝!

下圖是官方給的整理後的示意圖,意思與上圖一樣不再贅述。

image.png

如果上面的解釋還不能讓你明白,可以參考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



MARSGGBO原創





2018-12-27