gensim里內(nèi)置了word2vec模型,訓(xùn)練和使用都很方便。但word2vec本身這個(gè)模型的設(shè)計(jì)是很精妙的,自己從頭使用tensorflow實(shí)現(xiàn)一遍很有意義。
http://mattmahoney.net/dc/text8.zip
python可以直接讀zip文件,里邊是一個(gè)命名為text8的文件,約100M,總共就一行,以
空格分詞
from aipack.datasets import text8
words = text8.load_text8_dataset()#就是詞列表,約1700W
data_size = len(words)
print(data_size) # 17005207
print(words[0:10]) # ['anarchism', 'originated', 'as', 'a', 'term', 'of', 'abuse', 'first', 'used', 'against']
data, count, dictionary, reverse_dictionary = build_words_dataset(words, vocabulary_size=50000)
#下標(biāo),詞頻dict,詞序dict,序?qū)υ~didct
print('Most 5 common words (+UNK)', count[:5])
print('Sample data', data[:10], [reverse_dictionary[i] for i in data[:10]])
讀進(jìn)來的text8約有1700W個(gè)詞,排重后得到25W+個(gè)詞,我們只保留50000個(gè)高頻的,其余用UNK代替。
build_words_dataset返回:data:詞的下標(biāo)list,count:{詞:詞頻},dictionary:{詞:詞下標(biāo)},reverse_dictionary:{詞下標(biāo):詞}
有了原始的數(shù)據(jù)集,那就可以開始建模。
Word2Vec模型中,主要有Skip-Gram和CBOW兩種模型,從直觀上理解,Skip-Gram是給定input word來預(yù)測上下文。而CBOW是給定上下文,來預(yù)測input word,如下圖:
batch, labels, data_index = generate_skip_gram_batch(data=data,batch_size=8,num_skips=4,skip_window=2,data_index=0)
data就是之前取出來的word_list的下標(biāo)數(shù)組,每批取的pair數(shù)batch_size=8,num_skip=4是以這個(gè)詞為中心,取對的次數(shù)
skip_window=2,是以當(dāng)前詞為中心,前后兩個(gè)詞(往前看包含當(dāng)前詞本身)。
下面是建模,官方示例損失函數(shù)用到了如下這個(gè):
def nce_loss(vocab_size,embedding_size,embed,train_labels,num_sampled):
# 模型內(nèi)部參數(shù)矩陣,初始為截?cái)嗾植?br> nce_weight = tf.Variable(tf.truncated_normal([vocab_size, embedding_size],
stddev=1.0 / math.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocab_size]))
# 得到NCE損失(負(fù)采樣得到的損失)
loss = tf.nn.nce_loss(
weights=nce_weight, # 權(quán)重
biases=nce_biases, # 偏差
labels=train_labels, # 輸入的標(biāo)簽
inputs=embed, # 輸入向量
num_sampled=num_sampled, # 負(fù)采樣的個(gè)數(shù)
num_classes=vocab_size # 類別數(shù)目
)
print(loss)
nce_loss = tf.reduce_mean(loss)
return nce_loss
輸入N*embedding_size的詞嵌入之后的向量,與input_labels=[N,1]之間的loss。按常規(guī)情況,input_labels用one_hot得擴(kuò)展成[N,vocab_size]維,但vocab_size-1維都是負(fù)樣本。這樣使用負(fù)采樣,主要是減少計(jì)算量為[N,64]。
后面的訓(xùn)練就是正常輸入數(shù)據(jù),反向傳播,迭代了。
在迭代25W輪之后,看下相似度,還是不錯(cuò)的:
從直觀的視角,還原一下skip-gram的原理:
1,每批選擇N個(gè)字的下標(biāo),對應(yīng)的期望輸出是這N個(gè)字周圍的字(下標(biāo))。
2,對輸入進(jìn)行embedding,得到[N,embedding_size]的矩陣。
3,對這個(gè)embdding的結(jié)果,與期望輸出[N,1]計(jì)算負(fù)采樣損失。
關(guān)于作者:魏佳斌,互聯(lián)網(wǎng)產(chǎn)品/技術(shù)總監(jiān),北京大學(xué)光華管理學(xué)院(MBA),特許金融分析師(CFA),資深產(chǎn)品經(jīng)理/碼農(nóng)。偏愛python,深度關(guān)注互聯(lián)網(wǎng)趨勢,人工智能,AI金融量化。致力于使用最前沿的認(rèn)知技術(shù)去理解這個(gè)復(fù)雜的世界。
聯(lián)系客服