深度解析Word2vec

Word2vec 本质上是一种降维操作——把词语从 one-hot encoder 形式的表示降维到 Word2vec 形式的表示,即Distributed Representation。也就是,通过Embedding把原先词所在空间映射到一个新的空间中去,使得语义上相似的单词在该空间内距离相近。

Distributed Representation最早是Hinton于1986年提出,基本思想是:通过训练将某种语言中的每一个词映射成一个固定长度的短向量,所有这些向量构成一个词向量空间,而每一向量则可视为该空间中的一个点,在这个空间上引入“距离”,将可以根据词之间的距离来判断它们之间的相似性了。为什么叫Distributed Representation?相对于one-hot representation,向量中只有一个非零分量,非常集中;而对于Distributed Representation向量中有大量非零向量,相对分散,将词的信息分布到各个分量中去了。这一点,跟并行计算里的分布式并行很像。

关于word2vec的理论知识,建议配合Paper Reading:word2vec Parameter Learning Explained更佳。

word2vec主要有两个重要模型——CBOW模型和Skip-gram模型。由图可见,两个模型都包括三层:输入层、投影层和输出层。前者是在已知当前词\(w_t\)的上下文\(w_{t-2},w_{t-1},w_{t+1},w_{t+2}\)的前提下预测当前词\(w_t\);而后者恰恰相反,是在已知当前词\(w_t\)的前提下,预测其上下文\(w_{t-2},w_{t-1},w_{t+1},w_{t+2}\)

word2vec

训练word2vec的过程中还有很多工程技巧,比如用negative sampling或Hierarchical Softmax减少词汇空间过大带来的计算量,对高频词汇进行降采样避免对于这些低信息词汇的无谓计算等。

CBOW

这里主要介绍基于Hierarchical Softmax的CBOW模型。其输入层包含Context(w)中2c个词的词向量;投影层将输入层的2c个向量做求和累加;输出层对应一颗二叉树,它是以语料中出现的词当叶子节点,以各词在语料中出现的次数当权值构造出来的Huffman树。

CBOW

梯度计算

为了后续方便描述问题,首先对 CBOW 模型中用到的符号做一个统一的说明:

  • \(p^w\):从跟结点出发到达\(w\)对应叶子结点的路径。
  • \(l^w\):路径\(p^w\)中包含结点的个数。
  • \(p_1^w,p_2^w,...,p_l^w\):路径\(p^w\)中的\(l^w\)个结点,其中\(p_1^w\)表示根节点,\(p_l^w\)表示词\(w\)对应的结点。
  • \(d_2^w,d_3^w,...,d_l^w\):词\(w\)的Huffman编码,它由\(l^w-1位编码构成,\)d_jw$表示路径$pw$中第j个结点对应的编码(根节点不对应编码)。
  • \(\theta_1^w,\theta_2^w,...,\theta_l^w\):路径\(p^w\)中非叶子结点对应的向量,\(\theta_j^w\)表示路径\(p^w\)中第j个非叶子结点对应的向量。

具体推导见参考。

Skip-gram

Skip-gram也包括三层:输入层只含当前样本的中心词\(w\)的词向量\(v(w)\);投影层是一个恒等投影,主要是方便和CBOW模型的网络结构最对比;输出层也是一颗Huffman树。

Skip-gram

梯度计算

具体推导见参考。

Hierarchical Softmax

层次softmax,即构建一棵Huffman树,树的每个叶子节点是词,非叶子节点是一个个的label,从根节点出发,经过分类分到左子节点或者右子节点,然后在子节点继续分类,看是到下一层的左节点还是右节点,直到最终到达叶子结点,也就是具体的词,经过的所有节点的label合起来,就是Huffman编码。在word2vec中,我们采用了二元逻辑回归的方法,即规定沿着左子树走,那么就是负类(霍夫曼树编码1),沿着右子树走,那么就是正类(霍夫曼树编码0)。判别正类和负类的方法是使用sigmoid函数,即:

\[P(+) = \sigma(x_w^T\theta) = \frac{1}{1+e^{-x_w^T\theta}} \]

层次softmax本质是把 N 分类问题变成 log(N)次二分类

negative sampling

negative sampling目的是用来提高训练速度并改善所得词向量的质量。它利用相对简单的随机负采样,能大幅度提高性能。
那么如何进行负采样呢?词典中的词在语料中出现的次数有高有低,对于那些高频词,被选为负样本的概率就应该比较大,反之,对于那些低频词,其被选中的概率就应该比较小。本质上这是一个带权采样问题。word2vec中的采样的方法并不复杂,如果词汇表的大小为\(V\),那么我们就将一段长度为1的线段分成\(V\)份,每份对应词汇表中的一个词。当然每个词对应的线段长度是不一样的,高频词对应的线段长,低频词对应的线段短。每个词\(w\)的线段长度由下式决定:

\[len(w) = \frac{count(w)}{\sum\limits_{u \in vocab} count(u)} \]

在word2vec中,分子和分母都取了3/4次幂如下:

\[len(w) = \frac{count(w)^{3/4}}{\sum\limits_{u \in vocab} count(u)^{3/4}} \]

在采样前,我们将这段长度为1的线段划分成\(M\)等份,这里\(M>>V\),这样可以保证每个词对应的线段都会划分成对应的小块。而\(M\)份中的每一份都会落在某一个词对应的线段上。在采样的时候,我们只需要从\(M\)个位置中采样出\(neg\)个位置就行,此时采样到的每一个位置对应到的线段所属的词就是我们的负例词。在word2vec中,\(M\)取值默认为\(10^8\)
negative sampling本质是预测总体类别的一个子集。

实战

这里主要介绍在python中gensim包的适用以及其主要参数。

from gensim.models import word2vec

在gensim中,word2vec 相关的API都在包gensim.models.word2vec中。和算法有关的参数都在类gensim.models.word2vec.Word2Vec中。算法需要注意的参数有:

    1. sentences: 我们要分析的语料,可以是一个列表,或者从文件中遍历读出。后面我们会有从文件读出的例子。
    1. size: 词向量的维度,默认值是100。这个维度的取值一般与我们的语料的大小相关,如果是不大的语料,比如小于100M的文本语料,则使用默认值一般就可以了。如果是超大的语料,建议增大维度。
    1. window:即词向量上下文最大距离,这个参数在我们的算法原理篇中标记为cc,window越大,则和某一词较远的词也会产生上下文关系。默认值为5。在实际使用中,可以根据实际的需求来动态调整这个window的大小。如果是小语料则这个值可以设的更小。对于一般的语料这个值推荐在[5,10]之间。
    1. sg: 即我们的word2vec两个模型的选择了。如果是0, 则是CBOW模型,是1则是Skip-Gram模型,默认是0即CBOW模型。
    1. hs: 即我们的word2vec两个解法的选择了,如果是0, 则是Negative Sampling,是1的话并且负采样个数negative大于0, 则是Hierarchical Softmax。默认是0即Negative Sampling。
    1. negative:即使用Negative Sampling时负采样的个数,默认是5。推荐在[3,10]之间。这个参数在我们的算法原理篇中标记为neg。
    1. cbow_mean: 仅用于CBOW在做投影的时候,为0,则算法中的xwxw为上下文的词向量之和,为1则为上下文的词向量的平均值。在我们的原理篇中,是按照词向量的平均值来描述的。个人比较喜欢用平均值来表示xwxw,默认值也是1,不推荐修改默认值。
    1. min_count:需要计算词向量的最小词频。这个值可以去掉一些很生僻的低频词,默认是5。如果是小语料,可以调低这个值。
    1. iter: 随机梯度下降法中迭代的最大次数,默认是5。对于大语料,可以增大这个值。
    1. alpha: 在随机梯度下降法中迭代的初始步长。算法原理篇中标记为ηη,默认是0.025。
    1. min_alpha: 由于算法支持在迭代的过程中逐渐减小步长,min_alpha给出了最小的迭代步长值。随机梯度下降中每轮的迭代步长可以由iter,alpha, min_alpha一起得出。这部分由于不是word2vec算法的核心内容,因此在原理篇我们没有提到。对于大语料,需要对alpha, min_alpha,iter一起调参,来选择合适的三个值。

常用用法:

  • 找出某一个词向量最相近的词集合
    model.wv.similar_by_word('沙瑞金'.decode('utf-8'), topn =100)
  • 看两个词向量的相近程度
    model.wv.similarity('沙瑞金'.decode('utf-8'), '高育良'.decode('utf-8'))
  • 找出不同类的词
    model.wv.doesnt_match(u"沙瑞金 高育良 李达康 刘庆祝".split())

总结

在word2vec诞生之后,embedding的思想迅速从NLP领域扩散到几乎所有机器学习的领域,我们既然可以对一个序列中的词进行embedding,那自然可以对用户购买序列中的一个商品,用户观看序列中的一个电影进行embedding。而广告、推荐、搜索等领域用户数据的稀疏性几乎必然要求在构建DNN之前对user和item进行embedding后才能进行有效的训练。

具体来讲,如果item存在于一个序列中,item2vec的方法与word2vec没有任何区别。而如果我们摒弃序列中item的空间关系,在原来的目标函数基础上,自然是不存在时间窗口的概念了,取而代之的是item set中两两之间的条件概率。

Q&A

什么是embedding?为什么说embedding是深度学习的基本操作?

Embedding是用一个低维的向量表示一个物体,可以是一个词,或是一个商品,或是一个电影等等。这个Embedding向量的性质是能使距离相近的向量对应的物体有相近的含义,
Embedding能够用低维向量对物体进行编码还能保留其含义的特点非常适合深度学习。在传统机器学习模型构建过程中,我们经常使用one hot encoding对离散特征,特别是id类特征进行编码,但由于one hot encoding的维度等于物体的总数,这样的编码方式对于商品来说是极端稀疏的,甚至用multi hot encoding对用户浏览历史的编码也会是一个非常稀疏的向量。而深度学习的特点以及工程方面的原因使其不利于稀疏特征向量的处理。

为什么说深度学习的特点不适合处理特征过于稀疏的样本?

1、稀疏编码带来的全连接层参数爆炸,特征过于稀疏会导致整个网络收敛过慢,
2、因为每次更新只有极少数的权重会得到更新。这在样本有限的情况下几乎会导致模型不收敛。

Word2Vec中为什么使用负采样?

1.加速了模型计算。负采样的核心思想是:计算目标单词和窗口中的单词的真实单词对“得分”,再加一些“噪声”,即词表中的随机单词和目标单词的“得分”。放弃softmax函数,采用sigmoid函数,这样就不存在先求一遍窗口中所有单词的‘“得分”的情况了。
2.保证了模型训练的效果,其一模型每次只需要更新采样的词的权重,不用更新所有的权重,那样会很慢,其二中心词其实只跟它周围的词有关系,位置离着很远的词没有关系,也没必要同时训练更新。

word2vec中的负例采样为什么可以得到和softmax一样的效果?

softmax归一化因词表大而复杂度高,理论上NCE近似softmax。而负采样又是NCE的特例。实际中负采样数很少,因此近似NCE,又近似softmax,而且负采样公式更简单而被广泛运用。负采样在保证精读的前提下,提升了训练速度,很多大规模分布式模型训练的银弹。

word2vec实现
万物皆Embedding
秒懂词向量Word2vec的本质
word2vec 中的数学原理详解
word2vec原理 CBOW与Skip-Gram模型基础

posted @ 2019-10-04 10:51  Jamest  阅读(778)  评论(0编辑  收藏  举报