由于本人初學(xué)機(jī)器學(xué)習(xí)&Tensorflow蠢笋,文章中若有錯(cuò)誤,希望評論指出鳞陨,不勝感謝昨寞。
一、Word2Vec問題是什么?
Word2Vec 即 Word to vector厦滤,詞匯轉(zhuǎn)向量编矾。
我們希望詞義相近的兩個(gè)單詞,在映射之后依然保持相近馁害,詞義很遠(yuǎn)的單詞直接則保持很遠(yuǎn)的映射距離:如下圖所示窄俏,這里介紹到了 t-SNE 方法可以很好的達(dá)到效果:
關(guān)于t-SNE這里推薦一篇文章:
http://bindog.github.io/blog/2016/06/04/from-sne-to-tsne-to-largevis
二、從實(shí)例代碼中學(xué)習(xí)
起初看了網(wǎng)上很多資料碘菜,關(guān)于 Word2Vec 真心學(xué)的云里霧里凹蜈。在 Tensorflow 中 Vector Representations of Words 字詞的向量表示這一章卡了很久。于是我嘗試看 word2vec_basic.py 的源碼來理解一下Word2Vec最簡單的實(shí)現(xiàn)忍啸。
看完200多行的源碼仰坦, Tensorflow 自身的注釋 就關(guān)于Word2Vec實(shí)例總結(jié)為6步:
- 下載數(shù)據(jù)
- 將原詞匯數(shù)據(jù)轉(zhuǎn)換為字典映射
- 為 skip-gram模型 建立一個(gè)掃描器
- 建立并訓(xùn)練 skip-gram 模型
- 開始訓(xùn)練模型
- 結(jié)果可視化
這里忽視第六步,從第1步到第5步计雌,我將使用圖片和一些代碼來貫通Word2Vec整個(gè)過程悄晃。
首先打開下載進(jìn)來的word詞匯數(shù)據(jù),由于是無監(jiān)督學(xué)習(xí)凿滤,并沒有標(biāo)簽妈橄,就只是整整100M大小文本數(shù)據(jù)。
這是第一步下載得到的數(shù)據(jù):
然后開始第二步將原詞匯數(shù)據(jù)轉(zhuǎn)換為字典映射翁脆,比如我取出這段文本的頭一句眷蚓,它會(huì)進(jìn)行如下變換:
現(xiàn)在我們的詞匯文本變成了用數(shù)字編號替代的格式以及詞匯表和逆詞匯表。逆詞匯只是編號為key,詞匯為value反番。
接著開始第三步沙热,為skip-gram 模型建立一個(gè)掃描器,首先看一下掃描器函數(shù):
def generate_batch(batch_size, num_skips, skip_window):
batch_size是指一次掃描多少塊,skip_window為左右上下文取詞的長短罢缸,num_skips輸入數(shù)字的重用次數(shù)篙贸。假設(shè)我們的掃描器先掃這大段文字的前8個(gè)單詞,左右各取1個(gè)單詞枫疆,重用次數(shù)為2次爵川。我們就會(huì)觀察到如下結(jié)果:
現(xiàn)在通過上面一步,我們構(gòu)造出了input和label养铸,就可以進(jìn)行監(jiān)督學(xué)習(xí)雁芙,下面
什么是NCE Loss呢轧膘?這里為什么不用更為常見的Softmax + Cross-Entropy 呢?
因?yàn)槿绻谶@里使用Softmax + Cross-Entropy作為損傷函數(shù)會(huì)有一個(gè)問題兔甘,Softmax當(dāng)有幾萬+的分類時(shí)谎碍,速率會(huì)大大下降。
其速度對比如下:
10000 個(gè)類洞焙,Softmax每秒處理 10000 個(gè)樣本蟆淀,NCE每秒處理 30000 個(gè)樣本
100000 個(gè)類,Softmax每秒處理 1000 個(gè)樣本澡匪,NCE每秒處理 20000 個(gè)樣本
此實(shí)驗(yàn)結(jié)論由其他同學(xué)得出熔任,給出實(shí)驗(yàn)鏈接:https://zhuanlan.zhihu.com/p/21642643
這里再整理出其他同學(xué)關(guān)于 NCE LOSS 源碼的理解,下面就是一段 NCE LOSS 的實(shí)現(xiàn)代碼唁情,但不得而知 Tensorflow 是否使用該NCE LOSS的實(shí)現(xiàn)疑苔。
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的主要思想是,對于每一個(gè)樣本甸鸟,除了本身的label惦费,同時(shí)采樣出N個(gè)其他的label,從而我們只需要計(jì)算樣本在這N+1個(gè)label上的概率抢韭,而不用計(jì)算樣本在所有l(wèi)abel上的概率薪贫。而樣本在每個(gè)label上的概率最終用了Logistic的損失函數(shù)。
這里可謂是整個(gè) Word2Vec 的關(guān)鍵刻恭。
至此瞧省,已經(jīng)搭建好訓(xùn)練模型,然后便可以進(jìn)行分批次的訓(xùn)練即可鳍贾。那么下一個(gè)問題是完成訓(xùn)練后鞍匾,我們?nèi)绾闻袛鄡蓚€(gè)詞匯的相似度?
這里我們使用 cos 來表示相似度會(huì)比使用 l2 向量差值會(huì)好一些贾漏。
這是根據(jù)訓(xùn)練方式所決定的候学,因?yàn)橄蛄康拈L度與分類無關(guān),
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/