在自然语言处理(NLP)中,我们经常将词映射到包含数值的向量中,以便机器可以理解它。词嵌入是一种映射,允许具有相似含义的单词具有相似的表示。本文将介绍两种最先进的词嵌入方法,Word2Vec和FastText以及它们在Gensim中的实现。
表示单词的传统方式是单热(one-hot)向量,其本质上是仅具有一个元素为1且其他为0的向量。向量的长度等于语料库中的总唯一词汇(去重后)的大小。通常,这些独特的单词按字母顺序编码。也就是说,你应该希望one-hot向量用于表示以“a”开头的单词,其中“1”的索引较低,而以“z”开头的那些单词,“1”的索引较高。
虽然这种单词表示简单易行,但存在一些问题。首先,你不能推断两个单词之间的任何关系根据他们的one-hot表示。例如,“endure”和“tolerate”这两个词虽然具有相似的含义,但它们的目标“1”彼此相距很远。此外,稀疏性是另一个问题,因为向量中存在许多冗余“0”。这意味着我们浪费了大量的空间。我们需要更好地表达单词以解决这些问题。
Word2Vec是这些问题的有效解决方案,它利用了目标词的上下文。本质上,我们希望使用周围的单词来表示目标单词,通过神经网络的隐藏层对单词表示进行编码。
有两种类型的Word2Vec,Skip-gram和Continuous Bag of Words(CBOW)。我将在下面的段落中简要描述这两种方法是如何工作的。
对于skip-gram,输入是目标词,而输出是目标词周围的词。例如,在句子“I have a cute dog”中,输入为“a”,而输出为“I”,“have”,“cute”和“dog”,假设窗口大小为5.所有输入和输出数据都具有相同的维度和一个one-hot编码。网络包含1个隐藏层,其维度等于嵌入大小,小于输入/输出向量大小。在输出层的末端,应用softmax激活函数,以便输出向量的每个元素描述特定单词在上下文中出现的可能性。下图显示了网络结构。
Skip-gram(https://www.analyticsvidhya.com/blog/2017/06/word-embeddings-count-word2veec/)
Skip-gram(https://www.analyticsvidhya.com/blog/2017/06/word-embeddings-count-word2veec/)
通过将该单词的one-hot表示输入网络中之后提取隐藏层,可以获得目标词的词嵌入。
使用skip-gram,表示维度从词汇量大小(V)减小到隐藏层(N)的长度。此外,就描述单词之间的关系而言,向量更“有意义”。通过减去两个相关词而获得的向量有时表达一个有意义的概念,如性别或动词时态,如下图所示(维数减少)。
Visualize Word Vectors(https://www.tensorflow.org/images/linear-relationships.png)
连续词袋(CBOW)与skip-gram非常相似,只是它交换输入和输出。我们的想法是,在给定上下文的情况下,我们想知道哪个词最有可能出现在其中。
Skip-gram(https://www.analyticsvidhya.com/blog/2017/06/word-embeddings-count-word2veec/)
CBOW(https://www.analyticsvidhya.com/blog/2017/06/word-embeddings-count-word2veec/)
Skip-gram和CBOW之间最大的区别在于生成单词向量的方式。对于CBOW,将目标的所有示例输入到网络中,并取出提取的隐藏层的平均值。例如,假设我们只有两个句子,“He is a nice guy”和“She is a wise queen”。为了计算单词“a”的词表示,我们需要在神经网络中输入这两个例子,“He is nice guy”和“She is wise queen”并取隐藏层中的值的平均值。Skip-gram仅输入一个且仅一个目标词的one-hot向量作为输入。
据称,Skip-gram在罕见的词汇方面往往做得更好。然而,Skip-gram和CBOW的性能大致相似。
我将向你展示如何使用Gensim,强大的NLP工具包和TED Talk数据集表示词嵌入。
首先,我们使用urllib下载数据集,从文件中提取副标题。
import numpy as np
import os
from random import shuffle
import re
import urllib.request
import zipfile
import lxml.etree
#download the data
urllib.request.urlretrieve("https://wit3.fbk.eu/get.php?path=XML_releases/xml/ted_en-20160408.zip&filename=ted_en-20160408.zip", filename="ted_en-20160408.zip")
# extract subtitle
with zipfile.ZipFile('ted_en-20160408.zip', 'r') as z:
doc = lxml.etree.parse(z.open('ted_en-20160408.xml', 'r'))
input_text = '\n'.join(doc.xpath('//content/text()'))
让我们看一下input_text变量的存储,如下图所示。
input_text
input_text
显然,有些冗余信息对我们理解谈话没有帮助,例如括号中描述声音的单词和说话者的姓名。我们用正则表达式去除这些单词。
# remove parenthesis
input_text_noparens = re.sub(r'\([^)]*\)', '', input_text)
# store as list of sentences
sentences_strings_ted = []
for line in input_text_noparens.split('\n'):
m = re.match(r'^(?:(?P<precolon>[^:]{,20}):)?(?P<postcolon>.*)$', line)
sentences_strings_ted.extend(sent for sent in m.groupdict()['postcolon'].split('.') if sent)
# store as list of lists of words
sentences_ted = []
for sent_str in sentences_strings_ted:
tokens = re.sub(r"[^a-z0-9]+", " ", sent_str.lower()).split()
sentences_ted.append(tokens)
现在,sentences_ted已被转换为二维数组,每个元素都是一个单词。让我们打印出第一个和第二个元素。
sentences_ted
这是准备输入Gensim中定义的Word2Vec模型的表单。Word2Vec模型可以通过一行轻松训练,如下面的代码所示。
from gensim.models import Word2Vec
model_ted = Word2Vec(sentences=sentences_ted, size=100, window=5, min_count=5, workers=4, sg=0)
· sentences:切分句子的列表。
· size:嵌入向量的维数
· window:你正在查看的上下文单词数
· min_count:告诉模型忽略总计数小于这个数字的单词。
· workers:正在使用的线程数
· sg:是否使用skip-gram或CBOW
现在,让我们尝试哪些词与“man”这个词最相似。
model_ted.wv.most_similar(“man”)
看起来与men/women/kid相关的单词与“man”最相似。
尽管Word2Vec成功处理了one-hot向量引起的问题,但它有一些局限性。最大的挑战是它无法表示未出现在训练数据集中的单词。尽管使用包含更多词汇表的更大训练集,但很少使用的罕见单词永远不能映射到向量。
FastText是Facebook在2016年提出的Word2Vec的扩展。FastText不是将单个词输入神经网络,而是将词分成几个n-gram(sub-words)。例如,apple这个词的三元组是app,ppl和ple(忽略单词边界的起点和终点)。apple的词嵌入向量将是所有这些n-gram的总和。在训练神经网络之后,我们将根据训练数据集对所有n-gram进行词嵌入。现在可以恰当地表达稀有的单词,因为很可能他们的一些n-gram也出现在其他单词中。我将在下一节中向你展示如何在Gensim中使用FastText。
与Word2Vec类似,我们只需要一行来指定训练词嵌入的模型。
from gensim.models import FastText
model_ted = FastText(sentences_ted, size=100, window=5, min_count=5, workers=4,sg=1)
让我们尝试使用Gastroenteritis这个词,这个词很少使用,也没有出现在训练数据集中。
model_ted.wv.most_similar("Gastroenteritis")
即使训练数据集中不存在Gastroenteritis这个词,它仍然能够确定这个词与某些医学术语密切相关。如果我们在之前定义的Word2Vec中尝试这个,它会弹出错误,因为训练数据集中不存在这样的单词。虽然训练FastText模型需要更长的时间(n-gram的数量>单词的数量),但它比Word2Vec表现更好,并且允许恰当地表示罕见的单词。
你已经了解了Word2Vec和FastText以及使用Gensim工具包实现的内容。如果你有任何问题,请随时在下面发表评论。如果你喜欢这篇文章,请确保你在twitter上关注我,这样你就不会错过任何好的机器学习/深度学习博客文章!