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∣1i∈Bt∑f(xi,wt−1)=∣Bt∣1i∈Bt∑gi,t−1.
Supp: Preconditioning
在二阶优化中,使用Hessian matrix的逆矩阵(或者pseudo inverse)来左乘梯度向量 i . e . Δ x = H − 1 g i.e. \Delta_{x} = H^{-1}\mathbf{g} i.e.Δx=H−1g,这样的做法称为precondition,相当于将 H H H 映射为一个单位矩阵,拥有分布均匀的Spectrum,也即我们去优化的等价标函数的Hessian matrix为良好的identity matrix。
同一位置上,目标函数在竖直方向( x 2 x_2 x2轴方向)比在水平方向( x 1 x_1 x1轴方向)的斜率的绝对值更大。因此,给定学习率,梯度下降迭代自变量时会使自变量在竖直方向比在水平方向移动幅度更大。那么,我们需要一个较小的学习率从而避免自变量在竖直方向上越过目标函数最优解。然而,这会造成自变量在水平方向上朝最优解移动变慢。
动量法的提出是为了解决梯度下降的上述问题。设时间步
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←βmt−1+ηtgt,←xt−1−mt,
小批量随机梯度按元素平方的累加变量 s t \boldsymbol{s}_t st出现在学习率的分母项中。因此,如果目标函数有关自变量中某个元素的偏导数一直都较大,那么该元素的学习率将下降较快;反之,如果目标函数有关自变量中某个元素的偏导数一直都较小,那么该元素的学习率将下降较慢。然而,由于 s t \boldsymbol{s}_t st一直在累加按元素平方的梯度,自变量中每个元素的学习率在迭代过程中一直在降低(或不变)。所以,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。
11.8 RMSProp
我们在“AdaGrad算法”一节中提到,因为调整学习率时分母上的变量 s t \boldsymbol{s}_t st一直在累加按元素平方的小批量随机梯度,所以目标函数自变量每个元素的学习率在迭代过程中一直在降低(或不变)。因此,当学习率在迭代早期降得较快且当前解依然不佳时,AdaGrad算法在迭代后期由于学习率过小,可能较难找到一个有用的解。为了解决这一问题,RMSProp算法对AdaGrad算法做了修改。该算法源自Coursera上的一门课程,即“机器学习的神经网络”。
Algorithm
我们在“动量法”一节里介绍过指数加权移动平均。不同于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←β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−β)个时间步的小批量随机梯度平方项的加权平均。如此一来,自变量每个元素的学习率在迭代过程中就不再一直降低(或不变)。
11.9 AdaDelta
除了RMSProp算法以外,另一个常用优化算法AdaDelta算法也针对AdaGrad算法在迭代后期可能较难找到有用解的问题做了改进 [1]。有意思的是,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←ρ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 η。
11.10 Adam
Adam算法在RMSProp算法基础上对小批量随机梯度也做了指数加权移动平均 [1]。下面我们来介绍这个算法。
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←β1mt−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←β2vt−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−igi。将过去各时间步小批量随机梯度的权值相加,得到
(
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−β1tmt,
v ^ t ← v t 1 − β 2 t . \hat{\boldsymbol{v}}_t \leftarrow \frac{\boldsymbol{v}_t}{1 - \beta_2^t}. v^t←1−β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}, 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′.
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 实现与其类似,读者可之后自己尝试实现。后续的内容将大致从以下四个部分展开:
- PTB 数据集
- Skip-Gram 跳字模型
- 负采样近似
- 训练模型
二次采样
文本数据中一般会出现一些高频词,如英文中的“the”“a”和“in”。通常来说,在一个背景窗口中,一个词(如“chip”)和较低频词(如“microprocessor”)同时出现比和较高频词(如“the”)同时出现对训练词嵌入模型更有益。因此,训练词嵌入模型时可以对词进行二次采样。 具体来说,数据集中每个被索引词 w i w_i wi 将有一定概率被丢弃,该丢弃概率为
$$
P(w_i)=\max(1-\sqrt{\frac{t}{f(w_i)}},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 跳字模型
在跳字模型中,每个词被表示成两个 d d d 维向量,用来计算条件概率。假设这个词在词典中索引为 i i i ,当它为中心词时向量表示为 v i ∈ R d \boldsymbol{v}_i\in\mathbb{R}^d vi∈Rd,而为背景词时向量表示为 u i ∈ R d \boldsymbol{u}_i\in\mathbb{R}^d ui∈Rd 。设中心词 w c w_c wc 在词典中索引为 c c c,背景词 w o w_o wo 在词典中索引为 o o o,我们假设给定中心词生成背景词的条件概率满足下式:
$$
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)}
$$
负采样近似
由于 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(wo∣wc)=∑i∈Vexp(ui⊤vc)exp(uo⊤vc):
$$
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=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 次方。
在小规模数据集上训练了一个 Word2Vec 词嵌入模型,并通过词向量的余弦相似度搜索近义词。虽然 Word2Vec 已经能够成功地将离散的单词转换为连续的词向量,并能一定程度上地保存词与词之间的近似关系,但 Word2Vec 模型仍不是完美的,它还可以被进一步地改进:
- 子词嵌入(subword embedding):FastText 以固定大小的 n-gram 形式将单词更细致地表示为了子词的集合,而 BPE (byte pair encoding) 算法则能根据语料库的统计信息,自动且动态地生成高频子词的集合;
- 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=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∈Vexp(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∑xijlogqij
其中 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∑xij∈V∑pijlogqij
其中
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 线性相关的隐含假设;
- 用平方损失函数替代了交叉熵损失函数。
获得了 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
i∈V∑j∈V∑h(xij)(uj⊤vi+bi+cj−logxij)2
由于这些非零
x
i
j
x_{ij}
xij 是预先基于整个数据集计算得到的,包含了数据集的全局统计信息,因此 GloVe 模型的命名取“全局向量”(Global Vectors)之意。