本身動手實現word2vec(Skip-gram模型)

學習word2vec的skip-gram實現,除了skip-gram模型還有CBOW模型。 Skip-gram模式是根據中間詞,預測先後詞,CBOW模型恰好相反,根據先後的詞,預測中間詞。python

那麼什麼是中間詞呢?什麼樣的詞才叫作先後詞呢?git

首先,咱們須要定義一個窗口大小,在窗口裏面的詞,咱們纔有中間詞和先後詞的定義。通常這個窗口大小在5-10之間。 舉個例子,咱們設置窗口大小(window size)爲2:github

|The|quick|brown|fox|jump|
複製代碼

那麼,brown就是咱們的中間詞,Thequickfoxjump就是先後詞。算法

咱們知道,word2vec實際上就是一個神經網絡(後面會解釋),那麼這樣的數據,咱們是以什麼樣的格式用來訓練的呢? 看一張圖,你就明白了:bash

word2vec window

能夠看到,咱們老是以中間詞放在第一個位置,而後跟着咱們的先後相鄰詞。能夠看到,每一對詞都是一個輸入和一個輸出組成的數據對(X,Y)。其中,X是feature,Y是label。網絡

因此,咱們訓練模型以前,須要根據語料,整理出全部的像上面這樣的輸入數據用來訓練。dom

word2vec是一個神經網絡

word2vec是一個簡單的神經網絡,有如下幾個層組成:函數

  • 1個輸入層
  • 1個隱藏層
  • 1個輸出層

輸入層輸入的就是上面咱們說的數據對的數字表示,輸出到隱藏層。 隱藏層的神經網絡單元的數量,其實就是咱們所說的embedding size,只有爲何,咱們後面簡單計算一下就知道。須要注意的是,咱們的隱藏層後面不須要使用激活函數。 輸出層,咱們使用softmax操做,獲得每個預測結果的機率。學習

這裏有一張圖,可以表示這個網絡: 優化

skip-gram-net-arhc

輸入層

如今問題來了,剛剛咱們說,輸入層的輸入是咱們以前準備的數據對的數字表示,那麼咱們該如何用數字表示文本數據呢?

好像隨便一種方式均可以用來表示咱們的文本啊。

看上圖,咱們發現,它的輸入使用的是one-hot編碼。什麼是ont-hot編碼呢?如圖所示,假設有n個詞,則每個詞能夠用一個n維的向量來表示,這個n維向量只有一個位置是1,其他位置都是0。

那麼爲何要用這樣的編碼來表示呢?答案後面揭曉。

隱藏層

隱藏層的神經單元數量,表明着每個詞用向量表示的維度大小。假設咱們的hidden_size取300,也就是咱們的隱藏層有300個神經元,那麼對於每個詞,咱們的向量表示就是一個1*N的向量。 有多少個詞,就有多少個這樣的向量!

因此對於輸入層隱藏層之間的權值矩陣W,它的形狀應該是[vocab_size, hidden_size]的矩陣,

輸出層

那麼咱們的輸出層,應該是什麼樣子的呢?從上面的圖上能夠看出來,輸出層是一個[vocab_size]大小的向量,每個值表明着輸出一個詞的機率。

爲何要這樣輸出?由於咱們想要知道,對於一個輸入詞,它接下來的詞最有可能的若干個詞是哪些,換句話說,咱們須要知道它接下來的詞的機率分佈。

你能夠再看一看上面那張網絡結構圖。

你會看到一個很常見的函數softmax,爲何是softmax而不是其餘函數呢?不妨先看一下softmax函數長啥樣:

softmax(x) = \frac{e^x}{\sum_{i}^{N}e^{x_i}}

很顯然,它的取值範圍在(0,1),並別全部的值和爲1。這不就是自然的機率表示嗎?

固然,softmax還有一個性質,由於它函數指數操做,若是損失函數使用對數函數,那麼能夠抵消掉指數計算。

關於更多的softmax,請看斯坦福Softmax Regression

整個過程的數學表示

至此,咱們已經知道了整個神經網絡的結構,那麼咱們應該怎麼用數學表示出來呢?

回顧一下咱們的結構圖,很顯然,三個層之間會有兩個權值矩陣W,同時,兩個偏置項b。因此咱們的整個網絡的構建,能夠用下面的僞代碼:

import tensorflow as tf

# 假設vocab_size = 1000
VOCAB_SIZE = 1000
# 假設embedding_size = 300
EMBEDDINGS_SIZE = 300

# 輸入單詞x是一個[1,vocab_size]大小的矩陣。固然實際上咱們通常會用一批單詞做爲輸入,那麼就是[N, vocab_size]的矩陣了
x = tf.placeholder(tf.float32, shape=(1,VOCAB_SIZE))
# W1是一個[vocab_size, embedding_size]大小的矩陣
W1 = tf.Variable(tf.random_normal([VOCAB_SIZE, EMBEDDING_SIZE]))
# b1是一個[1,embedding_size]大小的矩陣
b1 = tf.Variable(tf.random_normal([EMBEDDING_SIZE]))
# 簡單的矩陣乘法和加法
hidden = tf.add(tf.mutmul(x,W1),b1)

W2 = tf.Variable(tf.random_normal([EMBEDDING_SIZE,VOCAB_SIZE]))
b2 = tf.Variable(tf.random_normal([VOCAB_SIZE]))
# 輸出是一個vocab_size大小的矩陣,每一個值都是一個詞的機率值
prediction = tf.nn.softmax(tf.add(tf.mutmul(hidden,w2),b2))
複製代碼

損失函數

網絡定義好了,咱們須要選一個損失函數來使用梯度降低算法優化模型。

咱們的輸出層,實際上就是一個softmax分類器。因此按照常規套路,損失函數就選擇交叉熵損失函數

哈哈,還記得交叉熵是啥嗎?

H(p,q)=-\sum_{x}p(x)logq(x)

p,q是真是機率分佈和估計機率分佈。

# 損失函數 
cross_entropy_loss = tf.reduce_mean(-tf.reduce_sum(y_label * tf.log(prediction), reduction_indices=[1]))
# 訓練操做
train_op = tf.train.GradientDescentOptimizer(0.1).minimize(cross_entropy_loss)
複製代碼

接下來,就能夠準備號數據,開始訓練啦!

爲啥輸入使用one-hot編碼?

咱們知道word2vec訓練後會獲得一個權值矩陣W1(暫時忽略b1),這個矩陣就是咱們的全部詞的向量表示啦!這個矩陣的每一行,就是一個詞的矢量表示。若是兩個矩陣相乘...

matrix_mult_w_one_hot

看到了嗎?ont-hot編碼的特色,在矩陣相乘的時候,就是選取出矩陣中的某一行,而這一行就是咱們輸入這個詞語的word2vec表示!

怎麼樣?是否是很妙?

由此,咱們能夠看出來,所謂的word2vec,實際上就是一個查找表,是一個二維的浮點數矩陣!

以上是word2vec的skip-gram模型的完整分析,怎麼樣,是否是弄清楚了word2vec的原理和細節?

完整代碼請查看luozhouyang/word2vec

聯繫我

相關文章
相關標籤/搜索