DL-Pytorch Task07:优化算法进阶;word2vec;词嵌入进阶

优化算法进阶

在每次迭代中,梯度下降根据自变量当前位置,沿着当前位置的梯度更新自变量。然而,如果自变量的迭代方向仅仅取决于自变量当前位置,这可能会带来一些问题。对于noisy gradient,我们需要谨慎的选取学习率和batch size, 来控制梯度方差和收敛的结果。

g t = ∂ w 1 ∣ B t ∣ ∑ i ∈ B t f ( x i , w t − 1 ) = 1 ∣ B t ∣ ∑ i ∈ B t g i , t − 1 . \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}. gt=wBt1iBtf(xi,wt1)=Bt1iBtgi,t1.

An ill-conditioned Problem

Condition Number of Hessian Matrix:
c o n d H = λ m a x λ m i n cond_{H} = \frac{\lambda_{max}}{\lambda_{min}} condH=λminλmax
where λ m a x , λ m i n \lambda_{max}, \lambda_{min} λmax,λmin is the maximum amd minimum eignvalue of Hessian matrix.(Hessian矩阵中的最大最小特征值)
让我们考虑一个输入和输出分别为二维向量 x = [ x 1 , x 2 ] ⊤ \boldsymbol{x} = [x_1, x_2]^\top x=[x1,x2]和标量的目标函数:
f ( x ) = 0.1 x 1 2 + 2 x 2 2 f(\boldsymbol{x})=0.1x_1^2+2x_2^2 f(x)=0.1x12+2x22 c o n d H = 4 0.2 = 20 → ill-conditioned cond_{H} = \frac{4}{0.2} = 20 \quad \rightarrow \quad \text{ill-conditioned} condH=0.24=20ill-conditioned
一般来说condition number比较大时即认定该问题为ill-conditioner problem.可参考神经网络优化中的病态问题

Maximum Learning Rate

  • For f ( x ) f(x) f(x), according to convex optimizaiton conclusions, we need step size η ≤ 1 L \eta \leq \frac{1}{L} ηL1 to have the fastest convergence,where L = max ⁡ X ∇ 2 f ( x ) L=\max _{X} \nabla^{2} f(x) L=maxX2f(x) called the smooth.(即L是 H e s s i a n 阵 ∇ 2 f ( x ) 中 对 应 某 一 维 度 的 值 Hessian阵 \nabla^{2} f(x)中对应某一维度的值 Hessian2f(x) )
  • To guarantee the convergence, we need to have η ≤ 2 L \eta \leq \frac{2}{L} ηL2 .

以上即说明当某一维坐标上学习率满足在该坐标上 η ≤ 1 L \eta \leq \frac{1}{L} ηL1时,函数将沿着单调区间进行递减迭代,当某一维坐标上学习率满足在该坐标上 η ≤ 2 L \eta \leq \frac{2}{L} ηL2 且不满足 η ≤ 1 L \eta \leq \frac{1}{L} ηL1时,函数将在区间谷壁对碰折线递减迭代,但依旧能够保证最终收敛到最小点,而当某一维坐标上学习率不满足在该坐标上 η ≤ 2 L \eta \leq \frac{2}{L} ηL2时,函数将不能递减迭代,也不能收敛到该维度上的最小点处。

Supp: Preconditioning

在二阶优化中,我们使用Hessian matrix的逆矩阵(或者pseudo inverse)来左乘梯度向量 i . e . Δ x = H − 1 g i.e. \Delta_{x} = H^{-1}\mathbf{g} i.e.Δx=H1g,这样的做法称为precondition,相当于将 H H H 映射为一个单位矩阵,拥有分布均匀的Spectrum,也即我们去优化的等价标函数的Hessian matrix为良好的identity matrix,同一个学习率在各个坐标维度上递减迭代减少量接近,就不会出现在某一维度上学习率过高而另一维度上则学习率过低的情况。

Solution to ill-condition

  • Preconditioning gradient vector: applied in Adam, RMSProp, AdaGrad, Adelta, KFC, Natural gradient and other secord-order optimization algorithms.
  • Averaging history gradient: like momentum, which allows larger learning rates to accelerate convergence; applied in Adam, RMSProp, SGD momentum.

Momentum Algorithm

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

m t ← β m t − 1 + η t g t , x t ← x t − 1 − m t , \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} mtxtβmt1+ηtgt,xt1mt,

Another version:

m t ← β m t − 1 + ( 1 − β ) g t , x t ← x t − 1 − α t m t , \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} mtxtβmt1+(1β)gt,xt1αtmt,

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

其中,动量超参数 β \beta β满足 0 ≤ β < 1 0 \leq \beta < 1 0β<1 β = 0 \beta=0 β=0 时,动量法等价于小批量随机梯度下降

在解释动量法的数学原理前,让我们先从实验中观察梯度下降在使用动量法后的迭代轨迹。

由实验观察可得,在学习率满足以上收敛条件时,优化仍旧收敛,并且没有之前在某一维度上陡增陡减的情况,反而是迭代得更为平缓;在学习率超出以上收敛条件规定范围时候,优化依旧收敛,即momentum对历史的梯度会有一个加权的平均,平均值就是 m t \boldsymbol{m}_{t} mt,经过加权之后更新参数时使用的并不是某点处的直接梯度方向,而是以某种更平缓更接近最优点的方向进行参数更新,所以momentum可以允许使用更大的学习率从而更快迭代寻找到最优点

Exponential Moving Average

为了从数学上理解动量法,让我们先解释一下指数加权移动平均(exponential moving average)。给定超参数 0 ≤ β < 1 0 \leq \beta < 1 0β<1,当前时间步 t t t 的变量 y t y_t yt 是上一时间步 t − 1 t-1 t1 的变量 y t − 1 y_{t-1} yt1 和当前时间步另一变量 x t x_t xt 的线性组合:

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

我们可以对 y t y_t yt 展开:

y t = ( 1 − β ) x t + β y t − 1 = ( 1 − β ) x t + ( 1 − β ) ⋅ β x t − 1 + β 2 y t − 2 = ( 1 − β ) x t + ( 1 − β ) ⋅ β x t − 1 + ( 1 − β ) ⋅ β 2 x t − 2 + β 3 y t − 3 = ( 1 − β ) ∑ i = 0 t β i x t − i \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} yt=(1β)xt+βyt1=(1β)xt+(1β)βxt1+β2yt2=(1β)xt+(1β)βxt1+(1β)β2xt2+β3yt3=(1β)i=0tβixti

( 1 − β ) ∑ i = 0 t β i = 1 − β t 1 − β ( 1 − β ) = ( 1 − β t ) (1-\beta)\sum_{i=0}^{t} \beta^{i} = \frac{1-\beta^{t}}{1-\beta} (1-\beta) = (1-\beta^{t}) (1β)i=0tβi=1β1βt(1β)=(1βt)
由于 0 ≤ β < 1 0 \leq \beta < 1 0β<1,所以我们可以知道离当前时间步越近 x t − i \mathcal{x}_{t-i} xti的权重越大。

Supp

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

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

lim ⁡ n → ∞ ( 1 − 1 n ) n = exp ⁡ ( − 1 ) ≈ 0.3679 , \lim_{n \rightarrow \infty} \left(1-\frac{1}{n}\right)^n = \exp(-1) \approx 0.3679, nlim(1n1)n=exp(1)0.3679,

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

y t ≈ 0.05 ∑ i = 0 19 0.9 5 i x t − i . y_t \approx 0.05 \sum_{i=0}^{19} 0.95^i x_{t-i}. yt0.05i=0190.95ixti.

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

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

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

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

Another version:

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

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

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

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

Pytorch Class

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

d2l.train_pytorch_ch7(torch.optim.SGD, {'lr': 0.004, 'momentum': 0.9},
                    features, labels)

当不传入momentum参数时该参数默认为零,即使用SGD。

AdaGrad

AdaGrad算法根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题 。

Algorithm

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

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

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

x t ← x t − 1 − η s t + ϵ ⊙ g t , \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \frac{\eta}{\sqrt{\boldsymbol{s}_t + \epsilon}} \odot \boldsymbol{g}_t, xtxt1st+ϵ ηgt,

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

例如当梯度较大(小)时, g t \boldsymbol{g}_{t} gt就很大(小),从而 s t \boldsymbol{s}_{t} st很大(小),所以学习率由 η \eta η调整为 η s t + ϵ \frac{\eta}{\sqrt{s_{t}+\epsilon}} st+ϵ η,对应学习率就很小(大)。

Feature

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

Pytorch Class

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

d2l.train_pytorch_ch7(torch.optim.Adagrad, {'lr': 0.1}, features, labels)

RMSProp

当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。为了解决这一问题,RMSProp算法对AdaGrad算法做了修改。该算法源自Coursera上的一门课程,即“机器学习的神经网络”。

Algorithm

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

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

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

x t ← x t − 1 − α v t + ϵ ⊙ g t , \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \frac{\alpha}{\sqrt{\boldsymbol{v}_t + \epsilon}} \odot \boldsymbol{g}_t, xtxt1vt+ϵ αgt,

以上 α \alpha α β \beta β均为超参数

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

Pytorch Class

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

d2l.train_pytorch_ch7(torch.optim.RMSprop, {'lr': 0.01, 'alpha': 0.9},
                    features, labels)

AdaDelta

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

Algorithm

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

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

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

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

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

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

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

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

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

Implement

AdaDelta算法需要对每个自变量维护两个状态变量,即 s t \boldsymbol{s}_t st Δ x t \Delta\boldsymbol{x}_t Δxt。我们按AdaDelta算法中的公式实现该算法。

Pytorch Class

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

d2l.train_pytorch_ch7(torch.optim.Adadelta, {'rho': 0.9}, features, labels)

Adam

Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均 。

Algorithm

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

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

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

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

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

m ^ t ← m t 1 − β 1 t , \hat{\boldsymbol{m}}_t \leftarrow \frac{\boldsymbol{m}_t}{1 - \beta_1^t}, m^t1β1tmt,

v ^ t ← v t 1 − β 2 t . \hat{\boldsymbol{v}}_t \leftarrow \frac{\boldsymbol{v}_t}{1 - \beta_2^t}. v^t1β2tvt.

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

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

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

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

Implement

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

Pytorch Class

d2l.train_pytorch_ch7(torch.optim.Adam, {'lr': 0.01}, features, labels)

word2vec

二次采样

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

P ( w i ) = max ⁡ ( 1 − t f ( w i ) , 0 ) P(w_i)=\max(1-\sqrt{\frac{t}{f(w_i)}},0) P(wi)=max(1f(wi)t ,0)

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

Skip-Gram 模型的前向计算

def skip_gram(center, contexts_and_negatives, embed_v, embed_u):
    '''
    @params:
        center: 中心词下标,形状为 (n, 1) 的整数张量
        contexts_and_negatives: 背景词和噪音词下标,形状为 (n, m) 的整数张量
        embed_v: 中心词的 embedding 层
        embed_u: 背景词的 embedding 层
    @return:
        pred: 中心词与背景词(或噪音词)的内积,之后可用于计算概率 p(w_o|w_c)
    '''
    v = embed_v(center) # shape of (n, 1, d)
    u = embed_u(contexts_and_negatives) # shape of (n, m, d)
    pred = torch.bmm(v, u.permute(0, 2, 1)) # bmm((n, 1, d), (n, d, m)) => shape of (n, 1, m)
    return pred

负采样近似

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

负采样方法用以下公式来近似条件概率 P ( w o ∣ w c ) = exp ⁡ ( u o ⊤ v c ) ∑ i ∈ V exp ⁡ ( u i ⊤ v c ) 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)=iVexp(uivc)exp(uovc)

$$

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

损失函数

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

∑ t = 1 T ∑ − m ≤ j ≤ m , j ≠ 0 [ − log ⁡ P ( D = 1 ∣ w ( t ) , w ( t + j ) ) − ∑ k = 1 , w k ∼ P ( w ) K log ⁡ P ( D = 0 ∣ w ( t ) , w k ) ] \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)] t=1Tmjm,j=0[logP(D=1w(t),w(t+j))k=1,wkP(w)KlogP(D=0w(t),wk)]

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

在这里插入图片描述
在这里插入图片描述

词嵌入进阶

在“Word2Vec的实现”一节中,我们在小规模数据集上训练了一个 Word2Vec 词嵌入模型,并通过词向量的余弦相似度搜索近义词。虽然 Word2Vec 已经能够成功地将离散的单词转换为连续的词向量,并能一定程度上地保存词与词之间的近似关系,但 Word2Vec 模型仍不是完美的,它还可以被进一步地改进:

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

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

GloVe 全局向量的词嵌入

GloVe 模型

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

− ∑ t = 1 T ∑ − m ≤ j ≤ m , j ≠ 0 log ⁡ P ( 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)}) t=1Tmjm,j=0logP(w(t+j)w(t))
其中
P ( w j ∣ w i ) = exp ⁡ ( u j ⊤ v i ) ∑ k ∈ V exp ⁡ ( u k ⊤ v i ) 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)} P(wjwi)=kVexp(ukvi)exp(ujvi)
w i w_i wi 为中心词, w j w_j wj 为背景词时 Skip-Gram 模型所假设的条件概率计算公式,我们将其简写为 q i j q_{ij} qij

注意到此时我们的损失函数中包含两个求和符号,它们分别枚举了语料库中的每个中心词和其对应的每个背景词。实际上我们还可以采用另一种计数方式,那就是直接枚举每个词分别作为中心词和背景词的情况:
− ∑ i ∈ V ∑ j ∈ V x i j log ⁡ q i j -\sum_{i\in\mathcal{V}}\sum_{j\in\mathcal{V}} x_{ij}\log q_{ij} iVjVxijlogqij
其中 x i j x_{ij} xij 表示整个数据集中 w j w_j wj 作为 w i w_i wi 的背景词的次数总和。

我们还可以将该式进一步地改写为交叉熵 (cross-entropy) 的形式如下:
− ∑ i ∈ V x i ∑ j ∈ V p i j log ⁡ q i j -\sum_{i\in\mathcal{V}}x_i\sum_{j\in\mathcal{V}}p_{ij} \log q_{ij} iVxijVpijlogqij
其中 x i x_i xi w i w_i wi 的背景词窗大小总和, p i j = x i j / x i p_{ij}=x_{ij}/x_i pij=xij/xi w j w_j wj w i w_i wi 的背景词窗中所占的比例。

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

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

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

综上,我们获得了 GloVe 模型的损失函数表达式:
∑ i ∈ V ∑ j ∈ V h ( x i j ) ( u j ⊤ v i + b i + c j − log ⁡ x i j ) 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 iVjVh(xij)(ujvi+bi+cjlogxij)2

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

载入预训练的 GloVe 向量

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

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

求类比词

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值