word2vec python 代码实现_从零开始学Python自然语言处理(三)——手把手带你实现word2vec(skip-gram)...

本文介绍了如何使用keras实现word2vec的skip-gram模型,通过对比CBOW模型,讲解skip-gram的数据构造和训练过程,包括负样本采集和训练数据生成。文章提供完整的代码示例,帮助理解skip-gram模型的工作原理。
摘要由CSDN通过智能技术生成

前文传送门:

上一期我们用keras实现了CBOW模型。

本期我们来实现skip-gram模型。其实只需要对CBOW模型稍加改动就可以了。

keras 实现 word2vec 的 skip-gram 模型

上一期也提到了,小编用keras写的CBOW模型是参考了苏神的代码。苏神在他博客中说,“上面是CBOW模型的代码,如果需要Skip-Gram,请自行修改,Keras代码这么简单,改起来也容易。”

format,png

大神是理解不了我们这些渣渣的难处的。网上几乎再也找不到用keras实现skip-gram的代码。看来真的要逼自己去实现了。

看了几个其它框架实现的模型,其与CBOW的差异主要是在于训练数据的构建。

我们都知道,CBOW模型是用周围词去预测中心词。例如对于一句话“我 喜欢 学习 NLP !”,CBOW就是要用“我”,“喜欢”,“NLP”,“!”去预测“学习”这个词。我们在构造训练数据的时候,要去再构造几个负样本,这样模型的训练目标就是从“学习”和几个负样本词中,使预测“学习”的概率最大化。这个套路很简单,也很make sense。

而skip-gram的做法,是用“学习”去预测“我”,“喜欢”,“NLP”,“!”这四个词。我看网上的代码在构建训练数据的时候,是分别给“我”,“喜欢”,“NLP”,“!”构造负样本,这样一句话就构造了四条训练数据。什么意思呢?就是说,输入“学习”,要从“我”、负样本1、负样本2...中,使模型预测“我”的概率最大化;输入“学习”,我要从“喜欢”、负样本1、负样本2...中,使模型预测“喜欢”的概率最大化,......,这样训练四次。

我就觉得这比较扯淡了啊,感觉把“我”,“喜欢”,“学习”,“NLP”,“!”这五个词的相关性硬生生给拆开了。因此小编对数据集的构造也进行了适当的修改。

gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==

上代码!

这里我们只把和CBOW模型代码不一样的地方写出来。首先是负样本的采集。def get_negtive_sample(x, word_range, neg_num):negs = [] while True:rand = random.randrange(0, word_range) if rand not in negs and rand notin x:negs.append(rand) if len(negs) == neg_num: return negs

构造训练数据!def data_generator(): #训练数据生成器x,y = [],[] for sentence in corpus:sentence = [0]*window + [word2id[w] for w in sentence if w in word2id] + [0]*window #上面这句代码的意思是,因为我们是通过滑窗的方式来获取训练数据的,那么每一句语料的第一个词和最后一个词 #如何出现在中心位置呢?答案就是给它padding一下,例如“我/喜欢/足球”,两边分别补窗口大小个pad,得到“pad pad 我 喜欢 足球 pad pad” #那么第一条训练数据的背景词就是['pad', 'pad','喜欢', '足球'],中心词就是'我' for i in range(window, len(sentence)-window):y.append([sentence[i]])surrounding = sentence[i-window: i]+sentence[i+1: window+i+1]+gey.append(surrounding+get_negtive_sample(surrounding, nb_word, nb_negative))x,y = np.array(x),np.array(y)z = np.zeros((len(x), nb_negative+len(surrounding)))z[:,:len(surrounding)]=1 return x,y,z

x 为中心词训练语料,y 是周围词+nb_negative 个负样本,因为我们是将正确的周围词放在 y 的最前面,因此在构造 z 时,把标签 1 放在每条 label 数据的前len(surrounding)位。输出 z[0],可以看到是[1,1,1,...,0]。x,y,z = data_generator() #获取训练数据#第一个输入是周围词input_words = Input(shape=(1,), dtype='int32')#建立中心词的Embedding层input_vecs = Embedding(nb_word, word_size, name='word2vec')(input_words)#第二个输入,背景词以及负样本词samples = Input(shape=(nb_negative+len(surrounding),), dtype='int32')#同样的,中心词和负样本词也有一个Emebdding层,其shape为 (?, nb_word, word_size)weights = Embedding(nb_word, word_size, name='W')(samples)biases = Embedding(nb_word, 1, name='b')(samples)#将中心词向量与周围词和负样本的词向量分别进行点乘#注意到使用了K.expand_dims,这是为了将input_vecs_sum的向量推展一维,才能和weights进行dotinput_vecs_dot_ = Lambda(lambda x: K.batch_dot(x[0], K.expand_dims(x[1],2)))([weights,input_vecs])#然后再将input_vecs_dot_与biases进行相加,相当于 y = wx+b中的b项add_biases = Lambda(lambda x: K.reshape(x[0]+x[1], shape=(-1, nb_negative+1)))([input_vecs_dot_,biases])#这里苏神用了K.sigmoidsigmoid = Lambda(lambda x: K.sigmoid(x))(add_biases)#编译模型model = Model(inputs=[input_words,samples], outputs=sigmoid)#使用binary_crossentropy二分类交叉熵作损失函数model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])model.summary()

上面代码的大概意思就是说,输入一个中心词,要从一群正确的周围词和负样本中,去使模型预测这些周围词的概率最大化,因此模型的输出是一个multi-hot向量(因为预测结果有多个正确的周围词),这里就不能再使用softmax去计算概率,而应该用sigmoid去分别计算每一个周围词的概率。这是skip-gram与CBOW模型上最显著的不同。

同时,小编这里将周围词一起预测,感觉上没有切分这些词之间的相关性,治疗了我个人的强迫症。model.fit([x,y],z, epochs=nb_epoch, batch_size=512)

整个代码就写好了,大家可以实际测试一下训练出来的词向量的效果。个人感觉skip-gram的训练时长要大于CBOW。

果然,实践才能够检验自己是否真的掌握某项知识。

扫码下图关注我们不会让你失望!

format,png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值