Word2Vec实现原理介绍
word2vec是一种将word(词)转为vector(向量)的方法,其有两种方法,分别是skip-gram和CBOW,它们的最大区别是skip-gram是通过中心词去预测中心词周围的词,而CBOW是通过周围的词去预测中心词。
本处详细介绍CBOW方法,该方法更利于刚学习的人或者刚了解神经网络的人理解,Skip-gram方法在理解CBOW后原理方法大同小异。
为什么需要word2vec?
在很多NLP或者其他任务中,我们往往需要从文本词语中获得信息,例如句子情感预测(该例子可能举的不是很恰当),我们需要从句子中的词来判断这个句子究竟是积极的还是消极的,那么如果我们使用bp神经网络来进行预测,那么我们首要面临的问题就是如何将句子或者句子中的词输入到bp神经网络中呢?
我们知道神经网络中运行计算的都是数字,而词语、句子并不是数字,那么如何将词语向数字转换呢?
那么你可能脱口而出一种方法,one-hot独热编码,利用该方法给每个词语一个唯一的表达方式,该方法是一个非常优秀的方法,但是在很多任务中,它有一些问题存在,比如向量维度很大,如果在数据集中含有不同的V个词,那么每一个词的one-hot表示是一个1xV的向量,当M很大的时候,就会出现每个词的向量维度会很大,V维。再比如,我们在进行计算时,由于one-hot编码向量中只有一个位置为1,其他位置为0,在任务中就会表现得非常稀疏。
鉴于以上列举的一些问题,那么我们会想,能不能将表示词的向量维度降低呢?能不能不稀疏呢?word2vec应运而生。
怎么实现word2vec
首先明确的是,word2vec采用神经网络实现。skip-gram和CBOW,它们的最大区别是skip-gram是通过中心词去预测中心词周围的词,而CBOW是通过周围的词去预测中心词。
本处详细介绍CBOW方法,该方法更利于刚学习的人或者刚了解神经网络的人理解,Skip-gram方法在理解CBOW后原理方法大同小异。
CBOW
word2vec的实现依赖于one-hot方法,需要首先将数据集中的每一个词进行one-hot表示。
其网络原理图如图所示:
图1:原理图
如果上图看的比较迷糊,那么简化一下,如下提所示:
图2:简化原理图
在上图2中,W即为图1中的大小为VxN的W,假设我们存在想要获取词向量,对语料库执行上图网络,进行训练即可。
例如语料库中存在一个句子,I come from a beautiful country,训练,假设from是待预测词w(t),那么w(t-2)就是I,w(t-1)就是come,t+1和t+2同理,
图中V表示one-hot编码词向量维度,也是语料库中单词个数。N表示你想要将每个单词表示成的维度,N是我们输入的数,例如我们想用100维的向量表示词,那么N就是100。W表示一个矩阵,大小为VxN。
上述网络训练如下:
1、输入待预测词w(t)周围的词的one-hot编码,每个编码是大小是1xV的向量。
2、周围词的one-hot编码与矩阵W相乘,获得中间向量,大小是(1xV)x(VxN)=1xN,周围词的中间向量相加取平均,作为隐藏层向量。
3、隐藏层与矩阵W'(W撇)相乘,相乘结果经过softmax获得w(t)的向量。
4、前向传播获得的w(t)的向量与w(t)的one-hot编码通过交叉熵(其他的也行)获得误差,然后进行反向传播。
5、重复上述过程,知道网络达到我们的要求,及网络训练结束,获得网络的权重矩阵,即为图中大小为VxN的矩阵W。
网络训练完成后,我们如何获得词的向量表示呢?
可以直接让该词的one-hot向量与W相乘,相乘结果即为该词向量表示。
实际上,矩阵W就是语料库中所有词的向量表示,而第几列即为对应语料库中不重复词第几个词的向量表示。
因为词的one-hot向量与W相乘,one-hot编码只有一个位置为1,其他为0,0乘以任何数还是0,所以实际上只有1起作用,1的位置对应W中的位置,即为向量表示。
如果还不理解,见下图:
假设一个词的one-hot编码是(0 0 0 1 0)。
W为图中间的大矩阵。
one-hot编码与矩阵W相乘,获得(10 12 9)
因为one-hot编码第4位置为1,其他位置0,所以相乘只是去除了W的第四行而已。
怎么使用word2vec
现有的库以一些中,对word2vec方法做了实现和优化,此处推荐使用python的gensim库。
使用一些方法如下:
from gensim.models import word2vec
// 直接用gemsim提供的API去读取txt文件,读取文件的API有LineSentence 和 Text8Corpus, PathLineSentences等。
sentences = word2vec.LineSentence("data.txt")
// 模型的训练
model = gensim.models.Word2Vec(sentences, size=200, sg=1, iter=8)
// 或:
model= Word2Vec()
model.build_vocab(sentences)
model.train(sentences,total_examples = model.corpus_count,epochs = model.iter)
// 模型的保存
model.save("word2vec.model") //保存可以在读取后追加训练
model.wv.save_word2vec_format("./word2Vec" + ".bin", binary=True) // 保存不能追加训练
model.wv.save_word2vec_format("./word2Vec" + ".txt", binary=False) // 保存不能追加训练
//模型的加载
model = Word2Vec.load("word2vec.model")
wordVec = gensim.models.load_word2vec_format("word2Vec.bin", binary=True)
wordVec = gensim.models.load_word2vec_format("word2Vec.txt", binary=False)
// 最省内存的加载方法
model = gensim.models.Word2Vec.load("word2vec.model")
word_vectors = model.wv
del model
word_vectors.init_sims(replace=True)
// 引入KeyedVectors 保存和加载bin,txt模型
wordVec = gensim.models.KeyedVectors.load_word2vec_format("word2Vec.bin", binary=True) // 载入 .bin文件
wordVec = gensim.models.KeyedVectors.load_word2vec_format("word2Vec.bin.gz", binary=True) // 载入 .bin文件
wordVec = gensim.models.KeyedVectors.load_word2vec_format("word2Vec.txt", binary=False) // 载入 .txt文件
//增量训练
model = gensim.models.Word2Vec.load("word2vec.model")
model.train(more_sentences)
// Word2Vec应用
model.wv['man'] // 获取词向量
model.wv.similarity('first','is') // 两个词的相似性距离
model.wv.doesnt_match("input is lunch he sentence cat".split()) // 找出不同类的词即不匹配的词语
model.wv.similar_by_word('沙瑞金'.decode('utf-8'), topn =100) // 找出某一个词向量最相近的词集合
model.wv.most_similar(['man']) //计算一个词的最近似的词,倒排序
for i in model.wv.most_similar(u"戏剧"):
print (i[0],i[1]) // 词,分数
model.wv.most_similar(positive=['first', 'second'], negative=['sentence'], topn=1) // 计算两词之间的余弦相似度
model.wv.n_similarity(list1,list2) //计算两个集合之间的余弦似度
skip=gram
与CBOW相比,区别是skip-gram是通过中心词去预测中心词周围的词,即CBOW模型的输入和输出对调。
原理类似,此处不做介绍
在自然语言处理应用中,一般使用skip-gram模型的中心词向量作为词的表征向量。
可观看论文了解
词向量词嵌入来源:《A Neural probabilistic language models 》http://www.iro.umontreal.ca/~vincentp/Publications/lm_jmlr.pdf
word2vec原始论文《Efficient Estimation of Word Representations in Vector Space》https://arxiv.org/abs/1301.3781v3