一文了解Word2vec 阐述训练流程

  • 个性嵌入(Personality Embeddings)
  • 词嵌入(Word Embeddings)
    • 嵌入向量效果分析
  • 语言模型
    • 模型介绍
    • 模型训练
  • Word2vec训练方法
    • CBOW方法
    • Skip-gram方法
    • CBOW方法与Skip-gram方法总结
  • 重构训练方法
    • 负采样
    • 基于负采样的Skip-gram(SGNS)
  • Word2vec的最终训练方法
  • 附数百个中文Word2vec向量下载地址
  • Word2vec为什么不如BERT模型?

  在机器学习领域,嵌入(embeddings)的概念无疑是其中最令人兴奋的创新之一。想象一下,每当你与SiriGoogle AssistantAlexaGoogle Translate互动,甚至在使用具有下一个词预测功能的手机输入法(比如苹果输入法、搜狗输入法)时,你其实都在享受词嵌入模型带来的便利。这些技术在过去几十年里取得了巨大进步,尤其是近期基于上下文的词嵌入技术的发展,催生了BERTGPT2ChatGPT等领先的预训练模型。

  自2013年以来,Word2vec一直是创建词嵌入的有效方法。它不仅仅是一种词嵌入技术,还在推荐系统和商业非语言任务的理解方面显示出了惊人的效果。像Airbnb、阿里巴巴、SpotifyAnghami这些公司都已经开始利用这一NLP界的精妙技术,并将其应用到生产环境中,从而推动了新一代推荐引擎的发展。

  在本文中,我们不仅将深入探讨嵌入的概念,还会详细解析Word2vec用于生成嵌入的机制。但首先,让我们从一个简单的例子开始:理解如何使用向量来表示事物。你可能不会相信,但一个由五个数字组成的列表(即一个向量)实际上可以代表一个人的个性。

个性嵌入(Personality Embeddings)

  让我们以一个简单的例子来开始:评估一个人的内向或外向程度。 用0到100分来衡量,其中0代表极度内向,而100则意味着极度外向。你可能听说过MBTI或五大性格特征测试,这些测试通过一系列问题,为你的性格的不同方面打分,其中内向/外向只是众多方面中的一个。


  想象一下,如果我的内向/外向得分是38/100,我们可以如下图所示表示这一得分:


  让我们将范围从-1切换到1:


  但是,仅仅一个维度的信息并不足以全面了解一个人。人类性格的复杂性远不止于此。因此,我们增加更多维度,比如测试中的其他特征分数。

  尽管我暂时隐藏了我们正在绘制的具体特征,但这个练习可以帮助你逐渐习惯于在不完全了解每个维度具体含义的情况下,从一个人的个性向量中汲取有价值的信息。

  现在,这个向量开始部分地代表我的个性了。想象一下,如果我需要被一个性格相似的人替换(比如说,我不幸被公交车撞了),我们该如何选择?下图中的两个人,谁与我更相似?

  在处理这类向量数据时,计算相似性得分的一个常见方法是使用余弦相似度。

  然而,仅有两个维度并不足以捕捉人与人之间的复杂差异。心理学的研究表明,至少需要五个主要特征(以及多个次级特征)来更全面地描述一个人。所以,让我们在进行比较时考虑所有这五个维度。

  在五维空间中,我们无法像在二维空间那样直观地绘制向量,这是机器学习中常见的挑战之一。我们需要学会在高维空间中思考。幸运的是,余弦相似性在任意维度中都同样适用:

  在这部分的结尾,我们提出两个核心思想:

  1、我们可以将人(以及事物)表示为数字向量(这对机器来说是个巨大的优势!)。
  2、我们可以轻而易举地计算出这些向量之间的相似度。

词嵌入(Word Embeddings)

  理解了向量的基本概念后,我们现在可以开始探索训练好的词向量(也称为词嵌入)的一些有趣特性。这里有一个例子:GloVe模型在维基百科数据上训练得到的“国王”(king)的词嵌入向量。

[ 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个数字的列表。单纯看这些数字,我们很难得出任何有意义的结论。但如果我们把这些数字可视化,就能更直观地和其他单词的向量进行比较。想象一下,如果我们把这50个数字排成一行,看起来会是怎样?

  我们可以通过颜色来编码这些数字——例如,数值接近2时显示为红色,接近0时为白色,而接近-2时为蓝色。

  这样的颜色编码让我们无需关注具体的数字,而是通过颜色来理解每个数字的大致意义。

  现在,如果我们用这种方式来比较“King”与其他单词的向量,会发现什么呢?你会注意到“Man”和“Woman”之间的相似程度远高于它们与“King”的相似程度。这表明这些词向量在某种程度上捕捉了这些单词的意义和关联。

  接下来,我们通过另一个示例来进一步探索。通过垂直扫描各列,我们寻找颜色相似的列,从而进行比较:

  值得注意的是:

  · 所有这些不同的单词都有一个红色的直柱。它们在那个维度上是相似的(我们不知道每个维度代表什么)
  · 你可以看到“woman”和“girl”在很多地方都很相似。“man”和“boy”也是如此
  · “boy”和“girl”也有彼此相似的地方,但不同于“woman”或“man”。
  · 上图中除了最后一个字其他词都是代表人的字。我们添加了一个对象“water”来显示类别之间的差异。比如,你可以看到蓝色的柱子一直向下,在嵌入“水”之前停止。
  · “king”和“queen”有明显的相似之处,但又与其他词截然不同。

嵌入向量效果分析

  嵌入技术展示了一些不可思议的特性,其中最秀的就是类比关系的处理能力。我们可以通过对单词向量进行加减操作来获得一些有趣的结果。一个经典的例子就是这个等式:“国王(King)”减去“男人(Man)”再加上“女人(Woman)”:

  使用Python中的Gensim库,我们可以轻松地对这些词向量进行加减操作,并找到与结果向量最接近的单词。一个有趣的现象是,这种操作通常会得到一些出人意料的结果。例如,对于上述的等式,虽然得到的向量并不完全等同于“女王(Queen)”,但在我们分析的400,000个单词嵌入中,“女王”却是最接近这个结果的单词。

  在我们深入研究训练过的单词嵌入之前,有必要先了解它们的来源:语言模型。这是单词嵌入概念的核心,并为我们后续讨论Word2Vec奠定了基础。

语言模型

模型介绍

  如果我们要举出NLP应用的一个典型例子,智能手机键盘的下一个单词预测功能无疑是其中之一。想象一下,这个功能每天被数十亿人使用数百次,其影响力和普及程度是显而易见的。

  下一个单词预测是一个典型的语言模型应用场景。简单来说,语言模型可以接收一串单词(比如两个单词),然后尝试预测接下来可能出现的单词。例如,在下面的截图中,模型接收到两个绿色的单词(thou shalt)并返回一个建议列表,其中“not”是最有可能的预测。

  我们可以将这个模型视为一个“黑匣子”,但实际上它的工作原理并非如此简单。

  这个模型不仅仅输出一个单词,而是会计算并输出它所知道的所有单词的概率分数。这些单词的数量可能从几千到超过一百万不等。然后,智能手机键盘应用程序会从这些得分中挑选出最高的几个单词,呈现给用户作为输入建议。

  神经语言模型的输出实际上是对所有已知单词的概率评分。这里我们用百分比来表示这些概率,但在模型的输出向量中,这些百分比实际上是以小数形式出现的,比如40%会表示为0.4。

  经过训练的神经语言模型(比如2003年Bengio团队开发的模型)会经历几个计算步骤来做出预测。

  当我们讨论嵌入时,这些步骤中的第一步尤为重要。嵌入矩阵是训练过程的一个结果,包含了词汇表中每个单词的嵌入向量。在进行预测时,我们只需要查找输入单词的嵌入,并使用它们来计算预测结果。

  现在,让我们深入了解训练过程,探索如何开发这种嵌入矩阵的更多细节。

模型训练

  语言模型在机器学习领域中占据了独特的优势。它们的主要好处在于能够直接利用丰富的文本数据进行训练。想象一下,我们周围充斥着各种书籍、文章、维基百科的内容以及其他形式的文本数据。这与那些需要大量手工特征工程和专门收集数据的机器学习模型形成了鲜明对比。

  在训练过程中,我们通过观察单词倾向于出现在哪些其他单词旁边来获取它们的嵌入。这个过程大致如下:

  1、首先,我们获得了大量的文本数据,例如所有的维基百科文章。
  2、然后,我们设定一个窗口(比如三个单词),在所有文本中滑动这个窗口,为模型生成训练样本。
  3、当窗口在文本中滑动时,我们会生成用于训练模型的数据集。

  为了更清楚地理解这个过程,让我们看看这个滑动窗口是如何处理一个简短短语Thou shalt not make a machine in the likeness of a human mind的:

  一开始,窗口覆盖了句子的前三个词。

  我们将前两个词作为特征,第三个词作为标签,从而生成了数据集中的第一个样本,稍后用于训练语言模型。

  随后,窗口向右滑动到下一个位置,创建了第二个样本。

  这样,我们很快就拥有了一个较大的数据集,其中的单词在不同的词对后面出现。

  实际上,模型通常在我们滑动窗口的同时进行训练。但从逻辑上看,将“数据集生成”和训练阶段分开可能更为清晰。除了基于神经网络的语言建模方法之外,还有一种名为N-grams的技术常被用于训练语言模型(参见《Speech and Language Processing》第三章)。

Word2vec训练方法

  了解到目前为止的内容后,让我们来尝试一个填空游戏:

  假设我提供给你的上下文是一个空白处之前的五个单词,加上“bus”。大部分人可能会直观地认为,紧接着这些单词的应该是“bus”。但如果我进一步透露,这个空白后面紧跟着的是另一个特定的词,这是否会改变你的猜测呢?

  这个额外的信息可能完全改变我们对空白处应填单词的预测。原先我们猜测的“bus”现在可能被另一个与新信息更匹配的词所取代。这表明,一个特定单词的前后上下文都蕴含着重要的信息。事实上,同时考虑一个词的左右上下文(即我们尝试预测的单词的前后单词)可以生成更准确的单词嵌入。下面,让我们探索如何调整我们的训练模型,以便更好地利用这一发现。

CBOW方法

  在训练语言模型时,不仅可以考虑目标词之前的单词,还可以考虑其后面的单词。

  这样一来,我们构建和训练模型时考虑的数据集将会是这样的:

  这种方法在Word2Vec的研究论文中被称为CBOW架构。与此同时,还有另一种架构也显示出良好的效果,但其方法略有不同。

Skip-gram方法

  这种架构并不是根据目标词的上下文(即目标词前后的单词)来预测目标词,而是使用当前的单词来预测其相邻的单词。我们可以将其在训练文本中滑动的窗口想象成以下形式:

  这里,绿色的槽代表输入的单词,而每个粉红色的方框代表可能的输出。每个粉红色方框的不同阴影表示这个滑动窗口实际上在我们的训练数据集中创建了四个独立的样本:

  这种方法被称为“跳跃模型”(Skip-gram)架构。我们可以想象滑动窗口按以下方式执行操作:

  这样,就在我们的训练数据集中添加了这四个样本:

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

  这样就生成了接下来的四个样本:

  经过几个位置的滑动之后,我们的数据集中就积累了更多这样的例子:

CBOW方法与Skip-gram方法总结

  从直观上理解,Skip-gram是给定input word来预测上下文。而CBOW是给定上下文,来预测input word。然而Word2Vec采用的是Skip-gram方法。

  重要问题:CBOW方法与Skip-gram方法有什么区别?为什么Word2vec用的是Skip-gram” />  首先贴个回答,https://zhuanlan.zhihu.com/p/37477611,回答中大概的意思有两点:
  一是Skip-gram学习次数是CBOW的K倍,K是窗口大小。
  二是Skip-gram采取一对一优化,CBOW采取一对多优化。他的核心观点是同一个词1次一对一优化比K次一对多优化好。

  我赞成观点二,反对观点一。下面给出理由:

  假设一个序列[a,b,c,d,e],窗口大小为3,CBOW造的数据集为([ac,b][bd,c][ce,d]);Skip-gram造的数据集为([b,a][b,c][c,b][c,d][d,c][d,e])。

  对于观点一,虽然CBOW只有3条数据,但对词c梯度更新时有2次;Skip-gram有6条数据,其实对词c梯度更新时也是2次。所以从更新次数上说,两种方法是一样的。
  对于观点二,CBOW标签为b时,同时更新ac的词向量;Skip-gram标签为b时,单独更新c的词向量,区别就在这里。理解为单独更新一个词的梯度比批量更新多个词的梯度更能学习到知识。

重构训练方法

  拥有了从现存运行文本中提取的Skip-gram训练数据集,我们现在可以探索如何使用这些数据来训练一个基本的神经语言模型,该模型专注于预测相邻的单词。

  首先,我们从数据集中挑选出第一个样本。我们取出这个特征,并将其输入到一个未经训练的模型中,要求它预测一个合适的相邻单词。

  这个模型会执行以下三个步骤,并输出一个预测向量,为词汇表中的每个单词分配一个概率。由于模型尚未经过训练,其在这一阶段的预测很可能是错误的。不过这没关系,因为我们已经知道正确的答案,即我们用来训练模型的当前行中的标签/输出单元格。

  那么,模型的预测和实际目标之间有多大差距呢?我们通过减去这两个向量来得到一个误差向量:

  这个误差向量接下来用于更新模型,这样当下一次模型接收到“not”作为输入时,它就更有可能做出正确的预测。

  这样,我们完成了对第一个样本的训练。接下来,我们对数据集中的下一个样本执行相同的步骤,然后再下一个,直至覆盖整个数据集。这完成了一个训练周期。我们重复进行多个周期,最终得到一个训练有素的模型,可以从中提取嵌入矩阵,用于其他各种应用。

  尽管这个过程增加了我们对训练过程的理解,但它仍然没有完全揭示Word2Vec实际上是如何训练的。我们还遗漏了一些关键的概念。

负采样

  在回顾这个神经语言模型计算其预测的三个步骤时,我们注意到第三步在计算上非常昂贵。特别是考虑到我们将对数据集中的每个训练样本执行这一过程(这很容易达到数千万次),我们需要寻找方法来提高性能。

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

  1. 生成高质量的单词嵌入(暂不考虑下一个单词的预测)。
  2. 使用这些高质量的嵌入来训练语言模型(进行下一个单词的预测)。

  我们现在专注于第一步。为了生成高质量的嵌入并利用高性能模型,我们可以将模型的任务从预测相邻单词:

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

  这种简单的转换意味着我们需要的模型从复杂的神经网络变为了更简单、计算速度更快的逻辑回归模型。

  为了适应这个转换,我们需要调整数据集的结构——现在,标签成为一个新列,值为0或1。由于我们添加的所有单词对都是相邻的,因此它们的标签都是1。

  这使得计算能够极快地进行——在几分钟内就能处理数百万个样本。但我们需要解决一个问题。如果我们的所有样本都是正面的(目标为1),则存在一个模型总是返回1的可能性,虽然实现了100%的准确率,但实际上没有学到任何东西,生成的嵌入也是无用的。

  为了解决这个问题,我们需要在数据集中引入负样本,即非相邻词的样本。我们的模型需要为这些样本返回0。现在,这成为了一个模型必须努力解决的真正挑战,但处理速度仍然非常快。

  对于数据集中的每个样本,我们都添加了对应的负例。它们有相同的输入单词和一个0标签,但输出词是如何选择的呢?我们从词汇表中随机选取。

  这种方法的灵感来自于噪声对比估计(NCE)。我们通过将实际信号(相邻单词的正例)与噪声(随机选择的非相邻单词)进行对比,实现了计算和统计效率的重大改进。

基于负采样的Skip-gram(SGNS)

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

Word2vec的最终训练方法

  现在我们已经理解了Skip-gram和负采样的核心概念,就让我们深入了解实际的Word2Vec训练过程。

  在训练开始前,我们对目标文本进行预处理。这一步骤涉及确定词汇表的大小(假设为10,000)和哪些单词将被包括在内。

  训练的初期,我们创建两个矩阵:嵌入矩阵和上下文矩阵。这两个矩阵都包含词汇表中每个单词的嵌入,其中一个维度是词汇量(vocab_size),另一个维度是嵌入长度(例如,一个常见的值是300,尽管我们之前看到了一个50维的例子)。

  这些矩阵在训练开始时被随机初始化。然后,训练过程启动。在每一步训练中,我们选取一个正例和相关的负例来训练。让我们看看第一组样本:

  我们的样本包括输入词“not”和输出/上下文词:“thou”(实际邻居)、“aaron”和”taco”(负例)。我们查找它们的嵌入——输入单词的嵌入在嵌入矩阵中,而上下文单词的嵌入则在上下文矩阵中。

  接下来,我们计算输入嵌入与每个上下文嵌入的点积。每个点积都产生一个数字,表示输入和上下文嵌入之间的相似度。

  现在,我们需要将这些分数转换成类似概率的值——它们需要是正数且在0到1之间。这是sigmoid函数的理想应用场景。

  将sigmoid函数的输出视为模型对这些示例的预测。你会注意到,”taco”的分数最高,而”aaron”的分数最低。

  这时,未经训练的模型已做出了预测,我们有实际的目标标签可用于比较,让我们计算模型预测的误差有多大。计算方法是目标标签减去sigmoid分数。

  现在进入“机器学习”中的“学习”阶段。我们可以利用这个误差分数来调整”not”、“thou”、”aaron”和”taco”的嵌入,使得下次我们进行这个计算时,结果更接近目标分数。

  这样,一次训练步骤就完成了。这些词(“not”、“thou”、“aaron”和”taco”)的嵌入现在略有改进。我们继续进行下一个训练步骤——处理下一个正例及其相关的负例,并重复同样的过程。

  随着我们多次遍历整个数据集,嵌入逐渐得到改善。最终,我们可以停止训练过程,弃用上下文矩阵,并将嵌入矩阵用作下一任务的预训练嵌入。

附数百个中文Word2vec向量下载地址

https://github.com/Embedding/Chinese-Word-Vectors/blob/master/README_zh.md

Word2vec为什么不如BERT模型?

  理解了这两个模型之后,很难评这个问题。

  Word2vec其实是一个字典,一个词对应一个固定的向量;而BERT是模型,是预训练模型,一个词输入BERT之后,其实是要经过神经网络模型计算得到表征向量的;所以它俩就不是一个东西,似乎没有可比性。

  但是,我们又经常把BERT整体当作一个Embedding层,所以要是从词向量角度,它俩又可以比一比:

  模型区别、一词多义、词汇表大小、句子编码

  模型区别 Word2Vec的核心特点是它的嵌入即是模型本身。无论是CBOW还是Skip-gram模型,Word2Vec没有除嵌入以外的其他参数。这使得模型结构相对简单,但也意味着其功能有一定的限制。

  一词多义 Word2Vec的一个主要限制是对于每个词条(token)只有一个固定表示,这一点长期以来一直受到批评。由于Word2Vec是基于词袋模型(BOW)的,它缺乏对单词位置的建模能力。虽然后续出现了如structure-Word2Vec等改进工作,但这些尝试依然没能完全解决这些固有的问题。

  词汇表大小 Word2Vec的词汇表通常非常庞大,常见的词汇量达到100万。相比之下,BERT系列模型由于掩蔽语言模型(MLM)任务的限制和字节对编码(BPE)分词器的流行,通常单一语言的词汇量大约在3万到5万之间。

  句子编码 BERT系列模型的一个显著优势是同时对token和句子进行建模。在Transformer模型中,每个token的表示都是基于对上一层所有token使用注意力机制的结果,这使得获取句子级别的表示变得非常简单。通常只需要使用如[CLS]这样的特殊token即可,从而充分利用模型的所有能力。相较而言,传统的Word2Vec模型没有直接获取句子级别表示的有效方法。一般做法是对所有词向量进行平均,但这并不等同于显式地对句子表示进行建模。