Word2Vec 模型和 Doc2Vec 模型
Word2Vec 模型
Word2Vec 是 Google 在 2013 年开源的一款将词表征为实数值向量的高效工具,采用的模型有 CBOW (Continuous Bag-Of-Words,即连续的词袋模型) 和 Skip-Gram 两种。Word2Vec 通过训练,可以把对文本内容的处理简化为 K 维向量空间中的向量运算,而向量空间上的相似度可以用来表示文本语义上的相似度。因此,Word2Vec 输出的词向量可以被用来做很多 NLP 相关的工作,比如聚类、找同义词、词性分析等等。
CBOW 模型能够根据输入周围 n-1 个词来预测出这个词本身,而 Skip-gram 模型能够根据词本身来预测周围有哪些词。也就是说,CBOW 模型的输入是某个词 A 周围的 n 个单词的词向量之和,输出是词 A 本身的词向量,而 Skip-gram 模型的输入是词 A 本身,输出是词 A 周围的 n 个单词的词向量。
Word2Vec 最常用的开源实现之一就是 gensim,网址为:
http://radimrehurek.com/gensim/ |
gensim 的安装非常简单:
pip install --upgrade gensim |
gensim 的使用非常简洁,加载数据和训练数据可以合并,训练好模型后就可以按照单词获取对应的向量表示:
sentences = [['first', 'sentence'], ['second', 'sentence']] |
其中 Word2Vec 有很多可以影响训练速度和质量的参数。第一个参数可以对字典做截断,少于 min_count 次数的单词会被丢弃掉,默认值为 5:
model = Word2Vec(sentences, min_count=10) |
另外一个是神经网络的隐藏层的单元数,推荐值为几十到几百。事实上 Word2Vec 参数的个数也与神经网络的隐藏层的单元数相同,比如 size=200,那么训练得到的 Word2Vec 参数个数也是 200:
model = Word2Vec(sentences, size=200) |
以处理 IMDB 数据集为例,初始化 Word2Vec 对象,设置神经网络的隐藏层的单元数为 200,生成的词向量的维度也与神经网络的隐藏层的单元数相同。设置处理的窗口大小为 8 个单词,出现少于 10 次数的单词会被丢弃掉,迭代计算次数为 10 次,同时并发线程数与当前计算机的 cpu 个数相同:
model=gensim.models.Word2Vec(size=200, window=8, min_count=10, iter=10, workers=cores) |
其中当前计算机的 cpu 个数可以使用 multiprocessing 获取:
cores=multiprocessing.cpu_count() |
创建字典并开始训练获取 Word2Vec。gensim 的官方文档中强调增加训练次数可以提高生成的 Word2Vec 的质量,可以通过设置 epochs 参数来提高训练次数,默认的训练次数为 5:
x=x_train+x_test |
经过训练后,Word2Vec 会以字典的形式保存在 model 对象中,可以使用类似字典的方式直接访问获取,比如获取单词 “love” 的 Word2Vec 就可以使用如下形式:
model[“love”] |
Word2Vec 的维度与之前设置的神经网络的隐藏层的单元数相同为 200,也就是说是一个长度为 200 的一维向量。通过遍历一段英文,逐次获取每个单词对应的 Word2Vec,连接起来就可以获得该英文段落对应的 Word2Vec:
def getVecsByWord2Vec(model, corpus, size): |
需要注意的是,出于性能的考虑,我们将出现少于 10 次数的单词会被丢弃掉,所以存在这种情况,就是一部分单词找不到对应的 Word2Vec,所以需要捕捉这个异常,通常使用 python 的 KeyError 异常捕捉即可。基于上述的 Word2Vec 的方法,Quoc Le 和 Tomas Mikolov 又给出了 Doc2Vec 的训练方法。如下图所示,其原理与 Word2Vec 相同,分为 Distributed Memory (DM) 和 Distributed Bag of Words (DBOW)。
以处理 IMDB 数据集为例,初始化 Doc2Vec 对象,设置神经网络的隐藏层的单元数为 200,生成的词向量的维度也与神经网络的隐藏层的单元数相同。设置处理的窗口大小为 8 个单词,出现少于 10 次数的单词会被丢弃掉,迭代计算次数为 10 次,同时并发线程数与当前计算机的 cpu 个数相同:
model=Doc2Vec(dm=0, dbow_words=1, size=max_features, window=8, min_count=10, iter=10, workers=cores) |
其中需要强调的是,dm 为使用的算法,默认为 1,表明使用 DM 算法,设置为 0 表明使用 DBOW 算法,通常使用默认配置即可,比如:
model = gensim.models.Doc2Vec.Doc2Vec(size=50, min_count=2, iter=10) |
Doc2Vec
与 Word2Vec 不同的地方是,Doc2Vec 处理的每个英文段落,需要使用一个唯一的标识标记,并且使用一种特殊定义的数据格式保存需要处理的英文段落,这种数据格式定义如下:
SentimentDocument = namedtuple('SentimentDocument', 'words tags') |
其中 SentimentDocument 可以理解为这种格式的名称,也可以理解为这种对象的名称,words 会保存英文段落,并且是以单词和符合列表的形式保存,tags 就是我们说的保存的唯一标识。最简单的一种实现就是依次给每个英文段落编号,训练数据集的标记为 “TRAIN_数字”,训练数据集的标记为 “TEST_数字”:
def labelizeReviews(reviews, label_type): |
创建字典并开始训练获取 Doc2Vec。与 Word2Vec 的情况一样,gensim 的官方文档中强调增加训练次数可以提高生成的 Doc2Vec 的质量,可以通过设置 epochs 参数来提高训练次数,默认的训练次数为 5:
x=x_train+x_test |
经过训练后,Doc2Vec 会以字典的形式保存在 model 对象中,可以使用类似字典的方式直接访问获取,比如获取段落 “I love tensorflow” 的 Doc2Vec 就可以使用如下形式:
model.docvecs[”I love tensorflow”] |
一个典型的 doc2ver 展开为向量形式,内容如下所示,为了显示方便只展示了其中一部分维度的数据:
array([ 0.02664499, 0.00475204, -0.03981256, 0.03796276, -0.03206162, |
Doc2Vec 的维度与之前设置的神经网络的隐藏层的单元数相同为 200,也就是说是一个长度为 200 的一维向量。以英文段落为单位,通过遍历训练数据集和测试数据集,逐次获取每个英文段落对应的 Doc2Vec,这里的英文段落就可以理解为数据集中针对电影的一段评价:
def getVecs(model, corpus, size): |
训练 Word2Vec 和 Doc2Vec 是非常费时费力的过程,调试阶段会频繁更换分类算法以及修改分类算法参数调优,为了提高效率,可以把之前训练得到的 Word2Vec 和 Doc2Vec 模型保存成文件形式,以 Doc2Vec 为例,使用 model.save 函数把训练后的结果保存在本地硬盘上,运行程序时,在初始化 Doc2Vec 对象之前,可以先判断本地硬盘是否存在模型文件,如果存在就直接读取模型文件初始化 Doc2Vec 对象,反之则需要训练数据:
if os.path.exists(doc2ver_bin): |