Tensorflow機器學習--圖文理解Word2Vec

由于本人初學機器學習&Tensorflow,文章中若有錯誤,希望評論指出,不勝感謝。

一、Word2Vec問題是什么?

Word2Vec 即 Word to vector,詞匯轉向量。
我們希望詞義相近的兩個單詞,在映射之后依然保持相近,詞義很遠的單詞直接則保持很遠的映射距離:如下圖所示,這里介紹到了 t-SNE 方法可以很好的達到效果:
關于t-SNE這里推薦一篇文章:
http://bindog.github.io/blog/2016/06/04/from-sne-to-tsne-to-largevis

416BA7CE-4342-4835-85DA-D0789F3A25D7.png

二、從實例代碼中學習

起初看了網上很多資料,關于 Word2Vec 真心學的云里霧里。在 Tensorflow 中 Vector Representations of Words 字詞的向量表示這一章卡了很久。于是我嘗試看 word2vec_basic.py 的源碼來理解一下Word2Vec最簡單的實現。
看完200多行的源碼, Tensorflow 自身的注釋 就關于Word2Vec實例總結為6步:

  1. 下載數據
  2. 將原詞匯數據轉換為字典映射
  3. 為 skip-gram模型 建立一個掃描器
  4. 建立并訓練 skip-gram 模型
  5. 開始訓練模型
  6. 結果可視化

這里忽視第六步,從第1步到第5步,我將使用圖片和一些代碼來貫通Word2Vec整個過程。
首先打開下載進來的word詞匯數據,由于是無監督學習,并沒有標簽,就只是整整100M大小文本數據。
這是第一步下載得到的數據:


0A4FB7AE-6C56-4960-97EC-86948F7613EF.png

然后開始第二步將原詞匯數據轉換為字典映射,比如我取出這段文本的頭一句,它會進行如下變換:

01E9A1B7-D990-4298-A303-967415F420FC.png

現在我們的詞匯文本變成了用數字編號替代的格式以及詞匯表和逆詞匯表。逆詞匯只是編號為key,詞匯為value。
接著開始第三步,為skip-gram 模型建立一個掃描器,首先看一下掃描器函數:

def generate_batch(batch_size, num_skips, skip_window):

batch_size是指一次掃描多少塊,skip_window為左右上下文取詞的長短,num_skips輸入數字的重用次數。假設我們的掃描器先掃這大段文字的前8個單詞,左右各取1個單詞,重用次數為2次。我們就會觀察到如下結果:

D309AA22-D064-48F9-8D36-B285A6BA4331.png

現在通過上面一步,我們構造出了input和label,就可以進行監督學習,下面

B26EC656-C5FA-4E6E-80C4-2B47B7509C08.png

什么是NCE Loss呢?這里為什么不用更為常見的Softmax + Cross-Entropy 呢?

19DB34BA-EF80-4902-B710-11CAAC8DA122.png

因為如果在這里使用Softmax + Cross-Entropy作為損傷函數會有一個問題,Softmax當有幾萬+的分類時,速率會大大下降。

其速度對比如下:
10000 個類,Softmax每秒處理 10000 個樣本,NCE每秒處理 30000 個樣本
100000 個類,Softmax每秒處理 1000 個樣本,NCE每秒處理 20000 個樣本
此實驗結論由其他同學得出,給出實驗鏈接:https://zhuanlan.zhihu.com/p/21642643

這里再整理出其他同學關于 NCE LOSS 源碼的理解,下面就是一段 NCE LOSS 的實現代碼,但不得而知 Tensorflow 是否使用該NCE LOSS的實現。


def nce_loss(data, label, label_weight, embed_weight, vocab_size, num_hidden, num_label):
    label_embed = mx.sym.Embedding(data = label, input_dim = vocab_size,
                                   weight = embed_weight,
                                   output_dim = num_hidden, name = 'label_embed')
    label_embed = mx.sym.SliceChannel(data = label_embed,
                                      num_outputs = num_label,
                                      squeeze_axis = 1, name = 'label_slice')
    label_weight = mx.sym.SliceChannel(data = label_weight,
                                       num_outputs = num_label,
                                       squeeze_axis = 1)
    probs = []
    for i in range(num_label):
        vec = label_embed[i]
        vec = vec * data
        vec = mx.sym.sum(vec, axis = 1)
        sm = mx.sym.LogisticRegressionOutput(data = vec,
                                             label = label_weight[i])
        probs.append(sm)
    return mx.sym.Group(probs)

NCE的主要思想是,對于每一個樣本,除了本身的label,同時采樣出N個其他的label,從而我們只需要計算樣本在這N+1個label上的概率,而不用計算樣本在所有label上的概率。而樣本在每個label上的概率最終用了Logistic的損失函數。

80229FA5-A268-4923-B2ED-24B6F8EFAA37.png

這里可謂是整個 Word2Vec 的關鍵。
至此,已經搭建好訓練模型,然后便可以進行分批次的訓練即可。那么下一個問題是完成訓練后,我們如何判斷兩個詞匯的相似度?


4B22A93C-0BAD-496D-B735-5DC021172302.png

這里我們使用 cos 來表示相似度會比使用 l2 向量差值會好一些。
這是根據訓練方式所決定的,因為向量的長度與分類無關,

 norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))
 normalized_embeddings = embeddings / norm
 valid_embeddings = tf.nn.embedding_lookup(
      normalized_embeddings, valid_dataset)
 similarity = tf.matmul(
      valid_embeddings, normalized_embeddings, transpose_b=True)

參考自Udacity中文本和序列的深度模型一課:https://classroom.udacity.com/courses/ud730/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容