《word2vec Parameter Learning Explained》论文学习笔记

目录:


由于word2vec模型学习生成的词向量表示方法能够携带句子的语义信息(semantic meanings),因此非常适用于多种NLP任务。
这篇论文详细地推导和解释了word2vec模型的参数更新公式,包括:CBOW(continuous bag-of-word)模型和SG(skip-gram)模型,以及两种参数优化技术:hierarchical softmaxnegative sampling.


1 Continuous Bag-of-Word Model


1.1 One-word context


我们从CBOW模型的最简单版本开始介绍——One-word context。即我们假定context(预测目标单词的上下文信息)只有一个单词,也就是说One-word context 模型是在只要一个上下文单词(one context word)的情况下来预测一个目标单词(one target word)的。(注:对于初学神经网络的读者,建议先看完附录A之后,在回到此处阅读下文)。
这里写图片描述

如图1描述的就是One-word context定义之下的神经网络模型。这里我们假设文本词汇量的大小为V,隐藏层的大小为N,相邻层的神经元是全连接的。输入层是一个用one-hot方式编码的单词向量 x = ( x 1 , . . . , x V ) \mathbf x=(x_1,...,x_V) x=(x1,...,xV),其中只有一个 x i x_i xi为1,其余均为0。
从输入层到 隐藏层的权重值可以用一个 V × N V\times N V×N维的矩阵 W W W来表示,即
W = ( ω 11 ω 12 . . . ω 1 N ω 21 ω 22 . . . ω 2 N . . . . . . . . . . . . ω V 1 ω V 2 . . . ω V N ) W= \begin{pmatrix} \omega_{11}&\omega_{12}&...&\omega_{1N}\\ \omega_{21}&\omega_{22}&...&\omega_{2N}\\ ...&...&...&...\\ \omega_{V1}&\omega_{V2}&...&\omega_{VN} \end{pmatrix} W=ω11ω21...ωV1ω12ω22...ωV2............ω1Nω2N...ωVN

其中 W W W矩阵的每一行代表的是一个与输入层相关的单词的N维向量表示形式 v ω \mathbf v_{\omega} vω。那么假设我们给定了一个输入单词(a context),其单词向量的第k个元素 x k = 1 x_k=1 xk=1,其余均为0,则有

h = W T x = W ( k , ∙ ) T x k = v ω I T (1) \mathbf h= \mathbf W^Tx=\mathbf W_{(k,\bullet)}^T x_k=\mathbf v_{\omega_I}^T\tag{1} h=WTx=W(k,)Txk=vωIT(1)

(1)式我们可以看出, h h h向量完全是从 W W W矩阵第k行复制过来的(同 v ω I \mathbf v_{\omega_I} vωI均为N维向量)。 v ω I \mathbf v_{\omega_I} vωI即为输入单词 ω I \omega_I ωI的一种向量表示(其实就是输入向量,我们后面会提到)。

分析完输入层到隐藏层之后,我们再看隐藏层到输出层,同样连接权重用一个新的N × V矩阵 W ′ = { ω i j ′ } \mathbf W'=\{\omega_{ij}' \} W={ωij}来表示如下:

W ′ = ( ω 11 ′ ω 12 ′ . . . ω 1 V ′ ω 21 ′ ω 22 ′ . . . ω 2 V ′ . . . . . . . . . . . . ω N 1 ′ ω N 2 ′ . . . ω N V ′ ) \mathbf W'= \begin{pmatrix} \omega_{11}'&\omega_{12}'&...&\omega_{1V}'\\ \omega_{21}'&\omega_{22}'&...&\omega_{2V}'\\ ...&...&...&...\\ \omega_{N1}'&\omega_{N2}'&...&\omega_{NV}' \end{pmatrix} W=ω11ω21...ωN1ω12ω22...ωN2............ω1Vω2V...ωNV

通过这些权重,我们可以为词表中的每一个单词都计算出一个得分 μ j \mu_j μj

μ j = v ω j ′ T h (2) \mu_j=\mathbf {v_{\omega_j}'}^T\mathbf h\tag{2} μj=vωjTh(2)
其中, v ω j ′ {v_{\omega_j}'} vωj即为矩阵 W ′ \mathbf W' W的第j列向量(也是N维向量,其实就是单词w的输出向量,我们后面会提到)。

经过以上讨论之后,我们可以使用一种对数-线性分类模型softmax函数来计算单词的后验分布(是多项式分布)

p ( ω j ∣ ω I ) = y j = exp ⁡ ( μ j ) ∑ j ′ = 1 V exp ⁡ ( μ j ′ ) (3) p(\omega_j|\omega_I)=y_j=\frac{\exp(\mu_j)}{\sum_{j'=1}^V\exp(\mu_{j'})}\tag{3} p(ωjωI)=yj=j=1Vexp(μj)exp(μj)(3)

其中, y j y_j yj表示输出层第j个神经单元的输出值。将(1)式和(2)式代入(3)式我们可以得到:

p ( ω j ∣ ω I ) = exp ⁡ ( v ω j ′ T v ω I ) ∑ j ′ = 1 V exp ⁡ ( v ω j ′ T v ω I ) (4) p(\omega_j|\omega_I)=\frac{\exp({\mathbf v_{\omega_j}'}^T \mathbf v_{\omega_I})}{\sum_{j'=1}^V\exp({\mathbf v_{\omega_j}'}^T \mathbf v_{\omega_I})}\tag{4} p(ωjωI)=j=1Vexp(vωjTvωI)exp(vωjTvωI)(4)

注意:正如前文所述, v ω \mathbf v_\omega vω v ω ′ \mathbf v_\omega' vω是单词的两种向量表示形式。其中 v ω \mathbf v_\omega vω实际上是权重矩阵 W \mathbf W W(input->hidden)的某一行向量, v ω ′ \mathbf v_\omega' vω则是权重矩阵 W ′ \mathbf W' W(hidden->output)的某一列向量。我们将 v ω \mathbf v_\omega vω v ω ′ \mathbf v_\omega' vω分别称为“输入向量(input vector)”和“输出向量(output vector)”(二者均为N维向量)。


Update equation for hidden→output weights


接下来让我们推到权重矩阵的更新公式,尽管在实际的计算过程中这样做是不切实际的(我们在之后再谈)。
在我们推导hidden→output权重的更新公式的过程中,需要用到神经网络的反向传播算法,对这部分内容不熟悉的读者可以参考附录A的内容。
由以上描述可知,该模型训练的目标就是求公式(4)的最大值。公式(4)代表的就是给定上下文信息(这里为一个单词 ω I \omega_I ωI)以及其权重矩阵的情况下,预测其实际输出单词(即上下文信息的中心词 ω O \omega_O ωO)的条件概率
max ⁡ p ( ω O ∣ ω I ) = max ⁡ y j ∗ = max ⁡ log ⁡ y j ∗ = μ j ∗ − log ⁡ ∑ j ′ = 1 V exp ⁡ ( μ j ′ ) : = − E (7) \begin{aligned} \max p(\omega_O|\omega_I) &=\max y_{j^*} \\ &=\max \log y_{j^*} \\ &=\mu_{j^*} - \log \sum_{j'=1}^V \exp(\mu_{j'}):=-E\tag{7} \end{aligned} maxp(ωOωI)=maxyj=maxlogyj=μjlogj=1Vexp(μj):=E(7)
其中, E = − log ⁡ p ( ω O ∣ ω I ) E=-\log p(\omega_O|\omega_I) E=logp(ωOωI) 为该模型的损失函数(我们需要找出它的最小值), μ j ∗ \mu_{j}^* μj的表示方式由公式(2)而来, j ∗ j^* j则为实际输出单词的索引下标。我们注意到该损失函数可以理解为一种特殊情形下的交叉熵计算。

现在我们开始推导从隐藏层到输出层的权重矩阵在模型训练过程中的参数更新公式。首先我们对损失函数 E = − log ⁡ p ( ω O ∣ ω I ) E=-\log p(\omega_O|\omega_I) E=logp(ωOωI) 求关于得分 μ j \mu_j μj的偏导数,得结果为:

∂ E ∂ μ j = y j − t j : = e j (8) \frac{\partial E}{\partial\mu_j}=y_j-t_j:=e_j\tag{8} μjE=yjtj:=ej(8)

其中, t j = 1 ( j = j ∗ ) t_j=1(j=j^*) tj=1(j=j) ,即当且仅当输出层的第j个神经单元为真实的输出单词时 t j t_j tj的取值为1。接下来我们根据链式法则求出损失函数 E E E关于矩阵 W ′ W' W元素 ω i j ′ \omega_{ij}' ωij的偏导数为:

∂ E ∂ ω i j ′ = ∂ E ∂ μ j ⋅ ∂ μ j ∂ ω i j ′ = e j ⋅ h i (9) \frac{\partial E}{\partial \omega_{ij}'}=\frac{\partial E}{\partial \mu_j}\cdot \frac{\partial \mu_j}{\partial \omega_{ij}'}=e_j\cdot h_i \tag{9} ωijE=μjEωijμj=ejhi(9)

因此,采用随机梯度下降算法(SGD),我们最终得到了隐藏层到输出层(hidden → \rightarrow output)权重的更新公式如下:
ω i j ′ ( n e w ) = ω i j ′ ( o l d ) − η ⋅ e j ⋅ h i (10) \begin{aligned} {\omega_{ij}'}^{(new)}={\omega_{ij}'}^{(old)}-\eta \cdot e_j \cdot h_i\tag{10} \end{aligned} ωij(new)=ωij(old)ηejhi(10)
or
v ω j ′ ( n e w ) = v ω j ′ ( o l d ) − η ⋅ e j ⋅ h    f o r   j = 1 , 2 , . . . V . (11) \begin{aligned} {\mathbf v_{\omega_j}'}^{(new)}= {\mathbf v_{\omega_j}'}^{(old)} - \eta \cdot e_j \cdot \mathbf h \space \space for\space j=1,2,...V.\tag{11} \end{aligned} vωj(new)=vωj(old)ηejh  for j=1,2,...V.(11)

其中, η > 0 \eta >0 η>0为参数更新的学习速率; e j = y j − t j e_j=y_j-t_j ej=yjtj h i h_i hi 为隐藏层的第i个神经单元; v ω j ′ \mathbf v_{\omega_j}' vωj ω j \omega_j ωj的输出向量。

由公式(11)我们可以看出:在更新权重参数的过程中,我们需要检查词汇表中的每一个单词,计算出它的输出概率 y j y_j yj,并与期望输出 t j t_j tj(取值只能为0或者1)进行比较。比较过程如下:

1)如果 y j > t j y_j>t_j yj>tj(“overestimating”),那么就从向量 v ω j ′ \mathbf v_{\omega_j}' vωj中减去隐藏向量 h \mathbf h h的一部分(例如 v ω I \mathbf v_{\omega_I} vωI),这样向量 v ω j ′ \mathbf v_{\omega_j}' vωj就会与向量 v ω I \mathbf v_{\omega_I} vωI相差更远。
2)如果 y j < t j y_j<t_j yj<tj(“underestimating”,这种情况只有在 t j = 1 t_j=1 tj=1时,才会发生,此时 ω j = ω O \omega_j=\omega_O ωj=ωO),则将隐藏向量 h h h的一部分加入 v ω O ′ \mathbf v_{\omega_O}' vωO,使得 v ω O ′ \mathbf v_{\omega_O}' vωO v ω I \mathbf v_{\omega_I} vωI更接近。
3)如果 y j y_j yj t j t_j tj非常接近,则此时 e j = y j − t j e_j=y_j-t_j ej=yjtj由于(公式(8))非常接近于0,故更新参数基本上没什么变化。

这里需要再次提醒的是: v ω \mathbf v_{\omega} vω v ω ′ \mathbf v_\omega ' vω是单词 ω \omega ω的两种不同的向量表示形式。


Update equation for input→hidden weights


在介绍完hidden → \rightarrow output的权重矩阵更新公式之后,我们接着介绍input → \rightarrow hidden的权重矩阵 W W W的更新过程。我们继续对损失函数 E E E求关于隐藏层 h i h_i hi 的偏导数,得:

∂ E ∂ h i = ∑ j = 1 V ∂ E ∂ μ j ⋅ ∂ μ j ∂ h i = ∑ j = 1 V e j ⋅ ω i j ′ : = E H i (12) \frac{\partial E}{\partial h_i}=\sum_{j=1}^V \frac{\partial E}{\partial \mu_j} \cdot \frac{\partial \mu_j}{\partial h_i}=\sum_{j=1}^V e_j \cdot \omega_{ij}':=EH_i \tag{12} hiE=j=1VμjEhiμj=j=1Vejωij:=EHi(12)

其中 h i h_i hi为隐藏层第i个神经单元的输出; μ j \mu_j μj在公式(2)中已经定义,表示输出层第j个神经单元的输入; e j = y j − t j e_j=y_j-t_j ej=yjtj为输出层第j个单词的预测误差。因此EH应该是一个N维向量,它的每一个元素代表的是词汇表中的每个单词的预测误差 e j e_j ej ω i j ′ \omega_{ij}' ωij在j=1到V上的乘积之和。

接下来,我们需要求出损失函数 E E E关于权重矩阵 W W W的偏导数。首先,分解公式(1),我们知道隐藏层激活单元的输出 h i h_i hi是输入层 x \mathbf x x与权重的线性组合,即

h i = ∑ k = 1 V x k ⋅ ω k i (13) h_i=\sum_{k=1}^V x_k \cdot \omega_{ki} \tag{13} hi=k=1Vxkωki(13)

因此对于权重矩阵 W W W的每一个元素,我们求关于 E E E的偏导数,得到:

∂ E ∂ ω k i = ∂ E ∂ h i ⋅ ∂ h i ∂ ω k i = E H i ⋅ x k (14) \frac{\partial E}{\partial \omega_{ki}}=\frac{\partial E}{\partial h_i} \cdot \frac{\partial h_i}{\partial \omega_{ki}}=EH_i \cdot x_k \tag{14} ωkiE=hiEωkihi=EHixk(14)

因此我们利用张量乘积的方式,便可得到:

∂ E ∂ W = x ⊗ E H = x E H T (15) \frac{\partial E}{\partial W}=\mathbf x \otimes EH = \mathbf xEH^T \tag{15} WE=xEH=xEHT(15)

我们再次得到了一个 V × N V\times N V×N的矩阵。由于 x \mathbf x x向量只有一个非0元素,因此 ∂ E ∂ W \frac{\partial E}{\partial W} WE 只有一行是N维非0向量 E H T EH^T EHT,因此矩阵 W W W的更新公式为:

v ω I ( n e w ) = v ω I ( o l d ) − η ⋅ E H T (16) {\mathbf v_{\omega_I}}^{(new)}={\mathbf v_{\omega_I}}^{(old)}-\eta \cdot EH^T \tag{16} vωI(new)=vωI(old)ηEHT(16)

其中 v ω I \mathbf v_{\omega_I} vωI是矩阵 W W W的其中一行,是唯一的上下文单词(context word)的“输入向量”,也是矩阵 W W W唯一的导数非0的行向量。 除了 v ω I \mathbf v_{\omega_I} vωI以外,矩阵 W W W的其他行向量在参数更新迭代过程中都会保持不变(因为其导数为0)。

与矩阵 W ′ W' W的更新过程相似,对于公式(16),我们分析如下:

1)如果过高地估计了某个单词 ω j \omega_j ωj作为最终输出单词的概率(即: y j > t j y_j>t_j yj>tj),则上下文单词 ω I \omega_I ωI(context word )的输入向量与单词 ω j \omega_j ωj的输出向量在更新的过程中会相差越来越大。

2)如果相反,某个单词 ω j \omega_j ωj作为最终输出单词的概率被低估(即: y j < t j y_j<t_j yj<tj),则单词 ω I \omega_I ωI的输入向量与单词 ω j \omega_j ωj的输出向量在更新过程中会越来越接近。

3)如果对于单词 ω I \omega_I ωI的概率预测是准确的,则对于单词的输入向量在更新过程中几乎保持不变。

因此,上下文单词 ω I \omega_I ωI(context word )的输入向量的更新取决于词汇表中所有单词的预测误差。预测误差越大,则该单词对于上下文单词的输入向量的更新过程影响越大。

在介绍完One-word context的CBOW模型之后,我们接着介绍multi-word context下的CBOW模型。


1.2 Multi-word context


根据字面意思我们就可以看出,基于multi-word context的CBOW模型就是利用多个上下文单词来推测中心单词target word的一种模型。其结构如图2所示:
这里写图片描述

其隐藏层的输出值的计算过程为:首先将输入的上下文单词(context words)的向量叠加起来并取其平均值,接着与input → \rightarrow hidden的权重矩阵相乘,作为最终的结果,公式如下:
h = 1 C W T ( x 1 + x 2 + ⋯ + x C ) = 1 C ( v ω 1 + v ω 2 + ⋯ + v ω C ) T (18) \begin{aligned} \mathbf h &= \frac{1}{C} \mathbf W^T(\mathbf x_1 + \mathbf x_2 + \cdots +\mathbf x_C)\\ & = \frac{1}{C}(\mathbf v_{\omega_1}+\mathbf v_{\omega_2} + \cdots+\mathbf v_{\omega_C})^T\tag{18} \end{aligned} h=C1WT(x1+x2++xC)=C1(vω1+vω2++vωC)T(18)

其中 C C C为上下文单词的个数, ω 1 , . . . , ω C \omega_1,...,\omega_C ω1,...,ωC为上下文单词, v ω \mathbf v_\omega vω为单词 ω \omega ω输入向量。损失函数为:
E = − log ⁡ p ( ω O ∣ ω I , 1 , . . . , ω I , C ) = − μ j ∗ + log ⁡ ∑ j ′ = 1 V e x p ( μ j ′ ) = − v ω O ′ T ⋅ h + log ⁡ ∑ j ′ = 1 V exp ⁡ ( v ω j ′ T ⋅ h ) (21) \begin{aligned} E &= - \log p(\omega_O|\omega_{I,1},...,\omega_{I,C})\\ & =- \mu_{j^*} + \log \sum_{j'=1}^{V} exp(\mu_{j'})\\ & = - {\mathbf v_{\omega_O}'}^T \cdot \mathbf h + \log \sum_{j'=1}^{V} \exp({\mathbf v_{\omega_j}'}^T \cdot \mathbf h)\tag{21} \end{aligned} E=logp(ωOωI,1,...,ωI,C)=μj+logj=1Vexp(μj)=vωOTh+logj=1Vexp(vωjTh)(21)
同样,由hidden → \rightarrow output的权重更新公式与one-word-context模型下的一模一样,即类似于公式(11),我们直接写在下面:

v ω j ′ ( n e w ) = v ω j ′ ( o l d ) − η ⋅ e j ⋅ h     f o r    j = 1 , 2 , . . . , V (22) {\mathbf v_{\omega_j}'}^{(new)}={\mathbf v_{\omega_j}'}^{(old)}-\eta \cdot e_j \cdot \mathbf h \space \space \space for \space \space j=1,2,...,V\tag{22} vωj(new)=vωj(old)ηejh   for  j=1,2,...,V(22)

由input → \rightarrow hidden 的权重矩阵更新公式与公式(16)类似,只不过现在我们需要对每一个上下文单词 ω I , c \omega_{I,c} ωI,c都执行如下更新公式:

v ω I , c ( n e w ) = v ω I , c ( o l d ) − 1 C ⋅ η ⋅ E H T    f o r    c = 1 , 2 , . . . , C . (23) {\mathbf v_{\omega_{I,c}}}^{(new)}={\mathbf v_{\omega_{I,c}}}^{(old)} - \frac{1}{C}\cdot \eta \cdot EH^T \space \space for \space \space c=1,2,...,C.\tag{23} vωI,c(new)=vωI,c(old)C1ηEHT  for  c=1,2,...,C.(23)

其中 v ω I , c {\mathbf v_{\omega_{I,c}}} vωI,c为上下文context中第c 个单词的输入向量; η \eta η为正学习速率; E H = ∂ E ∂ h i EH=\frac{\partial E}{\partial h_i} EH=hiE由公式(12)给出。


2 Skip-Gram Model


与CBOW模型正好相反,Skip-Gram模型是根据中心单词(target word)来预测其上上下文信息(context words)。如图3所示,为Skip-Gram模型的结构示意图。
这里写图片描述

我们仍然使用 v ω I \mathbf v_{\omega_I} vωI来表示输入层上唯一的那个单词的输入向量,因此,我们对于隐藏层的输出值 h \mathbf h h的计算公式与第一节公式(1)相同,表示如下:

h = W ( k , ∙ ) T : = v ω I (24) \mathbf h = {\mathbf W}_{(k,\bullet)}^T := \mathbf v_{\omega_I}\tag {24} h=W(k,)T:=vωI(24)
公式(24)显示: h \mathbf h h向量其实就是input->hidden权重矩阵 W \mathbf W W的某一行结合输入单词 ω I \omega _I ωI的向量拷贝。在输出层,与CBOW模型的输出为单个多项式分布不同的是,SG模型在输出层输出了C个多项式分布。每个输出都使用相同的hidden->output矩阵计算:

p ( ω c , j = ω O , c ∣ ω I ) = y c , j = exp ⁡ ( μ c , j ) ∑ j ′ = 1 V exp ⁡ ( μ j ′ ) (25) p(\omega_{c,j}=\omega_{O,c}|\omega_I)=y_{c,j}=\frac{\exp(\mu_{c,j})}{\sum_{{j}'=1}^V \exp(\mu_{j}')}\tag{25} p(ωc,j=ωO,cωI)=yc,j=j=1Vexp(μj)exp(μc,j)(25)

其中, ω c , j \omega_{c,j} ωc,j表示输出层的第c个panel的第j个单词(何为panel?就是输出层的表示每个上下文单词的神经元的组合,图中一种有C个context words,所以总共有C个panel); ω O , c \omega_{O,c} ωO,c实际上表示的是输出上下文单词(output context words)的第c个单词; ω I \omega_I ωI是唯一的输入单词; y c , j y_{c,j} yc,j为输出层的第c个panel上的第j个神经单元的概率输出值; μ c , j \mu_{c,j} μc,j表示的是输出层第c个panel的第j个神经元的输入值;由于输出层的所有panels共享同一权重矩阵 W ′ \mathbf W{}' W,因此:

μ c , j = μ j = v ω j ′ T ⋅ h ,   f o r   c = 1 , 2 , . . . , C (26) \mu_{c,j}=\mu_j={\mathbf v_{\omega_j}'}^T\cdot \mathbf h, \space for \space c=1,2,...,C\tag{26} μc,j=μj=vωjTh, for c=1,2,...,C(26)
其中, v ω j ′ \mathbf v_{\omega_j}' vωj为词汇表第j个单词 ω j \omega_j ωj的输出向量;同样,它也是取自于hidden → \rightarrow output权重矩阵 W ′ \mathbf W' W的一列。


SG模型参数更新公式的推导过程与one-word-context 模型的推导过程大体上一样。这里我们将损失函数变为:
E = − log ⁡ p ( ω O , 1 , ω O , 2 , . . . , ω O , C ∣ ω I ) = − log ⁡ ∏ c = 1 C exp ⁡ ( μ c , j c ∗ ) ∑ j ′ = 1 V exp ⁡ ( μ j ′ ) = − ∑ c = 1 C μ j c ∗ + C ⋅ log ⁡ ∑ j ′ = 1 V exp ⁡ ( μ j ′ ) (29) \begin{aligned} E &=-\log p(\omega_{O,1},\omega_{O,2},...,\omega_{O,C}|\omega_I)\\ &=-\log \prod_{c=1}^C \frac{\exp(\mu_{c,j_c^*})}{\sum_{j'=1}^V \exp(\mu_{j'})}\\ &=-\sum_{c=1}^C \mu_{j_c^* }+C\cdot\log\sum_{j'=1}^V\exp(\mu_{j'})\tag{29} \end{aligned} E=logp(ωO,1,ωO,2,...,ωO,CωI)=logc=1Cj=1Vexp(μj)exp(μc,jc)=c=1Cμjc+Clogj=1Vexp(μj)(29)
其中, j c ∗ j_c^* jc为第c个输出层输出的上下文单词在词汇表中的真实索引。
在得到损失函数 E E E之后,我们对输出层的每一个panel上的所有激活单元的输入值 μ c , j \mu_{c,j} μc,j,均求其关于 E E E的偏导数,得:

∂ E ∂ μ c , j = y c , j − t c , j : = e c , j (30) \frac{\partial E}{\partial \mu_{c,j}}=y_{c,j}-t_{c,j}:=e_{c,j}\tag {30} μc,jE=yc,jtc,j:=ec,j(30)
其中 e c , j e_{c,j} ec,j为输出层神经元的预测误差,与公式(8)类似。为了简化符号,我们定义一个 V V V维的向量 E I = { E I 1 , . . . , E I V } EI={\{EI_1,...,EI_V\}} EI={EI1,...,EIV}作为所有上下文单词的预测误差之和, E I j EI_j EIj用公式定义如下:

KaTeX parse error: \tag works only in display equations

接下来,我们计算hidden->output权重矩阵 W ′ \mathbf W' W关于 E E E的偏导数为:

∂ E ∂ ω i j ′ = ∑ c = 1 C ∂ E ∂ μ c , j ⋅ ∂ μ c , j ∂ ω i j ′ = E I j ⋅ h i (32) \frac{\partial E}{\partial \omega_{ij}'}=\sum_{c=1}^C\frac{\partial E}{\partial \mu_{c,j}}\cdot\frac{\partial \mu_{c,j}}{\partial \omega_{ij}'}=EI_j\cdot h_i\tag{32} ωijE=c=1Cμc,jEωijμc,j=EIjhi(32)

这样,我们就得到了hidden → \rightarrow output权重矩阵 W ′ \mathbf W' W的参数更新公式为:

ω i j ′ ( n e w ) = ω i j ′ ( o l d ) − η ⋅ E I j ⋅ h i (33) {\omega_{ij}^{'}}^{(new)}={\omega_{ij}^{'}}^{(old)}-\eta\cdot EI_j\cdot h_i\tag{33} ωij(new)=ωij(old)ηEIjhi(33)
或者

v ω j ′ ( n e w ) = v ω j ′ ( o l d ) − η ⋅ E I j ⋅ h     f o r   j = 1 , 2 , . . . , V . (34) {\mathbf v_{\omega_j}'}^{(new)}={\mathbf v_{\omega_j}'}^{(old)}-\eta \cdot EI_j \cdot \mathbf h \space\space\space for \space j=1,2,...,V.\tag{34} vωj(new)=vωj(old)ηEIjh   for j=1,2,...,V.(34)

上述参数更新公式的直观概念理解与上文公式(11)无二,除了一点就是:输出层的预测误差的计算是基于多个上下文单词context words,而不是单个目标单词 target word;需注意的是对于每一个训练样本,我们都要利用该参数更新公式来更新hidden → \rightarrow output权重矩阵 W ′ \mathbf W' W的每个元素。

同样,对于input → \rightarrow hidden权重矩阵 W \mathbf W W的参数更新公式的推导过程,除了考虑要将预测误差 e j e_j ej替换为 E I j EI_j EIj外,其他也与上文公式(12)到公式(16)类似。这里我们直接给出更新公式:

v ω I ( n e w ) = v ω I ( o l d ) − η ⋅ E H T (35) {\mathbf v_{\omega_I}}^{(new)}={\mathbf v_{\omega_I}}^{(old)}-\eta \cdot EH^T\tag{35} vωI(new)=vωI(old)ηEHT(35)

其中, E H EH EH是一个 N N N维向量,组成该向量的每一个元素可以用如下公式表示:
E H i = ∑ j = 1 V E I j ⋅ ω i j ′ (36) EH_i=\sum_{j=1}^V EI_j\cdot\omega_{ij}'\tag{36} EHi=j=1VEIjωij(36)
公式(36)的直观理解与公式(16)类似,这里不作描述。


3 Optimizing Computational Efficiency


总结以上的模型介绍,我们发现所有模型的词汇表中的每个单词都存在两个向量表示形式:输入向量 v ω \mathbf v_\omega vω与输出向量 v ω ′ \mathbf v_\omega' vω.对于输入向量的参数学习成本并不高,但对于输出向量的学习成本代价是非常昂贵的。根据更新公式(22)和(23),我们可以发现,为了更新输出向量 v ω ′ \mathbf v_\omega ' vω,对于每一个训练样例,我们必须迭代遍历词汇表中所有的单词 ω j \omega_j ωj,计算出它们的输入值 μ j \mu_j μj、概率预测值 y j y_j yj(或者SG模型中的 y c , j y_{c,j} yc,j),预测误差 e j e_j ej(或者SG模型的 E I j EI_j EIj)。最终使用预测误差更新它们的输出向量 v j ′ \mathbf v_j' vj.
显然,对于每一个训练样例都要对所有单词计算上述各值,其成本是昂贵的。特别是对于大型的词汇表,这种计算方式是不切实际的。因此为了解决这个问题,直观的方式是限制必须要更新的训练样例的输出向量的数目。一种有效的实现方式就是:hierarchical softmax(分层softmax),另一种实现通过采样的方式解决,我们在下个章节来讨论。
这两种方法都是通过只优化输出向量更新的计算过程来实现的。在我们的公式推导过程中,我们关心的有三个值:(1) E E E,新的目标函数;(2) ∂ E ∂ v ω ′ \frac{\partial E}{\partial \mathbf v_\omega'} vωE,新的关于输出向量的更新公式;(3) ∂ E ∂ h \frac{\partial E}{\partial \mathbf h} hE,为了更新输入向量反向传播的预测误差的加权和。


3.1 Hierarchical Softmax


Hierarchical softmax 是一种有效的计算 softmax 的方式。该模型使用一棵二叉树来表示词汇表中的所有单词。所有的 V V V个单词都在二叉树的叶节点上。非叶子节点一共有 V − 1 V-1 V1个。对于每个叶子节点,从根节点root到该叶子节点只有一条路径;这条路径用来评估用该叶子节点代表该叶子节点上单词的概率值。二叉树的结构如图4所示:
这里写图片描述
Figure 4: An example binary tree for the hierarchical softmax model.
其中白色的树节点代表的是词汇表中的单词,灰色节点为内部节点。图中高亮显示的是一条从根节点到 ω 2 \omega_2 ω2的路径。该条路径的长度为 L ( ω 2 ) = 4 L(\omega_2)=4 L(ω2)=4 n ( ω , j ) n(\omega,j) n(ω,j)表示从根节点到单词 ω \omega ω 的路径上的第j个节点。

在hierarchical softmax模型中,所有的词汇单词没有输出向量表示形式。不同的是,二叉树的每一个内部节点都有一个输出向量 v n ( ω , j ) ′ {\mathbf v_{n(\omega,j)}'} vn(ω,j)。因此一个单词作为输出单词的概率计算公式定义如下:

p ( ω = ω O ) = ∏ j = 1 L ( ω ) − 1 σ ( [ [ n ( ω , j + 1 ) = c h ( n ( ω , j ) ) ] ] ⋅ v n ( w , j ) ′ T h ) (37) p(\omega = \omega_O)=\prod_{j=1}^{L(\omega)-1}\sigma \bigg(\Big[\Big[n\big(\omega,j+1\big)=ch\big(n\small(\omega,j\small)\big)\Big]\Big]\cdot{\mathbf v_{n(w,j)}'}^T\mathbf h\bigg)\tag{37} p(ω=ωO)=j=1L(ω)1σ([[n(ω,j+1)=ch(n(ω,j))]]vn(w,j)Th)(37)

其中, c h ( n ) ch(n) ch(n)为节点 n n n的左孩子节点; v n ( ω , j ) ′ \mathbf v_{n(\omega,j)}' vn(ω,j)是内部节点 n ( ω , j ) n(\omega,j) n(ω,j)的向量表示(输出向量); h \mathbf h h是隐藏层的输出值(在SG模型中, h = v ω I h=\mathbf v_{\omega_I} h=vωI;而在CBOW模型中, h = 1 C ∑ c = 1 C v ω c \mathbf h=\frac{1}{C}\sum_{c=1}^C \mathbf v_{\omega_c} h=C1c=1Cvωc); [ [ x ] ] [[x]] [[x]]是一种特殊的函数定义如下:

[ [ x ] ] = { 1 if  x  is true − 1 , otherwise (38) [[x]]= \begin{cases} 1 & \text{if $x$ is true} \\ -1, & \text{otherwise} \end{cases}\tag{38} [[x]]={11,if x is trueotherwise(38)

接下来,我们通过一个直观地例子来理解公式(37)。如图4所示,假定我们需要计算单词 ω 2 \omega_2 ω2作为输出单词的概率。我们将这个概率定义为从根节点开始随机游走到叶节点 ω 2 \omega_2 ω2的概率。则在每一个内部节点(包括根节点),我们都需要确定其路径指向左孩子节点还是右孩子节点的概率。我们将经过内部节点的路径指向左孩子的概率定义为:

p ( n , l e f t ) = σ ( v n ′ T ⋅ h ) (39) p(n,left)=\sigma({\mathbf v_n'}^T\cdot\mathbf h)\tag{39} p(n,left)=σ(vnTh)(39)

我们可以看出,公式(39)的值取决于内部节点的向量表示 v n ′ \mathbf v_n' vn和隐藏层的输出值 h \mathbf h h( h \mathbf h h的值取决于输入单词的向量表示)。显然,内部节点的路径指向右孩子的概率则可以表示为:

p ( n , r i g h t ) = 1 − σ ( v n ′ T ⋅ h ) = σ ( − v n ′ T ⋅ h ) (40) p(n,right)=1-\sigma({\mathbf v_n'}^T\cdot\mathbf h)=\sigma(-{\mathbf v_n'}^T\cdot \mathbf h)\tag{40} p(n,right)=1σ(vnTh)=σ(vnTh)(40)

顺着图4中从根节点到单词 ω 2 \omega_2 ω2节点的路径,我们可以计算出 ω 2 \omega_2 ω2作为输出单词的概率为:
p ( ω 2 = ω O ) = p ( n ( ω 2 , 1 ) , l e f t ) ⋅ p ( n ( ω 2 , 2 ) , l e f t ) ⋅ p ( n ( ω 2 , 3 ) , r i g h t ) = σ ( v n ( ω 2 , 1 ) ′ T h ) ⋅ σ ( v n ( ω 2 , 2 ) ′ T h ) ⋅ σ ( − v n ( ω 2 , 3 ) ′ T h ) ⋅ (42) \begin{aligned} p(\omega_2=\omega_O) &=p\Big(n(\omega_2,1),left\Big)\cdot p\Big(n(\omega_2,2),left\Big)\cdot p\Big(n(\omega_2,3),right\Big)\\ & =\sigma \Big({\mathbf v_{n(\omega_2,1)}'}^T\mathbf h\Big)\cdot\sigma \Big({\mathbf v_{n(\omega_2,2)}'}^T\mathbf h\Big)\cdot\sigma \Big(-{\mathbf v_{n(\omega_2,3)}'}^T\mathbf h\Big)\cdot \tag{42} \end{aligned} p(ω2=ωO)=p(n(ω2,1),left)p(n(ω2,2),left)p(n(ω2,3),right)=σ(vn(ω2,1)Th)σ(vn(ω2,2)Th)σ(vn(ω2,3)Th)(42)
不难证明
∑ i = 1 V p ( ω i = ω O ) = 1 (43) \sum_{i=1}^{V}p(\omega_i=\omega_O)=1\tag{43} i=1Vp(ωi=ωO)=1(43)

现在我们开始推导内部节点的向量表示形式的参数更新公式。为了简化步骤,我们首先考虑单个上下文单词(one-word context)的模型。
为了简化公式,我们定义子公式的简化符号如下:

[ [ ⋅ ] ] : = [ [ n ( ω , j + 1 ) = c h ( n ( ω , j ) ) ] ] (44) [[\cdot]]:=[[n(\omega,j+1)=ch(n(\omega,j))]]\tag{44} [[]]:=[[n(ω,j+1)=ch(n(ω,j))]](44)
v j ′ : = v n ω , j ′ (45) \mathbf v_j':=\mathbf v_{n_{\omega,j}}'\tag{45} vj:=vnω,j(45)

则,给定一个训练样例,其误差函数我们可以定义如下:

E = − log ⁡ p ( ω = ω O ∣ ω I ) = − ∑ j = 1 L ( ω ) − 1 log ⁡ σ ( [ [ ⋅ ] ] v j ′ T h ) (46) E=-\log p(\omega = \omega_O|\omega_I)=-\sum_{j=1}^{L(\omega)-1}\log\sigma([[\cdot]]{\mathbf v_j'}^T\mathbf h)\tag{46} E=logp(ω=ωOωI)=j=1L(ω)1logσ([[]]vjTh)(46)

对于误差函数 E E E,我们取其关于 v j ′ h \mathbf v_j'\mathbf h vjh的偏导数,得:
∂ E ∂ v j ′ h = ( σ ( [ [ ⋅ ] ] v j ′ T h ) − 1 ) [ [ ⋅ ] ] = { σ ( v j ′ T h ) − 1 , ([[.]]=1) σ ( v j ′ T h ) , ([[.]]=-1) = σ ( v j ′ T h ) − t j (49) \begin{aligned} \frac{\partial E}{\partial \mathbf v_j'\mathbf h} &=\Big(\sigma([[\cdot]]{\mathbf v_j'}^T\mathbf h)-1\Big)[[\cdot]]\\ &= \begin{cases} \sigma({\mathbf v_j'}^T\mathbf h)-1 ,&\text{([[.]]=1)} \\ \sigma({\mathbf v_j'}^T\mathbf h),&\text {([[.]]=-1)} \end{cases}\\ &=\sigma({\mathbf v_j'}^T\mathbf h)-t_j\tag{49} \end{aligned} vjhE=(σ([[]]vjTh)1)[[]]={σ(vjTh)1,σ(vjTh),([[.]]=1)([[.]]=-1)=σ(vjTh)tj(49)
其中 t j = 1 t_j=1 tj=1(如果 [ [ ⋅ ] ] = 1 [[\cdot]]=1 [[]]=1)或者 t j = 0 t_j=0 tj=0(如果 [ [ ⋅ ] ] = − 1 [[\cdot]]=-1 [[]]=1)。

紧接着我们计算内部节点 n ( ω , j ) n(\omega,j) n(ω,j)的向量表示 v j ′ \mathbf v_j' vj关于函数 E E E的偏导数,得:

∂ E ∂ v j ′ = ∂ E ∂ v j ′ h ⋅ ∂ v j ′ h ∂ v j ′ = ( σ ( v j ′ T h ) − t j ) ⋅ h (50) \frac{\partial E}{\partial \mathbf v_j'}=\frac{\partial E}{\partial \mathbf v_j'\mathbf h}\cdot \frac{\partial \mathbf v_j'\mathbf h}{\partial \mathbf v_j'}=\Big(\sigma({\mathbf v_j'}^T\mathbf h)-t_j\Big)\cdot \mathbf h\tag{50} vjE=vjhEvjvjh=(σ(vjTh)tj)h(50)

因此,更新公式为:

v j ′ ( n e w ) = v j ′ ( o l d ) − η ( σ ( v j ′ T h ) − t j ) ⋅ h   ,   f o r   j = 1 , 2 , . . . , L ( ω ) − 1 (51) {\mathbf v_j'}^{(new)}={\mathbf v_j'}^{(old)}-\eta\Big(\sigma({\mathbf v_j'}^T\mathbf h)-t_j\Big)\cdot \mathbf h\space,\space for \space j=1,2,...,L(\omega)-1\tag{51} vj(new)=vj(old)η(σ(vjTh)tj)h , for j=1,2,...,L(ω)1(51)

我们可以将 σ ( v j ′ T h ) − t j \sigma({\mathbf v_j'}^T\mathbf h)-t_j σ(vjTh)tj理解为内部节点 n ( ω , j ) n(\omega,j) n(ω,j)的预测误差。每一个内部节点的“任务”就是预测其随机游走路径是指向左孩子节点还是指向右孩子节点。 t j = 1 t_j=1 tj=1意味着节点 n ( ω , j ) n(\omega,j) n(ω,j)的路径指向左孩子节点; t j = 0 t_j=0 tj=0则表示指向右孩子节点。 σ ( v j ′ T h ) \sigma({\mathbf v_j'}^T\mathbf h) σ(vjTh)是预测结果。对于一个训练实例,如果内部节点的预测值非常接近于真实值,则它的向量表示$ \mathbf v_j’ 的 更 新 变 化 很 小 ; 否 则 的更新变化很小;否则 \mathbf v_j’$向量指向一个适当的方向是的该实例的预测误差逐渐减小。以上更新公式既能应用于CBOW模型,又能应用于SG模型。当在SG模型中使用该更新公式时,我们需要对C个output context words的每一个单词都重复此更新过程。

为了使用反向传播该预测误差来学习训练input → \rightarrow hidden的权重,我们对误差函数 E E E求关于隐藏层输出值的偏导数,如下:
∂ E ∂ h = ∑ j = 1 L ( ω ) − 1 ∂ E ∂ v j ′ h ⋅ ∂ v j ′ h ∂ h = ∑ j = 1 L ( ω ) − 1 ( σ ( v j ′ T h ) − t j ) ⋅ v j ′ : = E H (54) \begin{aligned} \frac{\partial E}{\partial \mathbf h} &=\sum_{j=1}^{L(\omega)-1}\frac{\partial E}{\partial \mathbf v_j'\mathbf h}\cdot\frac{\partial \mathbf v_j'\mathbf h}{\partial \mathbf h}\\ &=\sum_{j=1}^{L(\omega)-1}\Big(\sigma({\mathbf v_j'}^T\mathbf h)-t_j\Big)\cdot \mathbf v_j'\\ &:=EH\tag{54} \end{aligned} hE=j=1L(ω)1vjhEhvjh=j=1L(ω)1(σ(vjTh)tj)vj:=EH(54)
接下来我们根据公式(23)便可以获得CBOW模型输入向量的更新公式。对于SG模型,我们需要计算上下文信息中的每个单词的 E H EH EH值,并将EH值的和带入公式(35),就能够得到输入向量的更新公式。

从以上更新公式我们可以看出:经过改进的模型Hierarchical softmax的每个训练样例的每个上下文单词的计算复杂度从 O ( V ) O(V) O(V)降为 O ( l o g ( V ) ) O(log(V)) O(log(V))级别。但是模型的参数几乎没有什么改变(内部节点对应V-1维向量,而原始模型的单词的输出向量维数为V)。


3.2 Negative Sampling


Negative Sampling模型的思想比hierarchical softmax模型更直接了当,即:在每次迭代的过程中,有大量的输出向量需要更新,为了解决这一困难,negative sampling提出了只更新其中一部分输出向量的解决方案。
显然,最终需要输出的上下文单词(正样本)在采样的过程中应该保留下来并更新,同时我们需要采集一些单词作为负样本(因此称为“negative sampling”)。在采样的过程中,我们可以任意选择一种概率分布。我们将这种概率分布称为“噪声分布”(the noise distribution),用 P n ( ω ) P_n(\omega) Pn(ω)来表示。我们可以根据经验选择一种较好的分布。

在 word2vec中,我们无需使用一种能够产生良好定义的后验多项式分布的负采样形式,本文作者证明了使用下面简单的训练目标函数能够产生可靠的、高质量的 word embeddings:

E = − log ⁡ σ ( v ω O ′ T h ) − ∑ ω j ∈ W n e g log ⁡ σ ( − v ω j ′ T h ) (55) E=-\log \sigma({\mathbf v_{\omega_O}'}^T\mathbf h)-\sum_{\omega_j\in W_{neg}} \log \sigma({-\mathbf v_{\omega_j}'}^T\mathbf h)\tag{55} E=logσ(vωOTh)ωjWneglogσ(vωjTh)(55)

其中 ω O \omega_O ωO是输出单词(the positive sample), v ω O ′ \mathbf v_{\omega_O}' vωO是输出向量; h \mathbf h h是隐藏层的输出值:在CBOW模型中 h = 1 C ∑ c = 1 C v ω c \mathbf h=\frac{1}{C}\sum_{c=1}^{C} \mathbf v_{\omega_c} h=C1c=1Cvωc,在SG模型中 h = v ω I \mathbf h=\mathbf v_{\omega_I} h=vωI W n e g = { ω j ∣ j = 1 , . . . , K } W_{neg}={\{\omega_j|j=1,...,K\}} Wneg={ωjj=1,...,K}是基于分布 P n ( ω ) P_n(\omega) Pn(ω)采样的一系列单词。
为了获得negative sampling模型的词向量更新公式,我们首先计算E关于输出单元 ω j \omega_j ωj的输入 v ω j ′ T h {\mathbf v_{\omega_j}'}^T\mathbf h vωjTh的偏导数:
∂ E ∂ v ω j ′ T h = { σ ( v ω j ′ T h ) − 1 , if   ω j = ω O σ ( v ω j ′ T h ) , if  ω j ∈ W n e g = σ ( v ω j ′ T h ) − t j (57) \begin{aligned} \frac{\partial E}{\partial{ \mathbf v_{\omega_j}'}^T\mathbf h} &= \begin{cases} \sigma({\mathbf v_{\omega_j}'}^T\mathbf h)-1 ,&\text{if }\space \omega_j=\omega_O \\ \sigma({\mathbf v_{\omega_j}'}^T\mathbf h),&\text {if}\space\omega_j\in W_{neg} \end{cases}\\ &=\sigma({\mathbf v_{\omega_j}'}^T\mathbf h)-t_j\tag{57} \end{aligned} vωjThE={σ(vωjTh)1,σ(vωjTh),if  ωj=ωOif ωjWneg=σ(vωjTh)tj(57)
其中,当 ω j \omega_j ωj是一个正样本时, t j = 1 t_j=1 tj=1;否则 t j = 0 t_j=0 tj=0。接下来我们计算E关于单词 ω j \omega_j ωj的输出向量的偏导数:

∂ E ∂ v ω j ′ = ∂ E ∂ v ω j ′ T h ⋅ ∂ v ω j ′ T h ∂ v ω j ′ = ( σ ( v ω j ′ T h ) − t j ) h (58) \frac{\partial E}{\partial \mathbf v_{\omega_j}'}=\frac{\partial E}{\partial {\mathbf v_{\omega_j}'}^T\mathbf h}\cdot \frac{\partial {\mathbf v_{\omega_j}'}^T\mathbf h}{\partial {\mathbf v_{\omega_j}'}}=\Big(\sigma({\mathbf v_{\omega_j}'}^T \mathbf h)-t_j\Big)\mathbf h \tag{58} vωjE=vωjThEvωjvωjTh=(σ(vωjTh)tj)h(58)

因此输出向量的更新公式为:

v ω j ′ ( n e w ) = v ω j ′ ( o l d ) − η ( σ ( v ω j ′ T h ) − t j ) h (59) {\mathbf v_{\omega_j}'}^{(new)}={\mathbf v_{\omega_j}'}^{(old)}-\eta\Big(\sigma({\mathbf v_{\omega_j}'}^T\mathbf h)-t_j\Big)\mathbf h\tag{59} vωj(new)=vωj(old)η(σ(vωjTh)tj)h(59)

negative sampling的关键就是公式(59)的更新过程只应用于词汇表的子集 { ω j ∣ ω j ∈ { ω O } ⋃ W n e g } \{\omega_j|\omega_j\in \{\omega_O\}\bigcup W_{neg}\} {ωjωj{ωO}Wneg},而并非应用于整个词汇表。
以上更新公式(59)的直观理解与公式(11)类似。公式(59)对两种应用模型CBOW和SG都适用。对于SG模型,我们每次更新一个上下文单词。

接着利用反向传播机制,计算E关于隐藏层输出 h \mathbf h h的偏导数:
∂ E ∂ h = ∑ ω j ∈ { ω O } ⋃ W n e g ∂ E ∂ v ω j ′ T h ⋅ ∂ v ω j ′ T h ∂ h \begin{aligned} \frac{\partial E}{\partial \mathbf h} &=\sum_{\omega_j \in\{\omega_O\}\bigcup W_{neg}}\frac{\partial E}{\partial {\mathbf v_{\omega_j}'}^T\mathbf h}\cdot \frac{\partial {\mathbf v_{\omega_j}'}^T\mathbf h}{\partial \mathbf h}\\ \end{aligned} hE=ωj{ωO}WnegvωjThEhvωjTh
= ∑ ω j ∈ { ω O } ⋃ W n e g ( σ ( v ω j ′ T h ) − t j ) v ω j ′ : = E H (61) \begin{aligned} &=\sum_{\omega_j \in\{\omega_O\}\bigcup W_{neg}}\Big(\sigma({\mathbf v_{\omega_j}'}^T\mathbf h)-t_j\Big)\mathbf v_{\omega_j}':=EH\tag{61} \end{aligned} =ωj{ωO}Wneg(σ(vωjTh)tj)vωj:=EH(61)
将EH代入公式(23),我们就可以得到CBOW模型关于输入向量的更新公式;对于SG模型,我们需要计算出每个上下文单词的EH值,将EH值的和代入公式(35)就能够得到其输入向量的更新公式。



PS:明人不说暗话,阁下留下一赞可好?

  • 130
    点赞
  • 175
    收藏
    觉得还不错? 一键收藏
  • 35
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值