1、詞向量(Word Vectors)
英語中大約有13億個符號,從Feline(貓科動物)到cat(貓),hotel(旅館)到motel(汽車旅館),很明顯它們之間是有關聯的。我們需要將單詞一一編碼到向量中,一個向量表示了詞空間中的一個點。
最簡單的一種詞向量就是one-hot向量:將每個詞都表示為一個$\mathbb{R}^{\left | V \right | \times 1}$的向量,改詞在詞表中索引的位置是1,其他位置都是0。$\left | V \right |$是詞表的大小。one-hot詞向量形式如下:
\begin{align*}
w^{aardvark} = \begin{bmatrix}1\\ 0\\ 0\\ \vdots \\ 0 \end{bmatrix},
w^{a} = \begin{bmatrix}0\\ 1\\ 0\\ \vdots \\ 0 \end{bmatrix},
w^{at} = \begin{bmatrix}0\\ 0\\ 1\\ \vdots \\ 0 \end{bmatrix},
\cdots ,
w^{zebra} = \begin{bmatrix}0\\ 0\\ 0\\ \vdots \\ 1 \end{bmatrix}
\end{align*}
雖然我們成功地將每個詞都表示為了不同的實體,但這一表示方式並不能提供詞之間相似性的概念,例如:
\begin{align*}
(w^{hotel})^Tw^{motel} = (w^{hotel})^Tw^{cat} = 0
\end{align*}
我們需要找到一個空間,該空間維度遠小於$\mathbb{R}^{\left | V \right | \times 1}$,並且該空間中的詞向量,可以體現出詞之間的相似度。
2、基於SVD(奇異值分解)的方法
為了找到詞嵌入(word embeddings,可以簡單的理解為與詞向量等價),可以首先遍歷巨大的語料庫,統計兩個詞之間某種形式的共現次數,並將統計值保存在一個矩陣$X$中。然后應用奇異值分解:$X = USV^T$。我們使用$U$中的每一行作為每個詞的詞嵌入。下面將討論$X$的一些選擇方式。
2.1 詞-文檔矩陣(Word-Document Matrix)
作為第一次嘗試,我們首先大膽地猜想,經常出現在同一文檔中的詞是有關聯的。比如,"banks"、"bonds"、"stocks"、"money"等等,很可能會共同出現。但是"banks"、"octopus"、"banana"、"hockey"並不能經常共現。基於這一事實創建一個詞-文檔矩陣$X$,方法如下:遍歷數以億計的文檔,每當第$i$個詞出現在第$j$個文檔中,就對矩陣中元素$X_{i,j}$加一。最終得到的是一個龐大的$\mathbb{R}^{\left | V \right | \times M}$矩陣,並且還會隨着文檔數量($M$)縮放比例。該方案顯然很不理想。
2.2 基於窗口的共現矩陣(Window based Co-occurrence Matrix)
這個方法不再考慮文檔的數量,統計兩個詞在給定窗口中的共現次數。例如,語料是下面的三句話,窗口大小是1:
1. I enjoy flying.
2. I like NLP.
3. I like deep learning.
共現矩陣為:
2.3 對共現矩陣應用SVD
對$X$應用SVD:
中間的矩陣除了對角線,其他位置都是0。對角線上的元素$\sigma_i$稱為奇異值。我們可以從矩陣U中選取前$k$列,作為詞向量。而這前$k$列所能保存的原數據的方差(variance)比例為:
\begin{align*}
\frac{\sum_{i=1}^{k} \left | \sigma_i \right |}{\sum_{i=1}^{\left | v \right |} \left | \sigma_i \right |}
\end{align*}
通過選取前$k$列奇異向量進行降維:
用這種方式生成的詞向量保留了足夠多的語法和語義信息,但也還是存在一些問題:
- 矩陣維度經常變動,比如新詞頻繁加入。
- 由於絕大部分詞並不會共現,造成矩陣過於稀疏。
- 矩陣維度一般很高,大約$10^6 \times 10^6$。
- 並且難以合並新詞或新的文檔。對於一個$m \times n$矩陣,訓練時的計算復雜度是$O(mn^2)$
- 由於詞頻的極度不平衡,需要對矩陣$X$應用一些黑科技。
上述問題的一些解決方案:
- 忽略the、he、has等虛詞。
- 應用一個斜坡窗口(ramp window,也就是說,不再對窗口內的所有詞一視同仁)——比如,根據距離當前詞的距離,對共現次數賦予相應的權重。
- 使用皮爾遜相關系數(Pearson correlation coefficient),取代直接計數。
看了下一小節我們就會發現,基於迭代的方法可以使用更優雅的方式解決上述問題。
3 基於迭代的方法——Word2vec
與之前計算和存儲巨大數據集的全局信息不同,現在我們用反向傳播訓練一個模型,預測一個詞在給定上下文存在的概率,該模型的參數就是詞向量。下面介紹一個簡單的、較新的、基於概率的模型——word2vec:
- 兩個算法:continuous bag-of-words (CBOW)和skip-gram。CBOW是給定上下文環境時,預測該環境中間的那個中心詞。Skip-gram正好相反,預測一個中心詞上下文環境的分布。
- 兩個訓練方法:負采樣(negative sampling)和分層softmax。
word2vec概況:https://myndbook.com/view/4900
3.1 語法模型(一元、二元)
首先我們來定義一個模型,該模型可以給一個符號序列分配概率。以下面的句子為例:
"The cat jumped over the puddle."
一個好的語言模型,會給上面的句子分配一個較高的概率。因為這個句子在語法和語義上都是正確的。數學上,這$n$個詞的概率可表示為:
\begin{align*}
p(w_1, w_2, \cdots, w_n)
\end{align*}
一元語法模型就是假設每個詞都是獨立的,這會大大降低聯合概率的復雜性:
\begin{align*}
p(w_1, w_2, \cdots, w_n) = \prod_{i=1}^{n}P(w_i)
\end{align*}
但這一模型是很荒唐的。因為我們知道,后詞對它前面序列的依賴性是很高的。這也可能會使得錯誤的句子得到一個較高的概率。所有這就有了二元語法模型,當前詞只依賴於它前面的一個詞:
\begin{align*}
p(w_1, w_2, \cdots, w_n) = \prod_{i=2}^{n}P(w_i|w_{i-1})
\end{align*}
雖然這一模型也很幼稚,但它的效果還可以。
現在我們已經知道,一個符號序列是可以有概率的。下面將介紹可以學習這些概率的模型。
3.2 連續詞袋模型(Continuous Bag of Words Model,CBOW)
把{"The", "cat", ’over", "the’, "puddle"}看作是上下文環境,來預測中間的單詞"jumped",這就是CBOW模型。這是一個簡單的神經網絡,如下所示:
圖1 CBOW工作原理
我們定義輸入層到隱層的權重矩陣為$\mathcal{V} \in \mathbb{R}^{n \times \left | V \right |} $(也就是上圖中的$W_{V \times N}$),隱層到輸出層的權重矩陣為$\mathcal{U} \in \mathbb{R}^{\left | V \right | \times n}$(也就是上圖中的$W'_{N \times V}$)。其中,$n$是隱層神經元個數,也是詞嵌入空間的維度。$\mathcal{V}$是輸入詞矩陣,當一個詞$w_i$作為輸入進入模型時,$\mathcal{V}$的第$i$列就是該詞的嵌入向量。這一$n \times1$向量記為$v_i$。類似的,$\mathcal{U}$是輸出詞矩陣,當一個詞$w_j$是該模型的輸出時,$\mathcal{U}$的第$j$行就是該詞的嵌入向量。這一$1 \times n$矩陣記為$u_j$。所以,在模型中,一個詞是對應兩個詞向量的。
用反向傳播訓練這一神經網絡:
- 假設窗口為$m$,輸入數據就是上下文環境中$2m$個詞的one-hot向量:$x^{(c-m)},\cdots ,x^{(c-1)},x^{(c+1)},\cdots x^{(c+m)} \in \mathbb{R}^{\left | V \right |}$
- 獲取上下文環境的嵌入詞向量:$v_{c-m} = \mathcal{V}x^{(c-m)},\cdots ,v_{c+m} = \mathcal{V}x^{(c+m)} \in \mathbb{R}^n$
- 計算這次嵌入詞向量的均值:$\hat{v} = \frac{v_{c-m} + \cdots + v_{c+m}}{2m}$
- 生成一個分值向量$z = \mathcal{U}\hat{v} \in \mathbb{R}^{\left | V \right |}$。由於相似向量的內積較大,這會使得相似詞的向量越來越相似,以便得到較高的分值
- 應用softmax激活函數,將分值轉換為概率$\hat{y} = \mbox{softmax}(z) \in \mathbb{R}^{\left | V \right |}$
- 神經網絡的目標值就是中心詞"jumped"的one-hot向量,記為$y$。定義損失函數為交叉熵損失,反向傳播進行訓練。
假設$c$是目標值one-hot向量為1的位置的索引,損失函數:
3.3 Skip-Gram模型
這次給定中心詞"jumped",然后預測它周圍的詞"The", "cat", "over", "the", "puddle"。這也是一個神經網絡,如下所示:
圖2 Skip-Gram工作原理
定義同樣的$\mathcal{V} \in \mathbb{R}^{n \times \left | V \right |} $和$\mathcal{U} \in \mathbb{R}^{\left | V \right | \times n}$,訓練這一神經網絡
- 輸入向量$x \in \mathbb{R}^{\left | V \right |}$是中心詞的one-hot向量。
- 計算中心詞的嵌入詞向量$v_c = \mathcal{V}x \in \mathbb{R}^n$
- 計算分值向量$z = \mathcal{U}v_c$
- 使用softmax激活函數,將分值轉換為概率$\hat{y} = \mbox{softmax}(z) \in \mathbb{R}^{\left | V \right |}$
- 目標值是中心詞周圍那些詞的one-hot向量,記為$y^{(c-m)},\cdots ,y^{(c-1)},y^{(c+1)},\cdots ,y^{(c+m)}$。
定義損失函數:
\begin{align*}
J = -logP(w_{c-m},\cdots ,w_{c-1},w_{c+1},\cdots ,c_{c+m}|w_c)
\end{align*}
為了簡化模型,我們假設這些條件概率相互獨立(朴素貝葉斯假設),從而有:
此外,
\begin{align*}
J &= -log\prod_{j=0,j\neq m}^{2m}P(w_{c-m+j}|w_c) \\
&= -log\prod_{j=0,j\neq m}^{2m}P(u_{c-m+j}|v_c) \\
&= -\sum_{j=0,j\neq m}^{2m}logP(u_{c-m+j}|v_c) \\
&= \sum_{j=0,j\neq m}^{2m}H(\hat{y},y_{c-m+j})
\end{align*}
其中,$H(\hat{y},y_{c-m+j})$是交叉熵。
3.4 Negative Sampling
我們再回頭觀察一下損失函數,對整個$\left | V \right |$求和,計算量巨大。每一次更新或者評估損失函數,都會有$O(\left | V \right |)$的時間復雜度。我們必須減少這一計算量。
在訓練時的每一步,與遍歷整個詞匯表不同,這次只需要抽樣部分反面例子(negative examples)。我們從一個噪聲分布($P_n(w)$,noise distribution)抽樣,其概率分布與詞匯表的詞頻相對應。
雖然negative sampling基於Skip-Gram模型,事實上前者是在優化一個不同的目標函數。考慮一個詞(word)和上下文環境(context)的組合$(w,c)$。將$(w,c)$在語料中存在的概率記為$P(D=1|w,c)$,在語料中不存在的概率記為$P(D=0|w,c)$。首先使用sigmoid函數對$P(D=1|w,c)$建模:
然后,應用極大似然估計,創建新的目標函數,使得$P(D=1|w,c)$最大,$P(D=0|w,c)$最小(此時模型的參數是$\theta$,在外面之前的例子中是$\mathcal{V}$和$\mathcal{U}$):
最大似然和最小負log似然是等價的:
其中,$\widetilde{D}$是錯誤的、反面的語料。也就是說,類似"stock boil fish is toy"不通順的句子。可以從詞庫中隨機抽樣,生成這種錯誤的句子。
對於skip-gram模型,給出中心詞$c$並觀察它窗口內的單詞$c-m+j$時的損失函數更新為:
對於CBOW模型,給出上下文環境$\hat{v} = \frac{v_{c-m} + \cdots + v_{c+m}}{2m}$並觀察到中心詞$u_c$時的損失函數更新為:
在上面的公式中,$\{ \widetilde{u_k}|k=1,\cdots ,K \}$是從$P_n(w)$抽樣而來的。為了達到最佳逼近效果,采用了一元語法模型,並對詞頻取3/4次冪。下面一個例子,說明了為什么是3/4:
"Bombastic"被抽樣的概率,增加到原來的三倍,而"is"幾乎不變。
3.5 分層Softmax
Mikolov等人表明,分層softmax的效率要遠高於普通的softmax。在實踐中,分層softmax更擅長於處理不常見的詞匯,而negative sampling更擅長處理常見詞匯以及生產更低維度的向量。
分層softmax使用一棵二叉樹來表示詞表中的所有詞匯。每個葉節點都是一個詞,並且從根節點到葉節點的路徑是唯一的。該模型並不會輸出詞匯的向量表示,不過圖中的每一個節點(除了根節點和葉子節點)都對應一個向量,這些向量是模型需要學習的參數。
在模型中,給定一個向量$w_i$,該向量表示詞$w$的概率$P(w|w_i)$,與從根節點到達葉子節點的概率相同。這種方式帶來的好處是,時間復雜度從常規softmax(3.1和3.2所使用的,計算概率的激活函數)的$O(\left | V \right |)$下降到了$O(log(\left | V \right |))$(其實就是將3.1、3.2使用的softmax激活函數替換成了一棵二叉樹,同時也引入了一堆需要訓練的參數)。
現在需要引入一些概念。用$L(w)$表示從根節點到葉節點$w$的節點數。例如,在圖3中,$L(w_2) = 3$。記$n(w,i)$是路徑上的第$i$個節點,相應的向量為$v_{n(w,i)}$。因此$n(w,1)$是根節點,$n(w,L(w))$是$w$的父節點。對於每個中間節點$n$,我們任意選擇它的一個子節點並稱之為$ch(n)$(這是在模型訓練開始之前就選好的。每個中間節點都有兩個子節點,可以任意選擇。在本文中,每次都選左側子節點)。
圖3 分層Softmax的二叉樹
然后計算如下形式的概率:
其中,
此外,$\sigma(\cdot)$是sigmoid函數。
上面的公式信息量比較大,我們來詳細地解釋一下:
公式的最右側是在計算向量內積。$v_{n(w,j)}$的長度是在變動的,所以每次$v_{w_i}$都截取相應的位數。我們假設$ch(n)$每次都是左側的子節點。所以單詞$w$真實的路徑是向左時,$[ n(w,j+1) = ch(n(w,j))]$返回1,否則返回-1。
此外,$[ n(w,j+1) = ch(n(w,j))]$可以起到標准化(normalization)的作用。對於任一節點,走向左節點的概率和走向右節點的概率之和都應該為1(因為就兩個子節點,不是向左就是向右)。對於任意的$v_n^Tv_{w_i}$,下面等式始終都是成立的:
\begin{align*}
\sigma(v_n^Tv_{w_i}) + \sigma(-v_n^Tv_{w_i}) = 1
\end{align*}
這其實也很好證明。sigmoid函數的定義:
\begin{align*}
\sigma(x) = \frac{1}{1 + e^{-x}}
\end{align*}
從而可得:
\begin{align*}
\sigma(x) + \sigma(-x) &= \frac{1}{1 + e^{-x}} + \frac{1}{1 + e^{x}} \\
&= \frac{1 \cdot e^x}{(1 + e^{-x}) \cdot e^x} + \frac{1}{1 + e^{x}} \\
&= \frac{e^x}{e^x + 1} + \frac{1}{1 + e^{x}} \\
&= 1
\end{align*}
這也保證了$\sum_{w=1}^{\left | V \right |} P(w|w_i) = 1$是成立的。
為了訓練這一模型,我們的損失函數依然是負log loss(與交叉熵一樣):$-logP(w|w_i)$。
本文翻譯自CS224n課程的官方筆記1,對應該課程的第1、2節。