当前位置:   article > 正文

Word2vec词向量_word2vec生成词向量

word2vec生成词向量

前文也零散的写了些 关于神经网络模型的篇幅,如NNLM,本文着重讲解有关词向量的内容,从静态词向量到动态词向量等,其实相关的内容,网上也有很多,本人主要是为了做些记录和巩固下基础的知识点

一、词向量概述

前面篇幅中介绍了NNLM 神经网络语言模型的计算原理,NNLM生成的产物初始化的矩阵也可以作为词向量。基于NNLM(神经网络语言模型)预训练方法存在一个问题,主要是通过 t 时刻的单词预测t+1时刻的单词,只能通过历史信息预测未来时刻的信息,损失了 当前单词与"未来"单词之间的 共现信息

本篇介绍新的词向量预训练方法——Word2Vec,其中包括skip-gram模型和CBOW(Continuous Bags of Words)模型,这两种模型是2013年提出,严格上来讲不算是语言模型,因为其是基于词语与词语之间的共现信息实现词向量的学习。不再是基于整句(句子长度为n )中前 n-1个 单词预测第n个单词

1.1 CBOW模型

        输入一句query,CBOW的中心思想是,根据上下文预测目标词汇,例如query为,...  _{Wt-2}_{Wt-1},_{Wt},_{Wt+1},_{Wt+2} ....  CBOW的任务就是根据 _{Wt} 的上下文 _{Ct} = {_{Wt-2}_{Wt-1},_{Wt+1},_{Wt+2} } 来预测 t 时刻的单词 _{Wt}, 由于窗口大小设置为5 所以 _{Wt} 的上下文文本中包含4个单词。 从上文中NNLM的文章得到 NNLM训练词向量的思想是,按照query的顺序进行输入前 n-1 个 word 来预测第 n 个word ,但是

CBOW模型不考虑上下文的顺序,因此CBOW模型也算是一个词袋子模型,后续有人验证 按照顺序输入在某些特定的任务(词性标注、句法分析)等表现更好

 

 (1). 输入层,以大小为5的窗口,在目标词的左边和右边各选择2个词语,作为模型的输入,输入层是由4个维度为词表长度的 |V|的 one-hot表示

(2). 初始化一个矩阵 E,维度为{V,dim}, dim 表示词向量维度,一般(50-300)之间,V表示的是词表的长度。矩阵E类似于tensorflow中的 look_up操作。如下图所示:

 (3).对上下文词向量取平均,就得到了 _{Wt} 的上下文表示_{Vt},使用 _{Wi} 表示单个word对应的列向量:

                                                 _{Vt}=\frac{1}{|_{Ct}|}\sum_{n}^{i}(_{Wi})

 (4).输出层,对目标词汇进行预测,进行多分类预测,分类的类别大小为词汇的size 大小 |V|,损失函数为常见的 交叉熵损失函数

1.2 skip-gram模型

skip-gram 模型和CBOW模型反过来,使用当前词汇预测上下文词汇,使用_{Wt}词汇预测上下文信息, _{Wt-2}_{Wt-1},_{Wt+1},_{Wt+2}  ,上下文中每一个词语当做独立的词汇进行预测,因此skip-gram模型是计算词与词之间的共现关系,P(_{Wt+j}|_{Wt})

具体运算过程如下所示:

 1.原始文本为“The quick brown fox jumps over the laze dog”,设置窗口大小为2;上图中蓝色的word 为目标词汇,右边的 Training Samples为构建的训练样本

2.从上文中发现一个问题,就是很多常见类似于"the" 这种词汇对于其上下文并不能带来很多有用的语义信息,因为几乎很多词汇中都会出现"the"这个单词,对于这种高频的词汇,原论文中使用的是通过抽样来删除该类单词;基本思想是,对于一开始训练的单词,每一个单词都有一定概率被删除,概率和单词出现的频率有关。

3.对于输出层也是类似于 NNLM的方式进行多分类预测,使用交叉熵进行损失迭代

二、负采样

2.1  CBOW和skip-gram模型的问题

我们从上文中的思路得到具体的流程如下:

 从章节一中,我们知道了 skip-gram和CBOW模型的输入层,词向量层,输出层,其中我们知道最终的预测是多分类,分类的类别数量和词汇的size相关,词汇表很大导致,训练过程中的矩阵很大,严重影响训练的效率

2.2  skip-gram模型的负采样训练

针对训练的效率问题,skip-gram 采用 负采样来加速训练,具体例子如下:

 如:当前词汇是 quick,目标词汇是 brow,假设词汇表的大小是10000,在模型的输出层是10000分类,单词brow的score得分最高,其他的9999的分类的score较低。对于这些9999得分低的单词 我们称之为 "negative " word.

负采样的方法提供一种新的任务视角:给定当前词语和其上下文。最大化两者之间的共现概率,问题简化成一种二分类问题,也就是说 quick—> brown 的分类为1, quick—>dong 的分类为0

对于 "negative " word 的选择,一个单词被选作negative sample的概率跟它出现的频次有关,出现频次越高的单词越容易被选作negative words,原来论文中指的是使用"一元分布模型",一般的情况 "negative " word 选择(5-20)个

2.3 word2vec无法处理一词多义

 由上文得到 bank有银行的意思,也有河岸的意思,对于这种静态词向量无法表达其多义性

三、总结以及代码展示

3.1总结

        1.CBOW 是通过上下文进行预测当前值

        2. SKip-gram 是通过当前值来预测上下文

        优点: (1).考虑到上下文,跟之前的Embedding 相比,效果更好

                 (2).相比较之前的Embedding 维度更少,速度更快

                 (3). 通用性较强,使用于各种NLP任务中

        缺点:(1).词和向量是一对一的关系,无法处理多义词问题

                   (2).word2vec 是一种静态方式,无法处理特定任务做动态优化

3.2 代码展示

  1. #code by 2021.8.1
  2. # learning by https://github.com/graykode
  3. import numpy as np
  4. from numpy import random
  5. import torch
  6. import torch.nn as nn
  7. import torch.optim as optim
  8. import pdb
  9. batch_size = 2 # mini-batch size
  10. embedding_size = 2 # embedding size
  11. ##随机选取 训练数据 从skip_gram中选取
  12. def random_batch():
  13. random_inputs = []
  14. random_labels = []
  15. random_index = np.random.choice(range(len(skip_gram)),batch_size,replace=False) ## 选取 len(skip_gram) 范围内 batch_size长度的数组
  16. for i in random_index:
  17. random_inputs.append(np.eye(voc_size)[skip_gram[i][0]]) ## skip_gram[i][0] 表示的是获取 中心词 target
  18. ### np.eye(voc_size) 表示的是 生成one-hot的向量 大小是 voc_size * voc_size ,word2vec的数据输入是one-hot的形式
  19. random_labels.append(skip_gram[i][1])
  20. return random_inputs, random_labels
  21. class word2vec(nn.Module):
  22. def __init__(self) -> None:
  23. super().__init__()
  24. self.W = nn.Linear(voc_size,embedding_size,bias=False) ## word-embedding的 初始化
  25. self.WT = nn.Linear(embedding_size,voc_size,bias=False) ## fc 输出
  26. def forward(self,X):
  27. ### X [batch_size , voc_size] one-hot 编码
  28. hidden_layers = self.W(X)
  29. out_layers = self.WT(hidden_layers)
  30. return out_layers
  31. if __name__ == '__main__':
  32. ### 训练数据
  33. sentences = ["apple banana fruit", "banana orange fruit", "orange banana fruit",
  34. "dog cat animal", "cat monkey animal", "monkey dog animal"]
  35. word_sequence = " ".join(sentences).split()
  36. word_list = " ".join(sentences).split()
  37. word_list = list(set(word_list)) ###所有的字典大小,去重统计
  38. word_dict = {w:i for i, w in enumerate(word_list)}
  39. voc_size = len(word_dict)
  40. ### create skip_gram data and window size = 1 , skip_window = 1, num_skips = 2
  41. ## skip_window的参数,它代表着我们从当前input word的一侧(左边或右边)选取词的数量
  42. # 另一个参数 num_skips ,表示从 window中选取多少个不同的 词 作为训练数据
  43. skip_gram = []
  44. for i in range(1,len(word_sequence) -1):
  45. target = word_dict[word_sequence[i]] ## 当前的 word
  46. context = [word_dict[word_sequence[i-1]],word_dict[word_sequence[i+1]]] ##获取前后的 words
  47. for w in context:
  48. skip_gram.append([target,w])
  49. model = word2vec()
  50. loss_fuc = nn.CrossEntropyLoss() ## 交叉熵 损失
  51. optimizer = optim.Adam(model.parameters(), lr=0.001) ## 定义优化器
  52. for epoch in range(50000):
  53. input_batch, target_batch = random_batch()
  54. input_batch = torch.Tensor(input_batch)
  55. target_batch = torch.LongTensor(target_batch)
  56. # pdb.set_trace()
  57. ## input_batch shape = [2,8] target_batch shape = [2]
  58. optimizer.zero_grad() ## 优化器初始化 梯度值设置为0
  59. output = model(input_batch) ## output : [batch_size, voc_size]
  60. loss = loss_fuc(output,target_batch)
  61. if(epoch + 1) % 1000 == 0:
  62. print(' Epoch:','%04d'%(epoch + 1), 'loss = ','{:.6f}'.format(loss))
  63. loss.backward() ## 反向传播
  64. optimizer.step() ## 每一步更新参数

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/607220
推荐阅读
相关标签
  

闽ICP备14008679号