# 自然語言處理 01:word2vec
###### tags: `NLP`
## 本篇都用 "You say goodbye and I say hello." 為例
## 1. 字詞分散式表示
以固定的長度向量表示字詞,且該項量的特色是以密集向量表示。例如:
you = [0.34, 3.24, 1.21]
say = [2.62, 1.67, 0.02]
<br>
### 1-1. 共生矩陣
根據**分布假說:詞義都是由周圍的字詞形成的**,因此最直觀的做法就是計算周圍出現了什麼字詞,也稱為<font color="#f00">**記數手法**</font>,而將全部的字詞整理成表格,這表格對應該字的向量,也稱為<font color="#f00">**共生矩陣**</font>。

<br>
### 1-2. 餘弦相似度(cosine similarity)
**餘弦相似度**表示兩個向量朝同方向的程度,當兩個方向完全一致時,餘弦相似度為 1,完全相反時為 -1,當有 $x$ 與 $y$ 兩個向量時,相似度如下:
<font size=4>$$similarity(x, y) = \frac{x \cdot y}{||x|| \space ||y||}
= \frac{x_1y_1+ \cdots+x_ny_n}{\sqrt{x_1^2+ \cdots + x_n^2}\sqrt{y_1^2+ \cdots + y_n^2}}$$</font>
其中分母為各向量的范數,代表向量的大小。如此可以搭配共生矩陣產生每個字與其他字的相似度。例如: you 和 i 的相似度為 0.7071067691154799。
<br>
### 1-3. 點間互資訊(Pointwise Mutual Information)
共生矩陣有一個缺點是對高頻率字詞並未有良好的表示方式,例如:**the car** 中 **car** 常會跟 **the** 一起出現,但其實 **car** 跟 **drive** 有更明顯的關聯性。
因此可使用**點間互資訊**當作指標:
<font size=4>$$PMI(x, y) = \log_2{\frac{P(x,y)}{P(x)P(y)}}$$</font>
$P(x)$ 代表 $x$ 發生的機率,$P(y)$ 代表 $y$ 發生的機率,而 $P(x, y)$ 是指 $x$ 與 $y$ 同時發生的機率。PMI 越高,代表關聯性越高。
套入自然語言中,$P(x, y)$ 就代表 the 和 car 一起出現的機率,假設 10,000 字中, the car 出現了 10 次,代表 $P(the, car)$ 為 $\frac{10}{10000} = 0.001$。
因此也可以改寫成下式:
<font size=4>$$PMI(x, y) = \log_2{\frac{P(x,y)}{P(x)P(y)}} = \log_2{\frac{\frac{C(x,y)}{N}}{\frac{C(x)}{N}\frac{C(y)}{N}}} = \log_2{\frac{C(x,y)\cdot N}{C(x)C(y)}}$$</font>
其中 $C(x, y)$ 代表 $x$ 和 $y$ 的共生次數,$C(x)$ 和 $C(y)$ 表示字詞 $x$ 和 $y$ 出現的次數,$N$ 則代表語料庫內涵的字詞數量。
但當兩個字詞共生次數為 0 時,會變成 $\log_20 = -∞$。所以實際上會使用**正向點間互資訊(PPMI)**。
<font size=4>$$PPMI(x, y) = max(0, PMI(x,y))$$</font>
但 PPMI 仍存在一個缺點,就是詞向量的維度會隨著語料庫詞彙增加而上升,且容易受雜訊影響,缺乏穩定性。
<br>
### 1-4. 降維
**奇異值分解(SVD)**是把任意矩陣分解成三個矩陣乘積。
<font size=4>$$X = USV^T$$</font>
其中 $U$ 和 $V$ 是正交矩陣,$S$ 為對角矩陣,其奇異值會按照遞減順序排列,奇異值可以當成是對應座標軸的重要程度,於是就可以用來刪除不重要元素。


<br>
## 2. word2vec
另一個計算向量的做法是<font color="#f00">**推論手法**</font>,相對於計數手法是一次統一處理資料,推論手法則是使用部分學習資料逐次學習。實際的做法則是根據上下文推測中間的文字為何。如下圖:

模型是把取得上下文當作輸入,再輸出各個字詞機率。
<br>
### 2-1. 類神經網路
處理字詞前必須要轉換成 one-hot 格式,並可以將其接上全連接層(沒有偏權值),

<br>
### 2-2. CBOW
<font color="#f00">**CBOW 模型是從上下文推測目標對象(中間的字詞)的類神經網路**</font>。透過訓練 CBOW 模型,即可獲得字詞的分散式向量。

CBOW 模型是有兩個輸入層的類神經網路,經過全連接的中間層,輸出層再透過 softmax 轉換成每個字詞的機率,其中中間層為輸入層全連接轉換後的**平均值**。
**其中 $W_{in}$ 是形狀 7x3 的矩陣,為字詞分散式的權重。**
需要注意的是,CBOW 只學習在語料庫中字詞出現的型態,因此當語料庫不同時,學習到的分散是向量也會不一樣。
到目前為止,word2vec 使用的類神經網路有兩個權重,分別是輸入端 $W_{in}$ 對應各字詞的分散式向量;輸出端 $W_{out}$ 則可以當作儲存了詞義編碼後的向量。但多數研究中,都不使用輸出端的權重,故我們也以 $W_{in}$ 當作字詞的分散式向量。
最後,CBOW 的損失函數稱為負對數似然,也稱為多類別的交叉熵,如下:
<font size=4>$$L = -\frac{1}{T} \sum_{t=1}^T{log P(w_t | w_{t-1}, w_{t+1})}$$</font>
### 2-3. Skip-gram
<font color="#f00">**Skip-gram 模型是用中間的字詞預測上下文**</font>,與 CBOW 相反。


而損失函數則是上下文的損失相加。
就精確度而言,Skip-gram 通常會獲得較好的結果,尤其隨著語料庫規模變大、面對低頻率的字詞或類推問題時。至於速度則是 CBOW 較快,因為 Skip-gram 是按照上下文的數量計算損失,成本較大。
## 3. word2vec 高速化
假設語料庫詞彙量為 100 萬、中間層神經元數量為 100,則要訓練一個神經網路擷取文字向量時會造成兩個問題:
- 輸入層的 one-hot 與權重矩陣 $W_{in}$ 的乘積會出現效能瓶頸。
- 中間層與權重矩陣 $W_{out}$ 的乘積與 Softmax 的運算
<br>
### 3-1. Embedding 層
<font color="#f00">**Embedding 層就是擷取該字的 index 來做處理**</font>,可降低記憶體的使用量,並省下多餘的運算,大幅改善輸入層到中間層的效率。

<br>
### 3-2. Negative Sampling
<font color="#f00">**Negative Sampling 的基本概念是二元分類,也就是想辦法將多元分類趨近於二值分類**</font>,舉例來說:當語料庫字彙為 100 萬時,原問題為 "從中選出正確的一個字",而這做法即是想辦法將這問題壓縮成二元分類,轉換成 "是否為這個字" 的問題。
所以先從正確答案來思考,如果要預測結果是否為該答案,第一步就是只擷取對應的 $W_{out}$ 詞向量,用來計算與中間層的內積,並套用 sigmoid 將分數轉換成機率。
實際上就是 train 的時候取上下字的 embedding,取正確答案為目標欄位當 output 計算 loss。

<br>
<br>
我們真正想做的事情是正確答案經過 Sigmoid 後輸出趨近 1,不是答案的經過 Sigmoid 輸出趨近 0,但不是答案的字彙量太多,因此 **Negative Sampling 就是只挑出幾個負例當作近似解**,也就是先計算正例為目標對象的 loss,同時計算取樣負例的 loss 相加。
<font color="#f00">**注意:我們的目的僅是要透過這個神經網路取得文字分散式向量,而不是訓練一個模型,因此不須考慮其他資料的正確答案為何。**</font>

而負例的取樣方法為:語料庫中較常用的字詞較容易擷取出來,反之不常用的則不易擷取。具體做法是計算語料庫詞彙出現次數,並根據以下公式將出現次數轉換成機率取出負例(乘上 0.75 可以略為提高低機率的字詞),如此一來常出現的字會比較容易被取出,卻也不會捨棄出現次數少的字詞。
<font size=4>$$P'(w_i) = \frac{P(w_i)^{0.75}}{\sum\limits_{j}^nP(w_j)^{0.75}}$$</font>
---
### reference
1. Deep Learning 2:用 Python 進行自然語言處理的基礎理論實作 - 齋藤康毅