第三节 词向量(1 / 1)

大家对计算机解决各类问题的方式已经有所了解,无论是图片、声音,还是视频,也无论采用何种算法,都需要先把处理对象转换成使用数值表示的形式,计算机才能进行计算。例如,在第八章处理与图片相关的任务时,使用图片像素的RGB值或者灰度值,就可以把图片转换成计算机能够处理的数值形式。当尝试进行文本的处理和理解任务时,会面临同样的问题,所以把需要处理的文本转换成数值表示的形式,是工作能够继续的首要前提。

这一节的主要内容是讲解如何让机器理解文本的基本单元——词。那么如何用数值的形式表示这些构成文本的基本单元呢?

有两种表示词的基本方法,一种是独热表示(one-hot representation),另一种是分布式表示(distributed representation)。

独热表示比较简单,它是常用的一种词表示方法。它的直观的理解方式是这样的,首先建立一个词表,这个词表的作用可以理解为一个大词典,在文本中可能出现的所有词都被收纳到了这个词表中并按某种顺序排列,每个词都以它在词表中出现的次序作为它唯一的编号。假设词表中一共有3个词,当需要表示某一个词时,查询到它在词表中出现的位置,比如它恰好是词表中的第2个词,则把这个词表示成一个三维的向量,向量的第2个分量是1,其他的2个分量都是0,也就是(0,1,0)。

更具体一些,例如,建立了如表9-1所示的词表。那么“我”的独热表示为(1,0,0,…),“是”表示为(0,1,0,…),“中学生”表示为(0,0,0,0,1,0,…)。

表9-1

这种表示方式简单明了,再结合其他算法,可以较好地解决自然语言处理中的很多问题。但是它有两个非常明显的缺陷。一是在文本中出现的词数量会非常多,词表的容量会非常大,通常数量是以万为单位的,所以每个词的表示向量也都是数万维的向量,这在各类算法中会造成计算复杂度的急剧上升,并且会带来“维数灾难”。还有一个更重要的缺陷,这种表示方式并未考虑到词与词之间的联系。例如,“美丽”和“漂亮”是两个含义相似的词,但是它们的表示方式并不能体现出这种相似性,这相当于词的大部分信息被舍弃了,使用这样的表示进行后续的语言处理任务效果可想而知。

而词的另一种表示方式——分布式表示恰恰针对这两个缺陷做出了改进。分布式表示的概念最早是由辛顿于1986年在他的论文《学习分布式表示》(Learning distributed representations of concepts)中提出的,这个概念到2000年之后才开始慢慢受到研究者的重视并被应用到实际的任务中。词的这种表示方式被称为“词向量”(word embedding)或“词嵌入”,实现这种表示有几种不同的方案,接下来介绍的是使用神经网络进行分布式词表示的方法。因为这种表示需要用到较多的代数和统计知识,所以在这里不具体解释它的实现过程,只用相对简单的语言描述这种表示方式的想法。

为了解决通过向量描述词的含义的问题,分布式表示需要根据大量的文本统计不同词在上下文中的关系。例如,通过统计出现的频率,发现在“中国”这个词的前后,经常会出现“北京”这个词,那就认为这两者有比较密切的关系。在词向量的分布式表示中,经常采用的模型有两种,分别是CBOW(连续式词袋模型)和SKIP-GRAM。其中CBOW是根据某个词前面的k个词和后面k个词,来计算某个词出现的概率,也就是通过上下文来预测词。例如,有一句话“我很困”,那这句话后面出现“睡觉”这个词的概率就比较大。SKIP-GRAM与此相反,是通过词来预测它的上下文,也就是计算某个词前面和后面出现其他词的概率。例如,出现了“睡觉”,那么它的上下文中出现“困”“床”的概率就会比较大。通过建立这种联系,就解决了独热表示方法注意不到词与词之间关系的问题。

使用神经网络进行词表示的另一个优点在于,通过搭建合适的网络结构、制订合适的训练方法,在一定程度上保留词的含义的前提下,可以压缩表示词的向量的维数。这个过程可以理解为,通过训练,自动抽取词的含义的主要特征,从而把词以较小维数的向量展示出来。本教材不详细解释这种训练方式的细节。

采用这种表示方式,2013年Google首先推出了用于获取词的向量表示的工具包,称为word2vec。现在基于类似想法的工具包还有很多,利用gensim实现词向量训练非常简单,本教材将采用gensim提供的工具包实现词向量的训练任务。

训练词向量首先需要准备一个语料库,从这个语料库学习词与词之间的联系以及词的含义。语料库其实是训练数据。从这个角度来说,语料库越大,训练出的向量的准确程度就越高。作为案例,本教材使用金庸先生的武侠小说《笑傲江湖》作为语料库。相关数据“xiaoao.txt”可从教材资源平台下载,需要说明的是,资源平台提供的文本数据仅为该小说的部分内容,并且已经进行了分词和其他必要的处理。

首先安装gensim模块。在命令行窗口输入如下命令即可。

pip install gensim

在IPython控制台导入需要使用的模块,包括编码转换模块codecs以及与词向量相关的模块。

In[1]:import codecs

In[2]:from gensim.models import Word2Vec

In[3]:from gensim.models.word2vec import LineSentence

打开语料库文件,这里的语料库文件已经经过必要地处理,可以直接使用。

In[4]:inp=codecs.open('xiaoao.txt','r','utf-8')

使用gensim的词向量训练工具进行训练。参数size表示训练好的词向量的维数,window表示需要考虑的上下文的长度,min_count表示所考虑的词出现的最低次数,低于此数的词将被忽略。训练完成后把模型和向量保存起来,以备将来使用。

In[5]:model=Word2Vec(LineSentence(inp),size=300,window=7,

min_count=5)

In[5]:model.save('xiaoao.model')

In[6]:model.wv.save_word2vec_format('xiaoao.vector',binary=False)

训练完成后,可通过调用相应的模型使用训练完成的词向量。

In[7]:import gensim

In[8]:model=gensim.models.Word2Vec.load("xiaoao.model")

查看与“盈盈”关联密切的词汇,输出结果如下。

In[9]:model.most_similar("盈盈")

Out[9]:

[('岳灵珊',0.9394630193710327),

('林平之',0.9115134477615356),

('举杯',0.8917326927185059),

('田伯光',0.8862934112548828),

('摇',0.8862836956977844),

('曲非烟',0.8856724500656128),

('忙',0.8839414119720459),

('走',0.8811073303222656),

('一眼',0.8792630434036255),

('曲非',0.8775876760482788)]

这里的输出结果是根据词向量计算出的与“盈盈”关系最密切的词的前十名。可以看到有一定的道理,但是也有些不准确或者奇怪的输出。这跟训练语料的大小、语料的处理过程、网络参数的设置等因素都有关系,读者可以尝试进行进一步的改进,通常能够得到更精确的结果。