万物皆可Vector之Word2vec:2个模型、2个优化及实战使用"/>
论文|万物皆可Vector之Word2vec:2个模型、2个优化及实战使用
万物皆可Embedding系列会结合论文和实践经验进行介绍,前期主要集中在论文中,后期会加入实践经验和案例,目前已更新:
- 万物皆可Vector之语言模型:从N-Gram到NNLM、RNNLM
- 万物皆可Vector之Word2vec:2个模型、2个优化及实战使用
- Item2vec中值得细细品味的8个经典tricks和thinks
后续会持续更新Embedding相关的文章,欢迎持续关注「搜索与推荐Wiki」
2.1、背景介绍
word2vec 是Google 2013年提出的用于计算词向量的工具,在论文Efficient Estimation of Word Representations in Vector Space中,作者提出了Word2vec计算工具,并通过对比NNLM、RNNLM语言模型验证了word2vec的有效性。
word2vec工具中包含两种模型:CBOW和skip-gram。论文中介绍的比较简单,如下图所示,CBOW是通过上下文的词预测中心词,Skip-gram则是通过输入词预测上下文的词。
2.2、CBOW 和 Skip-gram
原论文对这两种模型的介绍比较粗略,在论文《word2vec Parameter Learning Explained》中进行了详细的解释和说明,接下来我们详细看下CBOW和Skip-gram。
a)CBOW
One-word context
首先看一下只有一个上下文词的情况
其中单词的总个数为 V V V,隐藏层的神经元个数为 N N N,输入层到隐藏层的权重矩阵为 W V ∗ N W_{V*N} WV∗N,隐藏层到输出层的权重矩阵为 W N ∗ V ′ W'_{N*V} WN∗V′。
输入层是单词的One-hot编码。从输入层到隐藏层:
h = W T x : = v w I T h = W^T x := v^T_{w_I} h=WTx:=vwIT
v w I v_{w_I} vwI 表示的就是输入单词 w I w_I wI的向量表示(注意和输入层 x x x进行区分,输入向量即 W W W中的向量表示,输出向量即 W ′ W' W′中的向量表示),其维度为 [ N , 1 ] [N, 1] [N,1],转置后维度变成了 [ 1 , N ] [1,N] [1,N],用来表示向量的输入表述,要注意这里不是 [ 1 , N ] [1,N] [1,N],否则容易在往下的第二个公式中相乘时维度搞混,符号 : = := :=表示的定义为。
从隐藏层到输入层:
u j = ( v w j ′ ) T h u_j = (v'_{w_j})^T h uj=(vwj′)Th
其中 v w j ′ v'_{w_j} vwj′ 表示的是矩阵 W ′ W' W′的第 j j j 列,其维度为 [ N , 1 ] [N,1] [N,1],计算出来的 u j u_j uj为一个具体的值,表示的是第 j j j个输入的词在输出层第 j j j个位置对应的值。
最后使用 s o f t m a x softmax softmax进行归一化处理(因为输出层是固定的 V V V个单词,所以可以看作是多分类,因此使用 s o f t m a x softmax softmax进行归一化),得到输入单词 w I w_I wI所属词库中每个单词的概率:
p ( w j ∣ w I ) = y j = e x p ( u j ) ∑ j ′ = 1 V e x p ( u j ′ ) p(w_j | w_I) = y_j = \frac{exp(u_j)}{ \sum_{j'=1}^{V}exp(u_j') } p(wj∣wI)=yj=∑j′=1Vexp(uj′)exp(uj)
其中 y j y_j yj 表示的是输出层的第 j j j个神经元的值。
联合上面三个公式可得:
p ( w j ∣ w I ) = e x p ( ( v w j ′ ) T ∗ v w I T ) ∑ j ′ = 1 V e x p ( ( v w j ′ ′ ) T ∗ v w I T ) p(w_j | w_I) = \frac{ exp((v'_{w_j})^T * v^T_{w_I}) }{\sum_{j'=1}^{V}exp((v'_{w_{j'}})^T * v^T_{w_I})} p(wj∣wI)=∑j′=1Vexp((vwj′′)T∗vwIT)exp((vwj′)T∗vwIT)
其中 v w v_w vw可以理解为单词的输入向量表示, v w ′ v_w' vw′为单词的输出向量表示。
此种情况下的损失函数为:
m a x p ( w O ∣ w I ) = m a x y j ∗ = m a x l o g y j ∗ = u j ∗ − l o g ∑ j ′ = 1 V e x p ( u j ′ ) : = − E max \, \, p(w_O|w_I) = max \, y_{j*} \\ = max \, log \, y_{j*} \\ = u_{j*} - log \sum_{j'=1}^{V} exp(u_{j'}) \\ := -E maxp(wO∣wI)=maxyj∗=maxlogyj∗=uj∗−logj′=1∑Vexp(uj′):=−E
上述公式可以转化为:
E = − u j ∗ + l o g ∑ j ′ = 1 V e x p ( u j ′ ) E = - u_{j*} + log \sum_{j'=1}^{V} exp(u_{j'}) E=−uj∗+logj′=1∑Vexp(uj′)
其中 j ∗ j* j∗ 表示 实际输出层输出值对应的下标。因为 u j ∗ u^*_j uj∗ 是固定的,因此 m a x l o g y j ∗ max \,log \, y_{j*} maxlogyj∗时,只需对分母求 l o g log log即可
multi-word context
当有多个上下文单词时对应的图为:
此时的 h h h 表达式为:
h = 1 C W T ( x 1 + x 2 + . . . . + x C ) = 1 C ( v w 1 + v w 2 + . . . + v w C ) T h = \frac{1}{C} W^T (x_1 + x_2 + .... + x_C) \\ = \frac{1}{C} (v_{w_1} + v_{w_2}+ ... + v_{w_C})^T h=C1WT(x1+x2+....+xC)=C1(vw1+vw2+...+vwC)T
其中 C C C 表示上下文单词的个数, w 1 , w 2 , . . . , w C w_1, w_2, ..., w_C w1,w2,...,wC 表示上下文单词, v w v_w vw 表示单词的输入向量(注意和输入层 x x x区别)。
目标函数为:
E = − l o g p ( w O ∣ w I 1 , w I 2 , . . . , w I C ) = − u j ∗ l o g ∑ j ′ = 1 V e x p ( u j ′ ) = − ( v w O ′ ) T ∗ h + l o g ∑ j ′ = 1 V e x p ( ( v w j ′ ) T ∗ h ) E = -log \, p(w_O | w_{I_1}, w_{I_2}, ..., w_{I_C}) \\ = - u_j * log \sum_{j'=1}^{V} exp(u_j') \\ = -(v'_{w_O})^T * h + log \sum_{j'=1}^{V} exp((v'_{w_j})^T * h) E=−logp(wO∣wI1,wI2,...,wIC)=−uj∗logj′=1∑Vexp(uj′)=−(vwO′)T∗h+logj′=1∑Vexp((vwj′)T∗h)
b)Skip-gram
从输入层到隐藏层:
h = W k , . T : = v w I T h =W^T_{k,.} := v^T_{w_I} h=Wk,.T:=vwIT
从隐藏层到输出层:
p ( w c , j = w O , c ∣ w I ) = y c , j = e x p ( u c , j ) ∑ j ′ = 1 V e x p ( u j ′ ) p(w_{c,j}= w_{O,c} | w_I) = y_{c, j} = \frac{exp(u_{c,j})} {\sum_{j'=1}^{V}exp(u_{j'})} p(wc,j=wO,c∣wI)=yc,j=∑j′=1Vexp(uj′)exp(uc,j)
其中:
- w I w_I wI 表示的是输入词
- w c , j w_{c,j} wc,j 表示输出层第 c c c个词实际落在了第 j j j个神经元
- w O , c w_{O,c} wO,c 表示输出层第 c c c个词应该落在第 O O O个神经元
- y c , j y_{c,j} yc,j 表示输出层第 c c c个词实际落在了第 j j j个神经元上归一化后的概率
- u c , j u_{c,j} uc,j 表示输出层第 c c c个词实际落在了第 j j j个神经元上未归一化的值
因为输出层共享权重,所以:
u c , j = u j = ( v w j ′ ) T ∗ h , f o r c = 1 , 2 , . . . , C u_{c,j} = u_j = (v'_{w_j})^T * h, for \, c=1,2,..., C uc,j=uj=(vwj′)T∗h,forc=1,2,...,C
其中 v w j ′ v'_{w_j} vwj′ 表示第 j j j个单词的输出向量,其值为输出权重矩阵 W ′ W' W′的第 j j j 列。
损失函数变为:
E = − l o g p ( w O , 1 , w O , 2 , . . , w O , C ∣ w I ) = − l o g ∏ c = 1 C e x p ( u c , j c ∗ ) ∑ j ′ = 1 V e x p ( u j ′ ) = − ∑ c = 1 C u j c ∗ + C ∗ l o g ∑ j ′ = 1 V e x p ( u j ′ ) E = -log \, p(w_{O,1}, w_{O,2}, .., w_{O,C}|w_I) \\ = -log \prod_{c=1}^{C} \frac{exp(u_{c,j^*_c})}{ \sum_{j'=1}^{V} exp(u_{j'})} \\ = -\sum_{c=1}^{C} u_{j^*_c} + C * log \sum_{j'=1}^{V} exp(u_{j'}) E=−logp(wO,1,wO,2,..,wO,C∣wI)=−logc=1∏C∑j′=1Vexp(uj′)exp(uc,jc∗)=−c=1∑Cujc∗+C∗logj′=1∑Vexp(uj′)
注意⚠️
- 经验上一般选择使用skip-gram模型,因为效果较好
- 在Word2vec模型中,如果选择使用CBOW时,最终产出的word embedding为 单词的输出向量( W N ∗ V ′ W'_{N*V} WN∗V′)表示,如果选择使用skip-gram时,最终产出的word embedding为单词的输入向量( W N ∗ V W_{N*V} WN∗V)表示,因为更倾向于选择靠近中心词一端的权重矩阵。
2.3、hierarchical softmax 和negative sampling
因为基于word2vec框架进行模型训练要求语料库非常大,这样才能保证结果的准确性,但随着预料库的增大,随之而来的就是计算的耗时和资源的消耗。那么有没有优化的余地呢?比如可以牺牲一定的准确性来加快训练速度,答案就是 hierarchical softmax 和 negative sampling。
在论文《Distributed Representations of Words and Phrases and their Compositionality》中介绍了训练word2vec的两个技(同样在论文《word2vec Parameter Learning Explained》中进行了详细的解释和说明),下面来具体看一下。
a)霍夫曼树和霍夫曼编码
在了解层次softmax(hierarchical softmax)之前,先来理解一下什么是霍夫曼树和霍夫曼编码。
霍夫曼树本质上是一棵最优二叉树,是指对于一组带有确定权值的叶子节点所构造的具有带权路径长度最短的二叉树。
那么针对一组权重值,如何构造一棵霍夫曼树呢?根据权值大的结点尽量靠近根这一原则,给出了一个带有一般规律的算法,称为霍夫曼算法,其描述如下:
- 1、根据给定 n n n个权值 w 1 , w 2 , . . . , w n {w_1,w_2,...,w_n} w1,w2,...,wn构成 n n n棵二叉树的集合 F = T 1 , T 2 , . . . , T n F={T_1,T_2,...,T_n} F=T1,T2,...,Tn;其中,每棵二叉树 T i ( 1 < = i < = n ) T_i(1<=i<=n) Ti(1<=i<=n)只有一个带权值 w i w_i wi的根结点,其左、右子树均为空
- 2、在 F F F中选取两棵根结点权值最小的二叉树作为左、右子树来构造一棵新的二叉树,且置新的二叉树根结点权值为其左右子树根结点的权值之和
- 3、在 F F F中删除这两棵树,同时将生成新的二叉树加入到 F F F中
- 4、重复2、3,直到 F F F中只剩下一棵二叉树加入到 F F F中
例如一组数据其对应的权重为:[9,11,13,8,4,5],其生成的霍夫曼树为(图来源于百度经验):
注意⚠️:
- 在构造哈夫曼树时,叶子节点无左右之分,只需约定好一个规则,从头到尾遵守这个规则执行即可。习惯上左节点比右节点小。
那什么又是霍夫曼编码呢?霍夫曼编码是一种基于霍夫曼树的编码方式,是可变长编码的一种。
对于构造好的霍夫曼树进行0/1编码,左子树为0,右子树为1,则针对上图构造好的霍夫曼树,其各个叶子节点的霍夫曼编码分别为:
- 9 -> 00
- 11 -> 01
- 13 -> 10
- 8 -> 110
- 4 -> 1110
- 5 -> 1111
注意⚠️:
- 同样针对霍夫曼树的编码也没有明确规定说左子树为1或者左子树为0
- 在word2vec中,针对霍夫曼树的构建和编码和上边说的相反,即约定左子树编码为1,右子树编码为0(论文中说的是-1,含义一致),同时约定左子树的权重不小于右子树的权重
b)hierarchical softmax
上图为一棵霍夫曼编码树,其中白色结点表示词库中的所有单词,黑色结点表示内部的隐藏结点,单词 w 2 w_2 w2 对应的路径编码如图中黑色线连接所示,其路径长度为4, n ( w , j ) n(w,j) n(w,j)表示的是针对单词 w w w,其所在路径上的第 j j j个结点。
基于霍夫曼树进行优化的word2vec,移除了从隐藏层到输出层的权重矩阵(即输出向量),使用的是霍夫曼树中的隐藏结点编码代替(如上图中的黑色结点),那么输出结点是输入单词 w w w的概率可以表示为:
p ( w = w O ) = ∏ j = 1 L ( w ) − 1 σ ( [ n ( w , j + 1 ) = c h ( n ( w , j ) ) ] ⋅ ( v n ( w , j ) ′ ) T ∗ h ) p(w=w_O) = \prod_{j=1}^{L(w)-1} \sigma ( \left[ n(w,j+1)=ch(n(w,j)) \right] \cdot(v'_{n(w,j)})^T * h ) p(w=wO)=j=1∏L(w)−1σ([n(w,j+1)=ch(n(w,j))]⋅(vn(w,j)′)T∗h)
其中:
- c h ( n ) ch(n) ch(n) 表示路径中的隐藏的左结点
- v n ( w , j ) ′ v'_{n(w,j)} vn(w,j)′ 表示 隐藏结点的向量表示(整个算法优化过程中的辅助向量)
- n ( w , j ) n(w,j) n(w,j) 表示单词 w w w所在路径上的第 j j j个结点
- h h h 表示隐藏层的输出(skip-gram模型中其等于 v w I v_{w_I} vwI,cbow模型中其等于 1 C ∑ c = 1 C v w c \frac{1}{C} \sum_{c=1}^{C}v_{w_c} C1∑c=1Cvwc,即输入词向量求平均)
- L ( w ) L(w) L(w) 表示叶子结点是 w w w的最短路径中长度,减1表示的是到达该结点之前的隐藏结点
- [ x ] \left[ x \right ] [x] 的定义如下(即如果走的是左子树路径为1,右子树路径为-1)
[ x ] = { 1 i f x i s t r u e − 1 o t h e r w i s e \left [ x \right ] = \left\{\begin{matrix} 1 & if \, x \, is \, true \\ -1 & otherwise \end{matrix}\right. [x]={1−1ifxistrueotherwise
在上图中,我们定义是左结点的概率为:
p ( n , l e f t ) = σ ( ( v n ′ ) T ⋅ h ) p(n,left) = \sigma((v'_n)^T \cdot h) p(n,left)=σ((vn′)T⋅h)
其中 σ \sigma σ 表示的是 s i g m o i d sigmoid sigmoid函数
右结点的概率为:
p ( n , r i g h t ) = 1 − σ ( ( v n ′ ) T ⋅ h ) = σ ( − ( v n ′ ) T ⋅ h ) p(n, right) = 1 - \sigma((v'_n)^T \cdot h) = \sigma(- (v'_n)^T \cdot h) p(n,right)=1−σ((vn′)T⋅h)=σ(−(vn′)T⋅h)
那么针对图中的单词 w 2 w_2 w2,其概率为:
p ( w 2 = w O ) = p ( n ( w 2 , 1 ) , l e f t ) ⋅ p ( n ( w 2 , 2 ) , l e f t ) ⋅ p ( n ( w 2 , 3 ) , r i g h t ) = σ ( ( v n ( w 2 , 1 ) ′ ) T ⋅ h ) ⋅ σ ( ( v n ( w 2 , 2 ) ′ ) T ⋅ h ) ⋅ σ ( − ( v n ( w 2 , 3 ) ′ ) T ⋅ h ) p(w_2 = w_O) \\ = p(n(w_2,1),left) \cdot p(n(w_2,2), left) \cdot p(n(w_2,3),right) \\ = \sigma((v'_{n(w_2,1)})^T \cdot h) \cdot \sigma((v'_{n(w_2,2)})^T \cdot h) \cdot \sigma(- (v'_{n(w_2,3)})^T \cdot h) p(w2=wO)=p(n(w2,1),left)⋅p(n(w2,2),left)⋅p(n(w2,3),right)=σ((vn(w2,1)′)T⋅h)⋅σ((vn(w2,2)′)T⋅h)⋅σ(−(vn(w2,3)′)T⋅h)
针对所有的单词有:
∑ i = 1 V p ( w i = w O ) = 1 \sum_{i=1}^{V} p(w_i = w_O) = 1 i=1∑Vp(wi=wO)=1
此时其损失函数为:
E = − l o g p ( w = w O ∣ w I ) = − ∑ j = 1 L ( w ) − 1 l o g σ ( [ n ( w , j + 1 ) = c h ( n ( w , j ) ) ] ⋅ ( v n ( w , j ) ′ ) T ∗ h ) E = -log \, p(w=w_O|w_I) = - \sum_{j=1}^{L(w)-1} log \, \sigma ( \left[ n(w,j+1)=ch(n(w,j)) \right] \cdot(v'_{n(w,j)})^T * h ) E=−logp(w=wO∣wI)=−j=1∑L(w)−1logσ([n(w,j+1)=ch(n(w,j))]⋅(vn(w,j)′)T∗h)
c)negative sampling
除了hierarchical softmax,另外一种优化方法是Noise Contrasive Estimation(NCE),在论文《Noise-contrastive estimation of unnormalized statistical models, with applications to natural image statistics》中有详细的解释和说明,但因为NCE的逻辑有些复杂,所以这里使用的是简化版的,称之为:Negative Sampling。
因为每次计算全量的负样本计算量比较大,因此进行了负采样,负采样之后对应的损失函数为:
E = − l o g σ ( ( v w O ′ ) T h ) − ∑ w j ∈ W n e g l o g σ ( − ( v w i ′ ) T h ) = − l o g σ ( ( v w O ′ ) T h ) + ∑ w j ∈ W n e g l o g σ ( ( v w i ′ ) T h ) E = -log \sigma ((v'_{w_O})^T h) - \sum_{w_j \in W_{neg}} log \, \sigma(-(v'_{w_i})^T h) \\ = -log \sigma ((v'_{w_O})^T h) + \sum_{w_j \in W_{neg}} log \, \sigma((v'_{w_i})^T h) E=−logσ((vwO′)Th)−wj∈Wneg∑logσ(−(vwi′)Th)=−logσ((vwO′)Th)+wj∈Wneg∑logσ((vwi′)Th)
其中:
- w O w_O wO 表示输出的单词
- v w O ′ v'_{w_O} vwO′ 表示 w O w_O wO的输出词向量
- h h h 表示隐藏层的输出,当模型为CBOW时为 1 C ∑ c = 1 C v w c \frac{1}{C} \sum_{c=1}^{C}v_{w_c} C1∑c=1Cvwc,如果时skip-gram模型时为: v w I v_{w_I} vwI
- W n e g W_{neg} Wneg 表示的是负采样的样本数
注意⚠️:
- 基于层次softmax或者negative sampling优化的cbow或者skip-gram模型,输出的词向量应该是输入层到隐藏层之间的词向量(之所以说应该,是因为论文中没有进行特意说明,也没有在公开的资料中看到,可能是我看的不够认真)
- 猜想:能否根据最短路径节点的平均向量来表示叶子结点,即词向量?
- 以上两个问题有读者明白了可以在评论区进行留言,感谢!
2.4、Gensim中Word2vec的使用
关于gensim的文档可以参考:.html#documentation
使用之前需要先引入对应的模型类
f r o m g e n s i m . m o d e l s . w o r d 2 v e c i m p o r t W o r d 2 V e c from gensim.models.word2vec import Word2Vec fromgensim.models.word2vecimportWord2Vec
创建一个模型
m o d e l = W o r d 2 V e c ( s e n t e n c e s = t o p i c s l i s t , i t e r = 5 , s i z e = 128 , w i n d o w = 5 , m i n c o u n t = 0 , w o r k e r s = 10 , s g = 1 , h s = 1 , n e g a t i v e = 1 , s e e d = 128 , c o m p u t e l o s s = T r u e ) model = Word2Vec(sentences=topics_list, iter=5, size=128, window=5, min_count=0, workers=10, sg=1, hs=1, negative=1, seed=128, compute_loss=True) model=Word2Vec(sentences=topicslist,iter=5,size=128,window=5,mincount=0,workers=10,sg=1,hs=1,negative=1,seed=128,computeloss=True)
其对应的模型参数有很多,主要的有:
- sentences:训练模型的语料,是一个可迭代的序列
- corpus_file:表示从文件中加载数据,和sentences互斥
- size:word的维度,默认为100,通常取64、128、256等
- window:滑动窗口的大小,默认值为5
- min_count:word次数小于该值被忽略掉,默认值为5
- seed:用于随机数发生器
- workers:使用多少线程进行模型训练,默认为3
- min_alpha=0.0001
- sg:1 表示 Skip-gram 0 表示 CBOW,默认为0
- hs:1 表示 hierarchical softmax 0 且 negative 参数不为0 的话 negative sampling 会被启用,默认为0
- negative:0 表示不采用,1 表示采用,建议值在 5-20 表示噪音词的个数,默认为5
更多参数可以参考模型中的注释
保存模型
m o d e l . s a v e ( m o d e l p a t h ) model.save(model_path) model.save(modelpath)
加载模型
m o d e l = W o r d 2 V e c . l o a d ( m o d e l p a t h ) model = Word2Vec.load(model_path) model=Word2Vec.load(modelpath)
输出loss值
m o d e l . g e t l a t e s t t r a i n i n g l o s s ( ) model.get_latest_training_loss() model.getlatesttrainingloss()
计算相似度
m o d e l . w v . s i m i l a r i t y ( w o r d 1 , w o r d 2 ) model.wv.similarity(word1, word2) model.wv.similarity(word1,word2)
扫一扫关注「搜索与推荐Wiki」!号主「专注于搜索和推荐系统,以系列分享为主,持续打造精品内容!」
更多推荐
论文|万物皆可Vector之Word2vec:2个模型、2个优化及实战使用
发布评论