Word embedding 是很受欢迎的一种文档词汇表。它能够获取文档中单词的上下文,语义和句法相似性,与其他单词的关系等。它是一种语言建模技术,用于将词映射到实数向量。它代表向量空间中具有多个维度的单词或短语。可以使用各种方法(如神经网络,共现矩阵,概率模型等)来生成单词嵌入。
以下面句子为例:
Have a good day and Have a great day。
它们意思相同。如果我们构建一个详尽的词汇表(我们称其为V),则其V = {Have, a, good, great, day}。
现在,让我们为V中的每个单词创建一个独热编码(one-hot)的矢量。我们的独热编码的矢量的长度等于V的大小( =5)。除了索引中表示词汇表中相应单词的元素之外,我们将有一个零向量。该特定元素将是一个。下面的编码可以更好地说明这一点。
have= [1,0,0,0,0]'
a = [0,1,0,0,0]'
good = [0,0,1,0,0]'
great = [0,0,0,1,0]'
day = [0,0,0,0,1]'
':代表转置
如果我们尝试可视化这些编码,我们可以想到一个5维空间,其中每个单词占据一个维,而与其余单词无关(沿着其他维没有投影)。这意味着“good”和“great”与“day”和“have”一样,这是不正确的。
我们的目标是使上下文相似的单词占据紧密的空间位置。在数学上,此类矢量之间的角度的余弦值应接近1,即角度接近0。
word2Vec
Word2Vec包含用于生成单词嵌入的模型。这些模型是浅的两层神经网络,具有一个输入层,一个隐藏层和一个输出层。是使用浅层神经网络学习单词嵌入的最流行技术之一。它是由Tomas Mikolov于2013年在Google上开发的。Word2Vec利用两种体系结构:CBOW (Continuous Bag of Words) 和 Skip Gram。
CBOW模型在特定窗口内给定上下文词的情况下预测当前词。输入层包含上下文单词,输出层包含当前单词。隐藏层包含我们要表示输出层上存在的当前单词的维数
Skip Gram可在给定当前单词的情况下预测特定窗口内的周围上下文单词。输入层包含当前单词,输出层包含上下文单词。隐藏层包含我们要表示输入层中当前单词的维数。
代码示例:
alice.txt文档下载
from nltk.tokenize import sent_tokenize, word_tokenize
#sent_tokenize 定量句子成分
#word_tokenize 定量词的成分
import warnings
warnings.filterwarnings(action = 'ignore')
#去除警告
import gensim
from gensim.models import Word2Vec
sample = open("alice.txt", "r",encoding='utf8')
s = sample.read()
#读取文本数据
f = s.replace("\n", " ")
#去掉换行,替换成空格
data = []
# iterate through each sentence in the file
for i in sent_tokenize(f):
#按句子分子
temp = []
for j in word_tokenize(i):
#把分隔的句子再按每个词来分
temp.append(j.lower()) #把每个词变成小写,在处理每个句子时,循环添加到列表当中 ,列表作为每个句子的储存
data.append(temp) #将分隔后每个单词的每个句子,循环添加到列表中,列表所有句子的储存
CBOW 模型
model1 = gensim.models.Word2Vec(data, min_count = 1, size = 100, window = 5)
# # Print results
print("Cosine similarity between 'alice' " +"and 'wonderland' - CBOW : ", model1.similarity('alice', 'wonderland'))
print("Cosine similarity between 'alice' " +"and 'machines' - CBOW : ", model1.similarity('alice', 'machines'))
输出:
Cosine similarity between 'alice' and 'wonderland' - CBOW : 0.99912304
Cosine similarity between 'alice' and 'machines' - CBOW : 0.932251
Skip Gram 模型
model2 = gensim.models.Word2Vec(data, min_count = 1, size = 100,window = 5, sg = 1)
# # Print results
print("Cosine similarity between 'alice' " +"and 'wonderland' - Skip Gram : ", model2.similarity('alice', 'wonderland'))
print("Cosine similarity between 'alice' " +"and 'machines' - Skip Gram : ", model2.similarity('alice', 'machines'))
输出:
Cosine similarity between 'alice' and 'wonderland' - Skip Gram : 0.8836175
Cosine similarity between 'alice' and 'machines' - Skip Gram : 0.8675665
两者都有自己的优点和缺点。根据Mikolov的说法,Skip Gram可以很好地处理少量数据,并且可以很好地代表稀有单词。
另一方面,CBOW速度更快,对于更频繁的单词具有更好的表示
补充:Skip Gram
这里我们将继续以Skip Gram做一些解释分析,为了让我们能够更加对Skip Gram有个了解。
我们将训练神经网络执行以下操作。给定句子中间的特定单词(输入单词),查看附近的单词并随机选择一个。网络将告诉我们词汇中每个单词成为我们选择的“附近单词”的可能性。
附近单词其实指的就是上述代码的中window窗口参数所提供的单词:典型的窗口大小可能是5,这意味着后面5个单词,前面5个单词(共10个)
输出概率将与找到输入单词附近的每个词汇单词的可能性有关。例如,如果您给受过训练的网络输入了单词“Soviet(苏联)”,则“Union(联盟)”和“Russia(俄罗斯)”等单词的输出概率比“watermelon(西瓜)”和“kangaroo(袋鼠)”等无关的单词更高。
通过向神经网络提供训练文档中找到的单词对来训练神经网络。下面的示例显示了一些训练样本(单词对),我们将从句子“The quick brown fox jumps over the lazy dog”中得到。这里仅使用2的小窗口作为示例。
以蓝色突出显示的单词是输入单词
然后通过统计训练样本中词对的个数,并得出对于词的概率
这里我们假设用10000个独立的词汇构成词汇表,我们将把像“ants”这样的输入词表示为一个独热编码(one-hot)向量。该向量将具有10,000个成分(词汇中的每个单词一个),我们将在与“ants”对应的位置放置1,在所有其他位置放置0。
网络的输出是简单的向量(也具有10,000个组成部分),同样对应我们的词汇表中的每个单词,其中包含随机选择的邻近单词就是该词汇表单词的概率。
这是我们的神经网络的架构。
隐藏层神经元上没有激活功能,但输出神经元使用softmax。
当在单词对上训练此网络时,输入是代表输入单词的独热(one-hot)向量,训练输出也是代表输出单词的独热(one-hot)向量。但是,当您在一个输入单词上评估经过训练的网络时,输出向量实际上将是一个概率分布(即一堆浮点值,而不是一个独热(one-hot)向量)
这里示例具有300个特征的单词向量。因此,隐藏层将由权重矩阵表示,该矩阵具有10,000行(词汇表中的每个单词一个)和300列(每个隐藏神经元一个)。数据资料
此权重矩阵的行,这些实际上就是我们的单词向量
因此,所有这一切的最终目标实际上只是学习此隐藏层权重矩阵-完成后,我们只需要将输出层输出即可!
不过,让我们回到要训练的模型定义中来。
问:独热向量几乎全为0,有什么作用?
答:如果将1 x 10,000单热点向量乘以10,000 x 300矩阵,则将仅选择对应于“1”的矩阵行。如下展示个小例子:
这意味着该模型的隐藏层实际上只是用作查找表。隐藏层的输出只是输入单词的词向量。
然后1 x 300
,“ants”的词向量被馈送到输出层。输出层是softmax回归分类器。但它的要点是,每个输出神经元(每在我们的词汇表中的每个单词)会产生0和1之间的输出,所有这些输出值的总和加起来到1。
具体来说,每个输出神经元都有一个权重向量,将其与隐藏层的单词向量相乘,然后将函数应用于exp(x)
结果。最后,为了使输出总和为1,我们将这个结果除以所有10,000个输出节点的结果之和。
这是计算单词“ car”的输出神经元输出的示例。
对于输入之前的单词和之后的单词,它不会学习不同的概率集。为了理解其含义,可以说在我们的训练语料库中,单词“ York”的每个出现都以单词“ New”开头。也就是说,至少根据训练数据,“ New”将在“ York”附近的可能性为100%。但是,如果我们在“York”附近选择10个单词并随机选择其中一个,则“new”的可能性不是100%;您可能在附近选择了其他单词之一。
如果两个不同的词具有非常相似的“上下文”(即,它们周围可能出现什么词),则我们的模型需要为这两个词输出非常相似的结果。神经网络为这两个单词的一种方法是,如果单词向量相似,输出相似上下文预测。因此,如果两个单词具有相似的上下文,那么我们的网络便会为这两个单词学习相似的单词向量!
两个单词具有相似的上下文意味着什么?我认为您可以期望“intelligent”和“smart”等同义词具有非常相似的上下文。或者,诸如“engine”和“transmission”之类的相关词也可能具有相似的上下文。
在我给出的示例中,我们有包含300个成分的单词向量和10,000个单词的词汇。回想一下,神经网络有两个权重矩阵-隐藏层和输出层。这两个层都将具有一个权重矩阵,每个矩阵的权重为300 x 10,000 = 3百万!
在较大的神经网络上运行梯度下降会很慢。更糟糕的是,您需要大量的训练数据才能调整这么多的权重并避免过度拟合。数百万的重量乘以数十亿的训练样本意味着训练这种模型将是野兽。
Word2Vec的作者在第二篇论文中通过以下两项创新解决了这些问题:
- 对频繁的单词进行二次采样以减少训练示例的数量。
- 使用他们称为“负采样”的技术来修改优化目标,这会使每个训练样本仅更新模型权重的一小部分。
值得注意的是,对频繁出现的单词进行二次采样并应用负采样不仅减轻了训练过程的计算负担,而且还提高了它们产生的单词向量的质量。
参考
Word2Vec Tutorial - The Skip-Gram Model
Python | Word Embedding using Word2Vec
Introduction to Word Embedding and Word2Vec
Word2Vec Tutorial Part 2 - Negative Sampling