当前位置:   article > 正文

word2vec的原理以及实现_word2vec原理及实战

word2vec原理及实战

word2vec是早期NLP的必要预处理过程,其用于生成词的向量表示(embeding)。

w_{embed} = f(word)=[0.1,0.2,...,0.21,0.32]

其将单词映射为固定长度的向量(embeding向量),而通过向量表示可以更好地表达不同词之间的相关性,因此会使得后续的分类、生成等NLP任务更好地学习训练。word2vec描述不同词之间的相关性,主要是指词同其上下文的其他词的共现性,主要有两种范式:

  • 跳元模型Skip-gram:其是假设通过中心词w_c生成其上下文w_{c\pm i},因此其目标是在中心词下,其上下文的条件概率P(w_{c-k},...,w_{c-1},w_{c+1},...,w_{c+k}|w_c)最大,即如下优化式子,C表示中心词的数量,k表示上下文窗口数。

\prod^C_c P(w_{c-k},...,w_{c-1},w_{c+1},...,w_{c+k}|w_c)=\prod^C_c \prod^k_{i=1} P(w_{c-i}|w_c)*P(w_{c+i}|w_c)

  • 连续词袋CBOW:其是假设通过上下文w_{c\pm i}生成中心词w_c,因此其目标在上下文下,其中心词生成条件概率P(w_c | w_{c-k},...,w_{c-1},w_{c+1},...,w_{c+k})最大,即如下优化式子:

\prod^C_c P(w_c|w_{c-k},...,w_{c-1},w_{c+1},...,w_{c+k})

 本文重点介绍跳元模型Skip-gram,为了求解上述式子,将上式求log转换为最小化下式:

Min - \sum^C_c \sum^{\pm k}_{i=\pm 1} log(P(w_{c+i}|w_c))

P(w_u|w_c)=\frac{P(w_u, w_c)}{P(w_c)}=\frac{P(w_u, w_c)}{\sum P(w_*, w_c)}

其中上式中的词与词间的联合分布P(w_u,w_c)可以由词向量相似度衡量,word2vec为了方便计算,通过exp形式进行度量:

P(wu|wc)=P(wu,wc)P(wc)=P(wu,wc)P(w,wc)=eEwuEwceEwEwc

上式中的Ew表示词向量,word2vec就是通过Embeding模块实现由单词到词向量的转换,从而上面的Loss最小化。Embeding模块实际上类似由于一个全连接网络层,其输入是N维的one-hot向量(N是指全量词的个数),输出是L维的向量(L是词向量的长度),其参数量总共为N*L。

 word2vec主要是为求解上述Embeding模块的权重参数w_{c,h},其组成了中心词c的词向量Ewc,可以求其偏导数如下:

\\ \frac{\partial logP(w_o|w_c)}{\partial Ew_c}\\ =\frac{\partial }{\partial Ew_c}(Ew_o*Ew_c-log(\sum P(w_*, w_c)))\\ =Ew_o-\frac{\sum Ew_* P(w_*, w_c)}{\sum P(w_*, w_c)}\\ =Ew_o-\sum P(w_*|w_c)Ew_*

以下我们通过paddle代码实现word2vec网络结构的定义:

  1. class Word2Vec(nn.Layer):
  2. def __init__(self, num_embeddings, embedding_dim):
  3. super(Word2Vec, self).__init__()
  4. self.embed = nn.Embedding(num_embeddings, embedding_dim,
  5. weight_attr=paddle.ParamAttr(
  6. name="center_embed",
  7. initializer=paddle.nn.initializer.XavierUniform()))
  8. # 执行前向计算
  9. def forward(self, center, contexts_and_negatives=None):
  10. """Skip-Gram"""
  11. v = self.embed(center)
  12. if contexts_and_negatives is None:
  13. return v
  14. u = self.embed(contexts_and_negatives)
  15. pred = paddle.squeeze(paddle.bmm(v, u.transpose(perm=[0, 2, 1])), axis=1)
  16. return pred

上述定义中的pred用于表示EwuEwv来描述两个词向量相乘项。

在训练时,我们将原来Loss转换为批量进行训练,另外P(w_u|w_c)的求解由于涉及到softmax计算,计算相对困难,因此一种我们将简化的方式进行训练(负采样)。

其首先定义w_u,w_c共同出现时,定义词u在中心词窗口k内的概率为:

P(D=1|w_u,w_c)=\sigma (Ew_u*Ew_c)=\frac{1}{1 + e^{-Ew_u*Ew_c}}

同理不在中心词窗口k内的概率为:

P(D=0|w_u,w_c)=1-\sigma (Ew_u*Ew_c)=1-\frac{1}{1 + e^{-Ew_u*Ew_c}}

此时条件概率可以表示为:

P(wu|wc)=P(D=1|wu,wc)P(w)P(D=0|w,wc)

此时batch内的loss可以表示为:

-\sum^B(\sum^{\pm k }_{\pm i}logP(D=1|w_{c+i},w_c)+\sum^h log(P(D=0|w_h,w_c))))

其中k表示正例的窗口大小,h表示负例数(即不在上下文窗口的词),上述loss函数可以用binary_cross_entropy_with_logits损失函数表示:

Out = -label * log(\sigma (logits))+(1-label)log(1 - \sigma (logits))

其中中label表示词是正或负例,logits即为EwuEwv,因此我们可以设计如下的损失函数代码

  1. class SigmoidBCELoss(nn.Layer):
  2. # 带掩码的二元交叉熵损失
  3. def __init__(self):
  4. super().__init__()
  5. def forward(self, inputs, label, mask):
  6. out = nn.functional.binary_cross_entropy_with_logits(
  7. logit=inputs, label=label, weight=mask, reduction="none")
  8. return out.mean(axis=1)

整体的paddle训练代码如下:

  1. # 中心词
  2. center_spec = paddle.static.InputSpec([None, 1], 'int64', 'center')
  3. # 上下文正例词及负例词
  4. context_spec = paddle.static.InputSpec([None, max_context_len], 'int64', 'contexts_and_negatives')
  5. # 正例及负例的标识
  6. label_spec = paddle.static.InputSpec([None, max_context_len], 'float32', 'label')
  7. # mask,正例及负例以外的填充为0不参与训练
  8. mask_spec = paddle.static.InputSpec([None, max_context_len], 'float32', 'mask')
  9. model = paddle.Model(Word2Vec(num_embeddings, embedding_dim), [center_spec, context_spec], [label_spec, mask_spec])
  10. model.prepare(
  11. optimizer=paddle.optimizer.Adam(learning_rate=learning_rate, parameters=model.parameters()),
  12. loss=SigmoidBCELoss()
  13. )
  14. model.fit(
  15. train_dataset,
  16. valid_dataset,
  17. batch_size=batch_size,
  18. epochs=num_epochs,
  19. eval_freq=1,
  20. shuffle=True,
  21. save_dir=save_model_dir,
  22. callbacks=[loss_print, vdl_record]
  23. )

全局向量的词嵌入(GloVe)

GloVe主要在原来loss函数中引入了两点特性:

  • 引入全局共现权重,x_{u,c}表示词u和词c共现的次数,此时全局的损失函数可以表示为:

-\sum^N_u \sum^N_v x_{u,v} log(P(w_u|w_c))

  • 重新定义条件概率P(w_u|w_c)的计算,条件概率实际表示为xu,c/xc,假设P(wu|wc)αe(EwuEwc),此时学习目标为:

\alpha e^{(Ew_u*Ew_c)}-x_{u,c}/x_c=0\Rightarrow Ew_u*Ew_c+log(\alpha)-log(x_{u,c}) + log(x_c)=0

  • 此时GloVe的损失函数定义为:

\sum^N_u \sum^N_v h(x_{u,v})(Ew_u*Ew_v+a_u + b_v - log(x_{u,v}))^2

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号