【NLP CS224N筆記】Lecture 2 - Word Vectors2 and Word Senses

本次梳理基於Datawhale 第12期組隊學習 -CS224n-預訓練模塊
詳細課程內容參考(2019)斯坦福CS224n深度學習自然語言處理課程

1. 寫在前面

自然語言處理( NLP )是信息時代最重要的技術之一,也是人工智能的重要組成部分。NLP的應用無處不在,因爲人們幾乎用語言交流一切:網絡搜索、廣告、電子郵件、客戶服務、語言翻譯、醫療報告等。近年來,深度學習方法在許多不同的NLP任務中獲得了非常高的性能,使用了不需要傳統的、任務特定的特徵工程的單個端到端神經模型。 而【NLP CS224N】是入門NLP的經典課程, 所以這次藉着Datawhale組織的NLP學習的機會學習這門課程, 並從細節層面重新梳理NLP的知識。

今天是該課程的第二篇筆記, 在第一篇Introduction and Word VectorsWord2Vec的基礎上又進行了一步, 首先會簡單的回顧上一篇裏面Word2Vec的工作過程並補充一些訓練方面的微觀細節,因爲Word2Vec只是一種計算詞嵌入的一種模式, 實際訓練中我們得用它的具體的算法模型, 比如Skip-gram Model(這兩者之間的關係給我的感覺就是Word2Vec是理論基礎, 而Skip-gram是實踐層面), 所以針對Skip-gram Model, 也對微觀訓練細節進行補充, 然後分析Skip-Model模型的問題並介紹一種叫做負採樣的高效訓練方法, 而計算詞向量並不只是Word2Vec的方法, 這種計算詞向量的方法叫做Direct Prediction 模型, 還有一種叫做Count-based模型, 這個在第一節課的大作業中做了一些鋪墊, 就是那個共現矩陣的方式, 兩者各有優勢和不足, 所以後來又提出了一種結合這兩種計算詞向量的思路就是Glove, 最後會對Glove算法進行詳細介紹。 這次的內容會比較多, 並且有大量的數學公式, 所以感覺有壓力, 依然是建議先從吳恩達老師的課程學起!

大綱如下

  • 簡單Word2Vec的工作過程和細節補充
  • Skim-Model 的高效訓練方式負採樣
  • Count-Based 模型
  • Glove算法介紹

Ok, let’s go!

2. Word2Vec的工作過程和細節補充

Word2Vec的詳細工作過程, 在第一篇文章中已經做了介紹, 這裏簡單的回顧一下, 首先Word2Vec是一種計算單詞詞向量的一種方式, 核心思想是預測每個單詞和上下文單詞之間的關係。具體實現算法有Skip-gram和CBOW兩種模型。 具體的流程細節, 是下面的這張圖:

下面簡單的回顧Word2Vec的計算細節:
給定一箇中心詞, 其窗口內context出現的概率爲:

P(oc)=exp(uoTvc)wVexp(uwvc)P(o \mid c)=\frac{\exp \left(u_{o}^{T} v_{c}\right)}{\sum_{w \in V} \exp \left(u_{w} v_{c}\right)}
然後我們的目標是通過極大似然估計的方式最大化整個文本出現的概率:
L(θ)=t=1Tmjm,j0P(wt+jwt,θ)L(\theta)=\prod_{t=1}^{T} \prod_{-m \leq j \leq m, j \neq 0} P\left(w_{t+j} \mid w_{t}, \theta\right)
但是這個目標我們說連乘不好, 所以化簡了一下:
J(θ)=1TlogL(θ)=1Tt=1Tmjm,j0logP(wt+jwt,θ)J(\theta)=-\frac{1}{T} \log L(\theta)=-\frac{1}{T} \sum_{t=1}^{T} \sum_{-m \leq j \leq m, j \neq 0} \log P\left(w_{t+j} \mid w_{t}, \theta\right)
假設詞典中有V個單詞, 每個單詞詞向量長度爲d, 對於每一個詞, 作爲中心詞(center)和非中心詞(outside)時分別使用v和u兩個向量表示。
UV×d(outside)=[u1u2uV]U_{V \times d}(\text {outside})=\left[\begin{array}{c} u_{1} \\ u_{2} \\ \vdots \\ u_{V} \end{array}\right]
VV×d(center)=[v1v2vV]V_{V \times d}(\text {center})=\left[\begin{array}{c} v_{1} \\ v_{2} \\ \vdots \\ v_{V} \end{array}\right]

這就是我們每個單詞的詞向量, 每個是有兩個詞向量, 上一篇文章中,我們是把這兩個直接堆疊起來的, 而還可以將兩個向量平均作爲最終詞向量表示。這樣每個單詞就對應一個詞向量了。 而對於每個中心詞, 我們會先得到它的詞向量表示, 然後通過上下文矩陣得到該詞與其他詞之間的關係:
DV×1=UV×dv4=[d1d2dV]D_{V \times 1}=U_{V \times d} \cdot v_{4}=\left[\begin{array}{c} d_{1} \\ d_{2} \\ \vdots \\ d_{V} \end{array}\right]
然後進一步就可以通過softmax轉換成概率:
PV×1=softmax(DV×1)=[p1p2pV]P_{V \times 1}=\operatorname{softmax}\left(D_{V \times 1}\right)=\left[\begin{array}{c} p_{1} \\ p_{2} \\ \vdots \\ p_{V} \end{array}\right]
這個就是Word2Vec的計算細節了, 接下來就是根據目標函數進行求導數,然後進行U和V參數的更新了, 從而使詞向量模型需對出現在同一個context中的詞賦予較大的概率。

但是Word2Vec畢竟只是理論的部分, 真實實現上我們是通過訓練模型進行上面過程的計算的, 所以還是要注意一些細節, 比如我們用Skip-gram Model實現的時候, 我們是轉換成了一個監督學習的問題, 那麼該模型的損失函數就不是單純的上面那種目標函數了, 得能衡量出模型的預測與真實目標之間的差距, 由於這是一個多分類問題, 所以採用了交叉熵損失函數。 再比如梯度下降的時候, 如果是整個數據集上進行更新參數,那麼計算量會很大, 所以會採從數據及中隨機抽樣部分數據(batch), 在詞向量計算中對每一個window數據計算一次更新。再比如高頻詞(the)引起的問題,通過以上計算過程可以知道,如果兩個詞出現在一個context的次數越頻繁,那麼他們的詞向量就會越接近,這樣一來像the這樣的高頻詞,就會使它前後的詞向量高度集中,從而導致一些問題。所以採樣的時候得考慮這種情況等等。

所以上面的細節最好也瞭解一下, 下面就對真實訓練情景中的損失函數進行梳理, 也就是交叉熵損失函數。
在這裏插入圖片描述
那麼對於這樣的損失函數, 我們是怎麼求導的呢? 其實和上一篇裏面的求導方式基本一致, 只不過這裏得加上已知的東西:
在這裏插入圖片描述
這就是真實訓練裏面的梯度計算過程, 有了梯度, 就可以進行梯度下降更新參數了。當然真實實現裏面反向傳播不需要自己寫。

但是Skip-Gram Model訓練詞向量的方式有沒有問題呢? 其實是有的, 因爲我們分析一下輸出, 可以看到softmax這個公式,分母是有個求和的,也就是如果有10000個單詞的時候,模型最後輸出的時候都得考慮進來,10000個單詞究竟哪個單詞概率最大。如果1000000個單詞的話,模型得1000000次加和,這樣的計算量太大了,所以就有了一些訓練的改進版本, Distributed Representations of Words and Phrases and their Compositionality論文裏面提到了兩種,一個是Hierarchical softmax classifier, 另一個是負採樣的方式。我們重點看看第二種。

3. Skim-Model 的高效訓練方式負採樣

Negative sampling是另外一種有效的求解embedding matrix EE的方法。它的做法是判斷選取的context word和target word是否構成一組正確的context-target對,一般包含一個正樣本和k個負樣本。例如,“orange”爲context word,“juice”爲target word,很明顯“orange juice”是一組context-target對,爲正樣本,相應的target label爲1。若“orange”爲context word不變,target word隨機選擇“king”、“book”、“the”或者“of”等。這些都不是正確的context-target對,爲負樣本,相應的target label爲0。這就是如何生成訓練集的方法。選一個正樣本和K個負樣本(樣本是成對出現的)

Negative sampling的數學模型爲:
P(y=1c,o)=σ(uoTvc)P(\boldsymbol{y}=\mathbf{1} | \boldsymbol{c}, \boldsymbol{o})=\sigma\left(u_{o}^{T} \cdot \boldsymbol{v}_{c}\right)
其中,σ表示sigmoid激活函數。很明顯,negative sampling某個固定的正樣本對應k個負樣本,即模型總共包含了k+1個binary classification。對比之前介紹的10000個輸出單元的softmax分類,negative sampling轉化爲k+1個二分類問題,計算量要小很多,大大提高了模型運算速度。
(就是每一次訓練,都是K+1個二分類問題, 就看target的那幾個是不是我們想要的0或者1,然後用這幾個去計算損失更新參數即可)。 負採樣的損失函數長這個樣子:
Jnegsample(vc,o,U)=log(σ(uovc))k=1Klog(σ(ukvc))\boldsymbol{J}_{\mathrm{neg}-\operatorname{sample}}\left(\boldsymbol{v}_{c}, o, \boldsymbol{U}\right)=-\log \left(\sigma\left(\boldsymbol{u}_{o}^{\top} \boldsymbol{v}_{c}\right)\right)-\sum_{k=1}^{K} \log \left(\sigma\left(-\boldsymbol{u}_{k}^{\top} \boldsymbol{v}_{c}\right)\right)
這個損失函數可能看起來比較難理解, 那麼稍微改寫一下:
Jnegsample(vc,o,U)=[log(σ(uovc))+k=1Klog(σ(ukvc))]\boldsymbol{J}_{\mathrm{neg}-\operatorname{sample}}\left(\boldsymbol{v}_{c}, o, \boldsymbol{U}\right)=-[\log \left(\sigma\left(\boldsymbol{u}_{o}^{\top} \boldsymbol{v}_{c}\right)\right)+\sum_{k=1}^{K} \log \left(\sigma\left(-\boldsymbol{u}_{k}^{\top} \boldsymbol{v}_{c}\right)\right)]
我們分析中括號裏面那塊,理解起來的話,就是我們的輸入是選擇的中心詞,也就是這裏的vcv_{c}, 是embedding之後的向量,而輸出是正負樣本的embedding後的向量。 前面的那部分是正確的上下文詞和中心詞的關係,uou_{o}就是正樣本embedding後的形式,這兩個的內積操作其實就是兩者的關係程度(內積的幾何意義)。 後面的那部分是負樣本和中心詞的關係,我們希望的是中心詞與正樣本的關係儘可能的近,也就是前面那部分越大越好,希望負樣本與中心詞的關係儘可能的小,但是後面發現內積前加了個負號,那就表示後面那部分越大越好。 所以中括號那部分應該越大越好。而前面加了負號, 就是希望損失越小越好。

通過這樣的方式, 就可以提高訓練的效率, 因爲這種就相當於每次訓練做了一個K+1次的二分類任務, 而之前那種方式是做了V分類的任務, 而這個V是單詞的個數, 往往會上百萬。 而這裏的K, 如果訓練樣本小, 取5-20, 訓練樣本大, 取2-5即可。 所以這個計算量上就容易了很多。 當然,我們也可以看一下這個損失函數下各個參數的導數:
在這裏插入圖片描述
從這個偏導也可以看出, 梯度更新的時候, 負採樣的計算參數量要遠遠小於softmax的。 關於具體實現方式, 可以參考Pytorch入門+實戰系列三:Pytorch與詞向量

最後提一點,關於如何選擇負樣本對應的target單詞,可以使用隨機選擇的方法。但論文中提出一個更實用、效果更好的方法,就是根據該詞出現的頻率進行選擇,相應的概率公式爲:
P(wi)=f(wi)34j10000f(wj)34P\left(w_{i}\right)=\frac{f\left(w_{i}\right)^{\frac{3}{4}}}{\sum_{j}^{10000} f\left(w_{j}\right)^{\frac{3}{4}}}
這樣貌似可以解決一些高頻詞引起的問題。

好了,關於direct Prediction模型的理論部分就先整理這麼多, 下面就是Count-based Model。

4. Count-Based 模型

這種模型也是學習詞向量的一種方式, 這類模型的經典代表就是SVD模型, 就是大作業1裏面的思路, 在相似的上下文中我們一般會使用意思相似的單詞(同義詞),因此,意思相近的單詞會通過上下文的方式在一起出現。通過檢查這些上下文,我們可以嘗試把單詞用詞向量的方式表示出來,一種簡單的方式就是依賴於單詞在一起出現的次數, 所以就得到了一種叫做共現矩陣的策略,這是一個基於單詞頻數的詞向量矩陣, 然後再進行SVD分解降維得到每個單詞的詞向量。

共現矩陣XX的產生方式有兩種選擇:

  • word-document co-occurrence matrix: 基本假設是在同一篇文章中出現的單詞更有可能相互關聯。 假設單詞ii出現在文章jj中, 則矩陣元素XijX_{ij}加一, 當處理完所有文章後, 就得到了矩陣XX, 大小是V×M|V|\times M, V|V|表示詞彙量, MM是文章數。
  • word-word co-occurrence matrix: 利用某個定長窗口中單詞與單詞同時出現的次數來得到矩陣XX, 這個的大小會是V×V|V|\times|V|。 具體詳細的在大作業一里面已經說明並實現了這種方式, 這裏就不詳細說了。 看個例子吧:
    在這裏插入圖片描述
    這就是共現矩陣了, 當然我們很容易發現, 這種矩陣存在問題,可以看出,隨着詞彙量的增大,矩陣 XX的尺度會越來大,爲了有效的存儲,我們可以對其進行SVD處理:
    在這裏插入圖片描述
    爲了減少尺度同時儘量保存有效信息,可保留對角矩陣的最大的k個值,其餘置零,並將酉矩陣的相應的行列保留,其餘置零:
    在這裏插入圖片描述
    這就是經典的SVD算法, 具體實現上可以調用sklearn裏面的Truncated SVD包實現降維。
    在這裏插入圖片描述

好了, 這就是Count-Based Model了, 下面就對比分析一下direct prediction和Count based這兩種方式的優缺點:
在這裏插入圖片描述
Count based模型優點是訓練快速,並且有效的利用了統計信息,缺點是對於高頻詞彙較爲偏向,並且僅能概括詞組的相關性,而且有的時候產生的word vector對於解釋詞的含義如word analogy等任務效果不好;Direct Prediction優點是可以概括比相關性更爲複雜的信息,進行word analogy等任務時效果較好,缺點是對統計信息利用的不夠充分。

所以Manning教授他們想採取一種方法可以結合兩者的優勢, 並將這種算法命名爲GloVe(Global Vectors的縮寫),表示他們可以有效的利用全局的統計信息。那麼是怎麼做的呢?

5. GloVe算法

GloVe的思想是在word-word co-occurrence count的基礎上學習到詞語背後的含義。 爲了更好的描述這個問題, 可以先定義一些符號:

對於共現矩陣XX, XijX_{ij}表示單詞jj出現在單詞ii上下文中的次數, 則Xi=kXikX_{i}=\sum_{k} X_{i k},即代表所有單詞出現在單詞ii的上下文中的單詞次數。 相當於共現矩陣的一行, 我們用Pij=P(ji)=XijXiP_{ij}=P(j|i)=\frac{X_{ij}}{Xi}來代表單詞jj出現在單詞ii上下文中的概率。 這樣的感覺:

在這裏插入圖片描述

那麼有了這個如何表示詞彙的含義呢? 拿PPT裏面的例子:
在這裏插入圖片描述
例如, 我們想區分熱力學上兩種不同的狀態ice冰和蒸汽steam, 他們之間的關係可通過與不同的單詞xx的co-occurrence probability的比值來描述。 例如對於solid固態, 雖然P(solidice)P(solid|ice)P(solidstream)P(solid|stream)本身很小, 不能透露有效的信息, 但是它們的比值P(solidice)P(solidsteam)\frac{P(\text {solid} \mid i c e)}{P(\text {solid} \mid \text {steam})}卻比較大了, 因爲solid更常用來描述ice而不是steam的狀態, 所以在ice的上下文中出現的概率較大。 而gas就恰恰相反了, 所以從上面的例子中, 通過比值的方式就可以判斷solid和ice相關性大, gas和steam相關性大, water和fashion和這兩個中心詞基本沒啥關係。

所以共現概率比值能比較直觀地表達詞之間的關係。GloVe試圖用有關詞向量的函數來表達共現概率比值。 即:
F(wi,wj,w~k)=PikPjkF\left(w_{i}, w_{j}, \tilde{w}_{k}\right)=\frac{P_{i k}}{P_{j k}}
其中w~\tilde{w}代表了context vector,如上例中的solid,gas,water,fashion等。 wi,wjw_i, w_j則是我們要比較的兩個詞彙, 中心詞,如上例中的ice,steam。 下面的目標就是想辦法求出左邊的FF(這個FF的求解過程沒有嚴謹的正向推導, 而是往回推的一種思路。 課上老師提到的GloVe論文第一作者Jeffrey Pennington在成爲Manning組的PostDoc之前是理論物理的博士,他用了物理學家簡化假設做back-of-envelope計算合理推斷的習慣)。

FF的可選的形式過多,我們希望有所限定。首先我們希望的是FF能有效的在單詞向量空間內表示概率比值,由於向量空間是線性空間, 一個自然的假設是FF是關於向量wj,wiw_j, w_i的差的形式, 用向量之差來表達共現概率的比值:
F(wiwj,w~k)=PikPjkF\left(w_{i}-w_{j}, \tilde{w}_{k}\right)=\frac{P_{i k}}{P_{j k}}
等式右邊爲標量形式,左邊如何操作能將矢量轉化爲標量形式呢?一個自然的選擇是矢量的點乘形式:
F((wiwj)Tw~k)=PikPjkF\left((w_{i}-w_{j})^T \tilde{w}_{k}\right)=\frac{P_{i k}}{P_{j k}}

由於任意一對詞共現的對稱性, 我們希望下面兩個性質可以同時被滿足:

  1. 任意詞作爲中心詞和背景詞的詞向量應該相等: 對任意詞ii, wi=wi~w_i=\tilde{w_i}
  2. 詞與詞之間共現次數矩陣XX應該對稱: 對任意詞i,ji, j, xij=xjix_{ij}=x_{ji}

爲了滿足上面的性質, 一方面令F((wiwj)Tw~k)=F(wiTwk~)F(wjTwk~)F\left((w_{i}-w_{j})^T \tilde{w}_{k}\right)=\frac{F(w_i^T\tilde{w_k})}{F(w_j^T\tilde{w_k})}, 這樣能滿足對稱性, 就得到了F=expF=exp。 同時與F((wiwj)Tw~k)=PikPjkF\left((w_{i}-w_{j})^T \tilde{w}_{k}\right)=\frac{P_{i k}}{P_{j k}}聯立會得到F(wiTwk~)=Pik=XikXi=ewiTwk~F(w_i^T\tilde{w_k})=P_{ik}=\frac{X_{ik}}{X_i}=e^{w_i^T\tilde{w_k}}, 所以有了
wiTwk~=log(Pik)=log(Xik)log(Xi)w_i^T\tilde{w_k}=log(P_{ik})=log(X_{ik})-log(X_i)
但是單純的這個式子會破壞對稱性, 因爲log(Xi)log(X_i)的存在, 這個並不依賴kkiki與k一交換位置這個肯定沒法保證等式不變, 所以爲了保證平衡性, 把這個替換成了兩個偏移項之和bi+bkb_i+b_k, 得到了
wiTwk~=log(Xik)bibkw_i^T\tilde{w_k}=log(X_{ik})-b_i-b_k
將索引i和k互換,我們可驗證對稱性的兩個性質可以同時被上式滿足。

因此,對於任意一對詞iji和j用它們詞向量表達共現概率比值最終可以被簡化爲表達它們共現詞頻的對數
wiTwk~+bi+bk=log(Xik)w_i^T\tilde{w_k}+b_i+b_k=log(X_{ik})

這就類似於一種反推的思想, 要使得 F(wi,wj,w~k)=PikPjkF\left(w_{i}, w_{j}, \tilde{w}_{k}\right)=\frac{P_{i k}}{P_{j k}}成立, 需要使得F(wiwj,w~k)=PikPjkF\left(w_{i}-w_{j}, \tilde{w}_{k}\right)=\frac{P_{i k}}{P_{j k}}成立, 需要使得…, 到了wiTwk~+bi+bk=log(Xik)w_i^T\tilde{w_k}+b_i+b_k=log(X_{ik})也就是說如果最後的這個等式成立的話, 我們就能用詞向量表達共現概率比值

上式中的共現詞頻是直接在訓練數據上統計得到的,爲了學習詞向量和相應的偏移項,我們希望上式中的左邊與右邊越接近越好, 也就是等號儘可能成立, 這纔有了PPT裏面Glove的損失函數的形式:
在這裏插入圖片描述
另一方面作者注意到模型的一個缺點是對於所有的co-occurence的權重是一樣的,即使是那些較少發生的co-occurrence。作者認爲這些可能是噪聲,所以他加入了前面的f(Xij)f(X_{ij})項來做weighted least squares regression模型。 這個權重項需要滿足下面的條件:

  • f(0)=0f(0)=0, 因爲要求limx>0f(x)log2xlim_{x->0}f(x)log^2x是有限的
  • 較少發生的co-occurrence所佔權重小
  • 對於較多發生的co-occurrence, f(x)f(x)也不能過大

作者試驗的較好的權重函數形式:
在這裏插入圖片描述
GloVe的優勢:
在這裏插入圖片描述
當然, 實際應用中還是Word2Vec用的多一些, 具體任務具體分析吧。對了, 這個的代碼實現思路依然是類似於skip gram model那樣, 只不過損失函數需要改成上面的這種損失函數。

好了, 到了這裏, 基本上把Glove的一些東西交代清楚了, 後面就是分析了一些實驗裏面的內容了, 具體的看下面的最後一個鏈接, 這裏把結論記一下:

  • 關於詞向量的維度, 300是一個不錯的向量維度
  • 不對稱上下文(只使用單側的單詞)不是很好,但是這在下游任務重可能不同
  • window size 設爲 8 對 Glove向量來說比較好
  • 訓練時間越長越好, 數據集越大越好,並且維基百科數據集比新聞文本數據集要好
  • 對於多義的詞, 可以先將其上下文聚類, 得到一些清晰的簇,從而將這個常用詞分解爲多個單詞,例如bank_1, bank_2, bank_3

關於更多的內容, 見最後一篇鏈接吧。

6. 總結一下

這篇文章主要是對詞向量求解方式的細節進行了展開, 求解詞向量, 大方向上有兩種方式, 基於詞頻和直接預測模型, 後者就是Word2Vec, 所以基於上一篇文章又補充了一些實現層面的細節, 然後就介紹了基於詞頻的求解詞向量的方式, 最後把兩者的思路進行組合,得到了GloVe算法, 主要補充了GloVe目標函數的推導過程。

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章