一文带你通俗易懂地了解word2vec原理

2022-08-29 13:58:59 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

目录

  • 词向量表示
    • 一、one hot 表示
    • 二、分布式表示
  • Word embedding
  • 语言模型(Language Modeling)
  • 语言模型训练(Language Model Training)
  • CBOW(Continuous Bag of Words)
  • Skipgram
    • Skipgram介绍
    • CBOW和Skipgram比较
    • 再看训练过程
  • Negative Sampling(负采样)
  • Skipgram with Negative Sampling (SGNS,负采样的Skipgram)
  • Word2vec训练过程
  • Window Size and Number of Negative Samples(窗口及负采样样本大小的讨论)

词向量表示

单词表 V = { ω 1 , ω 2 , . . . , ω n } V = { ω_1, ω_2, … , ω_n} V={ ω1​,ω2​,...,ωn​}

一、one hot 表示

ω 1 = [ 1 0 0 . . . 0 ] T ω_1 = [1 0 0 … 0 ]^T ω1​=[1 0 0 ... 0]T ω 2 = [ 0 1 0 . . . 0 ] T ω_2 = [0 1 0 … 0 ]^T ω2​=[0 1 0 ... 0]T …

优点: 可解释性强(每一列都代表一个单词,第一列为1就是第一个单词,第二列为1就是第二个单词)

缺点:1. 维度高    2. 任取 i , j ( i ≠ j ) i,j(i≠j) i,j(i​=j),都有 ω i ⋅ ω j = 0 ω_i · ω_j = 0 ωi​⋅ωj​=0,很难去表示两个单词的相似性(比如自然语言处理和NLP本质上是同一含义,但one hot将其认定为两个不同的词)

二、分布式表示

ω 1 = [ x 1 x 2 x 3 . . . x d ] T ω_1 = [x_1 x_2 x_3 … x_d ]^T ω1​=[x1​ x2​ x3​ ... xd​]T

优点:1. 维度低    2. 每一位用实数来表示    3. 能够计算单词之间的相似度

Word embedding

将单词转化为向量也可以称为词嵌入(word embedding)

例如单词“king”的word embedding(在维基百科上训练的GloVe vector):

[ 0.50451 , 0.68607 , -0.59517 , -0.022801, 0.60046 , -0.13498 , -0.08813 , 0.47377 , -0.61798 , -0.31012 , -0.076666, 1.493 , -0.034189, -0.98173 , 0.68229 , 0.81722 , -0.51874 , -0.31503 , -0.55809 , 0.66421 , 0.1961 , -0.13495 , -0.11476 , -0.30344 , 0.41177 , -2.223 , -1.0756 , -1.0783 , -0.34354 , 0.33505 , 1.9927 , -0.04234 , -0.64319 , 0.71125 , 0.49159 , 0.16754 , 0.34344 , -0.25663 , -0.8523 , 0.1661 , 0.40102 , 1.1685 , -1.0137 , -0.21585 , -0.15155 , 0.78321 , -0.91241 , -1.6106 , -0.64426 , -0.51042 ]

这是一个有50个数字的列表。我们不能通过看这些值来了解更多。但是我们把它形象化一点,这样可以比较其他的向量:把这些数字放在一行,根据单元格的值(接近2时为红色,接近0时为白色,接近-2时为蓝色)对它们进行颜色编码:

只查看表示单元格值的颜色,现在对比一下“King”和其他单词:

“man”和“woman”之间的相似度比他们与“king”之间的相似度要高得多(如下图画框部分)。这说明了这些向量表示捕获了这些词的很多信息/含义/关联。

展示嵌入特性的著名例子是类比的概念。我们可以添加和减去单词嵌入,并得到有趣的结果。最著名的例子是公式 “king”-“man” “woman” ~= “queen”:

使用python中的Gensim库,我们可以加和减单词向量,它会找到与结果向量最相似的单词。该图像显示了最相似的单词列表,每个单词都有其余弦相似度。

我们可以把这个类比形象化,就像我们之前做的那样:

  现在我们已经学习了经过训练的单词嵌入,让我们进一步了解训练过程。但是在我们开始word2vec之前,我们需要看看单词嵌入的概念母体:神经语言模型。

语言模型(Language Modeling)

一个NLP应用程序最好的例子之一就是智能手机键盘的下一个单词预测功能,这是一个数十亿人每天使用数百次的功能。

下一个单词的预测是一项可以通过语言模型来完成的任务。语言模型可以获取一个单词列表(假设是两个单词),并尝试预测紧随其后的单词。

在上面的截图中,我们可以把这个模型想象成一个接受了这两个绿色单词(thou shall)的模型,并返回了一个建议列表(“not”是概率最高的那个):

我们可以把模型想象成:

但实际上,该模型并不只输出一个单词。它实际上为它所知道的所有单词(模型的“词汇量”,范围从几千个单词到超过100万个单词)输出一个概率分数。然后键盘应用程序必须找到得分最高的单词,并将它们呈现给用户。

神经语言模型的输出是该模型所知道的所有单词的概率得分,也就是说thou shalt的下一个单词有40%的概率是not,0.1%的概率是aaron等。我们在这里把概率表示成百分比,但40%实际上可以用输出向量的0.4表示。

早期的神经语言模型(Bengio 2003)经过训练后,会分三个步骤计算预测结果:

当我们讨论embedding时,第一步是最重要的。训练的结果之一就是这个矩阵,它包含了我们词汇表中每个单词的embedding。在预测时间,我们只是查找输入单词的embedding,并利用它们来计算预测:

现在,让我们转向训练过程,以了解这个embedding矩阵是如何开发出来的。

语言模型训练(Language Model Training)

当我们开始的时候,窗口是在句子的前三个单词上:

取前两个词为特征,第三个词为标签:

然后我们将窗口滑动到下一个位置,并创建第二个样本:

很快,我们就有了一个更大的数据集,其中的单词往往出现在不同的单词对之后:

实际上,当滑动窗口时,模型也会进行训练。但是从逻辑上将“数据集生成”阶段与训练阶段分开会更清楚。除了基于神经网络的语言建模方法外,一种被称为N-grams的技术也经常被用来训练语言模型。

CBOW(Continuous Bag of Words)

请填写以下空格中的内容:

在这里给出的上下文是空格之前的五个单词(以及前面提到的“bus”)。大多数人都会猜出空格中应该填bus。但如果再给你一条信息——在空格后面加一个单词,那会改变你的答案吗?

这完全改变了空格中内容的词性。现在空格中最有可能填“红色”这个词。我们从中学到的是一个特定单词前后的单词都有信息价值。事实证明,考虑两个方向(我们猜测的单词的左边和右边)可以更好地嵌入单词。让我们看看如何调整我们训练模型的方式来解释这一点。

除了看目标词前的两个词,我们还可以看目标词后的两个词。

如果我们这样做,我们实际上正在构建和训练模型的数据集将是这样的:

这被称为CBOW(Continuous Bag of Words),在word2vec的一篇论文中有描述。 另一种体系结构(skipgram)也倾向于显示出色的结果,但做的事情有点不同。

Skipgram

Skipgram介绍

这个体系结构不是根据上下文(单词之前和之后的单词)猜测单词,而是尝试使用当前单词猜测相邻的单词。我们可以把它在训练文本上滑动的窗口想象成这样:

粉红色框有不同的颜色,因为这个滑动窗口实际上在我们的训练数据集中创建了四个独立的样本:

这种方法称为skipgram架构。我们可以将滑动窗口可视化为这样做:

这将把这四个样本添加到我们的训练数据集:

然后我们将窗口滑动到下一个位置:

这就产生了我们接下来的四个样本:

在接下来的几次滑动中,我们会得到更多的样本:

CBOW和Skipgram比较

CBOW结构图:

skipgram结构图:

CBOW输入是某一个特征词的上下文相关的词对应的词向量,而输出就是这特定的一个词的词向量,即先验概率。 Skip-Gram模型和CBOW的思路是反着来的,即输入是特定的一个词的词向量,而输出是特定词对应的上下文词向量,即后验概率。

再看训练过程

现在我们有了从现有的运行文本中提取的skipgram训练数据集,让我们看看如何使用它来训练预测邻近单词的基本神经语言模型。

我们从数据集中的第一个样本开始。我们获取特征并将其反馈给未经训练的模型,要求它预测一个合适的邻词。

该模型执行这三个步骤,并输出一个预测向量(为其词汇表中的每个单词分配一个概率)。由于模型未经训练,现阶段的预测肯定是错误的。但是没关系。我们知道它应该猜到的单词——我们当前用来训练模型的行中的 标签/输出 单元格:

“target vector”指的是目标单词的概率为1,其他单词的概率为0。就是说我们实际期望的是得到thou的概率为1,得到其他单词的概率为0。 正确的预测值离我们有多远?我们将这两个向量相减,得到一个误差向量:

这个误差向量现在可以用来更新模型,所以下一次,当它将not作为输入时,它更有可能预测到thou。

这就是训练的第一步。我们继续对数据集中的下一个样本进行相同的处理,然后是下一个,直到我们覆盖了数据集中的所有样本。这就是训练的一个epoch。我们重复做了很多次,然后我们就得到了我们训练过的模型我们可以从中提取embedding矩阵并将其用于任何其他应用。

虽然这扩展了我们对过程的理解,但它仍然不是word2vec实际上是如何训练的。我们忽略了一些关键的观点。

Negative Sampling(负采样)

回想一下这个神经语言模型计算预测的三个步骤:

从计算的角度来看,第三步花费的代价是非常高的——特别是知道我们将在我们的数据集中对每个训练样本做一次(很容易数千万次)。我们需要做些事情来提高效率。

一种方法是将我们的目标分成两个步骤:

  1. 生成高质量的word embeddings(不要担心下一个单词的预测)。
  2. 使用这些高质量的word embeddings来训练语言模型(进行下一个单词的预测)。

我们将关注第一步。在这篇文章中,我们将关注embeddings。为了使用高性能的模型生成高质量的embeddings,我们可以将模型的任务从预测邻近的单词切换一下:

然后将它切换到一个模型,该模型接受输入和输出单词,并输出一个分数,表明它们是否相邻(0表示“不相邻”,1表示“相邻”)。

这个简单的改变将我们需要的神经网络模型转变为逻辑回归模型——因此计算起来更简单、更快。 这要求我们改变数据集的结构——标签现在是一个值为0或1的新列。它们都是1,因为我们添加的所有单词都相邻。

现在可以以极快的速度计算出来——在几分钟内处理数百万个样本。但有一个漏洞我们需要弥补,如果我们所有的样例都是积极的(target:1),我们就会得到一个“聪明模型”,它总是返回1(target=1)——以达到100%的准确性,但其实什么也学不到,并产生垃圾的embeddings。

为了解决这个问题,我们需要向我们的数据集引入负样本——不是相邻单词的样本。我们的模型需要为这些样本返回0。现在,这是一个模型必须努力解决的挑战——但仍然以极快的速度。

但是我们要将什么作为输出单词填充呢? 我们从自己的词汇中随机抽取单词(一般负采样5个可能好一些,Gensim库中默认的参数):

这个想法是受到噪声对比估计的启发。我们将实际信号(相邻词的正面样本)与噪声(随机选择的,不是相邻词)进行对比。这导致了计算效率和统计效率的巨大权衡。

Skipgram with Negative Sampling (SGNS,负采样的Skipgram)

我们现在已经讨论了word2vec中的两个中心思想:作为一对,它们被称为skipgram with negative sampling。

Word2vec训练过程

在训练过程开始之前,我们对训练模型所针对的文本进行预处理。在这一步中,我们决定词汇表的大小(我们将其称为vocab_size,假设它是10,000)以及哪些单词属于它。 在训练阶段的开始,我们创建两个矩阵——一个Embedding矩阵和一个Context矩阵。这两个矩阵对词汇表中的每个单词都有一个嵌入(所以vocab_size是它们的维度之一)。第二个维度是我们希望每次嵌入的长度(embedding_size = 300是一个常用的值,但是我们已经在本文的前面看到了一个50的例子)。

在训练过程的开始,我们用随机值初始化这些矩阵,然后我们开始训练。在每个训练步骤中,我们取一个积极样本和它相关的消极样本。让我们看看第一组:

现在我们有四个单词:输入单词not和输出/上下文单词thou(实际的邻居),aarontaco(负样本)。我们继续查找它们的embeddings——对于输入词,我们在Embedding矩阵中查找。对于上下文单词,我们查看Context矩阵(尽管两个矩阵都对词汇表中的每个单词进行了embedding)。

然后,我们取input embedding与每一个context embedding的点积。在每种情况下,都会得到一个数字,这个数字表示输入单词(input )和上下文单词(context)的embedding的相似性

现在我们需要一种方法将这些分数转换成类似概率的东西——我们需要它们都是正的,并且值在0到1之间,这是sigmoid的一项重要任务。

对于这些例子,我们现在可以把sigmod的输出当作模型的输出。可以看到,taco得分最高,aaron得分最低,无论在做sigmod计算之前还是之后。 现在,未经训练的模型已经做出了预测,并看到似乎我们有一个实际的目标标签来比较,让我们计算模型的预测有多少误差。要做到这一点,我们只需从目标标签中减去sigmoid分数。

error = target – sigmoid_scores 接下来是“机器学习”的“学习”部分。现在我们可以使用这个错误分数来调整not, thou, aaron和taco的embedding,这样下次我们进行这个计算时,结果将更接近目标分数。

训练至此结束。我们从这一步中出现的单词(not, thou, aaron和taco)的embeddings效果稍好一些。现在我们进入下一个步骤(下一个正样本和相关的负样本),并再次进行相同的过程。

当我们在整个数据集中循环多次时,embeddings将继续得到改善。然后,我们可以停止训练过程,抛弃Context矩阵,并使用Embeddings矩阵作为下一个任务的预训练embeddings。

Window Size and Number of Negative Samples(窗口及负采样样本大小的讨论)

word2vec训练过程中的两个关键超参数是窗口大小和负样本的数量。

不同的窗口大小可以更好地处理不同的任务。启发式是较小的窗口大小(2-15),可以表明,具有高相似性得分embeddings的两个词是可以互换的(注意反义词常常可以互换,如果我们只考虑周围的词——例如good和bad通常出现在类似的情况下)。较大的窗口尺寸(15-50或更多)会导致相似性更能表明单词相关性的embeddings。在实践中,常常需要提供注释来指导嵌入过程,从而为任务提供有用的相似感。Gensim的默认窗口大小是5(除了输入字本身之外,输入字之前和之后各有两个字)。

负样本的数量是训练过程的另一个因素。原论文规定5-20为良好的负样本数量。它还指出,当你有足够大的数据集时,2-5似乎就足够了。Gensim的默认值是5个负样本。

参考文章:The Illustrated Word2vec

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/145586.html原文链接:https://javaforall.cn

0 人点赞