目录
面向向量的推理
使用词向量的更多原因
如何计算Word2vec表示
skip-gram方法
什么是softmax
神经网络如何学习向量表示
用线性代数检索词向量
连续词袋方法
skip-gram和CBOW:什么时候用哪种方法
word2vec计算技巧
高频2-gram
高频词条降采样
负采样
如何使用gensim.word2vec模块
生成定制化词向量表示
预处理阶段
面向特定领域的word2vec模型
word2vec和GloVe
fastText
如何使用预训练fastText模型
Word2vec和LSA
词关系可视化
非自然词
利用Doc2vec计算文档相似度
如何训练文档向量
Mikolov发现了一种用一定维度的向量表示词的含义的方法,他训练了一个神经网络来预测每个目标词附近的共现词,之后,其在谷歌发布了创建这些词向量的软件,称为Word2vec。
Word2vec仅仅基于大型未标记文本语料库来学习词的含义,而不需要标记Word2vec词汇表中的词。我们不需要告诉西雅图是一个城市,也不需要告诉它足球是一项运动,Word2vec完全可以靠自己学到更多的知识。用户需要做的只是准备一个足够大的语料库。
Word2vec这种无监督的特性使它无比强大,因为现实世界中充满了未标记、未分类、非结构化的自然语言文本。
监督学习:
监督学习中,必须对训练数据进行某种标注;监督学习只有在能够度量预测输出(标签)与其预测值之间的差异时,模型才能变得更好。
相反,无监督学习使机器能够直接从数据中学习,而不需要人类的任何帮助。训练数据不要人工组织、结构化、标注。所以像Word2vec这样的无监督学习算法对自然语言文本来说非常完美。
无监督学习:
在无监督学习中,同样也是训练模型去执行某种人物,但没有任何任务标注,只有原始数据。聚类算法(比如k均值或DBSCAN)就属于无监督学习,像PCA、t-SNE这样的降维算法也属于无监督机器学习技术。在无监督学习中,模型根据数据点自身的关系中去发现模型。通过向无监督模型投入更多的数据,它可以变得更智能更精确。
教神经网络预测句子中目标词附近的词,而不是带有词含义的标签来直接学习目标词的含义。在这个意义上,也可以算是有标注:待预测的相邻词。不过这些标注来自数据集本身,不需要手工标注,因此Word2vec训练算法确实是一个无监督学习算法。
使用无监督训练技术的另一个领域是时间序列建模。时间序列建模通常被训练在一个序列中基于前一个窗口的值预测下一个值。时间序列问题与自然语言问题在很多方面非常相似,它们处理的都是有序值(词或数值)。
预测本身并不是Word2vec的关键,预测只是达到目的的一种手段。大家真正关心的是它的内部表示,即Word2vec在生成这些预测过程中逐渐构建的向量。与隐性语义分析和隐性狄利克雷分布中的词-主题向量不同,Word2vec词向量表示能够捕捉更丰富的目标词含义(语义)。
Word2vec将学习到一些人们可能认为的与所有词本身并不相关的东西,例如每个词都有一定的地理位置、情感和性别倾向性,如果语料库中有任何一个词具有某种属性,如“placeness”(平和)、“peopleness”(有人情味)等,那么其他所有的词也会在词向量的这些属性上得分。当Word2vec学习词向量时,可以认为某些词的意义“感染”了其相邻词。
语料库中的所有词都将由数字向量表示,类似于词-主题向量,只是这次主题具有更具体、更准确的含义。在LSA中,词只需要在相同文档出现,它们的含义就会相互“感染”,并融入词的主题向量中。对于Word2vec词向量,这些词必须彼此相邻——通常在同一个句子中的间隔不超过5个词。而且Word2vec词向量的主题权重可以通过加减运算来创建新的有意义的词向量。
下面,把词向量看作是一个权重或分数的列表,列表中的每个权重或分数都对应于这个词在某个特定维度的含义:
from nlpia.book.examples.ch06_nessvectors import *print(nessvector('Marie_Curie').round(2))
我们还可以使用nlpia提供的工具为Word2vec词汇表中的任何词或n-gram计算属性向量(nessvector),这种方法适用于我们创建的所有属性。
Mikolov在思考如何用数字向量表示词的过程中开发了Word2vec算法,而且不满足于不太精确的词“情感”计算。他想做的是面向向量的推理,就像前面的类比问题。这个概念意味着可以用词向量做数学运算,再把得到的结果向量转换成词,这样就能得到有意义的答案。我们可以对词向量做加减法来对它们所表示的词进行推理,从而回答一些问题,比如:
mv['Timbers']-mv['Portland']+mv['Seattle']=?
在理想情况下,希望这个数学运算(词向量推理)能返回这个结果:
mv['Seattle_Sounders']
LSA中基于整篇文档构建的主题向量非常适合文档分类、语义搜索和聚类。但是LSA生成的主题-词向量不够精确,不能用于短语或复合词的语义推理、分类和聚类。之后,可以对LSA词向量表示法进行改进。
面向向量的推理
在某个阶段,Word2vec词嵌入的精确率是LSA模型精确率的4倍(45%对11%)。
有了词向量之后,类似于 Portland Timbers + Seattle - Portland = ? 的问题就可以用向量代数来进行解答:
Word2vec模型包含词之间的关系信息,包括词的相似性。Word2vec模型“知道”术语Portland(波特兰)和Portland Timbers(波特兰伐木者队)之间的距离与Seattle(西雅图)和Seattle Sounders(西雅图海湾人队)之间的距离大致相同,并且这些距离的方向(向量对之间的差异)大致相同。所以Word2vec模型可以用来回答球队的类比问题。将Portland和Seattle两个向量之间的差值加到表示Portland Timbers的向量上,这样得到的结果应该接近术语Seattle Sounders的向量:
在进行词向量的加减法运算后,得到的向量一般不会正好等于词向量表中的某个向量。不过,Word2vec词向量通常由100维,每个维度上都有连续的实值,词向量表中与运算结果最接近的向量通常就是NLP问题的答案。与这个相邻向量相关联正式上述问题的自然语言答案。
Word2vec可以将表示词条的出现次数与频率的自然语言向量转换为更低维的Word2vec向量空间。在这个低维空间中,我们可以进行数学运算,并将结果转换回自然语言空间。这个功能在聊天机器人、搜索引擎、问答系统或信息提取算法中将发挥很大作用。
在英文等语言中,单词的单数和复数之间的差异在大小和方向上基本相同:
另外,距离关系远远超过简单的单复数关系。距离适用于其他语义关系。
使用词向量的更多原因
词向量表示法不但对推理和类比关系问题有用,而且对其他所有使用自然语言向量空间模型处理的问题都有用,词向量对NLP流水线的精确性和实用性的提高都有帮助,包括模式匹配、建模和可视化。
词向量对聊天机器人和搜索引擎也很有用。在这些应用场景中,词向量有助于克服模式匹配或关键词匹配的刻板性和脆弱性。
如何计算Word2vec表示
词向量将词的语义表示为训练语料库中上下文中的向量。这不仅能回答类比问题,还能让人们用更为通用的向量代数去推理词的含义。训练Word2vec嵌入有两种方法:
- skip-gram方法,基于目标词(输入词)预测上下文(输出词);
- 连续词袋(CBOW)方法,基于临近词(输入词)预测目标词(输出词)。
词向量表示的计算是资源密集型的。不过,对于大多数应用程序,不需要计算自己的词向量,职级使用预训练好的模型即可。
但是,对于依赖专业词汇表或语义关系的领域,通用的词向量模型就不够了。例如,如果希望“python”明确表示编程语言而不是动物,就需要一个特定领域的词向量模型。如果需要将词向量的使用限定在特定领域中,就要用这个特定领域的文本数据来进行训练。
下面是训练Word2vec模型的一些方法:
skip-gram方法
在skip-gram训练方法中,需要预测输入词周围窗口的词。在下面这个关于Monet的句子的例子中,“painted”是神经网络的训练输入,对应的预测词输出是其相邻词“Claude”、“Monet”、“the”、“Grand”,skip-gram相应的训练输出如图:
用来预测周围词的神经网络结构与网络结构相似。
如上图所示,网络由两层权重组成,隐藏层由n个神经元组成,其中n表示词的向量维数。输入层和输出层都包含M个神经元,其中M是模型的词汇表中的词的总数。输出层激活函数是分类问题中常用的softmax函数。
什么是softmax
当神经网络的目标是学习分类问题时,经常使用softmax函数作为神经网络输出层的激活函数。softmax可以将输出结果压缩为0到1之间的值,所有输出的和加起来等于1。这样,softmax函数的输出层结果就可以当做概率。
对于K个输出节点,softmax输出值通过归一化指数函数计算如下:
假设一个包含3个神经元的输出层的输出向量为:
则江歌softmax激活函数压缩后的结果向量降为:
注意,这些值的和约等于1,类似于概率分布。
神经网络如何学习向量表示
使用分词来训练word2vec模型。例如,在下表中,表示在位置t处的词条独热向量,如果使用skip-gram窗口大小为2(半径)来训练word2vec模型,则需要考虑每个目标词前后的两个词。这可以使用5-gram分词器来将下面的句子转换为10个以输入词为中心的5-gram,原句子中的每个词作为一个输入词都对应一个5-gram。
Cloude Monet painted the Grand Canal of Venice in 1806
输入词 | 期望输出 | 期望输出 | 期望输出 | 期望输出 |
Clanud | Monet | painted | ||
Monet | Claud | painted | the | |
painted | Claud | Monet | the | Grand |
the | Monet | painted | Grand | Claud |
Grand | painted | the | Claud | of |
Canal | the | Grand | of | Venice |
of | Grand | Claud | Venice | in |
Venice | Claud | of | in | 1908 |
in | of | Venice | 1908 | |
1908 | Venice | in |
由输入词和周围词(输出词)组成的训练集构成了这个神经网络训练的基础数据集。在周围词数量为4的情况下,将使用4次迭代训练网络,每次迭代都是基于输入词预测其中一个输出词。
每个词在进入网络前被表示为一个独热处理。神经网络做词嵌入的输出向量也类似于一个独热向量。通过输出层节点(输出层上每个节点对应词汇表中的一个词条)的softmax激活函数来计算输出词是输入词的周围词的概率。然后将最大概率的词转换为1,其余所有词转换为0,从而将输出词的概率向量转换为一个独热向量,这样处理可以减小损失函数的计算复杂度。
当完成神经网络训练后,经过训练后的网络权重可以用来表示语义。经过词条独热向量的转换,权重矩阵中的一行表示语料库词汇表中的一个词。语义相似的词被训练为预测相似的周围词,因此经过训练之后也会有相似的向量。
词向量模型训练结束后便不再进行额外的训练,因此可以忽略网络的输出层,只用隐藏层的输入权重来作为词嵌入表示。换句话说,这个权重矩阵就是大家所需要的词嵌入。输入词项的独热向量表示与权重的点积代表向量嵌入。
用线性代数检索词向量
神经网络中隐藏层的权重通常表示为矩阵:每列表示一个输入层神经元,每行表示一个输出层神经元。这样就能通过将权重矩阵与上一层输入的列向量相乘,来生成指向下一层的输出列向量,如下图。因此,如果我们将一个独热行向量与训练好的权重矩阵相乘(点积运算),将从每个神经元(从每个矩阵列)得到一个权重。同样,也可以用权重矩阵与词的独热向量相乘。
事实上,独热向量的点积运算只是从权重矩阵中选出包含这个词权重的那一行,来作为该词的词向量,只使用词汇表中的行号或索引号进行检索,同样也可以轻松地从权重矩阵中得到该行。
连续词袋方法
在连续词袋(CBOW)方法中,将根据周围词曲预测中心词。在这里不用创建输入和输出词条标记对,而可以创建一个多热向量作为输入向量。多热向量是围绕中心词的所有周围词的独热向量的和。
下表是CBOW方法下的Monet句子中的10个5-gram
期望输出 | 期望输出 | 期望输出 | 期望输出 | 输入词 |
Monet | painted | Claud | ||
Claud | painted | the | Monet | |
Claud | Monet | the | Grand | painted |
Monet | painted | Grand | Claud | the |
painted | the | Claud | of | Grand |
the | Grand | of | Venice | Canal |
Grand | Claud | Venice | in | of |
Claud | of | in | 1908 | Venice |
of | Venice | 1908 | in | |
Venice | in | 1908 |
我们可以在训练集的基础上创建多热向量作为输入,并将其映射到目标词上作为输出。多热向量是周围词对的独热向量之和。以多热向量作为输入,目标词作为输出,以构建训练样本对。在训练过程中,由输出层softmax导出概率最大的节点作为输出。
skip-gram和CBOW:什么时候用哪种方法
skip-gram方法对于小型语料库和一些罕见的词项比较适用。在skip-gram方法中,由于网络结构的原因,将会产生更多的训练样本。但CBOW方法在常用词上有更高的精确性,并且训练速度快很多。
word2vec计算技巧
下面是一些改进word2vec模型性能的方案:
高频2-gram
有些词经常与其他词组合出现,例如英文名字,从而构成了一个2-gram,然后这种预测并没有什么价值,为了提高word2vec嵌入的精确率,word2vec词汇表中加入了一些2-gram和3-gram作为词项。它们使用共现频率;来区分应该被认为是单个词项的2-gram、3-gram,如下公式:
如果和经计算得到的分数高于阈值, 则这两个词应当作为词项对被包含在word2vec词汇表中。word2vec会将频繁出现的2-gram的两次用一个字符(常用下划线“_”)连接起来,这样处理之后,这些2-gram就可以表示为单个独热向量,而不再是两个单独的向量。
词对的另一个影响是组合词通常与起其中的单个词表达的意思完全不一样。通过在word2vec模型中添加经常出现的2-gram可以很容易地将这些词对用独热向量进行表示,从而便于进行模型训练。
高频词条降采样
另一个改进原算法性能的方法是高频词条降采样,像“the”、“a”这样的常用词通常不包含重要信息,语料库中的“the”与很多名词都共现,因此并不会带来更多的含义,反而给word2vec语义相似性表示带来一定的混淆。
为了减少像停用词这样的高频词的影响,可以在训练过程中对词进行预期出现频率成反比的采样。其效果类似于IDF对TF-IDF向量的影响。相比于罕见词,高频词被赋以对向量更小的影响了。用下面的公式来确定给定次的采样频率,这个概率决定了在训练过程中是否将该词包含在skip-gram中:
在上式中,表示一个词在语料库中的出现频率,t表示频率阈值,超出这个阈值的才会进行降采样。阈值取决于语料库规模、平均文档长度和文档中词的多样性。文献中通常使用-之间的值。
如果一个词在整个语料库中出现了10次,而语料库中有100万个不同的词,将降采样阈值设置为,那么在分词期间构建n-gram的过程中,这个词留在某个n-gram中的概率是68%,剩下32%的概率会跳过它。
在回答类比问题等任务重,使用降采样可以提高词向量的精确率。
负采样
当一个训练样本(一对词)输入网络后,会引起网络中所有权重的更新,这样会改变词汇表中所有词的词向量。如果词汇表规模达到十亿级,为一个大型的独热向量更新所有权重将会变得机器低效。为了加快词向量模型的训练速度,可以采用负采样的方法。
只在输出向量中选取少量的负样本进行权重更新,而不去更新词窗口以外所有其他词的权重。选取n个负样本词对(目标输出词之外的词),根据其对输出的贡献来更新对应的权重。通过这种方法,可以极大地减小计算量,而且对训练网络性能不会有明显影响。
如何使用gensim.word2vec模块
很多公司都提供了预训练好的词向量模块,而且有很多针对各种编程语言的NLP库,可以方便的使用这些预训练模型。
用下面的命令来下载一个预训练模型:
from nlpia.data.loaders import get_dataword_vectors=get_data('word2vec')
如果命令不起作用,可以下载谷歌的原始二进制格式的模型后,将其放在本地路径中,然后用gensim包来进行加载:
word_vectors=KeyedVectors.load_word2vec_format('path/GoogleNews-vectors-negative300.bin.gz',binary=True,limit=20000)
limit参数是为了节省内存限制了加载词的个数。但是要注意,如果文档中包含了未加载词向量的那些词,那么这个只有有限词汇量的词向量模型将影响后续NLP流水线处理的效果。因此,最好只在开发阶段限制词向量模型的规模。
gensim.KeyedVectors.most_similar()方法提供了对于给定词向量,查找最近的相邻词的有效方法。关键词参数positive接受一个待求和的向量列表。同样,也可以用negative参数来做减法,以排除不相关的词项。参数topn用于指定返回结果中相关词项的数量。
与传统的同义词词典不同,word2vec的同义度(相似度)是连续值,代表向量距离,这是因为word2vec本身是一个连续的向量空间模型。word2vec的高维和每个维度的连续值特性使其能够捕捉到给定词的全部含义。这就是它能用于做类比、连接、多义并排的原因:
from gensim.models.keyedvectors import KeyedVectorsword_vectors=KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin',binary=True,limit=200000)
print(word_vectors.most_similar(positive=['cooking','potatoes'],topn=5))
词向量模型也可以用来检测不相关的词项。gensim库提供了一个名为doesnt_match的方法:
print(word_vectors.doesnt_match("potatoes milk cake computer".split()))
为了检测列表中最不相关的词项,该方法返回列表中与其他词项距离最远的词项。
如果想要完成计算,可以通过在调用方法most_similar中添加一个negative参数来实现:
print(word_vectors.most_similar(positive=['king','women'],negative=['man'],topn=2))
gensim库支持两个词项之间的相似度计算,如果要比较两个词并确定他们的余弦相似度,可以使用.similarity()方法:
print(word_vectors.similarity('princess','queen'))
如果要开发自己的函数并使用原始的词向量,那么可以在KEYedVector实例上通过Python的方括号语法或get()方法来实现,这样将加载的模型对象视为一个字典,而且标词是字典中的一个键,返回结果是一个数组,数组中的每个浮点数表示向量的一个维度。在刚刚的词向量模型中,返回的numpy数组的形状为1*300:
print(word_vectors['phone'])
如果要想知道所有这些数值的含义,也是可以做到的,需要去检查一些同义词,看看它们在这300个数值中有哪些是重复的。
生成定制化词向量表示
在某些情况下,需要创建面向特定领域的词向量模型。需要注意的是,定制化词向量需要大量的文档,另外,如果某些词很罕见,或者在特定领域有一些特定用法,比如医学、成绩单等,那么面向特定领域的词向量模型也可以提高模型精确率。
为了训练特定领域的word2vec模型,需要再次用到gensim库,另外,在开始训练模型之前,还需要对语料库进行预处理。
预处理阶段
首先,要把文档拆分成句子,然后将句子拆分为词条。gensim.word2vec模型接收的输入是一个句子列表,其中每个句子都已经切分为词条,这样可以确保词向量模型不会学习到相邻句子中出现的无关词,训练输入应该类似于以下结构:
一旦将文档转化为词条列表的列表(每个列表对应一个句子),接下来就可以进行word2vec的训练了。
面向特定领域的word2vec模型
首先加载word2vec模块:
from gensim.models.word2vec import Word2Vec
需要对训练过程进行一些设置:
#向量元素的数量表示词向量
num_feature=300
#word2vec模型中词的最低词频,如果语料库较小,可以将最低词频设置的较小
min_word_count=3
#训练使用的CPU核数
num_workers=2
#上下文窗口大小
window_size=6
#高频词条降采样率
subsampling=1e-3
现在开始训练:
model=Word2Vec(token_list,workers=num_workers,vector_size=num_feature,min_count=min_word_count,window=window_size,sample=subsampling
)
训练时长取决于语料库的规模和CPU性能。对于较小的语料库,几分钟就能完成训练,但是如果想要训练得到一个综合的词模型,语料库就需要包含数百万个句子,语料库中每个词的不同用法都需要有许多对应的版本。
word2vec模型消耗的内存很大,但是其中只有隐藏层的权重矩阵有意义。一旦词向量模型训练完成,则可以通过冻结模型以及丢弃不必要的信息来减少大约一半的占用内存。下面的命令可以丢弃神经网络中不需要的输出权重:
model.init_sims(replace=True)
init_sims方法将冻结模型,存储隐藏层的权重并丢弃用于预测共现词的输出权重。在大多数word2vec应用中不会用到这个输出权重。不过一旦丢弃输出层的权重,以后将无法进一步训练模型了。
可以使用以下命令保存已经训练的模型,供以后使用:
model_name="my_domin_word2vec_model"
model.save(model_name)
加载已经保存的模型并测试:
model_name="my_domin_word2vec_model"
model=Word2Vec.load(model_name)
model.wv.most_similar(positive=['to'],topn=5)
word2vec和GloVe
word2vec是一个巨大的突破,但是它依赖于必须经过反向传播来训练的神经网络模型。反向传播在效率上通常不如使用梯度下降法直接优化的代价函数。通过研究word2vec的工作原理,可以发现其中可优化的代价函数:计算词的共现次数并记录在一个正方形矩阵中,可以对这个共现矩阵进行奇异值分解,分解得到的两个权重矩阵的意义与word2vec产生的完全相同。关键点在于用同样的方法对共现矩阵进行归一化。在某些情况下,word2vec模型无法收敛,而通过SVD方法可以得到全局最优解。这个方法是对词共现的全局向量(在整个语料库中的共现)直接进行优化,因此命名为GloVe。
GloVe可以产生相当于word2vec输入权重矩阵和输出权重矩阵的矩阵,其生成的语言模型具有与word2vec相同的精确率,而且花费的时间更少。GloVe通过更高效地使用数据来加速训练进程。它可以在较小的语料库进行训练,并仍然能够收敛。因为SVd算法已经改进几十年,所以GloVe在调试和算法优化方面很有优势。相比之下,word2vec依赖反向传播来更新表示词嵌入的权重,而神经网络的反向传播效率低于GloVe使用的SVD的优化算法。
GloVe的优点如下:
- 训练进程更快;
- 更有效地利用CPU、内存(可以处理更大规模的文档);
- 更有效的利用数据(对小型语料库有帮助);
- 在相同的训练次数的情况下精确率更高。
fastText
与word2vec中预测周围词不同,fastText算法预测周围的n个字符。例如“whisper”将生成以下两字符的gram和3字符的gram:
wh,whi,hi,his,is,isp,sp,spe,pe,per,er
fastText为每个n字符的gram训练一个向量表示,其中包括词、拼错的词、词片段,甚至单个字符。这种方法比原来的Word2vec能够更好的处理罕见词。
如何使用预训练fastText模型
fastText的用法与word2vec模型一样,在fastText模型存储库下载对应语言的bin和text格式的模型。下载完成后,解压缩二进制文件,然后用以下代码把它加载到gensim中:
from gensim.models.fasttext import FastText
ft_model=FastText.load_fasttext_format(model_file=MODEL_PATH)
ft_model.wv.most_similar('soccer')
gensim提供的fastText API的功能与word2vec基本一致。
Word2vec和LSA
LSA主题-词向量为我们提供了词向量表征,LSA主题-文档向量是这些文档中所有词的主题-词向量的和。在Word2vec中,如果想要得到一个对应整篇文档的与主题-文档向量类似的词向量,需要对文档中的所有Word2vec词向量求和。这与Word2vec文档向量的原理十分接近
如果主题向量的LSA矩阵大小为N{词}*N{主题},则LSA矩阵中的行就是LSA词向量。这些行向量用200-300个实值的序列来表示词的含义,这与Word2vec类似。LSA主题-词向量对于发现相关词项和不相关词项都很有用。可以使用与LSA中原理完全相同的SVD算法来创建Word2vec向量。但是通过创建交叠文档的滑动窗口,Word2vec可以更有效的利用文档中相同数量的词。通过该方法可以对相同的词重复使用5次(即窗口大小为5)。
在增量式训练或在线式训练的方面,LSA和Word2vec算法都支持向语料库添加新文档,并根据新文档中词共现的情况来调整现有的词向量,但只有词汇表中已有的词可以得到更新。如果要在模型中添加新词,将会改变词汇表的大小,进而导致词对应的独热向量发生改变,这样的话就要重新开始训练。
LSA的训练速度比Word2vec快,而且在长文档分类和聚类方面,LSA表现更好。
Word2vec的“杀手级应用”是它推广的语义推理。LSA主题-词向量也可以做到这一点,但通常并不精确。如果想要得到接近于Word2vec推理的效果,我们必须把文档分成句子,然后只使用这些短句来训练LSA模型。通过Word2vec,我们可以得到类似于“哈利波特+大学=霍格沃兹”这种问题的答案。
LSA的优点:
- 训练速度快
- 长文本的区分度更好
Word2vec的优点:
- 对大型语料库的利用更有效
- 在回答类比问题等用词推理的领域更精确
词关系可视化
语义词之间的关系非常有用,通过可视化可以得到一些有趣的发现。
首先,从谷歌新闻语料库的Word2vec模型中加载所有词向量,这个语料库中包含很多关于波特兰、俄勒冈以及其他城市的名字,可以使用nlpia包来快速上手使用Word2vec向量:
from gensim.models.keyedvectors import KeyedVectors
import osword_vectors=KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin',binary=True,limit=200000)
print(len(word_vectors.vectors))
现在,gensim中的KeyedVectors对象拥有一个包含20万个word2vec向量的表。从word2vec模型文件中加载这些向量。该模型文件是基于谷歌新闻文章的大型语料库进行训练的,在这些新闻报道中,包含了大量关于城市的词,下面是词汇表中从第10万个词开始的几个词:
print(word_vectors.index_to_key[100000:100006])
要注意,复合词和常见的n-gram由下划线(“_”)连接在一起。另外,键值映射中的值是一个gensimVocab对象,它不仅包含了一个词对应的Word2vec向量的索引位置,还包含了该词在语料库中出现的次数。
如果要检索某个词或n-gram的300维向量,可以在这个KeyedVectors对象上使用方括号来执行 .__getitem__():
print(word_vectors['Revolutions'])
之所以选择从10万个词开始,是因为前几千个“词”都是标点符号序列,看一下任意两个词的距离:
import numpy as np
#欧几里得距离
print(np.linalg.norm(word_vectors['ramparts']-word_vectors['Revolutions']))
cos_similarity=np.dot(word_vectors['ramparts'],word_vectors['ramparts']/(np.linalg.norm(word_vectors['ramparts'])*np.linalg.norm(word_vectors['Revolutions'])))
print(cos_similarity)
#余弦距离
print(1-cos_similarity)
非自然词
像Word2vec这样的词嵌入模型不仅对英文有用,对于任何具有语义的符号序列,只要符号的序列和邻近性能表示其意义,词嵌入都能发挥作用。词嵌入也适用于英语以外的语言。
词嵌入也适用于象形语言,如传统的汉字或古墓中的象形文字。词嵌入和基于向量的推理甚至适用于一些故意混淆了词含义的语言,我们可以用词向量对大量的“秘密”消息进行基于向量的推理。人们甚至不需要解码环,只需要一个大的消息集合或n-gram集合,就可以通过Word2vec词嵌入来查找词语符号的共现情况。
Word2vec甚至可以用来行非自然的词或ID号中手机信息和关系。要获得关于这样的ID号之间关系的最有用信息,需要包含这些ID号的各种句子。如果ID号包含有意义的符号位置结构,将有助于把这些ID号切分为最小的语义包(例如自然语言中的词或音节)。
利用Doc2vec计算文档相似度
Doc2vec的概念也可以扩展到句子、段落或整个文档。根据前面的词预测下一个词的想法可以扩展到训练段落或文档的向量(如下图),这个模型不仅考虑前面的词,还考虑了段落或文档的向量表示,将其作为额外的输入来进行预测。随着时间的推移,算法将从训练集中学习文档或段落的向量表示。
训练结束后,在推理阶段,该算法将更多的文档向量添加到文档矩阵中,并根据冻结的词向量来计算添加的向量及其权重。通过推断文档向量,可以获得整个文档的语言表示。
通过在词预测中加入额外文档或段落向量,扩展了Word2vec的概念,现在就可以子啊各种任务重使用训练好的文档向量,例如在语料库中查找相似的文档。
如何训练文档向量
与训练词向量相似,可以使用gensim包来训练文档向量:
import multiprocessing
#CPU数量
num_cores=multiprocessing.cpu_count()from gensim.models.doc2vec import TaggedDocument,Doc2Vec
#粗分词器,会去除单字母词和标点符号
from gensim.utils import simple_preprocesscorpus=['this is the first document','another document','this is the third document']
training_corpus=[]
for i,text in enumerate(corpus):#gensim提供了一个数据结构,支持用字符串或整数标签来表示文档的类别标签、关键词或其他与文档有关的信息tagged_doc=TaggedDocument(simple_preprocess(text),[i])training_corpus.append(tagged_doc)
#实例化Doc2Vec对象,滑动窗口大小为10个词,每个词和文档向量为100维
model=Doc2Vec(vector_size=100,min_count=2,workers=num_cores,epochs=10)
#模型开始训练之前需要对词汇表进行编译
model.build_vocab(training_corpus)
#10个训练周期
model.train(training_corpus,total_examples=model.corpus_count,epochs=model.epochs)
一旦Doc2vec模型训练完成,就可以在实例化好的模型对象上调用infer_vector方法,来对新的未见过的文档进行文档向量推理:
print(model.infer_vector(simple_preprocess('this is the fourth document'),alpha=10))
通过这几个步骤,可以快速训练整个语料库的文档向量,并查找相似文件。具体做法是对语料库中的每篇文档生成一个文档向量,然后计算各个文档向量之间的余弦距离。另一个常见的任务是将语料库的文档向量通过类似与k均值的方法进行聚类,以此来创建文档分类器。