Word2Vec Tutorial - The Skip-Gram Model

本文章内容主要翻译自Word2Vec Tutorial - The Skip-Gram Model,英文水平还可以的建议去看原文。

word2vec是word embedding(词向量)的一种浅层神经网络训练方法。通俗的讲就是把一个词变成一个词向量。这篇文章主要介绍word2vec 中的skip gram模型。word2vec还用到了CBOW模型,这个在本文不做介绍。

Part 1

The Model

skip-gram模型结构实际上出乎意料的简单。
我们先从比较宏观的角度来看看我们到底要干嘛。Word2Vec使用了一个可能你在机器学习的其他地方也见过的trick。我们打算训练一个单隐藏层的简单网络模型来完成一个明确的任务。但是我们实际上并不用这个我们训练的神经网络来完成这个任务。我们的目的是为了学习到隐藏层的权重。实际上这些权重实际上就是 word vector。所以说我们构建这个神经网络只是个假任务,真任务是要它的隐藏层权重。

另一个你能够看到这个trick的地方是非监督特征的学习,你在隐藏层训练一个auto-encode用来压缩输入的vector。然后在输出的时候再进行解压回原样。当训练好后,直接去掉输出层(解压的部分),只使用隐藏层。这是在没有标签的训练数据的情况下学习好的图片特征的一个方法。

The Fake Task

我们先来谈谈这个假任务,然后再反过来再看看它是如何间接的给了我们所需的word vector。

我们打断这样来进行训练。首先给定一个sentence中的一个word(input word),然后随机取一个这个word上下文的word。这个网络用来告诉我们,对于词表中的每个单词,我们随机挑一个“nearby word”,每个词的概率。

当我们提到“nearby”的时候,实际上是这个算法中的窗口大小。一个典型的窗口大小是5,即前后各5个word,一共10个words。

输出的概率代表了在input word的nearby能够找到这个word的可能性大小。例如你给已经训练好的模型输入一个单词‘Soviet’,输出的各个单词,像‘Union’及‘Russia’这类的单词的概率要比‘watemelon’这类不相干的单词要高。

我们通过给网络输入一堆训练文本中的单词对来对其进行训练。下图展示了一些我们从句子”The quick brown fox jumps over the lazy dog.“中提取出来用来训练网络模型的单词对。在这个例子中,窗口大小设置为2,蓝色的word表示input word。

这个网络是用来统计单词对出现的频次的。所以,加入这个网络输入很多(“Soviet”, “Union”)的单词对以及比较少的 (“Soviet”, “Sasquatch”) 单词对。那么当训练结束后,我们输入“Soviet”,输出结果中“Union”的概率要比“Sasquatch”高。

Model Detail

所以这一切到底是如何表示的呢?

首先,我们知道我们给神经网络输入的是一些文本信息,比如一个word。那么我们必须有一个表示这些word的方式。所以首先我们得从我们的训练文本里构建一个词典,我们假设词典大小是10000个不一样的单词。

我们打算把input word比如“ants”表示成一个one-hot vector(比如说00001,只有一个1其他都是0)。我们的每个vector将有10000个components, “ants” 这个单词对应的位置设为1,其他的都为0。

这个网络的输出是一个vector(也是10000个components),对于我们词典种的每个单词,都有一个对应的概率。

下入是我们的网络的结构。


隐藏层没有用到激活函数,但是输出层用到了softmax函数,这个事情我们呆会再谈。

当我们训练这个网络的时候,input是一个one-hot vector表似乎一个input word,输出也是一个one-hot vector表示一个word。但是当我们进行评估的时候,输出实际上是一个概率的分布,一个bunch的float数值,而不是一个one-hot vector。

The Hidden Layer

在我们的例子种,我们打算学习word的300个特征。所以隐藏层是用一个有10000*300(300个神经元)的矩阵构成的。

Google在他们公布的训练谷歌新闻数据的模型里使用了300个特征。特征的数目是一个超参,你得尝试不同的值来获得好的结果。

看看权重矩阵的行,你会发现,卧槽,这个实际上就是我们想要的word vectors。

所以我们真正的最后的目标是获得这个隐藏层的权重矩阵,等训练完成了,我们就把输出层给扔了!

现在,是时候回头看看了,解决一下遗留的一些问题,模型种的一些概念。

现在,你可能问你自己,one-hot vector几乎所有都为0…那么,这有什么用呢?当你将1个1 * 10000的one-hot vector乘上一个10000 * 300的矩阵时候。它会很有效率的选择1对应的那一行。如下如所示:

这意味着这个模型的隐藏层实际上就像是一个lookup table。隐藏层的输出实际上就是input word的word vector。

The Output Layer

然后代表“ants”的1 * 300的word vector作为输出层的输入。输出层是一个softmax回归分类器。这里有关于softmax回归更深入的讲解。但是这里的要点是每一个输出层的神经元会产生一个范围为0到1的数(每一个都对应我们字典中的一个word),并且这些所有的值加起来和为1。

特别的是,每一个输出层神经元有一个权重向量,将它乘上word vector,然后代入函数exp(x)中。最后,我们再除以所有10000个输出节点的结果的和,使得所有输出的和为1。

下图是计算单词“car”的输出的一个示意图。

需要注意的是这个神经网络并不知道输出的word相对于输入word的偏移。它并没有学习到这个输出word是在输入word的前面或者是后面。假设说我们的训练语料中,每一个“York”前面都有一个单词“New”,这个概率是100%。但是,当我们的窗口大小是10,我们随机选择附近单词,“New”的概率并不是100%,你可能选择别的单词。

Intuition

好的,你准备好更深入的了解这个网络的令人兴奋的部分了吗?

假如两个不同的word有着非常类似的上下文,那么我们的模型就会输出非常相似的两个word vector。这个网络输出相同的上下文预测的一种情况就是这两个word vector非常相似。所以,假如两个words有着相似的上下文,那么我们的网络就会倾向于学习出两个相似的word vector。Ta da!(不知道啥意思haha)

这意味着什么呢?我认为你会期望说像“intelligent”和“smart”这种同义词会有着相似的上下文。或者说像“engine”和“transmission”这种相关的单词,可能也会有相同的上下文。

以及,对于“ant”和“ants”这种有着相似上下文的单词,这个网络也会输出相似的word vector。

Next Up

你可能会注意到skip-gram神经网络有着一个巨大的权重。就我们的例子来看,300个特征,10000个words,输入层到隐藏层以及隐藏层到输出层的权重都达到了3M。训练这个大dataset代价比较高的,所以word2vec的作者介绍了一些微调策略来使得这个训练是可行的。接下来我们就讲一讲这些。

Part 2

在这个部分,我们主要谈一谈一些基于basic skip-gram model的改进,这些改进使得模型的训练变得可行。

上文中我们提到,这个网络是一个很大的网络。在这么一个巨大的网络里,跑梯度下降是非常慢的。更糟糕的是,要训练好这么多的权重并且避免过拟合,你需要非常多的数据。百万级别的权重,十亿级别的训练样本,意味着训练这么一个模型,完全是在扯淡。

word2vec的作者在他的论文里提出了三个创新点:

  1. 在模型中把正常的单词对或者短语当做一个“words”;
  2. 对那些频繁出现的单词进行子采样(subsample),来减少所需要的训练样本;
  3. 用一个“ Negative Sampling”的技术来改变优化目标,这使得每个训练样本只更新模型中一部分的权重。

值得注意的是subsampling frequent words和Negative Sampling不仅仅减少了运算负担,并且还提升了结果的质量。

Word Pairs and “Phrases”(单词对和词组)

作者指出,像“Boston Globe”(一份报纸的名称)这种合起来和分开的意思大相径庭的词组,最好把它当作一个单词,不管是在上下文中还是在word vector中。

你可以看看他们公布的在100billion谷歌新闻数据上训练的模型的结果。加入词组(phrases)使得词汇量高达3M。假如你对这些词汇感兴趣你可以看看这里

我认为他们的这种把词汇当作一个word的方法不是他们论文的主要贡献,我在这里提一下因为实际上方法很直接。

在每一轮中,他们的工具只看相邻的两个words,但是你可以跑多次,以此来获得更多单词的词组。比如说,第一轮的时候,会选取New_York,然后再跑一遍,会选取New_York_City。

这个工具计算每两个连接的words出现的次数,然后根据次数带入方程式中来判断应该把哪些归类为词组。这个方程式是为了避免把那些独立的但是经常一起出现的word也归类为词组。它也比较倾向于把那些低频word的组合归类为词组,避免诸如“and the”或者“this is”之类的词组。如果你想更详细的了解细节,你可以看这里

我曾经想过的一个替代策略是采用wikipedia articles的标题来作为词汇。

Subsampling Frequent Words

再Part 1中,我展示了训练样本是如何从源文件中产生的,我会在这里重复一下。下面的例子展示了从“The quick brown fox jumps over the lazy dog.”里获得的一次训练样本(单词对)。我在这个例子中使用的窗口大小为2。高亮的蓝色单词是输入单词。

对于那些高频单词比如“the”,这里存在两个问题:

  1. 当我们观察单词对的时候,(“fox”, “the”) 并没有告诉我们太多关于“fox”的信息。“the”几乎出现在每一个单词的上下文中。
  2. 我们有着太多的 (“the”, …) 这类的单词对,我们并不需要那么多来学习出the一个好的vector。

word2vec采用一种“subsampling”的策略来解决这个问题。对于我们在训练文本中遇到的每一个单词,我们有一定的几率会把它删掉。这个概率是和这个单词出现的频率相关的。

假如我们设定的窗口大小为10,并且我们从文本中移除掉“the”的一个实例:

  1. 当我们利用剩下的单词进行训练的时候,“the”不会出现在它们的上下文窗口中;
  2. 当the是输入单词的时候,我们会有着小于10的训练样本。

思考一下这两个结果对我们解决上面提出的问题有什么帮助。

Sampling rate

word2vec的C代码中利用一个方程式来计算是否丢弃词表中的word的概率。

wi是一个单词,z(wi)表示这个单词出现的次数相对于总次数的比例。比如,“peanut”在包含1billion的语料中出现了1000次,那么z(“peanut”)就是1000/10 0000 0000 = 1/100 0000。

还有一个参数叫做“sample”用来控制subsampling出现的频率,默认值是0.001。words的“sample”越小表示越容易被丢弃。
P(wi)表示keeping这个单词的概率:

你可以把它画出来来看看形状:

没有一个word占整个语料的比例会很高,所以我们来看看当x比较小的时候是什么样子的。

这是在这个函数中一些关键的点(重申下sample采用的是默认的0.001):

你可能会注意到paper中定义的函数和C代码中采用的有些不太一样,但是我觉得C代码中的是更加权威的版本。

Negativa Sampling

训练一个神经网络指的是拿一个训练样本进行训练,然后细微的调整神经元中的权重,使得对训练样本的于此更加精确。换句话说,每一个训练样本对神经网络中的所有权重都有影响。

就我们前面提到的,巨大的词汇表意味着神经网络中巨大的权重,这些权重每次都会因我们的训练样本做出轻微改动,而我们的样本数量是十亿级别的。

Negativa Sampling通过每个训练样本只改变某些权重的方法来解决这个问题。接下来我们看看它是怎么工作的。

当我们用单词对 (“fox”, “quick”) 来训练网络的时候,输入和输出都是one-hot vector。也就是说,神经元的输出,对应“quick”的是1,其他的都是0。

采用negative sampling,我们只是随机选取少量的“negative” word(比如说5个)来更新它们的权重。(“negative” word在这里指的是那些输出对应为0的单词)。对于“positive” word也就是“quick”,我们仍然进行更新。

paper中说选择5-20个words在校的数据集中工作的很好。在大的数据集中,你可以选择更少,比如2-5个。

回忆一下,输出层的权重矩阵是300 * 10000的。假如我们只更新我们的“positive” word (“quick”),加上5个随机选择的negative单词。一共也才6个神经元,1800个权重值。只占了3M 权重的0.06%。

在隐藏层中,只有input word对应的权重进行更新,这个不管采不采用Negative Sampling都一样。

Selecting Negative Samples

刚才说的5个“negative samples”怎么确定呢,实际上不是完全随机的,采用“unigram distribution”的方法来确定。

实际上,选择一个word作为negative word的概率是和它出现的频率相关的,也就是说它出现的频率越高,那么它就越可能被选中。

在word2vec的C代码的实现中,你可以看到以下这个方程式。f(wi)表示wi出现次数的权重。所以选择一个word的概率就是它的权重除以所有words权重和。

选择3/4作为指数项应该是凭经验选择的,在他们的paper中,他们说这个选择比别的效果更好。你可以在google中画图:“plot y = x^(3/4) and y = x” 并且把x的范围设定为[0, 1],就可以看它的形状了。

这个选择的方式在C代码中的实现挺有趣的。他们有着个非常大的array,多大100M个元素(也就是unigram table)。他们根据word的index多次填充这个table,然后一个word的index在这个table中出现的次数等于P(wi) * table_size。所以实际上你选择一个negative sample的过程就是产生一个从0-100M的随机数,然后使用table中对应位置的word。因为高频单词在这个table中出现了多次,所以你有更大的概率选择它。

Other Resources

word2vec最详细的最准确的解释就是它的源代码了,这篇介绍的作者写了带更多注释的版本,在这里,或者直接去看源代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容