1. 程式人生 > >Python Tensorflow下的Word2Vec程式碼解釋

Python Tensorflow下的Word2Vec程式碼解釋

前言:

作為一個深度學習的重度狂熱者,在學習了各項理論後一直想通過專案練手來學習深度學習的框架以及結構用在實戰中的知識。心願是好的,但機會卻不好找。最近剛好有個專案,藉此機會練手的過程中,我發現其實各大機器學習以及tensorflow框架群裡的同學們也有類似的問題。於是希望借專案之手分享一點本人執行過程中的理解以及經驗,希望在有益大家工作的基礎上拋磚引玉,得到行業內各位專業人士的批評指點,多謝大家支援!

第一章部落格我將會分為兩個部分,這一部分將講述Word2Vec在tensorflow中官方提供的basic版本的構造原理以及如何搭建一個CBOW模型來彌補提供版本里缺失的模型構架。於下一個部分裡,我會重點對比tensorflow下basic, optimised以及gensim三個版本的Word2Vec的執行結果情況。

程式碼解析:

首先,Tensorflow提供的基礎教程已經講解了什麼是Word2Vec以及Tensorflow是如何構建這個網路來訓練的。教程的地址請看這裡。另外這個basic版本的程式碼可以在這裡找到。

程式碼的結構看似混亂,其實很直白。首先,第61行限制了這個demo可以學習一共50000個不同的單詞。之後,在build_dataset(words)函式裡,第65行展示了Python語言的強勁,即一行整理整個輸入。在count的UNK(也就是unknown單詞,即詞頻率少於一定數量的稀有詞的代號)後用extend函式嵌入count數為從高網低數第vocabulary_size-1個,這樣所有的重複數量少於49999個詞兒的就只能對不住了,count將會把它排擠在外。形成count後dictionary來自於對count裡的詞頻進行整理,除去重複次數但換做排行順序作為這個dict結構的key。單詞本身即成為了dict結構的value。之後,將輸入的單詞轉化為他們在dictionary中的程式碼以及最後,統計下輸入資料裡有多少詞不在這個dictionary裡,按照個數增加UNK的數量,並把dictionary函式按照由高頻到低頻的排序方法排好順序。由此,build_dataset函式成功的重建了輸入資料以及形成了程式碼單詞對照表,其中data將會被用於訓練模型而dictionary將可以最為最後查詢向量及單詞關係的翻譯本。如果大家不希望限定dictionary裡的vocabulary_size怎麼辦呢?其實答案很簡單。Mikolov原文裡表示只要除去頻率少於3到10的詞兒就好,那麼我們可以對該函式做以下修改將可以達成:

def build_dataset(words, min_cut_freq):
  count_org = [['UNK', -1]]
  count_org.extend(collections.Counter(words).most_common()) #這裡我們收集全部的單詞的詞頻
  count = [['UNK', -1]]
  for word, c in count_org:
    word_tuple = [word, c]
    if word == 'UNK':   #保留UNK的位置已備後用
        count[0][1] = c
        continue
    if c > min_cut_freq: #這裡定義一個para為min_cut_freq,少於這個數量的將會被咔掉
        count.append(word_tuple)
  dictionary = dict()
  for word, _ in count:
    dictionary[word] = len(dictionary)
  data = list()
  unk_count = 0
  for word in words:
    if word in dictionary:
      index = dictionary[word]
    else:
      index = 0  # dictionary['UNK']
      unk_count += 1
    data.append(index)
  count[0][1] = unk_count
  reverse_dictionary = dict(zip(dictionary.values(), dictionary.keys()))
  return data, count, dictionary, reverse_dictionary

 之後,原始碼第91行的generate_batch其實就是構建skip-gram模型的入口,而不是自第137行with graph.as_default()之後的框架。137行之後的為建立一個簡單的MLP模型以便tensor在模型裡flow。而這個tensor以及其target的形式才是構建模型的要素。如果大家仔細閱讀後會發現在一個輸入為“蝙蝠俠戰勝了超人,美國隊長卻被鋼鐵俠暴打”這句中,在build_dataset函式轉換後可能蝙蝠俠被它的在dictionary中的程式碼3替代,戰勝了被90替代,超人被600替代,美國隊長為58,被為77,鋼鐵俠為888以及暴打為965。於是這句話變成了[3,90,600,58,77,888,965]. 假設window size是3, 這裡的模型是skip-gram,這個generate_batch函式從90出發,輸出的batch為[90,90,600,600,58,58,77,77,888,888], 輸出的target為[3,600,90,58,600,77,58,888,77,965]. 那麼,如何構建CBOW模型呢?其實很簡單,注意到CBOW模型的輸入以及預測跟SkipGram正好相反,那麼我們把第109行的batch和第110行的labels對調不就okay了麼?具體程式碼如下:

def generate_cbow_batch(batch_size, num_skips, skip_window):
  global data_index
  assert batch_size % num_skips == 0
  assert num_skips <= 2 * skip_window
  batch = np.ndarray(shape=(batch_size), dtype=np.int32)
  labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)
  span = 2 * skip_window + 1 # [ skip_window target skip_window ]
  buffer = collections.deque(maxlen=span)
  for _ in range(span):
    buffer.append(data[data_index])
    data_index = (data_index + 1) % len(data)
  for i in range(batch_size // num_skips):
    target = skip_window  # target label at the center of the buffer
    targets_to_avoid = [ skip_window ]
    for j in range(num_skips):
      while target in targets_to_avoid:
        target = random.randint(0, span - 1)
      targets_to_avoid.append(target)
#這裡的batch和labels是skipgram模型的 #batch[i * num_skips + j] = buffer[skip_window] #labels[i * num_skips + j, 0] = buffer[target]
#這裡的batch和labels是CBOW模型的,原理是對掉上面skipgram模型的兩行。 batch[i * num_skips + j] = buffer[target] labels[i * num_skips + j, 0] = buffer[skip_window] buffer.append(data[data_index]) data_index = (data_index + 1) % len(data) return batch, labels

由此,我們只需要在後面的batch_inputs, batch_labels = generate_batch(batch_size, num_skips, skip_window)函式更換函式為你的CBOW模型函式就好了。

重要更新(2016-05-21):

感謝深圳大學陳老師推薦的關於word embedding的論文How to Generate a Good Word Embedding。 文中不僅闡述瞭如何對詞向量的質量進行分析外,也充分介紹了不同模型間的區別。在閱讀論文時發現,Skip-Gram與CBOW模型的區別並不單單存在於其模型的輸入與輸出為顛倒狀態,還有一個比較特別的地方,在模型上,CBOW模型的輸入層為sum函式,結果為輸入向量的加權平均值,而Skip-gram採用的是中間單詞代表環境,即one of the context owrds as the representation of the context. 在考慮了這個因素後,對比之上的generate_cbow_batch函式的程式碼,我們發現的問題是batch和labels的期望輸出不應該是[3,600,90,58,600,77,58,888,77,965]和[90,90,600,600,58,58,77,77,888,888], 而應該是[[3,600], [90, 58], [600,77],[58,888],[77,965]]為輸入,[90, 600, 58, 77, 88]為輸出。如何修改generate_cbow_batch程式碼做到這個呢?改動很簡單,如下:

def generate_cbow_batch(batch_size, num_skips, skip_window):
  global data_index
  assert batch_size % num_skips == 0
  assert num_skips <= 2 * skip_window
  #這裡batch要作為一個2d的array,每一行代表一個詞所對應的環境
  batch = np.ndarray(shape=(batch_size, num_skips), dtype=np.int32)
  labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)
  span = 2 * skip_window + 1 # [ skip_window target skip_window ]
  buffer = collections.deque(maxlen=span)
  for _ in range(span):
    buffer.append(data[data_index])
    data_index = (data_index + 1) % len(data)
  for i in range(batch_size):
    target = skip_window  # target label at the center of the buffer
    targets_to_avoid = [ skip_window ]
    #定義一個temp的batch array作為暫時儲存環境的array,在儲存完畢後輸出
    batch_temp = np.ndarray(shape=(num_skips), dtype=np.int32)
    for j in range(num_skips):
      while target in targets_to_avoid:
        target = random.randint(0, span - 1)
      targets_to_avoid.append(target)
      batch_temp[j] = buffer[target]
    batch[i] = batch_temp
    labels[i,0] = buffer[skip_window]
    buffer.append(data[data_index])
    data_index = (data_index + 1) % len(data)
  return batch, labels

 之後,由於CBOW模型對於Skip-Gram模型結構上的不同,我們需要定義一箇中間層作為加權層來疊加環境並平均答案來作為輸出,於是,對於tensorflow的skip-gram模型我們做出如下改動:

graph = tf.Graph()

with graph.as_default():
  
  # Input data.
  
  #變更1:
  #---------------------------------------------------------------------------------------------------------------
  # 這裡的輸入對應的是skip-gram,input大小是batch_size X 1
  #train_inputs = tf.placeholder(tf.int32, shape=[batch_size]) 
  #這裡由於我們的輸入對於每個詞而言有一個context的輸入,我們的input的大小為batch_size X context
  train_inputs = tf.placeholder(tf.int32,shape=[batch_size, skip_window * 2])
  #---------------------------------------------------------------------------------------------------------------

  train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])
  valid_dataset = tf.constant(valid_examples, dtype=tf.int32)

  # Ops and variables pinned to the CPU because of missing GPU implementation
  with tf.device('/cpu:0'):
    # Look up embeddings for inputs.
    embeddings = tf.Variable(
        tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
    # Embedding size is calculated as shape(train_inputs) + shape(embeddings)[1:]
    embed = tf.nn.embedding_lookup(embeddings, train_inputs)
    
    #變更2:
    #---------------------------------------------------------------------------------------------------------------
    #這裡增加的就是首先加權embed變數,然後平均。注意這個reduce_sum裡的第二個para設為1
    #原因在於假設我們的batch_size是200, window_size是4, 然後詞向量size是200, 我們會得到
    #一個大小為200X4X200的張量,因為我們一次執行200個例子,每個例子有4個環境詞,然後
    #每個詞的大小為200維。但是,別忘了我們需要對這些輸入加權,我們所期待的其實是把張量
    #裡4的那個維度加權起來,於是,我們需要把這個para設為1.設為0加權的是例子的200維,3加權
    #的是每個詞向量自身。
    reduced_embed = tf.div(tf.reduce_sum(embed, 1), skip_window*2)
    #---------------------------------------------------------------------------------------------------------------

    # Construct the variables for the NCE loss
    nce_weights = tf.Variable(
        tf.truncated_normal([vocabulary_size, embedding_size],
                            stddev=1.0 / math.sqrt(embedding_size)))
    nce_biases = tf.Variable(tf.zeros([vocabulary_size]))

  # Compute the average NCE loss for the batch.
  # tf.nce_loss automatically draws a new sample of the negative labels each
  # time we evaluate the loss.
  loss = tf.reduce_mean(
      tf.nn.nce_loss(nce_weights, nce_biases, reduced_embed, train_labels,
                     num_sampled, vocabulary_size))

  # Construct the SGD optimizer using a learning rate of 1.0.
  optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(loss)

  # Compute the cosine similarity between minibatch examples and all embeddings.
  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)

  # Add variable initializer.
  init = tf.initialize_all_variables()

# Step 5: Begin training.
num_steps = 100001

with tf.Session(graph=graph) as session:
  # We must initialize all variables before we use them.
  init.run()
  print("Initialized")

  average_loss = 0
  for step in xrange(num_steps):
    #變更3:
    #---------------------------------------------------------------------------------------------------------------
    #在這裡把generate_batch或者generate_skipgram_batch修改為generate_cbow_batch就可以了
    batch_inputs, batch_labels = generate_cbow_batch(
        batch_size, num_skips, skip_window)
    #---------------------------------------------------------------------------------------------------------------

    feed_dict = {train_inputs : batch_inputs, train_labels : batch_labels}

    # We perform one update step by evaluating the optimizer op (including it
    # in the list of returned values for session.run()
    _, loss_val = session.run([optimizer, loss], feed_dict=feed_dict)
    average_loss += loss_val

 試執行這個程式,我們得到了如下結果:

 Nearest to to: cruel, must, would, should, will, could, nigeria, captive,

 Nearest to may: can, would, could, will, might, must, should, cannot,

 Nearest to was: is, had, has, were, became, be, been, perceive,

 Nearest to into: through, delicious, from, comrades, reflexive, pellets, awarding, slowly,

 Nearest to some: many, these, any, various, several, both, their, wise,

 Nearest to that: which, meadow, how, battlefront, however, powell, animism, this,

 Nearest to also: never, still, often, actually, sometimes, usually, originally, below,

 Nearest to are: were, have, is, be, include, do, sprites, been,

 Nearest to new: nominally, dns, fermentable, final, proprietorships, aloe, junior, reservoirs,

 Nearest to their: its, his, her, the, your, some, my, whose,

 Nearest to years: decades, year, history, times, days, months, marmoset, wrangler,

 Nearest to there: they, it, she, he, these, generally, lemon, we,

 Nearest to th: eight, zero, nine, plasticizers, fairies, characteristic, documentation, anecdotes,

 Nearest to many: some, several, these, such, most, various, wise, other,

 Nearest to but: however, and, although, while, pursuing, marmoset, glowing, components,

 Nearest to see: wants, atomic, charlotte, crimson, tanaka, caius, maine, scuttled,

由此可見,該系統執行的還是可以的。其中,are對應詞有were, have, is be, include, do等,有英語基礎的朋友都瞭解,這些詞確實在在用法及意義上相似於are。 另外包括their在內的很多詞效果看似還是不錯的。有興趣的朋友歡迎閱讀我的原始碼

相關推薦

Python TensorflowWord2Vec程式碼解釋

前言: 作為一個深度學習的重度狂熱者,在學習了各項理論後一直想通過專案練手來學習深度學習的框架以及結構用在實戰中的知識。心願是好的,但機會卻不好找。最近剛好有個專案,藉此機會練手的過程中,我發現其實各大機器學習以及tensorflow框架群裡的同學們也有類似的問題。於是希望借專案之手分享一點本人執行過程中的

TensorFlow實現word2vec 詳細程式碼解釋

參考1:http://blog.csdn.net/mylove0414/article/details/69789203 參考2:《TensorFlow實戰》 參考3:http://www.jianshu.com/p/f682066f0586 程式碼配合參考3的圖形說明,可

WindowsAnaconda安裝 python + tensorflow

() hive cti const run 安裝 文件 blank nac 下載安裝Anaconda 首先下載Anaconda,可以從清華大學的鏡像網站進行下載。 安裝Anaconda,註意安裝時不要將添加環境變量的選項取消掉。 安裝完成之後,在安裝目錄下cmd,

Python+Tensorflow】Deep Q Network (DQN) 迷宮示例程式碼整理

Overview 本文程式碼整理自Morvan Zhou(莫煩Python [1])的機器學習相關教程 - 強化學習 - DQN部分 [2] Deep Q Network簡稱DQN,結合了Q learning和Neural networks的優勢。如果我們使用tabular Q

python TensorFlow 測試程式碼

import tensorflow as tf import numpy as np #create data x_data = np.random.rand(100).astype(np.float32) y_data = x_data*0.1 + 0.3 ###create tensorfl

python實現將某程式碼檔案複製/移動到指定路徑 (檔案、資料夾的移動、複製、刪除、重新命名)

    用python實現將某程式碼檔案複製/移動到指定路徑下。 場景例如:mv ./xxx/git/project1/test.sh ./xxx/tmp/tmp/1/test.sh (相對路徑./xxx/tmp/tmp/1/不一定存在)   # -

Linux系統python學習筆記——vim程式碼編輯器

Vim程式碼編輯器 一、Vim簡介           vi是"Visual Interface"的簡稱。它在Linux上的地位就彷彿Edit程式在DOS上一樣。它可以執行輸出、刪除、查詢、替換、塊操作等眾多文字操作,而且使用者可以根據自己的

Windows從頭開始安裝pythontensorflow / 急速安裝配置windows+python+tensorflow

1.下載anaconda3  下載地址https://www.anaconda.com/download/ 根據作業系統選擇對應的版本 安裝時勾選自動創立環境變數,如果未勾選,安裝完成後新增3個環境變數至Path,分別為:Anaconda3路徑,Anaconda3路徑\Scripts,

TensorFlow的序列模型程式碼解釋(RNN、LSTM)

  1、學習單步的RNN:RNNCell、BasicRNNCell、BasicLSTMCell、LSTMCell、GRUCell (1)RNNCell 如果要學習TensorFlow中的RNN,第一站應該就是去了解“RNNCell”,它是TensorFlow中實現RNN的基本單元,每個RNNC

python 環境gensim中的word2vec的使用筆記

centos 7, python2.7, gensim (0.13.1) 語料: 程式: # -*- coding: utf-8 -*- ''' Created on 2016年8月29日 測試gensim使用 @author: root ''

pythonword2vec詞向量訓練與載入方法

專案中要對短文字進行相似度估計,word2vec是一個很火的工具。本文就word2vec的訓練以及載入進行了總結。word2vec的原理就不描述了,word2vec詞向量工具是由google開發的,輸入為文字文件,輸出為基於這個文字文件的語料庫訓練得到的詞向量模型。通過該模型

Tensorflow實現CNN文字分類(詳細解釋及TextCNN程式碼解釋)

Ox00: Motivation最近在研究Yoon Kim的一篇經典之作Convolutional Neural Networks for Sentence Classification,這篇文章可以說是cnn模型用於文字分類的開山之作(其實第一個用的不是他,但是Ki

tensorflow跑文字二分類程式碼出現的問題

由於實習公司的一個project,需要對文字進行二分類,因此在tensorflow上跑了一下程式碼。連結如下:http://www.wildml.com/2015/12/implementing-a-cnn-for-text-classification-in-tensorf

WindowsAnaconda安裝 python + tensorflow GPU版

reat cti tps create dnn com logs tun naconda 這裏首先確認沒有安裝CPU版本,並默認已經安裝了CUDA和Cudnn以及anaconda。 接下來需要安裝GPU版本的tensorflow: 打開cmd並輸入: conda cr

pythonvirtualenv的解釋及使用

https://www.cnblogs.com/zh605929205/p/7705192.html   https

tensorflow實現Word2vec

while brush ber ear same split max ems red # coding: utf-8 ‘‘‘ Note: Step 3 is missing. That‘s why I left it. ‘‘‘ from __future__ impor

python 元類的簡單解釋

.html www ren 有用 copy tle 例子 sky -i 本文轉自博客:http://www.cnblogs.com/piperck/p/5840443.html 作者:piperck python 類和元類(metaclass)的理解和簡單運用 (一) p

[ZZ] 如何在多版本anaconda python環境轉換spyder

過程 pda 名稱 -- idt anaconda rip gin div https://www.zhihu.com/people/alexwhu/answers 使用anaconda的話,可以參考以下步驟: 1、打開anaconda navigator

python windows pip easy_install 使用錯誤的問題

log 路徑和 csdn 安裝包 launcher 裝包 python安裝 detail tails 最近電腦重裝了系統,又重新安裝python 。在官網下載了安裝包後電腦成功安裝了,但使用pip命令時出現以下錯誤 Fatal error in l

python劃線

運算符重載 __init__ 由於 開頭 var 用戶 成員 wiki 名稱 首先是單下劃線開頭,這個被常用於模塊中,在一個模塊中以單下劃線開頭的變量和函數被默認當作內部函數,如果使用 from a_module import * 導入時,這部分變量和函數不會被導入。不過值