4.1 SkipGram
(1)基本概念
Skip-gram模型从target word对context的预测中学习到word vector,该名称源于该模型在训练时会对上下文环境里的word进行采样。
按照上述思路产生的样本如下:
针对上述模型,将预测相邻单词这一任务,转换为判断两个单词是否为相邻的单词的问题(0表示“不是邻居”,1表示“邻居”):
此时模型如下:
将模型从神经网络改为逻辑回归模型——更简单,计算速度更快。但是此时所有单词都是相邻单词邻居(target=1),得到的训练模型可能永远返回1。
为了解决该问题,需要在数据集中引入负样本,即非相邻单词样本(target=0):
这个想法的灵感来自噪声对比估计,将实际信号(相邻单词的正例)与噪声(随机选择的不是邻居的单词)进行对比,导致了计算和统计效率的巨大折衷。
word2vec训练过程中的两个关键超参数是窗口大小和负样本的数量,不同的任务适合不同的窗口大小。
(2)数据模型
SkipGram关注是给定中心词w生成背景词c cc的条件概率p ( c ∣ w ) 假设给定中心词的情况下背景词的生成相互独立,则
在跳字模型中,每个词被表示成两个d维向量,用来计算条件概率,词典索引集
V={0,1,…,∣V∣−1}。
假设这个词在词典中索引为i ,当它为中心词时向量表示为v i ∈ R d ,而为背景词时向量表示为u i ∈ R d 。 设中心词w 在词典中索引为w ,背景词c 在词典中索引为c ,给定中心词生成背景词的条件概率可以通过对向量内积做softmax运算而得到:
因此,Skip-gram模型的本质是计算输入word的input vector v w 与目标word的output vector u c 之间的余弦相似度,并进行softmax归一化,要学习的模型参数θ 正是这两类词向量。
此时
为了计算最大值,对其求导
它的计算需要词典中所有词以w 为中心词的条件概率,其他词向量的梯度同理可得。训练结束后,对于词典中的任一索引为i 的词,均得到该词作为中心词和背景词的两组词向量v i 和u i,一般使用跳字模型的中心词向量作为词的表征向量。
上述计算过程极其耗时,为此Mikolov引入了两种优化算法:层次Softmax(Hierarchical Softmax)和负采样(Negative Sampling),核心思想都是降低计算量。
4.2 CBoW
CBoW模型从context对target word的预测中学习到词向量的表达,等价于一个词袋模型的向量乘以一个Embedding矩阵,从而得到一个连续的embedding向量。这也是CBoW模型名称的由来。
如:
使用目标词的前两个词(“by”,“a”)与后两个词(“bus”,“in”),来预测目标词(“red”):
4.3 Negative Sampling
令P ( D = 1 ∣ w , c )表示中心词w 与背景词c同时出现(正例)的概率,P ( D = 0 ∣ w , w k )表示中心词w ww与背景词w k 不同时出现(负例)的概率,
则
此时,
需要最大化p ( c ∣ w ) ,由于上式的计算复杂度由K 个负采样决定,计算量大大降低。
4.4 Hierarchical Softmax
为了避免要计算所有词的softmax概率,word2vec使用Huffman树来代替从隐藏层到输出softmax层的映射,softmax概率计算只需要沿着树形结构进行。
如图,沿着霍夫曼树从根节点一直走到叶子节点的词w2。
Huffman树的所有内部节点类似神经网络隐藏层的神经元,根节点的词向量对应投影后的词向量,而所有叶子节点就类似于之前神经网络softmax输出层的神经元,叶子节点的个数就是词汇表的大小。Huffman树中,隐藏层到输出层的softmax映射不是一下子完成的,而是沿着霍夫曼树一步步完成,因此这种softmax名为"Hierarchical Softmax"。
word2vec中采用了二元逻辑回归的方法,即规定沿着左子树走,那么就是负类(Huffman树编码1),沿着右子树走,那么就是正类(Huffman树编码0),使用sigmoid函数判别正类和负类。
使用Hierarchical Softmax后,由于Huffman树是二叉树,计算量为由V 变成了log2V;此外,由于Huffman树是高频的词靠近树根,这样高频词被找到花费时间更短。
5. 使用gensim
gensim是一个很好用的Python NLP的包,封装了google的C语言版的word2vec,不只可以用于word2vec,还有很多其他的API可以用。可以使用
pip install gensim安装。
在gensim中,word2vec 相关的API都在包gensim.models.word2vec中,和算法有关的参数都在类gensim.models.word2vec.Word2Vec中,主要参数如下:
sentences
: 要分析的语料,可以是一个列表,或者从文件中遍历读出。size
: 词向量的维度,默认值是100。这个维度的取值一般与语料的大小相关,如果是不大的语料,比如小于100M的文本语料,则使用默认值一般就可以了。如果是超大的语料,建议增大维度。- window:即词向量上下文最大距离,window越大,则和某一词较远的词也会产生上下文关系。默认值为5。在实际使用中,可以根据实际的需求来动态调整这个window的大小。如果是小语料则这个值可以设的更小。对于一般的语料这个值推荐在[5,10]之间。
- sg: word2vec两个模型的选择:如果是0(默认), 则是CBOW模型;是1则是Skip-Gram模型。
- hs: word2vec两个解法的选择:如果是0(默认), 则是Negative Sampling;是1的话并且负采样个数negative大于0, 则是Hierarchical Softmax。
- negative:即使用Negative Sampling时负采样的个数,默认是5。推荐在[3,10]之间。这个参数在我们的算法原理篇中标记为neg。
- cbow_mean: 仅用于CBOW在做投影的时候,为0,则算法中的xw为上下文的词向量之和,为1则为上下文的词向量的平均值。
- min_count:需要计算词向量的最小词频。这个值可以去掉一些很生僻的低频词,默认是5。如果是小语料,可以调低这个值。
- iter: 随机梯度下降法中迭代的最大次数,默认是5。对于大语料,可以增大这个值。
- alpha: 在随机梯度下降法中迭代的初始步长。算法原理篇中标记为η,默认是0.025。
- min_alpha: 由于算法支持在迭代的过程中逐渐减小步长,min_alpha给出了最小的迭代步长值。随机梯度下降中每轮的迭代步长可以由iter,alpha, min_alpha一起得出。
以《人民的名义》为语料,分析主要人物的特征:
- 通过词云可以发现,主要有侯亮平、李达康、高育良、祁同伟四位主角
- 使用model.wv.similarity方法计算相互之间相似度,发现侯亮平、李达康、祁同伟之间相似度较高
- 使用wv.doesnt_match比较沙瑞金与四位主角相似度,发现沙瑞金更加不同于四位主角
%matplotlib inline import jieba import jieba.analyse filePath = r'data/in_the_name_of_people.txt' stopPath = r'data/stopword_cn.txt' with open(filePath,encoding = 'utf-8') as file: mytext = file.read() #读取到string with open(stopPath,encoding = 'utf-8') as file: word_list = file.read().split() #返回一个字符串,都是小写 person = ['沙瑞金','田国富','高育良','侯亮平','祁同伟', '陈海','钟小艾', '陈岩石','欧阳菁','易学习','程度', '王大路','蔡成功','孙连城','季昌明', '丁义珍', '郑西坡','赵东来', '高小琴','赵瑞龙','田国富', '陆亦可', '刘新建', '刘庆祝','李达康'] for p in person: jieba.suggest_freq(p, True) result = " ".join(jieba.cut(mytext)) #string类型 from wordcloud import WordCloud import matplotlib.pyplot as plt wordcloud = WordCloud(font_path = "simsun.ttf",stopwords=word_list,max_words=30).generate(result) plt.imshow(wordcloud, interpolation='bilinear') plt.axis("off")
输出如下:
from gensim.models import word2vec segPath = r'data/in_the_name_of_people_segment.txt' with open(segPath, 'w',encoding = 'utf-8') as f: f.write(result) sentences = word2vec.LineSentence(segPath) model = word2vec.Word2Vec(sentences, hs=1,min_count=1,window=3,size=100) print(model.wv.similarity('侯亮平', '高育良')) #相似度0.93316543 print(model.wv.similarity('侯亮平', '祁同伟')) #相似度0.9705674 print(model.wv.similarity('侯亮平', '李达康')) #相似度0.96616036 print(model.wv.similarity('李达康', '高育良')) #相似度0.91774 print(model.wv.similarity('李达康', '祁同伟')) #相似度0.9734558 print(model.wv.similarity('高育良', '祁同伟')) #相似度0.9349903 print(model.wv.doesnt_match(u"沙瑞金 侯亮平 高育良 李达康 祁同伟".split()))
输出如下:
0.93316543
0.9705674
0.96616036
0.91774
0.9734558
0.9349903
沙瑞金