大致了解一下DeepWalk

转载于各位大佬:
word2vec算法:
https://blog.csdn.net/u013527419/article/details/74129996
http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/
https://blog.csdn.net/Layumi1993/article/details/72866235
https://blog.csdn.net/Layumi1993/article/details/72868399

DeepWalk算法:https://blog.csdn.net/wolfblood_zzx/article/details/73088111
https://blog.csdn.net/u013527419/article/details/76017528/

讲到DeepWalk,不得不说的Word2Vec

Word2Vec包含了两种词训练模型:CBOW模型和Skip-gram模型。

CBOW模型根据中心词W(t)周围的词来预测中心词
Skip-gram模型则根据中心词W(t)来预测周围词

它们的结构仅仅是输入层和输出层不同。

在这里插入图片描述skipgram

这两张结构图其实是被简化了的,读者只需要对两个模型的区别有个大致的判断和认知就ok了。接下来我们具体分析一下CBOW模型的构造,以及词向量是如何产生的。理解了CBOW模型,Skip-gram模型也就不在话下啦。

CBOW模型

CBOW模型的理解

其实数学基础及英文好的同学可以参照斯坦福大学Deep Learning for NLP课堂笔记。
当然,懒省事儿的童鞋们就跟随我的脚步慢慢来吧。
先来看着这个结构图,用自然语言描述一下CBOW模型的流程:
cbow
NOTE:花括号内{}为解释内容.

  1. 输入层:上下文单词的onehot. {假设单词向量空间dim为V,上下文单词个数为C}
  2. 所有onehot分别乘以共享的输入权重矩阵W. {V*N矩阵,N为自己设定的数,初始化权重矩阵W}
  3. 所得的向量 {因为是onehot所以为向量} 相加求平均作为隐层向量, size为1*N.
  4. 乘以输出权重矩阵W’ {N*V}
  5. 得到向量 {1*V} 激活函数处理得到V-dim概率分布 {PS: 因为是onehot嘛,其中的每一维斗代表着一个单词},概率最大的index所指示的单词为预测出的中间词(target word)
  6. 与true label的onehot做比较,误差越小越好

所以,需要定义loss function(一般为交叉熵代价函数),采用梯度下降算法更新W和W’。训练完毕后,输入层的每个单词与矩阵W相乘得到的向量的就是我们想要的词向量(word embedding),这个矩阵(所有单词的word embedding)也叫做look up table(其实聪明的你已经看出来了,其实这个look up table就是矩阵W自身),也就是说,任何一个单词的onehot乘以这个矩阵都将得到自己的词向量。有了look up table就可以免去训练过程直接查表得到单词的词向量了。

CBOW模型流程举例

假设我们现在的Corpus是这一个简单的只有四个单词的document:{I drink coffee everyday}
我们选coffee作为中心词,window size设为2
也就是说,我们要根据单词"I","drink"和"everyday"来预测一个单词,并且我们希望这个单词是coffee。

cbow
cbow
cbow
cbow

假设我们此时得到的概率分布已经达到了设定的迭代次数,那么现在我们训练出来的look up table应该为矩阵W。即,任何一个单词的one-hot表示乘以这个矩阵都将得到自己的word embedding。

Skip-Gram模型

大牛写的分析见此处

模型

skipgram model 常常让人惊讶于它的简单结构。我认为基本没啥弯路,或者需要复杂的模型解释。

让我们从高层语义开始。Word2Vec 使用了一个小trick,你可能在其他machine learning问题中见过。我们训练一个简单的网络来执行一个任务,但是最乎我们实际上并没有用这个网络 来预测 test中的任务。而是,利用这个任务(目标)去学习网络中的权重W。我们将看到这些学出来的权重W就是我们所要的词向量(word vectors)。

你可能会见到这个trick,的另一个地方是 auto-encoder。 auto-encoder 常常用来压缩一个输入,然后再把压缩后的结果解压回去。在训练完成之后,你可以把解压的网络扔掉,只使用压缩的网络。这就是一个trick来获得 无监督下 好的图像特征。

假任务

那现在我们来讨论一下我们所要建立网络的训练任务(假任务)。然后我们再来讨论为什么这个任务可以间接地学习到想要的word vector。
给定句子中一个特定的词(input word),随机选它附近的一个词。网络的目标是预测 我们选到这个附近词的概率。

实际上这个“附近“,是一个窗口。一个常用的窗口大小为5,意味着 输入词前方的5个词,后方的5个词。

输出的概率实际上关系到周围的每一个词。比如,如果你给出一个词苏联(Soviet),那么概率很高的输出 应该是 联合(Union)和俄罗斯(Russia)而不是西瓜或者袋鼠。
我们通过给网络 输入我们在训练文本中 找到的词对,来训练网络。下面这个例子展示了从“The quick brown fox jumps over the lazy dog.“ 中找到的一些词对. 我使用了window size=2,而蓝色标出的词为输入词。
skip-gram
网络将会去学这些pair出现的统计概率。举个例子来说,网络会得到很多(苏联,联合)的概率多过(苏联,大脚怪)。所以当训练完成的时候,当你输入了苏联这个词,联合会得到比大脚怪更高的预测概率。

模型细节

首先,你不能把word变成一个string输入给网络,所以我们要找另外一个形式。所以我们首先建立一个word的词典,比如我们有10,000个不同的词,那么我们就建立一个10,000的词典。
“ants“就可以变成一个10,000维的向量。这个向量每一维代表一个词,我们把代表“ants“的那一维置为1,其他维置为0。
而网络的输出也是一样也是一个10,000维的向量,代表每个词预测的概率。
网络结构如下图:
skip-gram
网络中没有激活函数,输出使用的是softmax。我们等下再回来讨论。
当使用这个网络来训练词pair的时候,训练的输入是one-hot向量训练的目标输出也是一个one-hot向量。但实际test这个网络的时候,输出会是一个概率分布。
(译者按:打个比方,苏联只和联合/俄罗斯有交集,所以会收敛到 50%, 50% 的分布。而不是联合就是100%。当然test的时候也可以找最高概率的结果,那也是one-hot。)

隐层

我们需要学习300维的word vector。所以我们使用的隐层将是10,000行(词典中词的数量)和300列的每个隐层神经元)。

300 是Google公布出来在Google news 上训练使用的维数,你可以通过这个链接([https://code.google.com/archive/p/word2vec/]) 下载(需翻墙)。这书300是个超参,可以按你的实际需求修改。

如果你从行的角度看,这个隐层的W实际上就是我们要的word vector。
skip-gram
所以最终目标就是让网络学习隐层中的权重。当我们训练完成的时候,我们就可以把后半部分output层给去掉了。
好,让我们回到之前的模型的定义。
现在你可能在问自己,one-hot向量就是基本全是0,只有一个是1的向量,那么这会产生什么结果呢?当你用1×10,000乘10,000×300的矩阵,他相当于只选择了那一行‘1‘的。下面是一个简单的例子:
skip-gram
这意味着这个网络的隐层实际上就是像一个查找表(lookup table)一样。输出的300维就是这个词的word vector。

输出层

‘ant‘的1×300的word vector 然后传到了输出层。输出层是一个softmax 的分类器(译者按:我认为不是regression,就是classification吧)主旨就是把每个输出normal到0~1之间,并且这些输出的和为1。
更具体的来说,每个word vector(1×300)将会乘一个W(300×10,000)再次映射到10,000维,每一维代表临近词的概率。而这些概率做一个softmax的normal(就是图中的exp的函数,来归一化)。下面是一张说明图。
skip-Gram

直觉

让我们对这个网络做一个直觉上的感受。
如果两个不同的词有相同的上下文,那么我们的模型会需要预测类似的输出。那么网络为了输出类似的预测,他就会使这两个不同词的word vector尽可能相似。所以,如果两个词有相同的上下文,我们就可以得到比较接近的word vector。
那么什么词有相同的上下文?比如一些近义词 smart 和intelligent 再比如 一些相关的词 engine 和 transmission。
这也可以帮你处理一些形式变化。比如ant和ants因为他们应该有类似的上下文。

下一步

你可能注意到skip-gram有很多权重需要学习。举个例子说,300维特征,10,000的词表, 3MB×2的权重需要去学习,输入输出层各一个。
所以如果在一个更大的词典上学习,word2vec的作者引入了一些trick让训练更可行。这将会在下一章中介绍。(我也做了翻译,欢迎大家继续看~)

一些常用的trick

Word2Vec的作者在他们第二篇paper中解决了这个问题,有3个创新点:

  1. 把常见的词组作为一个单词。
  2. 少采样常见的词(译者按:A the 啥的都滚粗)
  3. 修改优化目标函数,这个策略成为“Negative Sampling“,使得每个训练样本只去更新模型中一小部分的weights。
    值得注意的是 2和3 不仅仅减少了训练的计算量,而且提升了最后word vector的质量。

词组

作者指出像“Boston Globe“(一家报社)这种词对,和两个单词 Boston/Globe有着完全不同语义。所以更合理的是把“Boston Globe“看成一个单词,有他自己的word vector。公开的模型是由GoogleNews上有100bilion个单词训练得到的。额外增加的词组达到了3milion个。
如果你对词典有兴趣去你可以看。

词组检测是另外一个问题

降采样常用词

在part1中,我展示了如何从句子中产生训练样本,这里我再重复一下。例子为“The quick brown fox jumps over the lazy dog.“
skip-gram
比如像“the“这种常见的词,我们会遇到两个问题:

  1. 比如(fox,the)其实没有传递我们关于 fox的信息。‘the‘出现得太多了。
  2. 我们有太多 (‘the‘,…)的样本,多于我们真的需要的。
    所以word2vec采用了降采样的策略。对于每个我们在训练样本中遇到的词,我们有一个概率去删除它。这个概率与单词出现的频率相关。
    如果我们采用window size = 10,同时我们删除“the“:
  3. 当我们再去训练剩下的词,我们就不会再遇到‘the‘了;
  4. 我们减少了10个包含‘the‘的样本
    注意到这两个特点,使得我们可以解决之前提到的两个问题。

采样率

word2vec 的C代码使用了一个等式来计算是否留着这个word。
我们使用 ω i \omega_i ωi来表示单词, z ( ω i ) z(\omega_i) z(ωi)表示它出现在词库中的概率。比如花生在1bilion的词库中出现了1,000词,那么 z ( 花 生 ) = 1 e − 6 z(花生)=1e−6 z()=1e6
然后有个叫‘sample‘的参数控制了降采样的程度,一般设置为0.001。这个值越小代表更容易扔掉一些词。
下面这个等式 P ( ω i ) P(\omega_i) P(ωi)代表保留这个词的概率
P ( ω i ) = ( z ( ω i ) / 0.001 + 1 ) × 0.001 / z ( ω i ) P(\omega_i)=(\sqrt {z(\omega_i)/0.001}+1)×0.001/z(\omega_i) P(ωi)=(z(ωi)/0.001 +1)×0.001/z(ωi)
可以看到如果单词出现频率很低,
z ( ω i ) ⩽ 0.0026 z(\omega_i) \leqslant 0.0026 z(ωi)0.0026的情况下, P ( ω i ) = 1 P(\omega_i)=1 P(ωi)=1,我们不会把这些词扔掉;
z ( ω i ) ⩽ 0.00746 z(\omega_i) \leqslant 0.00746 z(ωi)0.00746 的情况下, P ( ω i ) = 0.5 P(\omega_i)=0.5 P(ωi)=0.5;
如果出现频率很高, z ( ω i ) = 1 z(\omega_i)=1 z(ωi)=1的情况下, P ( ω i ) = 0.033 P(\omega_i)=0.033 P(ωi)=0.033, 有很低的概率我们keep这个word。当然,如果每个训练样本对都有这个词,这种情况不会出现。

你可能注意到论文中提到的这个函数和C代码中的不同。但是我发现C代码中的函数可能更authoritative。

Negative Sampling

训练神经网络 意味着输入一个训练样本调整weight,让它预测这个训练样本更准。换句话说,每个训练样本将会影响网络中所有的weight。
像我们之前讨论的一样,我们词典的大小意味着我们有好多weight,所有都要轻微的调整。
Negative sampling 解决了这个问题,每次我们就修改了其中一小部分weight,而不是全部。
当训练(fox,quick)这个词对的时候,quick这个词的概率是1,其他的都是0。通过negative sample,我们只是随机的选了一部分negative词(假设是5个)来update weight。(这些negative 词就是我们希望是0的。)

论文中说5-20个词适合小数据集, 2-5个词适合大数据集。

回忆我们原来模型每次运行都需要 300×10,000 (译者按,其实没有减少数量,但是运行过程中,减少了需要载入的数量。) 现在只要 300×(1+5)减少了好多。
而在输出层始终只要update 输入单词的weight 就好了。(不论有没有采用negative sampling)
译者按:因为输入层就像lookup table,当然其他的都不影响的。

选择 negative samples

问题来了,如何选择5个negative sample呢。
negative sample也是根据他们出现频率来选的。更常出现的词,更容易被选为negative sample。
在word2vec的C实现中,你可以看到一个等式来表达这个概率。每个词给了一个和它频率相关的权重。这个概率公式为
P ( ω i ) = f ( ω i ) 0.75 ∑ j = 0 n f ( ω i ) 0.75 P(\omega_i)=\frac{{f(\omega_i)}^{0.75}}{\sum_{j=0}^n {f(\omega_i)}^{0.75}} P(ωi)=j=0nf(ωi)0.75f(ωi)0.75在paper中说0.75这个超参是试出来的,这个函数performance比其他函数好。
而这个实现方式在C代码。采用的就是一个100M的查找表,这个表里,按概率填入每个词,每个词填空的个数为 P ( ω i ) ∗ t a b e l s i z e P(\omega_i)∗tabelsize P(ωi)tabelsize 。每次选副样本,只要随机一个整数,然后查找表读就好了。因为高频词出现的更多,所以你更可能选到那些词。

DeepWalk

实现
用SkipGram的方法进行网络中节点的表示学习。那么,根据SkipGram的思路,最重要的就是定义Context,也就是Neighborhood。​NLP中,Neighborhood是当前Word周围的字,本文用随机游走得到Graph或者Network中节点的Neighborhood。

步骤

  1. 随机游走随机均匀地选取网络节点,并生成固定长度的随机游走序列,将此序列类比为自然语言中的句子(节点序列=句子,序列中的节点=句子中的单词),应用skip-gram模型学习节点的分布式表示,skip-gram模型
  2. 前提:如果一个网络的节点服从幂律分布,那么节点在随机游走序列中的出现次数也服从幂律分布,并且实证发现NLP中单词的出现频率也服从幂律分布。
    幂律分布
  3. 大体步骤:
    Network/graph ---------random walk ---------得到节点序列(representation mapping)-------- 放到skip-gram模型中(中间节点预测上下文节点)--------- output:representation
    steps

相关算法

在这里插入图片描述
在这里插入图片描述

  • 8
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值