1.簡述word2vec基本思想,並簡要描述CBOW和Skip-gram模型
word2vec的基本思想是一個詞的意思, 可以由這個詞的上下文來表示。 相似詞擁有相似的上下文, 這也就是所謂的離散分佈假設(distributional hypothesis),論文中的做法是通過神經語言模型訓練每個詞並將其映射成k維實值向量(k一般爲模型中的超參數),在高維空間中可以通過詞之間的距離來判斷語義相似度。
CBOW(Continuous Bag of Words,連續詞袋模型)採用給定上下文信息來預測一個詞的方法來訓練神經網絡,而Skip-gram與 CBOW 相反, 其採用給定一個詞來預測上下文的方法來訓練神經網絡。CBOW模型直觀上很好理解, 而Skip-gram模型類似於給你若干個詞,讓你擴展成一句話。另一個相關的模型是訓練句子向量用的 skip-thought,給定一個句子,去生成前一個句子和後一個句子。
1.1 word2vec是如何訓練模型的,用的什麼語料庫,如何處理擴充的語料?
1.2 什麼情況用CBOW,什麼情況用Skip-gram?
1.3 說明word2vec兩種訓練方式各自的優勢是什麼?哪種更好,爲什麼?
2.簡述word2vec中的兩個trick,這兩個trick的作用是什麼?
- 第一個trick是hierarchical softmax,其作用是爲了近似模擬softmax計算;
- 第二個trick是negative sampling,它是用來提高訓練速度並且改善所得到詞向量的質量的一種方法。不同於原本每個訓練樣本更新所有的權重,負採樣每次讓一個訓練樣本僅僅更新一小部分的權重,這樣就會降低梯度下降過程中的計算量。
2.1. 爲什麼要做層次softmax?
從隱藏層到輸出的softmax層的計算量很大,因爲要計算所有詞的softmax概率,再去找概率最大的值,其計算複雜度爲,是詞表總長度,而從數據結構角度來思考,一般查找的問題可以優化到,因此論文設計了層次softmax來近似模擬softmax計算,將計算複雜度降至
2.2 hierarchical softmax的哈弗曼樹怎麼構造的?葉子結點,根結點,非葉子結點都代表了什麼?訓練過程是怎麼樣的?
(1). hierarchical softmax的哈弗曼樹怎麼構造的?
hierarchical softmax的哈弗曼樹是根據單詞在語料庫中的詞頻來構造的,哈夫曼樹的構造過程與數據結構中哈夫曼樹的構造過程一致。假設詞表中有n個單詞,則構造出來的哈夫曼樹有n個葉子結點,假設這n個單詞的詞頻分別爲。
- 將當做𝑛棵樹(每棵樹1個結點)組成的森林。
- 選擇根結點權值最小的兩棵樹,合併,獲得一棵新樹,且新樹的根結點權值爲其左、右子樹根結點權值之和。詞頻大的結點作爲左孩子結點,詞頻小的作爲右孩子結點。
- 從森林中刪除被選中的樹,保留新樹。
- 重複2、3步,直至森林中只剩下一棵樹爲止。
(2). 葉子結點,根結點,非葉子結點都代表了什麼?
葉子結點代表詞表中的每一個單詞,根結點代表投影后的詞向量,即隱藏層輸出的向量,非葉子結點代表從根結點到某個單詞路徑上的中間結點,在數據結構中的含義是其所有子樹對應結點之和。
(3). hierarchical softmax的訓練過程是怎麼樣的?
hierarchical softmax建立哈夫曼樹的核心在於,將多分類問題,拆解成多個二分類問題。 簡單示例如下:
如上圖所示,我們可以沿着哈夫曼樹從根節點一直走到我們的葉子結點的詞。與神經網絡模型相比,我們的哈夫曼樹的所有內部節點就類似之前神經網絡隱藏層的神經元。其中,根結點的詞向量對應我們投影后的詞向量,而所有葉子結點就類似於之前神經網絡softmax輸出層的神經元,葉子結點的個數就是詞彙表的大小。在哈夫曼樹中,隱藏層到輸出層的softmax映射不是一下子完成的,而是沿着霍夫曼樹一步步完成的,這也是hierarchical softmax取名的由來。
以CBOW模型爲例,在word2vec原論文中,採用了二元邏輯迴歸的方法,即**規定沿着左子樹走,那麼就是負類(哈夫曼樹編碼爲1),沿着右子樹走,那麼就是正類(哈夫曼樹編碼0)。**判別正類和負類的方法是使用sigmoid函數,即爲:
其中是當前內部結點的詞向量,而則是我們需要通過訓練樣本訓練得到的邏輯迴歸的模型參數。
根據二叉樹的特性,對於詞典 中的任意詞,Huffman樹中必存在一條從根結點到詞對應結點的路徑(且這條路徑是唯一的)。路徑上存在(個分支,將每個分支看做一次二分類,每一次分類就產生一個概率,將這些概率乘起來,就是所需的。
故在這種建模方式下,條件概率的公式可以拆解爲:
其中
- 表示路徑中第個結點對應的編碼(根結點不對應編碼),也就相當於上述圖示中的黑色邊;
- 表示隱藏層輸出的向量,也就是根結點對應的詞向量;
- 表示路徑中第個非葉子結點對應的參數向量。
而
考慮到只有0和1兩種取值,我們可以用指數形式方便地將其寫到一起:
再對條件概率(目標函數)取對數似然,則有:
其中 表示語料庫,爲方便梯度推導,將上式中雙重求和符號下花括號中的內容簡記爲,即爲:
上式則爲CBOW模型的目標函數,因此下面的目標則是如何將這個函數最大化,即最大化似然函數。word2vec中採用的是隨機梯度上升法。
隨機梯度上升法的做法是:每取一個樣本,就對目標函數中的所有(相關)參數做一次刷新。觀察目標函數 易知,該函數中的參數包括向量。因此,先給出函數關於這些向量的梯度。
-
考慮關於的梯度:
- 故的更新公式爲:
其中表示學習率。
- 故的更新公式爲:
-
考慮關於的梯度,中關於變量和時對稱的(即兩者可交換位置),故:
不過 是上下文的詞向量的和,不是上下文單個詞的詞向量。怎麼把這個更新量應用到單個詞的詞向量上去呢?word2vec採取的是直接將 的更新量整個應用到每個單詞的詞向量上去,這個也很好理解,既然本身就是中各個單詞詞向量的累加,那麼求完梯度之後也應該將其貢獻到每個分量上去:
其中代表上下文中某一個單詞的詞向量。考慮到每個單詞的上下文單詞數量可能不太一致,需要思考的是,這裏是否採用平均貢獻更合理?即使用公式:
其中表示中詞的個數。其實**“平均貢獻”**與word2vec中的公式,僅僅只是學習率的不同,因此在數據量充分的情況下也沒有很大的區別。以樣本爲例,給出CBOW模型中採用梯度上升法更新各個參數的僞代碼:
注意,步驟3.3和步驟3.4不能交換次序,即應等貢獻到e後再做更新。如果梯度收斂,則結束梯度迭代,當2c(c表示窗口大小)個詞向量和中心詞所對應的參數 的變化量分別小於某一個閾值,即停止更新,
對於Skip-gram模型,其推導過程與CBOW模型基本一致,其目標函數相對於CBOW模型,多了一層求和 ,因此,言簡意賅的說明Hierarchical Softmax是如何訓練的,可總結如下:
利用哈夫曼樹(最優二叉樹)將多分類問題拆解成多個二分類問題,即將條件概率拆解爲:
其中向量爲待更新的參數,利用極大似然的方法獲得待優化的目標函數,之後再利用隨機梯度上升法更新參數。
(4).哈夫曼樹二分類,左右子樹怎麼分的,爲什麼左爲負1,右爲正0,能不能換?
關於Huffman樹和Hufman編碼,有兩個約定:
(1) 將權值大的結點作爲左孩子結點,權值小的作爲右孩子結點;
(2) 左孩子結點編碼爲1, 右孩子結點編碼爲0。
在word2vec源碼中將權值較大的孩子結點編碼爲1,較小的孩子結點編碼爲0。
這個只是一個約定,方便之後計算概率,也可以約定左邊爲0,右邊爲1,推導過程都是類似的。
(5). 最終所有葉子結點的概率和是否等於1?
用數學歸納法證明,以三層滿二叉樹爲例:
其所有葉結點的概率之和爲:
當二叉樹擴展到n層時,計算方式也是一樣的,其本質上就相當於根結點往左右孩子結點的概率的連乘“裂變”。一個具體例子如下:
(6). 爲什麼要用哈夫曼樹?有什麼好處?
使用哈夫曼樹的好處在於:
- 降低了計算量,平均計算複雜度從降到了,其中爲詞表長度(其實這個嚴格來說不算是哈夫曼樹的優點,但是用了哈夫曼樹確實降低了計算複雜度);
- 利用哈夫曼樹可以很方便的按照詞頻構造的,因此高頻詞可以在較短時間內找到(本質上是哈夫曼樹可以構成“優先隊列”,這樣,按照詞頻降序建立哈夫曼樹,保證了高頻詞接近根結點,這樣高頻詞計算少,低頻詞計算多,貪心優化思想)。
(7). hierarchical softmax的缺點是什麼?
遇到生僻詞的時候,在哈夫曼樹中也要走很久,計算量也很大。
2.3 負採樣的基本思想是什麼?負採樣的模型參數是如何更新的?其採樣方法是怎麼樣的?
(1). 負採樣的基本思想是什麼?
比如我們有一個訓練樣本,中心詞是,它周圍上下文共有(c爲窗口大小)個詞,記爲,那麼就得到一個正例的訓練樣本,通過Negative Sampling採樣,我們得到neg個和不同的中心詞,這樣和就組成了neg個並不真實存在的負例。利用這一個正例和neg個負例,我們進行二元邏輯迴歸,得到負採樣對應每個詞對應的模型參數,和每個詞的詞向量。
(2). 負採樣的模型參數是如何更新的?
Negative Sampling也是採用了二元邏輯迴歸來求解模型參數,通過負採樣,我們得到了neg個負例。爲了統一描述,我們將正例定義爲。下面以CBOW模型爲例簡單講解一下。
在邏輯迴歸中,我們的正例應該期望滿足:
我們的負例期望滿足:
根據極大似然,我們希望最大化:
由於其標籤只有0,1,因此上式可以寫成:
因此,對於一個給定的語料庫 ,我們可以得到整體的優化目標爲:
此時對應的對數似然函數(目標函數)爲:
和hierarchical softmax類似,負採樣同樣採用隨機梯度上升法更新參數,目標函數 中的參數包括向量。因此,先給出函數關於這些向量的梯度。
- 首先我們計算的梯度:
- 之後按照同樣的方法計算的梯度如下:
之後與hierarchical softmax類似,可得的更新公式爲:
(3). 負採樣的採樣方法是怎麼樣的?
詞典 中的詞在語料 中出現的次數有高有低,對於那些高頻詞,被選爲負樣本的概率就應該比較大;反之,對於那些低頻詞,其被選中的概率就應該比較小。這就是我們對採樣過程的一個大致要求,本質上就是一個帶權採樣問題。
首先在語料庫中計算每個詞的詞頻,即某個詞的count/語料庫中所有單詞總count(源碼中分子分母都用的是count四分之三次方,並非直接用count),即爲被採樣到的概率:
在word2vec裏的具體做法是,經過概率的計算,可將所有詞映射到一條線上,這條線是非等距劃分的N份(因爲N個詞的概率不同,N爲詞彙表的長度),再對同樣的線進行等距劃分成M份,其中M >> N,在採樣時,每次隨機生成一個[1,M-1]之間的數,在對應到該小塊所對應的單詞區域,選取該單詞(當選到正例單詞自己時,跳過),具體如圖:
在源碼中,M的取值爲
2.4 負採樣中的採樣到的負樣本是否更新?如果更新的話,是怎麼樣的更新方式?
2.5 爲什麼利用層次softmax和負採樣能夠提升速度和效率?
2.6 在實際使用過程中,負抽樣使用多還是層級softmax多,爲什麼?
3. 源碼的一些細節
3.1 源碼中是如何進行負採樣的?是否可能採樣到同一個負樣本?如何進行層次化softmax的?sigmod在源碼裏面的計算方法是什麼?
(1). 源碼中是如何進行負採樣的?是否可能採樣到同一個負樣本?
源碼中進行負採樣的代碼如下(添加註釋):
// NEGATIVE SAMPLING
// 如果採用負採樣優化
// 遍歷生成正負樣本(1個正樣本+negative個負樣本)
if (negative > 0) for (d = 0; d < negative + 1; d++) {
if (d == 0) {
// 第一次循環處理的是目標單詞,即正樣本
target = word;
label = 1;
} else {
// 隨機抽取負樣本,table_size默認值爲1e8,也就是上述理論部分提到的M
next_random = next_random * (unsigned long long)25214903917 + 11;
target = table[(next_random >> 16) % table_size];
if (target == 0) target = next_random % (vocab_size - 1) + 1;
if (target == word) continue;
label = 0;
}
// 下面是參數更新部分,之後再詳細解讀
// syn1neg數組存着Negative Sampling的參數,每個詞在syn1neg數組中對應一個輔助向量(這是什麼?)
// 此時的l2爲syn1neg中目標單詞向量的起始位置,layer1_size爲word embedding size
l2 = target * layer1_size;
f = 0;
// f爲輸入向量neu1與輔助向量的內積
for (c = 0; c < layer1_size; c++) f += syn0[c + l1] * syn1neg[c + l2];
if (f > MAX_EXP) g = (label - 1) * alpha;
else if (f < -MAX_EXP) g = (label - 0) * alpha;
else g = (label - expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))]) * alpha;
for (c = 0; c < layer1_size; c++) neu1e[c] += g * syn1neg[c + l2];
for (c = 0; c < layer1_size; c++) syn1neg[c + l2] += g * syn0[c + l1];
}
上述隨機抽取負樣本的做法是參考C語言中僞隨機函數rand的實現:
《The GNU C Library》裏說的,大概如下:
公式: Y=(a*X+c)mod m
其中:a,c,m都是常數,一種取值是:
a = 0x5DEECE66D = 25214903917,c = 0xb = 11,m = 2^48,用C語言代碼實現即爲:
__int64 rand ()
{
static __int64 r = 0;
const __int64 a = 25214903917;
const __int64 c = 11;
const __int64 m = 1 << 48;
r = (r * a + c) % m;
return r;
}
有可能採樣到同一個負樣本,負採樣的方法與之前理論部分的說明是一致的
(2). 源碼中如何進行層次softmax的?
在源碼中CreateBinaryTree()函數封裝了構建哈夫曼樹的過程,其利用統計到的詞頻構建哈夫曼樹,出現頻率越高的詞其二叉樹上的路徑越短,即二進制編碼越短。隨後其前向傳播的過程以及更新各個參數的過程,與前面僞代碼一致。需要注意的是,在word2vec源碼中,爲了能夠加快計算,作者在開始的時候存儲了一份sigmod的值,因此,對於需要從expTable中查詢到對應的值。
(3). sigmod在源碼裏面的計算方法是什麼?
源碼中在預處理部分,實現了sigmoid函數值的近似計算。 在利用神經網絡模型對樣本進行預測的過程中,需要對其進行預測,此時,需要使用到sigmoid函數,sigmoid函數的具體形式爲:
如果每一次都請求計算sigmoid值,對性能將會有一定的影響,當sigmoid的值對精度的要求並不是非常嚴格時,可以採用近似計算,將一個極小區間內的值統一緩存成一個值(微元法的思想),在word2vec中,將區間[−6, 6)(設置的參數MAX_EXP爲6)等距離劃分成EXP_TABLE_SIZE(EXP_TABLE_SIZE默認值爲1000)等份,並將每個區間中的sigmoid值計算好存入到數組expTable中,需要使用時,直接從數組中查找。計算sigmoid值的代碼如下所示:
expTable = (real *)malloc((EXP_TABLE_SIZE + 1) * sizeof(real));// 申請EXP_TABLE_SIZE+1個空間
// 計算sigmoid值
for (i = 0; i < EXP_TABLE_SIZE; i++) {
expTable[i] = exp((i / (real)EXP_TABLE_SIZE * 2 - 1) * MAX_EXP); // Precompute the exp() table
expTable[i] = expTable[i] / (expTable[i] + 1); // Precompute f(x) = x / (x + 1)
}
之後在hierarchical softmax和負採樣操作中,對於需要從expTable中查詢到對應的值,代碼如下:
for (c = 0; c < layer1_size; c++) f += neu1[c] * syn1[c + l2];// 映射層到輸出層,送入sigmod函數前的結果
if (f <= -MAX_EXP) continue;
else if (f >= MAX_EXP) continue;
else f = expTable[(int)((f + MAX_EXP) * (EXP_TABLE_SIZE / MAX_EXP / 2))]; // sigmoid結果
最後這塊查表可能初看起來比較難懂,但是變換一下形式就很容易看明白:
(int)(((f + MAX_EXP) / (MAX_EXP * 2)) * EXP_TABLE_SIZE)
上式應該就很清晰了,相當於在expTable查找對應索引,並獲取緩存的sigmoid的值。
3.2 word2vec滑動窗口大小以及負採樣個數的參數設置?
(1). word2vec滑動窗口大小設置
word2vec滑動窗口大小默認設置爲5,但是在實際訓練過程中,有如下操作:
// next_random:用來輔助生成隨機數
unsigned long long next_random = (long long)id;
...
next_random = next_random * (unsigned long long)25214903917 + 11;
b = next_random % window;
// 以cbow模型爲例,//一個詞的窗口爲[setence_position - window + b, sentence_position + window - b]
//因此窗口總長度爲 2*window - 2*b + 1
for (a = b; a < window * 2 + 1 - b; a++) {
...
}
實際上在訓練過程中,滑動窗口有一定的減小,由之前的2*window減爲2*(window - b),
(2). 負採樣個數的設置
負樣本數量默認爲5,常用值爲3-10,0表示不採用負採樣
5.word2vec三層結構很簡單,但是爲什麼效果這麼好,word2vec激活函數是什麼?
6.word2vec的參數數量是多少?
word2vec的參數集中在輸入層到隱層的權重矩陣和隱層到輸出層的權重矩陣,輸入層到隱層的權重矩陣的參數量爲,而隱層到輸出層有兩種優化方式,對於hierarchical softmax,整棵哈夫曼樹的參數量爲, 表示每經過一次二分類時對應參數的維度;而對於負採樣的優化方式,
待補充
7.word2vec的優點和缺點是什麼?
優點:
- 由於 word2vec 會考慮上下文,跟之前的 Embedding 方法相比,效果要更好(但不如 18 年之後的方法);
- 網絡結構簡單,而且word embedding維度比較低,訓練速度比較快;
- 通用性很強,可以用在各種 NLP 任務中。
缺點:
- 由於詞和向量是一對一的關係,所以多義詞的問題無法解決;
- word2vec訓練模式下,考慮的context很小,沒有用到word在全局的信息統計。
8.word2vec如果有oov怎麼解決?
主流思路有以下幾種:
-
UNK處理,在訓練過程中,把訓練集中所有出現頻率小於某個閾值的詞都標記爲UNK,那麼UNK就會學到了自己的embedding,也有一定的語義信息,但是可能效果也會不好,因爲UNK代表的單詞可能比較多;
-
還可能是UNK沒有對應的embedding,這樣的話一般有兩種處理方式
a. 把UNK都初始化成0的向量
b. 每次都把UNK初始化成一個新的隨機向量
都初始化成0向量,會使得UNK都共享相同的語義信息,所以更多時候傾向於對UNK直接隨機,因爲本身 每個UNK都不同,隨機更符合我們對UNK基於最大熵的估計。
-
-
Character Model,即把所有的OOV詞,拆成字符。比如 Jessica,變成*J,*e,s,s,i,c,a。其中是Begin,Middle,End的標記。這樣處理的好處就是消滅了全部的OOV。壞處就是文本序列變得非常長,對於性能敏感的系統,這是難以接受的維度增長。
- 相似的做法還有利用Wordpiece Model的方法進行分詞,這是sub-word粒度的分詞手段
-
擴大詞表,終極解決辦法。通常情況不使用大詞表,一方面是因爲訓練數據的多樣性有限,另一方面是softmax的計算速度受限。而對於上述兩種情況,也有針對性解決辦法。對於第一種情況,擴大語料範圍。對於第二種情況,相關的加速策略可以將詞表擴大10倍而GPU上的預測速度只降低一半(從5W詞到50W詞)。
9.word2vec如何得到詞向量?怎麼衡量學到的詞向量的好壞?
(1). word2vec如何得到詞向量?
以CBOW模型爲例,輸入層到隱層的權重矩陣設爲W,訓練結束之後**任何一個單詞的one-hot表示乘以這個矩陣都將得到自己的word embedding。**Skip-gram模型也是一樣的,這裏需要注意的是,如果採取負採樣的優化方式,會得到兩套embedding,也就是輸入層到隱層的權重矩陣和隱層到輸出層的權重矩陣。但是一般選取輸入層到隱層的權重矩陣作爲單詞的embedding,也有說法是選擇兩套embedding的平均作爲單詞最終的embedding,這個也沒啥理論依據,都是通過實驗驗證得來;而如果採取hierarchical softmax的優化方式,那麼只得到一套embedding,也就是輸入層到隱層的權重矩陣,hierarchical softmax構建的哈夫曼樹的葉結點對應的參數一般不作爲word embedding,因爲其參數不對稱(單詞在樹中所在層次不一樣),做詞向量不是好選擇。
(2). 怎麼衡量學到的詞向量的好壞?
詞向量的評價大體上可以分成兩種方式:
第一種是把詞向量融入現有系統中,看對系統性能的提升;
- 直接用於神經網絡模型的輸入層。將訓練好的詞向量作爲輸入,用前饋網絡和卷積網絡或者循環神經網絡等完成了詞性標註、語義角色標註、句法分析和情感分析等一系列任務。
- 作爲輔助特徵擴充現有模型。比如將詞向量作爲額外的特徵加入到接近 state of the art 的方法中,進一步提高了命名實體識別和短語識別的效果。
第二種是直接從語言學的角度對詞向量進行分析,如相似度、語義偏移等。
Mikolov論文中使用類比(analogy)的方式來評測。如已知 a 之於 b 猶如 c 之於 d。現在給出 a、b、c,看最接近的詞是否是 d。與此同時,Mikolov在論文中也對比了詞法關係(名詞單複數 good-better:rough-rougher、動詞第三人稱單數、形容詞比較級最高級等)和語義關係(clothing-shirt:dish-bowl)。
這些實驗結果中最容易理解的是:語料越大,詞向量就越好。其它的實驗由於缺乏嚴格控制條件進行對比,談不上哪個更好哪個更差。不過這裏的兩個語言學分析都非常有意思,尤其是向量之間存在這種線性平移的關係,可能會是詞向量發展的一個突破口。
關於如何產生一個好的詞向量,也可以參考博客及其提及的論文:
《How to Generate a Good Word Embedding?》導讀
參考:
word2vec原理(二) 基於Hierarchical Softmax的模型
Hierarchical softmax(分層softmax)簡單描述