GENSIM官方教程(4.0.0beta最新版)-Word2Vec词向量模型


译文目录

GENSIM官方文档(4.0.0beta最新版)-面向新手的核心教程

GENSIM官方教程(4.0.0beta最新版)-LDA模型

GENSIM官方教程(4.0.0beta最新版)-Word2Vec词向量模型

GENSIM官方教程(4.0.0beta最新版)-LDA模型评价与可视化

博主全天在线,欢迎评论或私信讨论NLP相关问题


原文链接

本章节介绍Gensim的词向量模型,并将其应用于 Lee Evaluation Corpus

词向量模型是一种基于神经网络的广泛使用的算法。这种算法通常属于深度学习,尽管word
2vec本身通俗易懂。通过使用大量未注释的纯文本,word2vec会自动学习单词之间的关系。每个单词用一个向量来表示,且具有明显的线性关系:

  • vec(“国王”) - vec(“男人”) + vec(“女人”) =~ vec(“女王”)
  • vec(“Montreal Canadiens”) – vec(“Montreal”) + vec(“Toronto”) =~ vec(“Toronto Maple Leafs”).

词向量模型在自动文本标记,推荐系统和机器翻译中很有用。

本教程将介绍如下内容:

  1. 介绍词向量模型相对于词袋模型的改进方面,也就是优点
  2. 展示一个已经处理好的词向量模型。
  3. 展示如何用你自己的数据去训练一个新模型。
  4. 展示如何保存和载入模型。
  5. 介绍一些训练参数并展示他们的作用。
  6. 讨论和内存相关的一些细节。
  7. 通过将为来可视化词向量嵌入。

回顾:词袋模型

您可能已经对词袋模型很熟悉。此模型将每个文档转换为固定长度的整数向量。例如,给定句子:

  • John likes to watch movies. Mary likes movies too.
  • John also likes to watch football games. Mary hates football.

词袋模型会输出向量:

  • [1, 2, 1, 1, 2, 1, 1, 0, 0, 0, 0]
  • [1, 1, 1, 1, 0, 1, 0, 1, 2, 1, 1]

每个向量有10个元素,其中每个元素计算文档中特定单词出现的次数。元素的顺序是任意的。在上面的示例中,元素的顺序对应于单词:
[“John”, “likes”, “to”, “watch”, “movies”, “Mary”, “too”, “also”, “football”, “games”, “hates”]

词袋模型总的来说蛮有效的,但有几个缺点。

  • 首先,这样会丢失所有有关单词顺序的信息,如“约翰喜欢玛丽”和“玛丽喜欢约翰”对应于相同的向量。有一个解决方案:n元词袋模型,但存在数据稀疏和维度过高的问题。

  • 其次,该模型不会尝试学习单词的含义,这意味着向量之间的距离并不能准确反映出含义的差异。而词向量模型则解决了该问题。


简介:词向量模型

Word2Vec是一种较新的模型,该模型使用浅层神经网络将单词嵌入到低维向量空间中。结果是一组词向量,其中在向量空间中靠在一起的向量根据上下文具有相似的含义,而距离较远的词向量具有不同的含义。例如,strong和powerful距离较近,strong和Paris会相对较远。

该模型有两个版本,gensim的Word2Vec 类同时实现了这两个版本:

  1. Skip-grams (SG)
  2. Continuous-bag-of-words (CBOW)

注意!下面这些模型的细节看不懂就跳过,不影响你用
什么是词向量的Skip-Gram模型,举例来说,使用长度为2的滑窗遍历语料集,得到很多词对(单词一,单词二)。用它们训练含有一个隐藏层的神经网络,输入一个单词到网络,输出它临近词的概率分布。因为神经网络只能处理数字,所以要把单词进行独热编码。所以如果隐藏层有三百个节点(译者注:单词在隐藏层由300个特征表示),我们最终的目标就是训练这个300维(译者注:300列,n行,n是你词典大小)的隐层权重矩阵。

CBOW和 skip-gram很类似,也是仅有一个隐藏层的神经网络,与skip-gram这种给定输入词汇预测上下文的模型不同的是,CBOW是给定上下文,来预测输入词汇,相同的是,在投影层也要进行独热编码。


词向量模型训练实例

要查看Word2Vec可以做什么,让我们下载一个预先用词向量模型训练好的数据集并开始玩弄。该模型涵盖大约300万个单词和短语。这样的模型可能需要花费数小时来训练,但是由于已经被训练好了,使用Gensim进行下载和加载仅需要几分钟(译者注:请结合您的地理位置重新估算下载时间)。

注意!该模型大约为2GB,因此您需要进行良好的网络连接。否则,请跳到下面的“训练自己的模型”部分。


载入数据并检索里面的单词。

import gensim.downloader as api
wv = api.load('word2vec-google-news-300')
for index, word in enumerate(wv.index_to_key):
    if index == 10:
        break
    print(f"word #{index}/{len(wv.index_to_key)} is {word}")
#输出
'''
word #0/3000000 is </s>
word #1/3000000 is in
word #2/3000000 is for
word #3/3000000 is that
word #4/3000000 is is
word #5/3000000 is on
word #6/3000000 is ##
word #7/3000000 is The
word #8/3000000 is with
word #9/3000000 is said
'''

词向量模型只能获得模型中存在的词的向量,但是却无法推断出不在模型中的词的向量,这是word2vec的一个限制。

vec_king = wv['king']
try:
    vec_cameroon = wv['cameroon']
except KeyError:
    print("The word 'cameroon' does not appear in this model")
#输出
'''
    The word 'cameroon' does not appear in this model
'''

词向量模型支持相似性查询这类的工作。通过下面的例子可直观的发现,两个词越不相似,得分越低。

pairs = [
    ('car', 'minivan'),   # a minivan is a kind of car
    ('car', 'bicycle'),   # still a wheeled vehicle
    ('car', 'airplane'),  # ok, no wheels, but still a vehicle
    ('car', 'cereal'),    # ... and so on
    ('car', 'communism'),
]
for w1, w2 in pairs:
    print('%r\t%r\t%.2f' % (w1, w2, wv.similarity(w1, w2)))
#输出
'''
'car'   'minivan'       0.69
'car'   'bicycle'       0.54
'car'   'airplane'      0.42
'car'   'cereal'        0.14
'car'   'communism'     0.06
'''

打印出与“ car”或“ minivan”最相似的5个词

print(wv.most_similar(positive=['car', 'minivan'], topn=5))
# 输出:
'''
[('SUV', 0.8532192707061768), ('vehicle', 0.8175783753395081), ('pickup_truck', 0.7763688564300537), ('Jeep', 0.7567334175109863), ('Ford_Explorer', 0.7565720081329346)]
'''

找出最与众不同的词

print(wv.doesnt_match(['fire', 'water', 'land', 'sea', 'air', 'car']))
# 输出
'''
car
'''

训练一个你自己的模型

首先,您需要一些数据来训练模型。对于下面的例子中,我们将使用 Lee Evaluation Corpus (译者注:Gensim包自带的,不用你重新下了,是不是很贴心)。

这个语料库很小,可以完全容纳在内存中,但是我们将使用一个内存友好的迭代器,该迭代器逐行读取它,以演示如何处理更大的语料库。

from gensim.test.utils import datapath
from gensim import utils

class MyCorpus:
    """An iterator that yields sentences (lists of str)."""

    def __iter__(self):
        corpus_path = datapath('lee_background.cor')
        for line in open(corpus_path):
            # assume there's one document per line, tokens separated by whitespace
            yield utils.simple_preprocess(line)

如果我们想进行任何自定义的预处理,例如解码非标准编码,小写化,删除数字…所有这些都可以在MyCorpus迭代器中完成,word2vec无需知道。所需要做的就是把单词列表一行一行的输入迭代器。

现在开始训练模型,一开始咱先别太在意训练参数,后面我们会慢慢调:

import gensim.models

sentences = MyCorpus()
model = gensim.models.Word2Vec(sentences=sentences)
#(译者注:sentences也可以是简单的列表形式,如[["cat", "say", "meow"], ["dog", "say", "woof"]])

一旦有了模型,就可以使用与上面演示相同的方式。
该模型的主要部分是model.wv,其中“ wv”代表“word vectors”(单词向量)

vec_king = model.wv['king']
for index, word in enumerate(wv.index_to_key):
    if index == 10:
        break
    print(f"word #{index}/{len(wv.index_to_key)} is {word}")
#输出
'''
word #0/3000000 is </s>
word #1/3000000 is in
word #2/3000000 is for
word #3/3000000 is that
word #4/3000000 is is
word #5/3000000 is on
word #6/3000000 is ##
word #7/3000000 is The
word #8/3000000 is with
word #9/3000000 is said
'''

储存和加载模型

您会注意到,训练非实验性的模型可能会花费一些时间。一旦你训练好模型并按预期工作后,可以将其保存到磁盘。这样一来,以后不必再花时间在后面重新训练它。

您可以使用标准gensim方法存储/加载模型:

import tempfile

with tempfile.NamedTemporaryFile(prefix='gensim-model-', delete=False) as tmp:
    temporary_filepath = tmp.name
    model.save(temporary_filepath)#保存

    new_model = gensim.models.Word2Vec.load(temporary_filepath)#加载

此外,您可以使用原始C工具的文本格式和二进制格式加载模型:

model = gensim.models.KeyedVectors.load_word2vec_format('/tmp/vectors.txt', binary=False)
# using gzipped/bz2 input works too, no need to unzip
model = gensim.models.KeyedVectors.load_word2vec_format('/tmp/vectors.bin.gz', binary=True)

训练参数

Word2Vec 模型中有些参数会影响训练速度和质量。

  • min_count

min_count用于修剪内部字典。在十亿个单词的语料库中只出现一次或两次的单词可能是错别字和垃圾信息。此外,没有足够的数据来对这些单词进行任何有意义的训练,因此最好忽略它们(译者注:预处理时也有去除低频词的步骤,这一步放在哪效果会更好呢?)min_count = 5是默认值。

  • vector_size

vector_size 是Word2Vec将单词映射到的N维空间的维数(N)就是说用N个特征来表示这个词向量。

设定较大的值需要更多的训练数据,但可以产生更好(更准确)的模型。合理的值在 10 − 1 0 3 10-10^3 10103之间。默认值是100.

  • workers

这是最后一个主要的参数,主要用于并行化训练,以加快训练速度。默认值是3。

注意,你装了Cpython后这个参数才起作用。(译者注:别担心,担心的人装的都是Cpython,装的不是Cpython的人他们也不会为这点小事担心)


内存相关的细节

word2vec模型参数的核心是存储为矩阵(NumPy数组)。每个数组是语料集词典长度(由min_count参数控制)乘以float的矢量大小(vector_size参数)(单精度,亦称4个字节)。

有三个这样的矩阵保存在RAM中。因此,如果您词典长度是100000,并且您要求layer vector_size=200,则该模型将需要大约100,0002004*3 bytes = ~229MB

存储词汇树需要一点额外的内存(100,000个单词将花费几兆字节),但是除非您的单词是长的离谱的字符串,否则内存占用量将由上述三个矩阵控制。


模型评估

Word2Vec培训是一项无监督的任务,没有一种很好的方法可以客观地评估结果。因此模型的好坏取决于您的最终应用效果。

Google遵循“ A是B,C是D的任务”发布了大约20,000个语法和语义测试示例的测试集。它在“数据集”文件夹中提供。

例如一个的句法类比是bad:worse;good:?。数据集中共有9种类型的语法比较,例如复数名词和相反含义的名词。

语义类比包含五种,例如首都(Paris:France;Tokyo:?)或家庭成员(brother:sister;dad:?)。

Gensim以完全相同的格式支持相同的评估集:

model.wv.evaluate_word_analogies(datapath('questions-words.txt'))

在2016年12月发布的Gensim中,我们添加了一种更好的方法来评估语义相似性。

默认情况下,它使用学术数据集WS-353,但是可以基于它创建针对您的企业的数据集。它包含单词对以及人类赋予的相似性判断。它测量两个单词的相关性或同时出现。例如,“海岸”和“海岸”在相同的上下文中非常相似。同时,“衣服”和“壁橱”不太相似,因为它们是相关的但不可互换。

model.wv.evaluate_word_pairs(datapath('wordsim353.tsv'))

注意!在Google或WS-353测试装置上的良好性能并不意味着word2vec将在您的应用程序中正常运行,反之亦然。始终最好直接评估您的预期任务。有关如何在分类器管道中使用word2vec的示例,请参见本教程


在线学习

你也可以加载模型并继续使用更多的句子和新的词汇来训练它:

model = gensim.models.Word2Vec.load(temporary_filepath)
more_sentences = [
    ['Advanced', 'users', 'can', 'load', 'a', 'model',
     'and', 'continue', 'training', 'it', 'with', 'more', 'sentences'],
]
model.build_vocab(more_sentences, update=True)
model.train(more_sentences, total_examples=model.corpus_count, epochs=model.epochs)

# cleaning up temporary file
import os
os.remove(temporary_filepath)

计算训练损失

模型的compute_loss参数用于设定是否计算损失。计算的loss存储在模型属性running_training_loss中,可以使用get_latest_training_loss来获取:

# instantiating and training the Word2Vec model
model_with_loss = gensim.models.Word2Vec(
    sentences,
    min_count=1,
    compute_loss=True,
    hs=0,#1是分级softmax,0是负采样
    sg=1,#训练算法,1是skip-gram,0是CBOW
    seed=42,
)

# getting the training loss value
training_loss = model_with_loss.get_latest_training_loss()
print(training_loss)
#输出
#1369454.25

(译者注:搞不懂gensim损失函数的物理意义是啥,感觉怎么调都很大,也不知道这个值对模型的效果有啥实际影响。)


基准测试

(译者注:这块直接谷歌翻译的,没太能get到官方教程体现出来的实践价值)

让我们运行一些基准测试以查看训练损失计算代码对训练时间的影响。

我们将使用以下数据作为基准:

  1. Lee Background语料库:包含在gensim的测试数据中

  2. Text8语料库。为了演示语料库大小的影响,我们将研究语料库的前1MB,10MB,50MB以及整个内容。

import io
import os

import gensim.models.word2vec
import gensim.downloader as api
import smart_open


def head(path, size):
    with smart_open.open(path) as fin:
        return io.StringIO(fin.read(size))


def generate_input_data():
    lee_path = datapath('lee_background.cor')
    ls = gensim.models.word2vec.LineSentence(lee_path)
    ls.name = '25kB'
    yield ls

    text8_path = api.load('text8').fn
    labels = ('1MB', '10MB', '50MB', '100MB')
    sizes = (1024 ** 2, 10 * 1024 ** 2, 50 * 1024 ** 2, 100 * 1024 ** 2)
    for l, s in zip(labels, sizes):
        ls = gensim.models.word2vec.LineSentence(head(text8_path, s))
        ls.name = l
        yield ls


input_data = list(generate_input_data())

现在,我们比较输入数据和模型训练参数如hs和sg。

对于每种组合,我们重复测试几次,以获得测试持续时间的平均值和标准偏差。
代码加输出近一千行,这里略了。


词嵌入可视化

使用tSNE把模型的将词嵌入维数减少到2维,这样就可以进行可视化展示了。

可视化可用于注意到数据中的语义和句法趋势。

如:

  • 语义:诸如猫,狗,牛等之类的词倾向于聚在一起。

  • 句法:奔跑,奔跑或割伤等词会聚在一起。

也可以注意到矢量关系,例如vKing-vMan = vQueen-vWoman。

注意!用于可视化的模型在一个小语料库上训练。因此,某些关系可能不太清楚。

from sklearn.decomposition import IncrementalPCA    # inital reduction
from sklearn.manifold import TSNE                   # final reduction
import numpy as np                                  # array handling


def reduce_dimensions(model):
    num_dimensions = 2  # final num dimensions (2D, 3D, etc)

    # extract the words & their vectors, as numpy arrays
    vectors = np.asarray(model.wv.vectors)
    labels = np.asarray(model.wv.index_to_key)  # fixed-width numpy strings

    # reduce using t-SNE
    tsne = TSNE(n_components=num_dimensions, random_state=0)
    vectors = tsne.fit_transform(vectors)

    x_vals = [v[0] for v in vectors]
    y_vals = [v[1] for v in vectors]
    return x_vals, y_vals, labels


x_vals, y_vals, labels = reduce_dimensions(model)

def plot_with_plotly(x_vals, y_vals, labels, plot_in_notebook=True):
    from plotly.offline import init_notebook_mode, iplot, plot
    import plotly.graph_objs as go

    trace = go.Scatter(x=x_vals, y=y_vals, mode='text', text=labels)
    data = [trace]

    if plot_in_notebook:
        init_notebook_mode(connected=True)
        iplot(data, filename='word-embedding-plot')
    else:
        plot(data, filename='word-embedding-plot.html')


def plot_with_matplotlib(x_vals, y_vals, labels):
    import matplotlib.pyplot as plt
    import random

    random.seed(0)

    plt.figure(figsize=(12, 12))
    plt.scatter(x_vals, y_vals)

    #
    # Label randomly subsampled 25 data points
    #
    indices = list(range(len(labels)))
    selected_indices = random.sample(indices, 25)
    for i in selected_indices:
        plt.annotate(labels[i], (x_vals[i], y_vals[i]))

try:
    get_ipython()
except Exception:
    plot_function = plot_with_matplotlib
else:
    plot_function = plot_with_plotly

plot_function(x_vals, y_vals, labels)

在这里插入图片描述

  • 14
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值