优化算法进阶
1.Momentum
目标函数有关自变量的梯度代表了目标函数在自变量当前位置下降最快的方向。因此,梯度下降也叫作最陡下降(steepest descent)。在每次迭代中,梯度下降根据自变量当前位置,沿着当前位置的梯度更新自变量。然而,如果自变量的迭代方向仅仅取决于自变量当前位置,这可能会带来一些问题。对于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?=?w?∣Bt?∣1?i∈Bt?∑?f(xi?,wt?1?)=∣Bt?∣1?i∈Bt?∑?gi,t?1?.
- An ill-conditioned Problem
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.
让我们考虑一个输入和输出分别为二维向量 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?=20→ill-conditioned
- Maximum Learning Rate
- Forf ( x ) 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
同一位置上,目标函数在竖直方向( x 2 x_2 x2?轴方向)比在水平方向( x 1 x_1 x1?轴方向)的斜率的绝对值更大。因此,给定学习率,梯度下降迭代自变量时会使自变量在竖直方向比在水平方向移动幅度更大。那么,我们需要一个较小的学习率从而避免自变量在竖直方向上越过目标函数最优解。然而,这会造成自变量在水平方向上朝最优解移动变慢。
- Momentum Algorithm
在时间步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} mt?xt??←βmt?1?+ηt?gt?,←xt?1??mt?,?
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} mt?xt??←βmt?1?+(1?β)gt?,←xt?1??αt?mt?,?
α 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 时,动量法等价于小批量随机梯度下降。使用较小的学习率η = 0.4 \eta=0.4 η=0.4 和动量超参数β = 0.5 \beta=0.5 β=0.5 时,动量法在竖直方向上的移动更加平滑,且在水平方向上更快逼近最优解。
- Exponential Moving Average
y t = β y t ? 1 + ( 1 ? β ) x t . y_t = \beta y_{t-1} + (1-\beta) x_t. yt?=βyt?1?+(1?β)xt?.
【D2L-pytorch版 Task07笔记】我们可以对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?+βyt?1?=(1?β)xt?+(1?β)?βxt?1?+β2yt?2?=(1?β)xt?+(1?β)?βxt?1?+(1?β)?β2xt?2?+β3yt?3?=(1?β)i=0∑t?βixt?i??
( 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=0∑t?βi=1?β1?βt?(1?β)=(1?βt)
- Supp
令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)} (1?1/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, n→∞lim?(1?n1?)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.9520≈exp(?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}. yt?≈0.05i=0∑19?0.95ixt?i?.
因此,在实际中,我们常常将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?←βmt?1?+(1?β)(1?βηt??gt?).
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?←βmt?1?+(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} xt??←xt?1??αt?mt?,?
α 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\} {ηt?i?gt?i?/(1?β):i=0,…,1/(1?β)?1} 做了指数加权移动平均。换句话说,相比于小批量随机梯度下降,动量法在每个时间步的自变量更新量近似于将前者对应的最近1 / ( 1 ? β ) 1/(1-\beta) 1/(1?β) 个时间步的更新量做了指数加权移动平均后再除以1 ? β 1-\beta 1?β。所以,在动量法中,自变量在各个方向上的移动幅度不仅取决当前梯度,还取决于过去的各个梯度在各个方向上是否一致。在本节之前示例的优化问题中,所有梯度在水平方向上为正(向右),而在竖直方向上时正(向上)时负(向下)。这样,我们就可以使用较大的学习率,从而使自变量向最优解更快移动。
- Implement
states
表示。目标函数值在后期迭代过程中的变化不够平滑。直觉上,10倍小批量梯度比2倍小批量梯度大了5倍,我们可以试着将学习率减小到原来的1/5。此时目标函数值在下降了一段时间后变化更加平滑。
- Pytorch Class
torch.optim.SGD
已实现了Momentum。2.AdaGrad
在之前介绍过的优化算法中,目标函数自变量的每一个元素在相同时间步都使用同一个学习率来自我迭代。举个例子,假设目标函数为 f f f,自变量为一个二维向量 [ x 1 , x 2 ] ? [x_1, x_2]^\top [x1?,x2?]?,该向量中每一个元素在迭代时都使用相同的学习率。例如,在学习率为 η \eta η的梯度下降中,元素 x 1 x_1 x1?和 x 2 x_2 x2?都使用相同的学习率 η \eta η来自我迭代:
x 1 ← x 1 ? η ? f ? x 1 , x 2 ← x 2 ? η ? f ? x 2 . 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}}. x1?←x1??η?x1??f?,x2?←x2??η?x2??f?.
在“动量法”一节里我们看到当 x 1 x_1 x1?和 x 2 x_2 x2?的梯度值有较大差别时,需要选择足够小的学习率使得自变量在梯度值较大的维度上不发散。但这样会导致自变量在梯度值较小的维度上迭代过慢。动量法依赖指数加权移动平均使得自变量的更新方向更加一致,从而降低发散的可能。本节我们介绍AdaGrad算法,它根据自变量在每个维度的梯度值的大小来调整各个维度上的学习率,从而避免统一的学习率难以适应所有维度的问题 [1]。
- Algorithm
s t ← s t ? 1 + g t ⊙ g t , \boldsymbol{s}_t \leftarrow \boldsymbol{s}_{t-1} + \boldsymbol{g}_t \odot \boldsymbol{g}_t, st?←st?1?+gt?⊙gt?,
其中 ⊙ \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, xt?←xt?1??st?+? ?η?⊙gt?,
其中 η \eta η是学习率, ? \epsilon ?是为了维持数值稳定性而添加的常数,如 1 0 ? 6 10^{-6} 10?6。这里开方、除法和乘法的运算都是按元素运算的。这些按元素运算使得目标函数自变量中每个元素都分别拥有自己的学习率。
- Feature
下面我们仍然以目标函数 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?为例观察AdaGrad算法对自变量的迭代轨迹。我们实现AdaGrad算法并使用和上一节实验中相同的学习率0.4。可以看到,自变量的迭代轨迹较平滑。但由于 s t \boldsymbol{s}_t st?的累加效果使学习率不断衰减,自变量在迭代后期的移动幅度较小。
- Pytorch Class
Trainer
实例,我们便可使用Pytorch提供的AdaGrad算法来训练模型。3.RMSProp 我们在“AdaGrad算法”一节中提到,因为调整学习率时分母上的变量 s t \boldsymbol{s}_t st?一直在累加按元素平方的小批量随机梯度,所以目标函数自变量每个元素的学习率在迭代过程中一直在降低(或不变)。因此,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。为了解决这一问题,RMSProp算法对AdaGrad算法做了修改。该算法源自Coursera上的一门课程,即“机器学习的神经网络”。
- Algorithm
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?←βvt?1?+(1?β)gt?⊙gt?.
和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, xt?←xt?1??vt?+? ?α?⊙gt?,
其中 η \eta η是学习率, ? \epsilon ?是为了维持数值稳定性而添加的常数,如 1 0 ? 6 10^{-6} 10?6。因为RMSProp算法的状态变量 s t \boldsymbol{s}_t st?是对平方项 g t ⊙ g t \boldsymbol{g}_t \odot \boldsymbol{g}_t gt?⊙gt?的指数加权移动平均,所以可以看作是最近 1 / ( 1 ? β ) 1/(1-\beta) 1/(1?β)个时间步的小批量随机梯度平方项的加权平均。如此一来,自变量每个元素的学习率在迭代过程中就不再一直降低(或不变)。
照例,让我们先观察RMSProp算法对目标函数 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?中自变量的迭代轨迹。回忆在“AdaGrad算法”一节使用的学习率为0.4的AdaGrad算法,自变量在迭代后期的移动幅度较小。但在同样的学习率下,RMSProp算法可以更快逼近最优解。
- Pytorch Class
Trainer
实例,我们便可使用Gluon提供的RMSProp算法来训练模型。注意,超参数 γ \gamma γ通过gamma1
指定。4. AdaDelta 除了RMSProp算法以外,另一个常用优化算法AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有用解的问题做了改进 [1]。有意思的是,AdaDelta算法没有学习率这一超参数。
- Algorithm
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?←ρst?1?+(1?ρ)gt?⊙gt?.
与RMSProp算法不同的是,AdaDelta算法还维护一个额外的状态变量 Δ x t \Delta\boldsymbol{x}_t Δxt?,其元素同样在时间步0时被初始化为0。我们使用 Δ x t ? 1 \Delta\boldsymbol{x}_{t-1} Δxt?1?来计算自变量的变化量:
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, gt′?←st?+?Δxt?1?+?? ?⊙gt?,
其中 ? \epsilon ?是为了维持数值稳定性而添加的常数,如 1 0 ? 5 10^{-5} 10?5。接着更新自变量:
x t ← x t ? 1 ? g t ′ . \boldsymbol{x}_t \leftarrow \boldsymbol{x}_{t-1} - \boldsymbol{g}'_t. xt?←xt?1??gt′?.
最后,我们使用 Δ 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?←ρΔxt?1?+(1?ρ)gt′?⊙gt′?.
可以看到,如不考虑 ? \epsilon ?的影响,AdaDelta算法与RMSProp算法的不同之处在于使用 Δ x t ? 1 \sqrt{\Delta\boldsymbol{x}_{t-1}} Δxt?1? ?来替代超参数 η \eta η。
- Pytorch Class
Trainer
实例,我们便可使用pytorch提供的AdaDelta算法。它的超参数可以通过rho
来指定。5.Adam Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均 [1]。下面我们来介绍这个算法。
- Algorithm
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?←β1?mt?1?+(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 gt?⊙gt?做指数加权移动平均得到 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?←β2?vt?1?+(1?β2?)gt?⊙gt?.
由于我们将 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?β1t?i?gi?。将过去各时间步小批量随机梯度的权值相加,得到( 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?β1t?i?=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^t?←1?β1t?mt??,
v ^ t ← v t 1 ? β 2 t . \hat{\boldsymbol{v}}_t \leftarrow \frac{\boldsymbol{v}_t}{1 - \beta_2^t}. v^t?←1?β2t?vt??.
接下来,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}, gt′?←v^t? ?+?ηm^t??,
其中 η \eta η是学习率, ? \epsilon ?是为了维持数值稳定性而添加的常数,如 1 0 ? 8 10^{-8} 10?8。和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'. xt?←xt?1??gt′?.
- Implement
hyperparams
参数传入adam
函数。- Pytorch Class
word2vec 词嵌入基础 我们之前使用 one-hot 向量表示单词,虽然它们构造起来很容易,但通常并不是一个好选择。一个主要的原因是,one-hot 词向量无法准确表达不同词之间的相似度,如我们常常使用的余弦相似度。
Word2Vec 词嵌入工具的提出正是为了解决上面这个问题,它将每个词表示成一个定长的向量,并通过在语料库上的预训练使得这些向量能较好地表达不同词之间的相似和类比关系,以引入一定的语义信息。基于两种概率模型的假设,我们可以定义两种 Word2Vec 模型:
- Skip-Gram 跳字模型:假设背景词由中心词生成,即建模P ( w o ∣ w c ) P(w_o\mid w_c) P(wo?∣wc?),其中w c w_c wc? 为中心词, w o w_o wo? 为任一背景词;
文章图片
- CBOW (continuous bag-of-words) 连续词袋模型:假设中心词由背景词生成,即建模P ( w c ∣ W o ) P(w_c\mid \mathcal{W}_o) P(wc?∣Wo?),其中W o \mathcal{W}_o Wo? 为背景词的集合。
文章图片
在这里我们主要介绍 Skip-Gram 模型的实现,CBOW 实现与其类似,读者可之后自己尝试实现。后续的内容将大致从以下四个部分展开:
1.PTB 数据集
2.Skip-Gram 跳字模型
3.负采样近似
4. 训练模型
- PTB 数据集
- 载入数据集
ptb.train.txt
示例:aer banknote berlitz calloway centrust cluett fromstein gitano guterman ...
pierreN years old will join the board as a nonexecutive director nov. N
mr.is chairman ofn.v. the dutch publishing group
...
- 建立词语索引
- 二次采样
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(1?f(wi?)t? ?,0)
其中f ( w i ) f(w_i) f(wi?) 是数据集中词w i w_i wi? 的个数与总词数之比,常数t t t 是一个超参数(实验中设为1 0 ? 4 10^{?4} 10?4)。可见,只有当f ( w i ) > t f(w_i)>t f(wi?)>t 时,我们才有可能在二次采样中丢弃词w i w_i wi?,并且越高频的词被丢弃的概率越大。
- 提取中心词和背景词
- Skip-Gram 跳字模型
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(wo?∣wc?)=∑i∈V?exp(ui??vc?)exp(uo??vc?)?
- 提取中心词和背景词
- Skip-Gram 跳字模型
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(wo?∣wc?)=∑i∈V?exp(ui??vc?)exp(uo??vc?)?
- PyTorch 预置的 Embedding 层
- PyTorch 预置的批量乘法
- Skip-Gram 模型的前向计算
- 负采样近似
负采样方法用以下公式来近似条件概率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(wo?∣wc?)=∑i∈V?exp(ui??vc?)exp(uo??vc?)?:
P ( w o ∣ w c ) = P ( D = 1 ∣ w c , w o ) ∏ k = 1 , w k ~ P ( w ) K P ( D = 0 ∣ w c , w k ) 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(wo?∣wc?)=P(D=1∣wc?,wo?)k=1,wk?~P(w)∏K?P(D=0∣wc?,wk?)
其中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=1∣wc?,wo?)=σ(uo??vc?), σ ( ? ) \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=1∑T??m≤j≤m,j?=0∑?[?logP(D=1∣w(t),w(t+j))?k=1,wk?~P(w)K∑?logP(D=0∣w(t),wk?)]
根据这个损失函数的定义,我们可以直接使用二元交叉熵损失函数进行计算.
词嵌入进阶 Word2Vec 模型仍不是完美的,它还可以被进一步地改进:
- 子词嵌入(subword embedding):FastText 以固定大小的 n-gram 形式将单词更细致地表示为了子词的集合,而 BPE (byte pair encoding) 算法则能根据语料库的统计信息,自动且动态地生成高频子词的集合;
- GloVe 全局向量的词嵌入: 通过等价转换 Word2Vec 模型的条件概率公式,我们可以得到一个全局的损失函数表达,并在此基础上进一步优化模型。
GloVe 全局向量的词嵌入
- GloVe 模型
? ∑ 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=1∑T??m≤j≤m,j?=0∑?logP(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(wj?∣wi?)=∑k∈V?exp(uk??vi?)exp(uj??vi?)?
是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} ?i∈V∑?j∈V∑?xij?logqij?
其中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} ?i∈V∑?xi?j∈V∑?pij?logqij?
其中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 模型,则是在之前的基础上做出了以下几点改动:
- 使用非概率分布的变量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) q′ij?=exp(uj??vi?),并对它们取对数;
- 为每个词w i w_i wi? 增加两个标量模型参数:中心词偏差项b i b_i bi? 和背景词偏差项c i c_i ci?,松弛了概率定义中的规范性;
- 将每个损失项的权重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? 线性相关的隐含假设;
- 用平方损失函数替代了交叉熵损失函数。
∑ 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 i∈V∑?j∈V∑?h(xij?)(uj??vi?+bi?+cj??logxij?)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 词向量。- 求近义词和类比词
- 求近义词
- 求类比词