推荐系列论文三:Word2vec

前言

前面提到过在Embedding这块,绕不开的就是Word2vec,今天就简单为大家介绍一下Word2vec,现在网络上有许多写的很多的介绍Word2vec的文章,因此本文也主要是引用这些文章来对Word2vec进行介绍(主要是懒),参考的内容见文末。

什么是Word2vec?

  • Word2vec由来自google的Mikolov于2013年发表的《Efficient estimation of word representations in vector space》一文中提出,看论文题目便可知其作用主要是将word在向量空间中进行表达,这是什么意思呢?
  • 通常来说,我们对于单词的表达,使用的都是Onehot编码,但是Onehot编码有几个缺点,维度高、稀疏、表达能力欠缺,而Word2vec正是用于弥补Onehot的这种缺点,利用将word嵌入到另一个实数向量空间从而完成降维,并且word的向量表示具备具体意义,向量的余弦距离通常也能刻画在预料中两个单词之间的差异性。
  • 假设V(word)代表word的嵌入表达,通过Word2vec甚至可以表征出V(king)-V(man)+V(women)=V(queen),如下图:
    在这里插入图片描述
  • Word2vec这么神奇,接下来一起看一下其具体是怎么回事。

Word2vec形式

  • 假设现在有一个句子,“i will go home and have my dinner.”,分词后就有8个单词,word2vec中有窗口的概念,假设当前的中心词是home,取窗口为2,那么影响home单词的是will go和and have,我们希望P(home|(will,go,and,have))最大或者P((will,go,and,have)|home)最大,其中P指代概率,这两种不同的形式也就形成了Word2vec的两种形式
    1.CBOW
    2.skip-gram
  • 其中CBOW是根据窗口的上下词来预测当前的中心词,skip-gram是以当前的中心词来预测上下的窗口词,word2vec训练使用的SGD优化器,因此输入每一个句子,其需要根据滑动中心词与其窗口词定义其对数似然函数,求取极大值从而更新向量,说起来有点绕,下面分开介绍两种形式。

CBOW

  • CBOW的结构图如下
    在这里插入图片描述

  • 其中,w(t)为当前词,w(t-2)为当前词前的第二个词,图中使用的窗口大小为2,通过将前后窗口词的词向量(需要进行初始化)进行求和获得sum向量,然后利用该向量预测当前词w(t),使得p(w(t)|sum)最大,由于一个句子可能会有多个中心词,假设一个句子的长度为n,那么其整个句子的概率积为
    P ( s e n t e n c e ) = ∏ t = 1 n P ( w ( t ) ∣ c o n t e x t ( w ( t ) , c ) ) P(sentence)=\prod_{t=1}^{n}P(w(t)|context(w(t),c)) P(sentence)=t=1nP(w(t)context(w(t),c))

  • 采用对数似然进行计算=》
    l o g P ( s e n t e n c e ) = ∑ t = 1 n l o g P ( w ( t ) ∣ c o n t e x t ( w ( t ) , c ) ) logP(sentence)=\sum_{t=1}^{n}logP(w(t)|context(w(t),c)) logP(sentence)=t=1nlogP(w(t)context(w(t),c))

  • 接下来,核心问题就变成了P(w(t)|context(w(t),c))该如何定义,熟悉神经网络的同学应该很容易想到softmax(不知道的建议搜一下),假设我们使用softmax的话,模型的结构是怎样的呢?

  • 假设有1万个单词,softmax层的输出就是10000维向量,那么每次计算的计算量有多大呢?假设单词嵌入维度为100维,那么就是100*10000即100万次计算量,计算消耗太大,Word2vec是怎么做的呢? 其采用了两种优化方案:Hierarchical Softmax和Negative Sample,下面分别进行一下介绍。

Hierarchical Softmax
  • 相信学计算机的同学对Huffman Tree都很不陌生,通信的同学应该也了解,Huffman Tree是一种生成最优二叉树的算法,可以有效地用于数据压缩以及编码优化节约存储以及运输的空间,Huffman Tree不在本文的介绍中,网上的资料也非常多哈,这里假设大家都了解Huffman Tree

  • 前面说到了,使用softmax的问题就是需要预测每个单词的概率计算量非常大,有没有什么办法进行优化呢?作者想到使用Huffman树来对每个单词进行编码,这样每个单词都可以得到一个唯一的编码表示,如下图:
    在这里插入图片描述

  • huffman树的特点是在预料中出现的次数越多其对应的叶子节点也就越靠近根节点,获得的编码长度也就越短。

  • 这里假设以足球为例,其利用huffman树获得的编码为1001,在这样的一棵树中1001等于进行了四次的二分类,依次为图中的38,23,9,4节点,其中每个节点都有一个对应的向量 θ \theta θ,该值与word的嵌入维度相等,根节点输入为w,以第一层为例,假设38节点对应的向量为 θ \theta θ,输入(前面的窗口词的向量和)为w,则通过sigmoid可以求得该次分类为1的概率 p ( l a b e l = 1 ) = 1 / ( 1 + e − w ∗ x ) p(label=1)=1/(1+e^{-w*x}) p(label=1)=1/(1+ewx)

  • 如上所说,足球的编码为1001,那么只需要从上到下依次计算便可获得四层的概率值,p(label=1|depth=1),p(label=0|depth=2),p(label=0|depth=3),p(label=1|depth=4),还记得前面介绍的要定义P(w(t)|context(w(t),c))吗?将上面四个概率相乘便是P(w(t)|context(w(t),c)),如下:
    P ( w ( t ) ∣ c o n t e x t ( w ( t ) , c ) ) = ∏ d e p t h = 1 m a x d e p t h ( p ( l a b e l ∣ l a y e r = d e p t h ) ) P(w(t)|context(w(t),c)) = \prod_{depth=1}^{max depth}(p(label|layer=depth)) P(w(t)context(w(t),c))=depth=1maxdepth(p(labellayer=depth))

  • 其中p(label|layer=depth)代表当前词在第depth层其分类等于编码值的概率,那么总体的概率便是如下形式了:
    l o g P ( s e n t e n c e ) = ∑ t = 1 n ∑ d e p t h = 1 m a x d e p t h l o g ( p ( l a b e l ∣ l a y e r = d e p t h ) ) logP(sentence)=\sum_{t=1}^{n}\sum_{depth=1}^{max depth}log(p(label|layer=depth)) logP(sentence)=t=1ndepth=1maxdepthlog(p(labellayer=depth))

  • p(label|layer=depth)的计算依赖对应节点的 θ \theta θ以及输入x,输入x为上下窗口词向量的平均,在Hierarchical Softmax中我们最终需要训练的就是Huffman Tree中每个非叶子节点对应的向量 θ \theta θ以及x,再将利用随机梯度下降获得更新的x更新到每个输入的窗口词。

  • 具体的梯度计算公式就不推导了,可以参考文末的参考文章,下面贴一下总体的步骤,如下:在这里插入图片描述

  • 总体而言就是对于每条样本(每一个中心词)进行一次计算,并更新huffman树中的非叶子节点对应的权重以及窗口词的词向量表示。

  • Hierarchical Softmax利用Huffman Tree根据预料为每个单词进行编码,将一次多分类转化为若干次的二分类有效地缩减了每次预测的消耗,平均二分类的次数为 l o g 2 n log_2^n log2n,每次计算量为embedding_size,相比于embedding_size*n而言大大地获得了减少。

Negtive Sample
  • 基于负采样的CBOW是如何做的呢?还是那个问题,最重要的是我们如何来定义P(w(t)|context(w(t),c)。

  • 负采样的做法是对于给定的中心词w(t),(w(t),context(w(t)))是一条正样本,不是w(t)的则都是负样本,但是由于语料中单词的数量实在太多了,所以NS中在剩余的单词中进行了带权的负采样从而形成了相应的负样本,假设其为NEG(w),w为中心词,如下图所示:
    在这里插入图片描述

  • 每个单词我们都为其生成一个特定的辅助向量 θ \theta θ,context(w)的向量嵌入和为sum,那么p(label=1|Context(w))的概率便可以用sigmoid(当做一次二分类,判断是不是该单词)来进行计算了,p(label=0|context(w))=1-p(label=1|Context(w)),这样对于一条正样本和其采样得到的负样本集合{w}AND NEG(w)便可以通过概率积拼接从而求取极大似然了。

  • 之后的步骤就和Huffman Tree的步骤一样了,如下:
    在这里插入图片描述

  • 根据上述内容可以知道,核心问题是如何进行负采样?一种很自然的想法,在预料中出现频率越高的单词其成为负样本的概率也应该更大,因此负采样简单来说是这样的:
    1.计算预料中每个单词的先验概率作为其概率估计
    2.获得每个单词的相对概率(即使得所有单词出现的概率和为1)
    3.生成随机数选择对应区间的单词作为负样本


Skip-gram

  • Skip-gram使用的非常多,spark中也仅实现了基于Negative Sample的Skip-gram,一般而言skip-gram的效果也更好,下面介绍下skip-gram的原理,下面是原理图
    在这里插入图片描述

  • CBOW是通过上下文来预测当前词,skip-gram则是通过当前词来预测上下文(当然图是这么画了,但是实际实现中却并不是这样实现的)

  • 和前面一样,CBOW最重要的是定义P(w(t)|context(w(t),c)),skip-gram中则是定义P(context(w(t),c)|w(t)),但是这样定义存在这个问题,就是每条样本每次更新只会更新输入的w(t)的词向量,训练速度会比较慢,考虑找个问题,我们希望最大化P(w(t-1)|w(t)),同时也希望可以最大化P(w(t)|w(t-1)),而采取最大化P(w(t)|w(t-1))的好处就是可以更新到窗口词的每个词,所以skip-gram实际上并不是像上图这样进行训练的,而是类似于CBOW,但是每次只选择一个窗口词进行计算,对于窗口c=2,则进行了四次计算并挨个更新了四个窗口词,基于Hierarchical Softmax的skip-gram算法的步骤如下:
    在这里插入图片描述

  • 其中, x i x_i xi是窗口词,可以看到在一条样本中,即一个中心词2c个窗口词下循环了四次,并且挨个更新了窗口词。

  • 根据源码内容可以发现,skip-gram实质上是CBOW的精确版本,CBOW中将所有的输入求和后作为统一的输入,而skip-gram只使用一个窗口词进行计算,精确计算的背后带来的是计算量的上升。

  • 基于negative sample的skip-gram这里就不进一步进行讨论了,基本上大同小异。


结尾语

  • 能力有限,对于word2vec的描述还有很多欠缺之处,对于想了解Word2vec原理的同学可以看参考的几篇博客以及word2vec的源码,可以帮助更好地理解算法,后续有机会会写一篇word2vec源码剖析的文章,这次的先到这吧。

参考

word2vec源码

word2vec原理

word2vec 中的数学原理详解

Efficient estimation of word representations in vector space

Distributed Representations of Sentences and Documents

word2vec Explained: deriving Mikolov et al.'s negative-sampling word-embedding method

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值