Word2Vec (Part 2): NLP With Deep Learning with Tensorflow (CBOW)

2023-10-24 19:59

本文主要是介绍Word2Vec (Part 2): NLP With Deep Learning with Tensorflow (CBOW),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Tensorflow上其实本来已经有word2vec的代码了,但是我第一次看的时候也是看得云里雾里,还是看得不太明白。并且官方文档中只有word2vec的skip-gram实现,所以google了一下,发现了这两篇好文章,好像也没看到中文版本,本着学习的态度,决定翻译一下,一来加深一下自己的理解,二来也可以方便一下别人。第一次翻译,如有不当,欢迎指出。

    原文章地址:

    Word2Vec (Part 1): NLP With Deep Learning with Tensorflow (Skip-gram)

    Word2Vec (Part 2): NLP With Deep Learning with Tensorflow (CBOW)


上一篇文章,也就是Skip-gram模型,点这里

下面带来CBOW模型的讲解:


CBOW是什么?

CBOW是什么呢?它的全称是 continuous bag-of-words ,中文是连续词袋模型。它的框架可以说就是将skip-gram模型倒转过来。在skip-gram模型中,是根据目标词预测上下文。而CBOW模型,则是根据上下文预测目标词。


为什么要使用CBOW模型?

既然我们已经有了skip-gram模型,为什么我们还要学习CBOW模型呢?原因就是CBOW模型的表现更加优秀。一部分的原因在于CBOW模型的 inputs 更加丰富。换句化说,假定如下句子:the dog barked at the mailman , 在skip-gram模型中,输入输出为 (input:'dog',output:'barked') ,而在CBOW模型中,将有以下输入输出:(input:['the','barked','at'],output:'dog') 。可以看出在CBOW中,只有当 [the, barked, at] 等词准确地出现了,才会预测dog 出现,而不像skip-gram那样,只能预测出 dog 将出现在 barked 附近。


CBOW模型

CBOW的概念模型看起来就像倒过来的skip-gram模型一样。尽管看起来如此,但是CBOW模型和skip-gram模型并不是对称的。下面是模型的框架图。


注意到,因为实现框架图跟skip-gram的十分相似,所以没有给出来。如何把这个概念模型如转化成实现模型呢,我们要做的就是生成 (input, output) 的 batch。换句化说,对每一列每次处理处理 b 个(b - batch大小)词(比如b x word[t-2],b x word[t-1]b x word[t+1],b x word[t+2])。

CBOW 背后的思想是,我们使用所有 input 词的平均词向量作为学习模型的输入。


数据生成

现在,生成数据的函数需要做一些小小的修改来适应CBOW模型。下面是修改后的代码:

[python]  view plain copy
  1. def generate_batch(batch_size, skip_window):  
  2.     # skip window is the amount of words we're looking at from each side of a given word  
  3.     # creates a single batch  
  4.     global data_index  
  5.     assert skip_window%2==1  
  6.    
  7.     span = 2 * skip_window + 1 # [ skip_window target skip_window ]  
  8.    
  9.     batch = np.ndarray(shape=(batch_size,span-1), dtype=np.int32)  
  10.     labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)  
  11.     # e.g if skip_window = 2 then span = 5  
  12.     # span is the length of the whole frame we are considering for a single word (left + word + right)  
  13.     # skip_window is the length of one side  
  14.    
  15.     # queue which add and pop at the end  
  16.     buffer = collections.deque(maxlen=span)  
  17.    
  18.     #get words starting from index 0 to span  
  19.     for _ in range(span):  
  20.         buffer.append(data[data_index])  
  21.         data_index = (data_index + 1) % len(data)  
  22.    
  23.     # num_skips => # of times we select a random word within the span?  
  24.     # batch_size (8) and num_skips (2) (4 times)  
  25.     # batch_size (8) and num_skips (1) (8 times)  
  26.     for i in range(batch_size):  
  27.         target = skip_window  # target label at the center of the buffer  
  28.         target_to_avoid = [ skip_window ] # we only need to know the words around a given word, not the word itself  
  29.    
  30.         # do this num_skips (2 times)  
  31.         # do this (1 time)  
  32.    
  33.         # add selected target to avoid_list for next time  
  34.         col_idx = 0  
  35.         for j in range(span):  
  36.             if j==span//2:  
  37.                 continue  
  38.             # e.g. i=0, j=0 => 0; i=0,j=1 => 1; i=1,j=0 => 2  
  39.             batch[i,col_idx] = buffer[j] # [skip_window] => middle element  
  40.             col_idx += 1  
  41.         labels[i, 0] = buffer[target]  
  42.    
  43.         buffer.append(data[data_index])  
  44.         data_index = (data_index + 1) % len(data)  
  45.    
  46.     assert batch.shape[0]==batch_size and batch.shape[1]== span-1  

可以注意到,batch 的大小变为 (b x span-1),而修改前为(b x 1)。并且去除了num_skips.因为我们将使用 span 中所有的词。直观地说,batch 的索引(i,j)可以理解为文档labels 中第 j 个词的 i-skip_window 偏移量( i<skip_window 时)或 i-skip_window+1 个词 (i>=skip_window时)。举个例子,假设 skip_window=1 , 输入句子 the dog barked at the mailman,我们将会得到,

batch: [['the','barked'],['dog','at'],['barked','the'],['at','mailman']]

labels: ['dog','barked','at','the']

训练模型

同样,训练模型的阶段也需要做一些调整。但是这也没有很复杂,我们要做的就是把 data placholder 的大小做一些调整,并且为多个输入写

写入正确的符号操作以得到其平均值。考虑到训练过程比较重要,因此我将会把代码分成几个小片段,并且从中挑取重要的来讲解。


变量初始化

首先我们要把 train_dataset 的 placeholder 改变为 (b x 2*skip_window)(记住,span-1 = 2*skip_window)。其它保持不变。

[python]  view plain copy
  1. if __name__ == '__main__':  
  2.     batch_size = 128  
  3.     embedding_size = 128 # Dimension of the embedding vector.  
  4.     skip_window = 1 # How many words to consider left and right.  
  5.     num_skips = 2 # How many times to reuse an input to generate a label.  
  6.     valid_size = 16 # Random set of words to evaluate similarity on.  
  7.     valid_window = 100 # Only pick dev samples in the head of the distribution.  
  8.     # pick 16 samples from 100  
  9.     valid_examples = np.array(random.sample(range(valid_window), valid_size//2))  
  10.     valid_examples = np.append(valid_examples,random.sample(range(1000,1000+valid_window), valid_size//2))  
  11.     num_sampled = 64 # Number of negative examples to sample.  
  12.    
  13.     graph = tf.Graph()  
  14.    
  15.     with graph.as_default(), tf.device('/cpu:0'):  
  16.    
  17.         # Input data.  
  18.         train_dataset = tf.placeholder(tf.int32, shape=[batch_size,2*skip_window])  
  19.         train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])  
  20.         valid_dataset = tf.constant(valid_examples, dtype=tf.int32)  
  21.    
  22.         # Variables.  
  23.         # embedding, vector for each word in the vocabulary  
  24.         embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.01.0))  
  25.         softmax_weights = tf.Variable(tf.truncated_normal([vocabulary_size, embedding_size],  
  26.                          stddev=1.0 / math.sqrt(embedding_size)))  
  27.         softmax_biases = tf.Variable(tf.zeros([vocabulary_size]))  

向量查找与求平均

这里我们要做些大的变动。我们要重新编写 embedding lookup 并且正确地求出它们的平均值。总来的来说,我们要查找 train_dataset (大小为 b x 2*skip_window) 的每一行,查找行中词ID对应的向量。然后将这些向量保存在临时变量( embedding_i )中,在把这些向量连接起来称为复合向量(embeds)(大小为 2*skip_window x b x D),进而在 axis 0 上求得 reduce mean 。最终我们可以对 data 的每个 batch 生成 train_labels 中词相应上下文的平均向量。

[python]  view plain copy
  1. # Model.  
  2. embeds = None  
  3. for i in range(2*skip_window):  
  4.     embedding_i = tf.nn.embedding_lookup(embeddings, train_dataset[:,i])  
  5.     print('embedding %d shape: %s'%(i,embedding_i.get_shape().as_list()))  
  6.     emb_x,emb_y = embedding_i.get_shape().as_list()  
  7.     if embeds is None:  
  8.         embeds = tf.reshape(embedding_i,[emb_x,emb_y,1])  
  9.     else:  
  10.         embeds = tf.concat(2,[embeds,tf.reshape(embedding_i,[emb_x,emb_y,1])])  
  11.    
  12. assert embeds.get_shape().as_list()[2]==2*skip_window  
  13. print("Concat embedding size: %s"%embeds.get_shape().as_list())  
  14. avg_embed =  tf.reduce_mean(embeds,2,keep_dims=False)  
  15. print("Avg embedding size: %s"%avg_embed.get_shape().as_list())  

Loss 函数以及优化

现在相较于skip-gram模型,我们在CBOW模型的 sampled_softmax_loss 中使用了平均向量。代码方面没有很大的变动。

[python]  view plain copy
  1. loss = tf.reduce_mean(tf.nn.sampled_softmax_loss(softmax_weights, softmax_biases, avg_embed,  
  2.                        train_labels, num_sampled, vocabulary_size))  
  3. optimizer = tf.train.AdagradOptimizer(1.0).minimize(loss)  
  4.    
  5. # We use the cosine distance:  
  6. norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))  
  7. normalized_embeddings = embeddings / norm  
  8. valid_embeddings = tf.nn.embedding_lookup(normalized_embeddings, valid_dataset)  
  9. similarity = tf.matmul(valid_embeddings, tf.transpose(normalized_embeddings))  

运行程序

最后我们要来让tensorflow跑起来。

[python]  view plain copy
  1. with tf.Session(graph=graph) as session:  
  2.     tf.initialize_all_variables().run()  
  3.     print('Initialized')  
  4.     average_loss = 0  
  5.     for step in range(num_steps):  
  6.         batch_data, batch_labels = generate_batch(batch_size, skip_window)  
  7.         feed_dict = {train_dataset : batch_data, train_labels : batch_labels}  
  8.         _, l = session.run([optimizer, loss], feed_dict=feed_dict)  
  9.         average_loss += l  
  10.         if step % 2000 == 0:  
  11.             if step > 0:  
  12.                 average_loss = average_loss / 2000  
  13.                 # The average loss is an estimate of the loss over the last 2000 batches.  
  14.             print('Average loss at step %d: %f' % (step, average_loss))  
  15.             average_loss = 0  
  16.         # note that this is expensive (~20% slowdown if computed every 500 steps)  
  17.         if step % 10000 == 0:  
  18.             sim = similarity.eval()  
  19.             for i in range(valid_size):  
  20.                 valid_word = reverse_dictionary[valid_examples[i]]  
  21.                 top_k = 8 # number of nearest neighbors  
  22.                 nearest = (-sim[i, :]).argsort()[1:top_k+1]  
  23.                 log = 'Nearest to %s:' % valid_word  
  24.                 for k in range(top_k):  
  25.                     close_word = reverse_dictionary[nearest[k]]  
  26.                     log = '%s %s,' % (log, close_word)  
  27.                 print(log)  
  28.     final_embeddings = normalized_embeddings.eval()  

结果

最终我们得到的结果如下

[python]  view plain copy
  1. Average loss at step 07.687360  
  2. Nearest to he: annoying, menachem, publicize, unwise, skinny, attractors, devastating, declination,  
  3. Nearest to is: iarc, agrarianism, revoluci, bachman, distinguish, schliemann, carbons, ne,  
  4. Nearest to some: routed, oscillations, reverence, collaborating, invitational, murderous, mortimer, migratory,  
  5. Nearest to only: walkway, loud, today, headshot, foundational, asceticism, tracked, hare,  
  6. ...  
  7. Nearest to i: intermediates, backed, techs, duly, inefficiencies, ibadi, creole, poured,  
  8. Nearest to bbc: mprp, catching, slavic, mol, dorian, mining, inactivity, applet,  
  9. Nearest to cost: cakes, voltages, halter, disappeared, poking, buttocks, talents, salle,  
  10. Nearest to proposed: prisoners, ecuador, sorghum, complying, saturdays, positioned, probing, observables,  

[python]  view plain copy
  1. Average loss at step 1000002.422888  
  2. Nearest to he: she, it, they, there, who, eventually, neighbors, theses,  
  3. Nearest to is: was, has, became, remains, be, becomes, seems, cetacean,  
  4. Nearest to some: many, several, certain, most, any, all, both, these,  
  5. Nearest to only: settling, orchids, commutation, until, either, first, alcohols, rabba,  
  6. ...  
  7. Nearest to i: we, you, ii, iii, iv, they, t, lm,  
  8. Nearest to bbc: news, corporation, coffers, inactivity, mprp, formatted, cara, pedestrian,  
  9. Nearest to cost: cakes, length, completion, poking, measure, enforcers, parody, figurative,  
  10. Nearest to proposed: introduced, discovered, foreground, suggested, dismissed, argued, ecuador, builder,  

完整代码可以在这里下载: 5_word2vec_cbow.py

这篇关于Word2Vec (Part 2): NLP With Deep Learning with Tensorflow (CBOW)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/277449

相关文章

vue解决子组件样式覆盖问题scoped deep

《vue解决子组件样式覆盖问题scopeddeep》文章主要介绍了在Vue项目中处理全局样式和局部样式的方法,包括使用scoped属性和深度选择器(/deep/)来覆盖子组件的样式,作者建议所有组件... 目录前言scoped分析deep分析使用总结所有组件必须加scoped父组件覆盖子组件使用deep前言

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

Level3 — PART 3 — 自然语言处理与文本分析

目录 自然语言处理概要 分词与词性标注 N-Gram 分词 分词及词性标注的难点 法则式分词法 全切分 FMM和BMM Bi-direction MM 优缺点 统计式分词法 N-Gram概率模型 HMM概率模型 词性标注(Part-of-Speech Tagging) HMM 文本挖掘概要 信息检索(Information Retrieval) 全文扫描 关键词

MySQL record 02 part

查看已建数据库的基本信息: show CREATE DATABASE mydb; 注意,是DATABASE 不是 DATABASEs, 命令成功执行后,回显的信息有: CREATE DATABASE mydb /*!40100 DEFAULT CHARACTER SET utf8mb3 / /!80016 DEFAULT ENCRYPTION=‘N’ / CREATE DATABASE myd

基于Python的自然语言处理系列(1):Word2Vec

在自然语言处理(NLP)领域,Word2Vec是一种广泛使用的词向量表示方法。它通过将词汇映射到连续的向量空间中,使得计算机可以更好地理解和处理文本数据。本系列的第一篇文章将详细介绍Word2Vec模型的原理、实现方法及应用场景。 1. Word2Vec 原理         Word2Vec模型由Google的Tomas Mikolov等人在2013年提出,主要有两种训练方式

简单的Q-learning|小明的一维世界(3)

简单的Q-learning|小明的一维世界(1) 简单的Q-learning|小明的一维世界(2) 一维的加速度世界 这个世界,小明只能控制自己的加速度,并且只能对加速度进行如下三种操作:增加1、减少1、或者不变。所以行动空间为: { u 1 = − 1 , u 2 = 0 , u 3 = 1 } \{u_1=-1, u_2=0, u_3=1\} {u1​=−1,u2​=0,u3​=1}

简单的Q-learning|小明的一维世界(2)

上篇介绍了小明的一维世界模型 、Q-learning的状态空间、行动空间、奖励函数、Q-table、Q table更新公式、以及从Q值导出策略的公式等。最后给出最简单的一维位置世界的Q-learning例子,从给出其状态空间、行动空间、以及稠密与稀疏两种奖励函数的设置方式。下面将继续深入,GO! 一维的速度世界 这个世界,小明只能控制自己的速度,并且只能对速度进行如下三种操作:增加1、减

win10不用anaconda安装tensorflow-cpu并导入pycharm

记录一下防止忘了 一、前提:已经安装了python3.6.4,想用tensorflow的包 二、在pycharm中File-Settings-Project Interpreter点“+”号导入很慢,所以直接在cmd中使用 pip install -i https://mirrors.aliyun.com/pypi/simple tensorflow-cpu下载好,默认下载的tensorflow

稀疏自编码器tensorflow

自编码器是一种无监督机器学习算法,通过计算自编码的输出与原输入的误差,不断调节自编码器的参数,最终训练出模型。自编码器可以用于压缩输入信息,提取有用的输入特征。如,[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]四比特信息可以压缩成两位,[0,0],[1,0],[1,1],[0,1]。此时,自编码器的中间层的神经元个数为2。但是,有时中间隐藏层的神经元

Tensorflow实现与门感知机

感知机是最简单的神经网络,通过输入,进行加权处理,经过刺激函数,得到输出。通过输出计算误差,调整权重,最终,得到合适的加权函数。 今天,我通过tensorflow实现简单的感知机。 首先,初始化变量:     num_nodes = 2     output_units = 1     w = tf.Variable(tf.truncated_normal([num_nodes,output