Press "Enter" to skip to content

Keras深度学习实战(25)——使用skip-gram和CBOW模型构建单词向量

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

Keras深度学习实战(25)——使用skip-gram和CBOW模型构建单词向量

1. skip-gram 和 CBOW 模型

1.1 CBOW 模型基本原理
1.2 skip-gram 模型基本原理

2.2 使用算法 CBOW 模型构建单词向量
2.3 使用 skip-gram 模型构建单词向量

3. 使用预训练的单词向量执行向量算术

3.1 预训练单词向量下载与加载
3.2 使用预训练的单词向量执行向量算术

0. 前言

 

在《构建单词向量》一节中,我们已经学习了如何从零开始构建了一个用于计算单词向量的模型。在本节中,我们将继续学习如何使用 gensim 库构建 skip-gramCBOW 模型获取单词向量。

 

1. skip-gram 和 CBOW 模型

 

1.1 CBOW 模型基本原理

 

我们在本小节中使用连续单词袋 ( Continuous Bag of Words , CBOW ) 模型构建单词向量。以 “ I love watching movie ” 为例, CBOW 模型处理此语句的方式如下:

 

 

输入单词输出单词
{I, watching}{love}
{love, movie}{watching}

 

1.2 skip-gram 模型基本原理

构建单词向量的另一种方法是使用 skip-gram 模型,其步骤与 CBOW 步骤恰好相反,如下所示:

输出单词输入单词
{love}{I, watching}
{watching}{love, movie}

 

2. 构建单词向量

 

无论是 skip-gram 模型还是 CBOW 模型,得到单词在隐藏层的编码向量的方法都与在《构建单词向量》一节中介绍的方法相同。

 

2.1 数据集分析

 

了解了单词向量构建的原理后,我们使用 skip-gramCBOW 模型构建单词向量。为了构建模型,我们将使用航空公司的情感数据集,其中给出了推文文本,并提供了与推文相对应的情感。

 

我们所用的 Twitter US Airline Sentiment 数据 来源于 Crowdflower’s Data for Everyone ,其中包含了美国各大航空公司 Twitter 评论的情绪分析数据,该数据集收集了自 20152 月以来的数据,并推文进行分类,包括正面、负面和中立,数据集还对负面评价原因的进行分类,例如“航班迟到”或“服务粗鲁”等。可以在 Kaggle 上获取格式化数据集。可以看到,数据集中包含每条推文对六家美国航空公司的评价情绪是正面的、中性的还是负面的:

 

 

2.2 使用算法 CBOW 模型构建单词向量

 

接下来,我们利用 gensim 库构建 CBOW 模型生成单词向量。如果未安装 gensim 库,首先使用 pip 命令进行安装:

 

pip install gensim

 

(1)导入相关库,并读取航空公司 Twitter 情感数据集,其中包含与航空公司及其相应情感相关的评论内容:

 

import gensim
import pandas as pd
data = pd.read_csv('archive/Tweets.csv')
print(data.head())

 

预览数据集,如下所示:

 

tweet_id airline_sentiment  ...  tweet_location               user_timezone
0  570306133677760513           neutral  ...             NaN  Eastern Time (US & Canada)
1  570301130888122368          positive  ...             NaN  Pacific Time (US & Canada)
2  570301083672813571           neutral  ...       Lets Play  Central Time (US & Canada)
3  570301031407624196          negative  ...             NaN  Pacific Time (US & Canada)
4  570300817074462722          negative  ...             NaN  Pacific Time (US & Canada)

 

(2)对读取的文本进行预处理,执行以下操作:

将每个单词都转换为小写
删除标点符号,仅保留数字和字母
删除停用词

import re
import nltk
from nltk.corpus import stopwords
stop = set(stopwords.words('english'))
def preprocess(text):
    text = text.lower()
    text = re.sub('[^0-9a-zA-Z]+', ' ', text)
    words = text.split()
    words2 = [i for i in words if i not in stop]
    words3 = ' '.join(words2)
    return words3
data ['text'] = data['text'].apply(preprocess)

 

(3)将句子拆分为分词 ( token ) 列表,以便随后将其传递给 gensim ,打印出第一句的分词结果:

 

print(data['text'][0].split())

 

以上代码将句子按空格分隔,输出如下所示:

 

['virginamerica', 'dhepburn', 'said']

 

(4)遍历所有文本,并将分词结果添加到列表中:

 

list_words = []
for i in range(len(data)):
    list_words.append(data['text'][i].split())

 

检查 list_words 列表中的前 5 个分词结果:

 

print(list_words[:5])

 

前三个句子的列表如下:

 

[['virginamerica', 'dhepburn', 'said'], ['virginamerica', 'plus', 'added', 'commercials', 'experience', 'tacky'], ['virginamerica', 'today', 'must', 'mean', 'need', 'take', 'another', 'trip'], ['virginamerica', 'really', 'aggressive', 'blast', 'obnoxious', 'entertainment', 'guests', 'faces', 'amp', 'little', 'recourse'], ['virginamerica', 'really', 'big', 'bad', 'thing']]

 

(5)接下来,构建 Word2Vec 模型,定义单词向量大小、要查看的上下文窗口大小,以及要考虑单词的最小数量,以使其具有被编码为向量的资格:

 

from gensim.models import Word2Vec
model = Word2Vec(vector_size=50, window=5, min_count=30, sg=0, alpha=0.025)

 

在以上代码中, vector_size 表示单词向量的维度, window 表示要考虑的单词的上下文大小, min_count 指定要考虑的单词的最小频率, sg 表示采用的编码模型为使用 skip-gram ( sg = 1 ) 或 CBOW ( sg = 0 ), alpha 表示模型的学习率。

 

(6)定义模型后,传递 list_words 列表以构建词汇表,如下所示:

 

model.build_vocab(list_words)

 

构建词汇表后,可以找到在整个语料库中过滤掉少于 30 次的单词后剩下的最终单词:

 

print(model.wv.index_to_key)

 

输出结果如下所示:

 

['united', 'flight', 'usairways', 'americanair', 'southwestair', 'jetblue', 'get', 'co', 'http', 'thanks', 'cancelled', 'service'...]

 

(7)通过指定输入数据和要训练的 epoch 数来训练模型:

 

model.train(list_words, total_examples=model.corpus_count, epochs=200)

 

train 方法中, list_words 列表包含了所有输入分词列表, total_examples 表示要考虑的分词列表总数, epochs 是要运行的 epoch 数。

 

此外,我们也可以通过在 Word2Vec 方法中使用 iter 参数来指定训练模型 epoch 数:

 

model.train(list_words, total_examples=model.corpus_count, iter=200)

 

(8)训练完成后,可以提取给定单词的单词编码向量:

 

print(model.wv.get_vector('day'))

 

对应于单词 “ day ” 的单词向量如下:

 

[-7.04173684e-01 -5.72516641e-04 -4.10758048e-01  1.84985828e+00
 -1.15435565e+00 -3.16574931e-01 -5.16422510e-01  2.28969193e+00
  1.91934001e+00 -1.18813097e+00 -2.94377494e+00  9.51616392e-02
 -8.44838619e-02 -7.18616024e-02 -1.14567673e+00  6.77643716e-01
  1.61244774e+00  1.13801873e+00 -4.42255348e-01  1.07233655e+00
  1.16125333e+00  2.79197335e+00  2.07479763e+00 -1.21500826e+00
 -9.10723388e-01  4.01439548e-01 -1.65728176e+00 -1.75016761e-01
 -9.88252282e-01 -3.28201318e+00 -1.22636998e+00 -6.90755486e-01
 -1.92077053e+00  1.75805852e-01 -2.02697372e+00 -9.76259783e-02
  1.68322384e+00 -1.77150667e+00  3.45278442e-01 -2.07601279e-01
 -1.24472260e+00  7.59482205e-01  7.28200555e-01 -2.57247114e+00
 -1.04648125e+00  2.81359744e+00 -2.41322589e+00 -1.54843581e+00
  2.38953400e+00 -1.05442435e-01]

 

两个词之间的相似度可以使用 similarity 计算如下:

 

print(model.wv.similarity('day', 'week'))
# 输出结果
# 0.53549874

 

同样,我们可以计算与给定单词最相似的单词,以及它们之间的相似度:

 

print(model.wv.most_similar('day'))

 

与单词 “ day ” 最相似的单词打印如下:

 

[('days', 0.6186136603355408), ('week', 0.5354987382888794), ('trip', 0.5184321999549866), ('time', 0.4801279306411743), ('destination', 0.4254339635372162), ('hrs', 0.4112888276576996), ('night', 0.41115307807922363), ('hours', 0.40979164838790894), ('year', 0.3568463921546936), ('sat', 0.3532494604587555)]

 

尽管这些相似度看起来很低,并且一些相似的单词并没有被准确的识别,这是由于该数据库中的数据量并不足以得到更精确的结果,可以在一个更大的数据集上进行训练。

 

(9)接下来,我们减小模型训练的 epoch 数,以对比模型性能,并在训练完成后,查看与单词 “ day ” 最相似的单词输出:

 

model = Word2Vec(vector_size=50, window=5, min_count=30, sg=0)
model.build_vocab(list_words)
model.train(list_words, total_examples=model.corpus_count, epochs=10)
print(model.wv.most_similar('day'))

 

与 “ day ” 一词最相似的词如下:

 

[('week', 0.8697884678840637), ('since', 0.8324152231216431), ('bc', 0.827883243560791), ('night', 0.820859432220459), ('different', 0.7866533398628235), ('destination', 0.7702142596244812), ('2nd', 0.7699329853057861), ('one', 0.7678356766700745), ('without', 0.7577207088470459), ('iad', 0.7566713094711304)]

 

可以看到,如果模型没有得到充分训练(使用较小的 epoch ),则得到与单词 day 最相似的词结果更不准确,而经过更多 epoch 训练后,结果更加符合直觉。

 

2.3 使用 skip-gram 模型构建单词向量

 

通过将 sg 参数的值替换为 1 ,则可以使用 skip-gram 模型获得单词低维向量:

 

model = Word2Vec(vector_size=50, window=5, min_count=30, sg=1)

 

3. 使用预训练的单词向量执行向量算术

 

在上一小节中,我们已经知道所构建的 skip-gramCBOW 模型得到的单词向量结果并不准确的原因之一是数据集中句子数量太少,不足以训练得到鲁棒性模型。

 

为了克服这一缺陷,我们可以使用 Google 使用大量数据集训练的单词向量。 Google 的预训练的单词向量中包括 3000000 个单词/短语的编码向量,这些编码向量是根据 Google 新闻数据集中的单词进行训练的。

 

3.1 预训练单词向量下载与加载

 

可从以下链接下载预训练的单词向量: https://pan.baidu.com/s/1wjgUm2Y9435FU4KVQMdnbA ,提取码: z29h ,下载完成后解压缩文件。

 

解压完成后,加载模型:

 

from gensim.models import KeyedVectors
filename = 'GoogleNews-vectors-negative300.bin'
model = KeyedVectors.load_word2vec_format(filename, binary=True)

 

3.2 使用预训练的单词向量执行向量算术

 

计算与给定单词最相似的单词:

 

print(model.most_similar('day'))

 

与 “ day ” 最相似的单词如下:

 

[('week', 0.6552987098693848), ('days', 0.6244534254074097), ('morning', 0.606966495513916), ('month', 0.600459098815918), ('hours', 0.5727264881134033), ('afternoon', 0.5589555501937866), ('hour', 0.5330364108085632), ('weekend', 0.5174919962882996), ('evening', 0.5169619917869568), ('time', 0.512919008731842)]

 

接下来,我们可以执行单词向量算术尝试回答以下问题: woman 之于 queen ,相当于 man 之于(__)?也就是计算向量 man + queen - woman

 

result = model.most_similar(positive=['man', 'queen'], negative=['woman'], topn=1)
print(result)

 

以上程序的输出如下:

 

[('king', 0.6958590149879456)]

 

以上代码计算了从 woman 单词向量中减去 man 的单词向量,并将其添加到 queen 的单词向量中,从而得出最接近计算结果向量的单词 king

 

在学习了如何从零开始构建了一个用于计算单词向量的模型之后,我们在本节介绍了如何使用 gensim 库构建 skip-gramCBOW 模型获取单词向量,从而得到了更加准确的单词向量。最后,为了克服数据集中句子数量太少而导致的模型鲁棒性较差的问题,我们介绍了如何使用预训练的单词向量执行向量算术。

 

Be First to Comment

发表回复

您的电子邮箱地址不会被公开。