D2L-pytorch版 Task07笔记

情到浓时终转凉″ 提交于 2020-02-27 10:45:59

优化算法进阶

1.Momentum

目标函数有关自变量的梯度代表了目标函数在自变量当前位置下降最快的方向。因此,梯度下降也叫作最陡下降(steepest descent)。在每次迭代中,梯度下降根据自变量当前位置,沿着当前位置的梯度更新自变量。然而,如果自变量的迭代方向仅仅取决于自变量当前位置,这可能会带来一些问题。对于noisy gradient,我们需要谨慎的选取学习率和batch size, 来控制梯度方差和收敛的结果。

gt=w1BtiBtf(xi,wt1)=1BtiBtgi,t1. \mathbf{g}_t = \partial_{\mathbf{w}} \frac{1}{|\mathcal{B}_t|} \sum_{i \in \mathcal{B}_t} f(\mathbf{x}_{i}, \mathbf{w}_{t-1}) = \frac{1}{|\mathcal{B}_t|} \sum_{i \in \mathcal{B}_t} \mathbf{g}_{i, t-1}.

  • An ill-conditioned Problem

Condition Number of Hessian Matrix:

condH=λmaxλmin cond_{H} = \frac{\lambda_{max}}{\lambda_{min}}

where λmax,λmin\lambda_{max}, \lambda_{min} is the maximum amd minimum eignvalue of Hessian matrix.

让我们考虑一个输入和输出分别为二维向量x=[x1,x2]\boldsymbol{x} = [x_1, x_2]^\top和标量的目标函数:

f(x)=0.1x12+2x22 f(\boldsymbol{x})=0.1x_1^2+2x_2^2

condH=40.2=20ill-conditioned cond_{H} = \frac{4}{0.2} = 20 \quad \rightarrow \quad \text{ill-conditioned}

  • Maximum Learning Rate

  • For f(x)f(x), according to convex optimizaiton conclusions, we need step size η\eta.
  • To guarantee the convergence, we need to have η\eta .
  • Supp: Preconditioning

在二阶优化中,我们使用Hessian matrix的逆矩阵(或者pseudo inverse)来左乘梯度向量 i.e.Δx=H1gi.e. \Delta_{x} = H^{-1}\mathbf{g},这样的做法称为precondition,相当于将 HH 映射为一个单位矩阵,拥有分布均匀的Spectrum,也即我们去优化的等价标函数的Hessian matrix为良好的identity matrix。
同一位置上,目标函数在竖直方向(x2x_2轴方向)比在水平方向(x1x_1轴方向)的斜率的绝对值更大。因此,给定学习率,梯度下降迭代自变量时会使自变量在竖直方向比在水平方向移动幅度更大。那么,我们需要一个较小的学习率从而避免自变量在竖直方向上越过目标函数最优解。然而,这会造成自变量在水平方向上朝最优解移动变慢。

  • Momentum Algorithm

动量法的提出是为了解决梯度下降的上述问题。设时间步 tt 的自变量为 xt\boldsymbol{x}_t,学习率为 ηt\eta_t
在时间步 t=0t=0,动量法创建速度变量 m0\boldsymbol{m}_0,并将其元素初始化成 0。在时间步 t>0t>0,动量法对每次迭代的步骤做如下修改:

mtβmt1+ηtgt,xtxt1mt, \begin{aligned} \boldsymbol{m}_t &\leftarrow \beta \boldsymbol{m}_{t-1} + \eta_t \boldsymbol{g}_t, \\ \boldsymbol{x}_t &\leftarrow \boldsymbol{x}_{t-1} - \boldsymbol{m}_t, \end{aligned}

Another version:

mtβmt1+(1β)gt,xtxt1αtmt, \begin{aligned} \boldsymbol{m}_t &\leftarrow \beta \boldsymbol{m}_{t-1} + (1-\beta) \boldsymbol{g}_t, \\ \boldsymbol{x}_t &\leftarrow \boldsymbol{x}_{t-1} - \alpha_t \boldsymbol{m}_t, \end{aligned}

αt=ηt1β \alpha_t = \frac{\eta_t}{1-\beta}

其中,动量超参数 β\beta满足 0β<10 \leq \beta < 1。当 β=0\beta=0 时,动量法等价于小批量随机梯度下降。使用较小的学习率 η=0.4\eta=0.4 和动量超参数 β=0.5\beta=0.5 时,动量法在竖直方向上的移动更加平滑,且在水平方向上更快逼近最优解。

  • Exponential Moving Average

为了从数学上理解动量法,让我们先解释一下指数加权移动平均(exponential moving average)。给定超参数 0β<10 \leq \beta < 1,当前时间步 tt 的变量 yty_t 是上一时间步 t1t-1 的变量 yt1y_{t-1} 和当前时间步另一变量 xtx_t 的线性组合:

yt=βyt1+(1β)xt. y_t = \beta y_{t-1} + (1-\beta) x_t.

我们可以对 yty_t 展开:

yt=(1β)xt+βyt1=(1β)xt+(1β)βxt1+β2yt2=(1β)xt+(1β)βxt1+(1β)β2xt2+β3yt3=(1β)i=0tβixti \begin{aligned} y_t &= (1-\beta) x_t + \beta y_{t-1}\\ &= (1-\beta)x_t + (1-\beta) \cdot \beta x_{t-1} + \beta^2y_{t-2}\\ &= (1-\beta)x_t + (1-\beta) \cdot \beta x_{t-1} + (1-\beta) \cdot \beta^2x_{t-2} + \beta^3y_{t-3}\\ &= (1-\beta) \sum_{i=0}^{t} \beta^{i}x_{t-i} \end{aligned}

(1β)i=0tβi=1βt1β(1β)=(1βt) (1-\beta)\sum_{i=0}^{t} \beta^{i} = \frac{1-\beta^{t}}{1-\beta} (1-\beta) = (1-\beta^{t})

  • Supp

Approximate Average of 11β\frac{1}{1-\beta} Steps

n=1/(1β)n = 1/(1-\beta),那么 (11/n)n=β1/(1β)\left(1-1/n\right)^n = \beta^{1/(1-\beta)}。因为

limn(11n)n=exp(1)0.3679, \lim_{n \rightarrow \infty} \left(1-\frac{1}{n}\right)^n = \exp(-1) \approx 0.3679,

所以当 β1\beta \rightarrow 1时,β1/(1β)=exp(1)\beta^{1/(1-\beta)}=\exp(-1),如 0.9520exp(1)0.95^{20} \approx \exp(-1)。如果把 exp(1)\exp(-1) 当作一个比较小的数,我们可以在近似中忽略所有含 β1/(1β)\beta^{1/(1-\beta)} 和比 β1/(1β)\beta^{1/(1-\beta)} 更高阶的系数的项。例如,当 β=0.95\beta=0.95 时,

yt0.05i=0190.95ixti. y_t \approx 0.05 \sum_{i=0}^{19} 0.95^i x_{t-i}.

因此,在实际中,我们常常将 yty_t 看作是对最近 1/(1β)1/(1-\beta) 个时间步的 xtx_t 值的加权平均。例如,当 γ=0.95\gamma = 0.95 时,yty_t 可以被看作对最近20个时间步的 xtx_t 值的加权平均;当 β=0.9\beta = 0.9 时,yty_t 可以看作是对最近10个时间步的 xtx_t 值的加权平均。而且,离当前时间步 tt 越近的 xtx_t 值获得的权重越大(越接近1)。

  • 由指数加权移动平均理解动量法

现在,我们对动量法的速度变量做变形:

mtβmt1+(1β)(ηt1βgt). \boldsymbol{m}_t \leftarrow \beta \boldsymbol{m}_{t-1} + (1 - \beta) \left(\frac{\eta_t}{1 - \beta} \boldsymbol{g}_t\right).

Another version:

mtβmt1+(1β)gt. \boldsymbol{m}_t \leftarrow \beta \boldsymbol{m}_{t-1} + (1 - \beta) \boldsymbol{g}_t.

xtxt1αtmt, \begin{aligned} \boldsymbol{x}_t &\leftarrow \boldsymbol{x}_{t-1} - \alpha_t \boldsymbol{m}_t, \end{aligned}

αt=ηt1β \alpha_t = \frac{\eta_t}{1-\beta}

由指数加权移动平均的形式可得,速度变量 vt\boldsymbol{v}_t 实际上对序列 {ηtigti/(1β):i=0,,1/(1β)1}\{\eta_{t-i}\boldsymbol{g}_{t-i} /(1-\beta):i=0,\ldots,1/(1-\beta)-1\} 做了指数加权移动平均。换句话说,相比于小批量随机梯度下降,动量法在每个时间步的自变量更新量近似于将前者对应的最近 1/(1β)1/(1-\beta) 个时间步的更新量做了指数加权移动平均后再除以 1β1-\beta。所以,在动量法中,自变量在各个方向上的移动幅度不仅取决当前梯度,还取决于过去的各个梯度在各个方向上是否一致。在本节之前示例的优化问题中,所有梯度在水平方向上为正(向右),而在竖直方向上时正(向上)时负(向下)。这样,我们就可以使用较大的学习率,从而使自变量向最优解更快移动。

  • Implement

相对于小批量随机梯度下降,动量法需要对每一个自变量维护一个同它一样形状的速度变量,且超参数里多了动量超参数。实现中,我们将速度变量用更广义的状态变量states表示。

目标函数值在后期迭代过程中的变化不够平滑。直觉上,10倍小批量梯度比2倍小批量梯度大了5倍,我们可以试着将学习率减小到原来的1/5。此时目标函数值在下降了一段时间后变化更加平滑。

  • Pytorch Class

在Pytorch中,torch.optim.SGD已实现了Momentum。

2.AdaGrad

在之前介绍过的优化算法中,目标函数自变量的每一个元素在相同时间步都使用同一个学习率来自我迭代。举个例子,假设目标函数为ff,自变量为一个二维向量[x1,x2][x_1, x_2]^\top,该向量中每一个元素在迭代时都使用相同的学习率。例如,在学习率为η\eta的梯度下降中,元素x1x_1x2x_2都使用相同的学习率η\eta来自我迭代:

x1x1ηfx1,x2x2ηfx2. x_1 \leftarrow x_1 - \eta \frac{\partial{f}}{\partial{x_1}}, \quad x_2 \leftarrow x_2 - \eta \frac{\partial{f}}{\partial{x_2}}.

在“动量法”一节里我们看到当x1x_1x2x_2的梯度值有较大差别时,需要选择足够小的学习率使得自变量在梯度值较大的维度上不发散。但这样会导致自变量在梯度值较小的维度上迭代过慢。动量法依赖指数加权移动平均使得自变量的更新方向更加一致,从而降低发散的可能。本节我们介绍AdaGrad算法,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题 [1]。

  • Algorithm

AdaGrad算法会使用一个小批量随机梯度gt\boldsymbol{g}_t按元素平方的累加变量st\boldsymbol{s}_t。在时间步0,AdaGrad将s0\boldsymbol{s}_0中每个元素初始化为0。在时间步tt,首先将小批量随机梯度gt\boldsymbol{g}_t按元素平方后累加到变量st\boldsymbol{s}_t

stst1+gtgt, \boldsymbol{s}_t \leftarrow \boldsymbol{s}_{t-1} + \boldsymbol{g}_t \odot \boldsymbol{g}_t,

其中\odot是按元素相乘。接着,我们将目标函数自变量中每个元素的学习率通过按元素运算重新调整一下:

xtxt1ηst+ϵgt, \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \frac{\eta}{\sqrt{\boldsymbol{s}_t + \epsilon}} \odot \boldsymbol{g}_t,

其中η\eta是学习率,ϵ\epsilon是为了维持数值稳定性而添加的常数,如10610^{-6}。这里开方、除法和乘法的运算都是按元素运算的。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。

  • Feature

需要强调的是,小批量随机梯度按元素平方的累加变量st\boldsymbol{s}_t出现在学习率的分母项中。因此,如果目标函数有关自变量中某个元素的偏导数一直都较大,那么该元素的学习率将下降较快;反之,如果目标函数有关自变量中某个元素的偏导数一直都较小,那么该元素的学习率将下降较慢。然而,由于st\boldsymbol{s}_t一直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。

下面我们仍然以目标函数f(x)=0.1x12+2x22f(\boldsymbol{x})=0.1x_1^2+2x_2^2为例观察AdaGrad算法对自变量的迭代轨迹。我们实现AdaGrad算法并使用和上一节实验中相同的学习率0.4。可以看到,自变量的迭代轨迹较平滑。但由于st\boldsymbol{s}_t的累加效果使学习率不断衰减,自变量在迭代后期的移动幅度较小。

  • Pytorch Class

通过名称为“adagrad”的Trainer实例,我们便可使用Pytorch提供的AdaGrad算法来训练模型。

3.RMSProp

我们在“AdaGrad算法”一节中提到,因为调整学习率时分母上的变量st\boldsymbol{s}_t一直在累加按元素平方的小批量随机梯度,所以目标函数自变量每个元素的学习率在迭代过程中一直在降低(或不变)。因此,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。为了解决这一问题,RMSProp算法对AdaGrad算法做了修改。该算法源自Coursera上的一门课程,即“机器学习的神经网络”。

  • Algorithm

我们在“动量法”一节里介绍过指数加权移动平均。不同于AdaGrad算法里状态变量st\boldsymbol{s}_t是截至时间步tt所有小批量随机梯度gt\boldsymbol{g}_t按元素平方和,RMSProp算法将这些梯度按元素平方做指数加权移动平均。具体来说,给定超参数0γ00 \leq \gamma 0计算

vtβvt1+(1β)gtgt. \boldsymbol{v}_t \leftarrow \beta \boldsymbol{v}_{t-1} + (1 - \beta) \boldsymbol{g}_t \odot \boldsymbol{g}_t.

和AdaGrad算法一样,RMSProp算法将目标函数自变量中每个元素的学习率通过按元素运算重新调整,然后更新自变量

xtxt1αvt+ϵgt, \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \frac{\alpha}{\sqrt{\boldsymbol{v}_t + \epsilon}} \odot \boldsymbol{g}_t,

其中η\eta是学习率,ϵ\epsilon是为了维持数值稳定性而添加的常数,如10610^{-6}。因为RMSProp算法的状态变量st\boldsymbol{s}_t是对平方项gtgt\boldsymbol{g}_t \odot \boldsymbol{g}_t的指数加权移动平均,所以可以看作是最近1/(1β)1/(1-\beta)个时间步的小批量随机梯度平方项的加权平均。如此一来,自变量每个元素的学习率在迭代过程中就不再一直降低(或不变)。

照例,让我们先观察RMSProp算法对目标函数f(x)=0.1x12+2x22f(\boldsymbol{x})=0.1x_1^2+2x_2^2中自变量的迭代轨迹。回忆在“AdaGrad算法”一节使用的学习率为0.4的AdaGrad算法,自变量在迭代后期的移动幅度较小。但在同样的学习率下,RMSProp算法可以更快逼近最优解。

  • Pytorch Class

通过名称为“rmsprop”的Trainer实例,我们便可使用Gluon提供的RMSProp算法来训练模型。注意,超参数γ\gamma通过gamma1指定。

4. AdaDelta

除了RMSProp算法以外,另一个常用优化算法AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有用解的问题做了改进 [1]。有意思的是,AdaDelta算法没有学习率这一超参数。

  • Algorithm

AdaDelta算法也像RMSProp算法一样,使用了小批量随机梯度gt\boldsymbol{g}_t按元素平方的指数加权移动平均变量st\boldsymbol{s}_t。在时间步0,它的所有元素被初始化为0。给定超参数0ρ00 \leq \rho 0,同RMSProp算法一样计算

stρst1+(1ρ)gtgt. \boldsymbol{s}_t \leftarrow \rho \boldsymbol{s}_{t-1} + (1 - \rho) \boldsymbol{g}_t \odot \boldsymbol{g}_t.

与RMSProp算法不同的是,AdaDelta算法还维护一个额外的状态变量Δxt\Delta\boldsymbol{x}_t,其元素同样在时间步0时被初始化为0。我们使用Δxt1\Delta\boldsymbol{x}_{t-1}来计算自变量的变化量:

gtΔxt1+ϵst+ϵgt, \boldsymbol{g}_t' \leftarrow \sqrt{\frac{\Delta\boldsymbol{x}_{t-1} + \epsilon}{\boldsymbol{s}_t + \epsilon}} \odot \boldsymbol{g}_t,

其中ϵ\epsilon是为了维持数值稳定性而添加的常数,如10510^{-5}。接着更新自变量:

xtxt1gt. \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \boldsymbol{g}'_t.

最后,我们使用Δxt\Delta\boldsymbol{x}_t来记录自变量变化量gt\boldsymbol{g}'_t按元素平方的指数加权移动平均:

ΔxtρΔxt1+(1ρ)gtgt. \Delta\boldsymbol{x}_t \leftarrow \rho \Delta\boldsymbol{x}_{t-1} + (1 - \rho) \boldsymbol{g}'_t \odot \boldsymbol{g}'_t.

可以看到,如不考虑ϵ\epsilon的影响,AdaDelta算法与RMSProp算法的不同之处在于使用Δxt1\sqrt{\Delta\boldsymbol{x}_{t-1}}来替代超参数η\eta

  • Pytorch Class

通过名称为“adadelta”的Trainer实例,我们便可使用pytorch提供的AdaDelta算法。它的超参数可以通过rho来指定。

5.Adam

Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均 [1]。下面我们来介绍这个算法。

  • Algorithm

Adam算法使用了动量变量mt\boldsymbol{m}_t和RMSProp算法中小批量随机梯度按元素平方的指数加权移动平均变量vt\boldsymbol{v}_t,并在时间步0将它们中每个元素初始化为0。给定超参数0β1<10 \leq \beta_1 < 1(算法作者建议设为0.9),时间步tt的动量变量mt\boldsymbol{m}_t即小批量随机梯度gt\boldsymbol{g}_t的指数加权移动平均:

mtβ1mt1+(1β1)gt. \boldsymbol{m}_t \leftarrow \beta_1 \boldsymbol{m}_{t-1} + (1 - \beta_1) \boldsymbol{g}_t.

和RMSProp算法中一样,给定超参数0β2<10 \leq \beta_2 < 1(算法作者建议设为0.999),
将小批量随机梯度按元素平方后的项gtgt\boldsymbol{g}_t \odot \boldsymbol{g}_t做指数加权移动平均得到vt\boldsymbol{v}_t

vtβ2vt1+(1β2)gtgt. \boldsymbol{v}_t \leftarrow \beta_2 \boldsymbol{v}_{t-1} + (1 - \beta_2) \boldsymbol{g}_t \odot \boldsymbol{g}_t.

由于我们将m0\boldsymbol{m}_0s0\boldsymbol{s}_0中的元素都初始化为0,
在时间步tt我们得到mt=(1β1)i=1tβ1tigi\boldsymbol{m}_t = (1-\beta_1) \sum_{i=1}^t \beta_1^{t-i} \boldsymbol{g}_i。将过去各时间步小批量随机梯度的权值相加,得到 (1β1)i=1tβ1ti=1β1t(1-\beta_1) \sum_{i=1}^t \beta_1^{t-i} = 1 - \beta_1^t。需要注意的是,当tt较小时,过去各时间步小批量随机梯度权值之和会较小。例如,当β1=0.9\beta_1 = 0.9时,m1=0.1g1\boldsymbol{m}_1 = 0.1\boldsymbol{g}_1。为了消除这样的影响,对于任意时间步tt,我们可以将mt\boldsymbol{m}_t再除以1β1t1 - \beta_1^t,从而使过去各时间步小批量随机梯度权值之和为1。这也叫作偏差修正。在Adam算法中,我们对变量mt\boldsymbol{m}_tvt\boldsymbol{v}_t均作偏差修正:

m^tmt1β1t, \hat{\boldsymbol{m}}_t \leftarrow \frac{\boldsymbol{m}_t}{1 - \beta_1^t},

v^tvt1β2t. \hat{\boldsymbol{v}}_t \leftarrow \frac{\boldsymbol{v}_t}{1 - \beta_2^t}.

接下来,Adam算法使用以上偏差修正后的变量m^t\hat{\boldsymbol{m}}_tm^t\hat{\boldsymbol{m}}_t,将模型参数中每个元素的学习率通过按元素运算重新调整:

gtηm^tv^t+ϵ, \boldsymbol{g}_t' \leftarrow \frac{\eta \hat{\boldsymbol{m}}_t}{\sqrt{\hat{\boldsymbol{v}}_t} + \epsilon},

其中η\eta是学习率,ϵ\epsilon是为了维持数值稳定性而添加的常数,如10810^{-8}。和AdaGrad算法、RMSProp算法以及AdaDelta算法一样,目标函数自变量中每个元素都分别拥有自己的学习率。最后,使用gt\boldsymbol{g}_t'迭代自变量:

xtxt1gt. \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \boldsymbol{g}_t'.

  • Implement

我们按照Adam算法中的公式实现该算法。其中时间步tt通过hyperparams参数传入adam函数。

  • Pytorch Class

torch.optim.Adam

word2vec

词嵌入基础

我们之前使用 one-hot 向量表示单词,虽然它们构造起来很容易,但通常并不是一个好选择。一个主要的原因是,one-hot 词向量无法准确表达不同词之间的相似度,如我们常常使用的余弦相似度。

Word2Vec 词嵌入工具的提出正是为了解决上面这个问题,它将每个词表示成一个定长的向量,并通过在语料库上的预训练使得这些向量能较好地表达不同词之间的相似和类比关系,以引入一定的语义信息。基于两种概率模型的假设,我们可以定义两种 Word2Vec 模型:

  1. Skip-Gram 跳字模型:假设背景词由中心词生成,即建模 P(wowc)P(w_o\mid w_c),其中 wcw_c 为中心词,wow_o 为任一背景词;

Image Name

  1. CBOW (continuous bag-of-words) 连续词袋模型:假设中心词由背景词生成,即建模 P(wcWo)P(w_c\mid \mathcal{W}_o),其中 Wo\mathcal{W}_o 为背景词的集合。

Image Name

在这里我们主要介绍 Skip-Gram 模型的实现,CBOW 实现与其类似,读者可之后自己尝试实现。后续的内容将大致从以下四个部分展开:

1.PTB 数据集
2.Skip-Gram 跳字模型
3.负采样近似
4. 训练模型

  1. PTB 数据集

简单来说,Word2Vec 能从语料中学到如何将离散的词映射为连续空间中的向量,并保留其语义上的相似关系。那么为了训练 Word2Vec 模型,我们就需要一个自然语言语料库,模型将从中学习各个单词间的关系,这里我们使用经典的 PTB 语料库进行训练。PTB (Penn Tree Bank) 是一个常用的小型语料库,它采样自《华尔街日报》的文章,包括训练集、验证集和测试集。我们将在PTB训练集上训练词嵌入模型。

  • 载入数据集

数据集训练文件 ptb.train.txt 示例:

aer banknote berlitz calloway centrust cluett fromstein gitano guterman ...
pierre  N years old will join the board as a nonexecutive director nov. N 
mr.  is chairman of  n.v. the dutch publishing group 
...
  • 建立词语索引

  • 二次采样

文本数据中一般会出现一些高频词,如英文中的“the”“a”和“in”。通常来说,在一个背景窗口中,一个词(如“chip”)和较低频词(如“microprocessor”)同时出现比和较高频词(如“the”)同时出现对训练词嵌入模型更有益。因此,训练词嵌入模型时可以对词进行二次采样。 具体来说,数据集中每个被索引词 wiw_i 将有一定概率被丢弃,该丢弃概率为

P(wi)=max(1tf(wi),0) P(w_i)=\max(1-\sqrt{\frac{t}{f(w_i)}},0)

其中 f(wi)f(w_i) 是数据集中词 wiw_i 的个数与总词数之比,常数 tt 是一个超参数(实验中设为 10410^{−4})。可见,只有当 f(wi)>tf(w_i)>t 时,我们才有可能在二次采样中丢弃词 wiw_i,并且越高频的词被丢弃的概率越大。

  • 提取中心词和背景词

  1. Skip-Gram 跳字模型

在跳字模型中,每个词被表示成两个 dd 维向量,用来计算条件概率。假设这个词在词典中索引为 ii ,当它为中心词时向量表示为 viRd\boldsymbol{v}_i\in\mathbb{R}^d,而为背景词时向量表示为 uiRd\boldsymbol{u}_i\in\mathbb{R}^d 。设中心词 wcw_c 在词典中索引为 cc,背景词 wow_o 在词典中索引为 oo,我们假设给定中心词生成背景词的条件概率满足下式:

P(wowc)=exp(uovc)iVexp(uivc) P(w_o\mid w_c)=\frac{\exp(\boldsymbol{u}_o^\top \boldsymbol{v}_c)}{\sum_{i\in\mathcal{V}}\exp(\boldsymbol{u}_i^\top \boldsymbol{v}_c)}

  • 提取中心词和背景词

  • Skip-Gram 跳字模型

在跳字模型中,每个词被表示成两个 dd 维向量,用来计算条件概率。假设这个词在词典中索引为 ii ,当它为中心词时向量表示为 viRd\boldsymbol{v}_i\in\mathbb{R}^d,而为背景词时向量表示为 uiRd\boldsymbol{u}_i\in\mathbb{R}^d 。设中心词 wcw_c 在词典中索引为 cc,背景词 wow_o 在词典中索引为 oo,我们假设给定中心词生成背景词的条件概率满足下式:

P(wowc)=exp(uovc)iVexp(uivc) P(w_o\mid w_c)=\frac{\exp(\boldsymbol{u}_o^\top \boldsymbol{v}_c)}{\sum_{i\in\mathcal{V}}\exp(\boldsymbol{u}_i^\top \boldsymbol{v}_c)}

  • PyTorch 预置的 Embedding 层

nn.Embedding

  • PyTorch 预置的批量乘法

torch.bmm

  • Skip-Gram 模型的前向计算

  1. 负采样近似

由于 softmax 运算考虑了背景词可能是词典 V\mathcal{V} 中的任一词,对于含几十万或上百万词的较大词典,就可能导致计算的开销过大。我们将以 skip-gram 模型为例,介绍负采样 (negative sampling) 的实现来尝试解决这个问题。

负采样方法用以下公式来近似条件概率 P(wowc)=exp(uovc)iVexp(uivc)P(w_o\mid w_c)=\frac{\exp(\boldsymbol{u}_o^\top \boldsymbol{v}_c)}{\sum_{i\in\mathcal{V}}\exp(\boldsymbol{u}_i^\top \boldsymbol{v}_c)}

P(wowc)=P(D=1wc,wo)k=1,wkP(w)KP(D=0wc,wk) P(w_o\mid w_c)=P(D=1\mid w_c,w_o)\prod_{k=1,w_k\sim P(w)}^K P(D=0\mid w_c,w_k)

其中 P(D=1wc,wo)=σ(uovc)P(D=1\mid w_c,w_o)=\sigma(\boldsymbol{u}_o^\top\boldsymbol{v}_c)σ()\sigma(\cdot) 为 sigmoid 函数。对于一对中心词和背景词,我们从词典中随机采样 KK 个噪声词(实验中设 K=5K=5)。根据 Word2Vec 论文的建议,噪声词采样概率 P(w)P(w) 设为 ww 词频与总词频之比的 0.750.75 次方。

  1. 训练模型

损失函数

应用负采样方法后,我们可利用最大似然估计的对数等价形式将损失函数定义为如下

t=1Tmjm,j0[logP(D=1w(t),w(t+j))k=1,wkP(w)KlogP(D=0w(t),wk)] \sum_{t=1}^T\sum_{-m\le j\le m,j\ne 0} [-\log P(D=1\mid w^{(t)},w^{(t+j)})-\sum_{k=1,w_k\sim P(w)^K}\log P(D=0\mid w^{(t)},w_k)]

根据这个损失函数的定义,我们可以直接使用二元交叉熵损失函数进行计算.

词嵌入进阶

Word2Vec 模型仍不是完美的,它还可以被进一步地改进:

  1. 子词嵌入(subword embedding):FastText 以固定大小的 n-gram 形式将单词更细致地表示为了子词的集合,而 BPE (byte pair encoding) 算法则能根据语料库的统计信息,自动且动态地生成高频子词的集合;
  2. GloVe 全局向量的词嵌入: 通过等价转换 Word2Vec 模型的条件概率公式,我们可以得到一个全局的损失函数表达,并在此基础上进一步优化模型。

实际中,我们常常在大规模的语料上训练这些词嵌入模型,并将预训练得到的词向量应用到下游的自然语言处理任务中。本节就将以 GloVe 模型为例,演示如何用预训练好的词向量来求近义词和类比词。

GloVe 全局向量的词嵌入

  • GloVe 模型

先简单回顾以下 Word2Vec 的损失函数(以 Skip-Gram 模型为例,不考虑负采样近似):

t=1Tmjm,j0logP(w(t+j)w(t)) -\sum_{t=1}^T\sum_{-m\le j\le m,j\ne 0} \log P(w^{(t+j)}\mid w^{(t)})

其中

P(wjwi)=exp(ujvi)kVexp(ukvi) P(w_j\mid w_i) = \frac{\exp(\boldsymbol{u}_j^\top\boldsymbol{v}_i)}{\sum_{k\in\mathcal{V}}\exp(\boldsymbol{u}_k^\top\boldsymbol{v}_i)}

wiw_i 为中心词,wjw_j 为背景词时 Skip-Gram 模型所假设的条件概率计算公式,我们将其简写为 qijq_{ij}

注意到此时我们的损失函数中包含两个求和符号,它们分别枚举了语料库中的每个中心词和其对应的每个背景词。实际上我们还可以采用另一种计数方式,那就是直接枚举每个词分别作为中心词和背景词的情况:

iVjVxijlogqij -\sum_{i\in\mathcal{V}}\sum_{j\in\mathcal{V}} x_{ij}\log q_{ij}

其中 xijx_{ij} 表示整个数据集中 wjw_j 作为 wiw_i 的背景词的次数总和。

我们还可以将该式进一步地改写为交叉熵 (cross-entropy) 的形式如下:

iVxijVpijlogqij -\sum_{i\in\mathcal{V}}x_i\sum_{j\in\mathcal{V}}p_{ij} \log q_{ij}

其中 xix_iwiw_i 的背景词窗大小总和,pij=xij/xip_{ij}=x_{ij}/x_iwjw_jwiw_i 的背景词窗中所占的比例。

从这里可以看出,我们的词嵌入方法实际上就是想让模型学出 wjw_j 有多大概率是 wiw_i 的背景词,而真实的标签则是语料库上的统计数据。同时,语料库中的每个词根据 xix_i 的不同,在损失函数中所占的比重也不同。

注意到目前为止,我们只是改写了 Skip-Gram 模型损失函数的表面形式,还没有对模型做任何实质上的改动。而在 Word2Vec 之后提出的 GloVe 模型,则是在之前的基础上做出了以下几点改动:

  1. 使用非概率分布的变量 pij=xijp'_{ij}=x_{ij}qij=exp(ujvi)q′_{ij}=\exp(\boldsymbol{u}^\top_j\boldsymbol{v}_i),并对它们取对数;
  2. 为每个词 wiw_i 增加两个标量模型参数:中心词偏差项 bib_i 和背景词偏差项 cic_i,松弛了概率定义中的规范性;
  3. 将每个损失项的权重 xix_i 替换成函数 h(xij)h(x_{ij}),权重函数 h(x)h(x) 是值域在 [0,1][0,1] 上的单调递增函数,松弛了中心词重要性与 xix_i 线性相关的隐含假设;
  4. 用平方损失函数替代了交叉熵损失函数。

综上,我们获得了 GloVe 模型的损失函数表达式:

iVjVh(xij)(ujvi+bi+cjlogxij)2 \sum_{i\in\mathcal{V}}\sum_{j\in\mathcal{V}} h(x_{ij}) (\boldsymbol{u}^\top_j\boldsymbol{v}_i+b_i+c_j-\log x_{ij})^2

由于这些非零 xijx_{ij} 是预先基于整个数据集计算得到的,包含了数据集的全局统计信息,因此 GloVe 模型的命名取“全局向量”(Global Vectors)之意。

载入预训练的 GloVe 向量

GloVe 官方 提供了多种规格的预训练词向量,语料库分别采用了维基百科、CommonCrawl和推特等,语料库中词语总数也涵盖了从60亿到8,400亿的不同规模,同时还提供了多种词向量维度供下游模型使用。

torchtext.vocab 中已经支持了 GloVe, FastText, CharNGram 等常用的预训练词向量,我们可以通过声明 torchtext.vocab.GloVe 类的实例来加载预训练好的 GloVe 词向量。

  • 求近义词和类比词

  • 求近义词

由于词向量空间中的余弦相似性可以衡量词语含义的相似性(为什么?),我们可以通过寻找空间中的 k 近邻,来查询单词的近义词。

  • 求类比词

除了求近义词以外,我们还可以使用预训练词向量求词与词之间的类比关系,例如“man”之于“woman”相当于“son”之于“daughter”。求类比词问题可以定义为:对于类比关系中的4个词“aa 之于 bb 相当于 cc 之于 dd”,给定前3个词 a,b,ca,b,cdd。求类比词的思路是,搜索与 vec(c)+vec(b)vec(a)\text{vec}(c)+\text{vec}(b)−\text{vec}(a) 的结果向量最相似的词向量,其中 vec(w)\text{vec}(w)ww 的词向量。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!