电子说
在第 12.4 节中,我们回顾了在执行随机梯度下降时会发生什么,即,在只有梯度的噪声变体可用的情况下执行优化时。特别是,我们注意到对于噪声梯度,我们在选择面对噪声的学习率时需要格外谨慎。如果我们将它降低得太快,收敛就会停滞。如果我们过于宽容,我们将无法收敛到一个足够好的解决方案,因为噪声会不断驱使我们远离最优解。
12.6.1。基本
在本节中,我们将探索更有效的优化算法,尤其是针对实践中常见的某些类型的优化问题。
12.6.1.1。漏平均值
在上一节中,我们讨论了小批量 SGD 作为加速计算的一种方法。它还有一个很好的副作用,即平均梯度减少了方差量。小批量随机梯度下降可以通过以下方式计算:
(12.6.1)gt,t−1=∂w1|Bt|∑i∈Btf(xi,wt−1)=1|Bt|∑i∈Bthi,t−1.
为了保持符号简单,我们在这里使用 hi,t−1=∂wf(xi,wt−1) 作为样本的随机梯度下降i使用及时更新的权重t−1. 如果我们能够从方差减少的效果中受益,甚至超越小批量的平均梯度,那就太好了。完成此任务的一个选择是用“leaky average”代替梯度计算:
(12.6.2)vt=βvt−1+gt,t−1
对于一些β∈(0,1). 这有效地将瞬时梯度替换为对多个过去梯度进行平均的梯度 。v称为速度。它积累了过去的梯度,类似于一个重球从目标函数景观上滚下来如何对过去的力进行积分。为了更详细地了解发生了什么,让我们展开vt递归地进入
(12.6.3)vt=β2vt−2+βgt−1,t−2+gt,t−1=…,=∑τ=0t−1βτgt−τ,t−τ−1.
大的β相当于长期平均水平,而小 β仅相当于相对于梯度法的轻微修正。新的梯度替换不再指向特定实例上最速下降的方向,而是指向过去梯度的加权平均值的方向。这使我们能够实现批量平均的大部分好处,而无需实际计算其梯度的成本。稍后我们将更详细地重新讨论这个平均过程。
上述推理构成了现在所谓的 加速梯度方法的基础,例如动量梯度。他们享有额外的好处,即在优化问题是病态的情况下更有效(即,在某些方向上进展比其他方向慢得多,类似于狭窄的峡谷)。此外,它们允许我们对后续梯度进行平均以获得更稳定的下降方向。事实上,即使对于无噪声凸问题,加速方面也是动量起作用的关键原因之一。
正如人们所预料的那样,由于其功效,势头是深度学习及其他领域优化的一个深入研究的课题。例如,请参阅Goh(2017 年)撰写的 精美说明文章,以获取深入分析和交互式动画。它是由Polyak ( 1964 )提出的。Nesterov(2018)在凸优化的背景下进行了详细的理论讨论。长期以来,众所周知,深度学习的势头是有益的。参见例如Sutskever等人的讨论 。( 2013 )了解详情。
12.6.1.2。病态问题
为了更好地理解动量法的几何特性,我们重新审视了梯度下降法,尽管它的目标函数明显不太令人满意。回想一下我们在12.3 节中使用的f(x)=x12+2x22,即适度扭曲的椭球物镜。我们通过在x1方向通过
(12.6.4)f(x)=0.1x12+2x22.
像之前一样f有它的最小值(0,0). 这个函数 在方向上非常平坦x1. 让我们看看当我们像以前一样对这个新函数执行梯度下降时会发生什么。我们选择一个学习率0.4.
%matplotlib inline import torch from d2l import torch as d2l eta = 0.4 def f_2d(x1, x2): return 0.1 * x1 ** 2 + 2 * x2 ** 2 def gd_2d(x1, x2, s1, s2): return (x1 - eta * 0.2 * x1, x2 - eta * 4 * x2, 0, 0) d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
epoch 20, x1: -0.943467, x2: -0.000073
%matplotlib inline from mxnet import np, npx from d2l import mxnet as d2l npx.set_np() eta = 0.4 def f_2d(x1, x2): return 0.1 * x1 ** 2 + 2 * x2 ** 2 def gd_2d(x1, x2, s1, s2): return (x1 - eta * 0.2 * x1, x2 - eta * 4 * x2, 0, 0) d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
epoch 20, x1: -0.943467, x2: -0.000073
%matplotlib inline import tensorflow as tf from d2l import tensorflow as d2l eta = 0.4 def f_2d(x1, x2): return 0.1 * x1 ** 2 + 2 * x2 ** 2 def gd_2d(x1, x2, s1, s2): return (x1 - eta * 0.2 * x1, x2 - eta * 4 * x2, 0, 0) d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
epoch 20, x1: -0.943467, x2: -0.000073
通过构造,梯度在x2方向比水平方向高得多 ,变化也快得多x1 方向。因此,我们陷入了两个不受欢迎的选择之间:如果我们选择一个小的学习率,我们确保解决方案不会在x2方向,但我们背负着缓慢收敛x1方向。相反,随着学习率的提高,我们在x1方向但分歧 x2. 下面的例子说明了即使在学习率略有增加之后会发生什么0.4到0.6. 趋同于x1方向有所改善,但整体解决方案质量更差。
eta = 0.6 d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
epoch 20, x1: -0.387814, x2: -1673.365109
eta = 0.6 d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
epoch 20, x1: -0.387814, x2: -1673.365109
eta = 0.6 d2l.show_trace_2d(f_2d, d2l.train_2d(gd_2d))
epoch 20, x1: -0.387814, x2: -1673.365109
12.6.1.3。动量法
动量法使我们能够解决上述梯度下降问题。看看上面的优化轨迹,我们可能会凭直觉认为对过去的梯度进行平均会很有效。毕竟,在x1direction 这将聚合良好对齐的梯度,从而增加我们每一步覆盖的距离。相反,在x2梯度振荡的方向,聚合梯度将由于相互抵消的振荡而减小步长。使用vt而不是渐变 gt产生以下更新方程:
(12.6.5)vt←βvt−1+gt,t−1,xt←xt−1−ηtvt.
请注意,对于β=0我们恢复常规梯度下降。在深入研究数学属性之前,让我们快速了解一下该算法在实践中的表现。
def momentum_2d(x1, x2, v1, v2): v1 = beta * v1 + 0.2 * x1 v2 = beta * v2 + 4 * x2 return x1 - eta * v1, x2 - eta * v2, v1, v2 eta, beta = 0.6, 0.5 d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
epoch 20, x1: 0.007188, x2: 0.002553
def momentum_2d(x1, x2, v1, v2): v1 = beta * v1 + 0.2 * x1 v2 = beta * v2 + 4 * x2 return x1 - eta * v1, x2 - eta * v2, v1, v2 eta, beta = 0.6, 0.5 d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
epoch 20, x1: 0.007188, x2: 0.002553
def momentum_2d(x1, x2, v1, v2): v1 = beta * v1 + 0.2 * x1 v2 = beta * v2 + 4 * x2 return x1 - eta * v1, x2 - eta * v2, v1, v2 eta, beta = 0.6, 0.5 d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
epoch 20, x1: 0.007188, x2: 0.002553
正如我们所看到的,即使使用我们之前使用的相同学习率,动量仍然收敛得很好。让我们看看当我们减少动量参数时会发生什么。减半到β=0.25导致几乎不收敛的轨迹。尽管如此,它比没有动量(当解决方案发散时)要好得多。
eta, beta = 0.6, 0.25 d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
epoch 20, x1: -0.126340, x2: -0.186632
eta, beta = 0.6, 0.25 d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
epoch 20, x1: -0.126340, x2: -0.186632
eta, beta = 0.6, 0.25 d2l.show_trace_2d(f_2d, d2l.train_2d(momentum_2d))
epoch 20, x1: -0.126340, x2: -0.186632
请注意,我们可以将动量与随机梯度下降结合起来,特别是小批量随机梯度下降。唯一的变化是在那种情况下我们替换梯度gt,t−1 和gt. 最后,为了方便我们初始化 v0=0在时间t=0. 让我们看看泄漏平均实际上对更新做了什么。
12.6.1.4。有效样品重量
回想起那个 vt=∑τ=0t−1βτgt−τ,t−τ−1. 在极限条件下,项加起来为 ∑τ=0∞βτ=11−β. 换句话说,而不是迈出一大步η在梯度下降或随机梯度下降中,我们采取一个大小的步骤 η1−β同时,处理可能表现更好的下降方向。这是二合一的好处。为了说明加权如何针对不同的选择β考虑下图。
d2l.set_figsize() betas = [0.95, 0.9, 0.6, 0] for beta in betas: x = torch.arange(40).detach().numpy() d2l.plt.plot(x, beta ** x, label=f'beta = {beta:.2f}') d2l.plt.xlabel('time') d2l.plt.legend();
d2l.set_figsize() betas = [0.95, 0.9, 0.6, 0] for beta in betas: x = np.arange(40).asnumpy() d2l.plt.plot(x, beta ** x, label=f'beta = {beta:.2f}') d2l.plt.xlabel('time') d2l.plt.legend();
d2l.set_figsize() betas = [0.95, 0.9, 0.6, 0] for beta in betas: x = tf.range(40).numpy() d2l.plt.plot(x, beta ** x, label=f'beta = {beta:.2f}') d2l.plt.xlabel('time') d2l.plt.legend();
12.6.2。实践实验
让我们看看动量在实践中是如何工作的,即,当在适当的优化器的上下文中使用时。为此,我们需要一个更具可扩展性的实现。
12.6.2.1。从零开始实施
与(minibatch)随机梯度下降相比,动量法需要维护一组辅助变量,即速度。它与梯度(和优化问题的变量)具有相同的形状。在下面的实现中,我们称这些变量为states。
def init_momentum_states(feature_dim): v_w = torch.zeros((feature_dim, 1)) v_b = torch.zeros(1) return (v_w, v_b) def sgd_momentum(params, states, hyperparams): for p, v in zip(params, states): with torch.no_grad(): v[:] = hyperparams['momentum'] * v + p.grad p[:] -= hyperparams['lr'] * v p.grad.data.zero_()
def init_momentum_states(feature_dim): v_w = np.zeros((feature_dim, 1)) v_b = np.zeros(1) return (v_w, v_b) def sgd_momentum(params, states, hyperparams): for p, v in zip(params, states): v[:] = hyperparams['momentum'] * v + p.grad p[:] -= hyperparams['lr'] * v
def init_momentum_states(features_dim): v_w = tf.Variable(tf.zeros((features_dim, 1))) v_b = tf.Variable(tf.zeros(1)) return (v_w, v_b) def sgd_momentum(params, grads, states, hyperparams): for p, v, g in zip(params, states, grads): v[:].assign(hyperparams['momentum'] * v + g) p[:].assign(p - hyperparams['lr'] * v)
让我们看看这在实践中是如何工作的。
def train_momentum(lr, momentum, num_epochs=2): d2l.train_ch11(sgd_momentum, init_momentum_states(feature_dim), {'lr': lr, 'momentum': momentum}, data_iter, feature_dim, num_epochs) data_iter, feature_dim = d2l.get_data_ch11(batch_size=10) train_momentum(0.02, 0.5)
loss: 0.248, 0.133 sec/epoch
def train_momentum(lr, momentum, num_epochs=2): d2l.train_ch11(sgd_momentum, init_momentum_states(feature_dim), {'lr': lr, 'momentum': momentum}, data_iter, feature_dim, num_epochs) data_iter, feature_dim = d2l.get_data_ch11(batch_size=10) train_momentum(0.02, 0.5)
loss: 0.243, 29.702 sec/epoch
def train_momentum(lr, momentum, num_epochs=2): d2l.train_ch11(sgd_momentum, init_momentum_states(feature_dim), {'lr': lr, 'momentum': momentum}, data_iter, feature_dim, num_epochs) data_iter, feature_dim = d2l.get_data_ch11(batch_size=10) train_momentum(0.02, 0.5)
loss: 0.243, 1.087 sec/epoch
当我们将动量超参数增加到momentum0.9 时,它相当于一个显着更大的有效样本量 11−0.9=10. 我们稍微降低学习率 0.01以控制事情。
train_momentum(0.01, 0.9)
loss: 0.259, 0.156 sec/epoch
train_momentum(0.01, 0.9)
loss: 0.245, 28.885 sec/epoch
train_momentum(0.01, 0.9)
loss: 0.253, 1.084 sec/epoch
降低学习率进一步解决了非平滑优化问题的任何问题。将其设置为0.005产生良好的收敛特性。
train_momentum(0.005, 0.9)
loss: 0.243, 0.141 sec/epoch
train_momentum(0.005, 0.9)
loss: 0.248, 22.937 sec/epoch
train_momentum(0.005, 0.9)
loss: 0.243, 1.088 sec/epoch
12.6.2.2。简洁的实现
在 Gluon 中几乎不需要做任何事情,因为标准sgd求解器已经内置了动量。设置匹配参数会产生非常相似的轨迹。
trainer = torch.optim.SGD d2l.train_concise_ch11(trainer, {'lr': 0.005, 'momentum': 0.9}, data_iter)
loss: 0.250, 0.141 sec/epoch
d2l.train_concise_ch11('sgd', {'learning_rate': 0.005, 'momentum': 0.9}, data_iter)
loss: 0.245, 21.439 sec/epoch
trainer = tf.keras.optimizers.SGD d2l.train_concise_ch11(trainer, {'learning_rate': 0.005, 'momentum': 0.9}, data_iter)
loss: 0.259, 1.141 sec/epoch
12.6.3。理论分析
到目前为止的二维示例f(x)=0.1x12+2x22似乎很做作。我们现在将看到,这实际上非常具有代表性,至少在最小化凸二次目标函数的情况下可能遇到的问题类型。
12.6.3.1。二次凸函数
考虑函数
(12.6.6)h(x)=12x⊤Qx+x⊤c+b.
这是一个一般的二次函数。对于正定矩阵 Q≻0,即,对于具有正特征值的矩阵,它有一个最小值 x∗=−Q−1c最小值 b−12c⊤Q−1c. 因此我们可以重写h作为
(12.6.7)h(x)=12(x−Q−1c)⊤Q(x−Q−1c)+b−12c⊤Q−1c.
梯度由下式给出 ∂xh(x)=Q(x−Q−1c). 也就是说,它由之间的距离给出x和最小化器,乘以Q. 因此,速度也是项的线性组合 Q(xt−Q−1c).
自从Q是正定的,它可以分解成它的特征系统通过 Q=O⊤ΛO对于正交(旋转)矩阵O和一个对角矩阵 Λ正特征值。这允许我们执行变量的更改x到 z=defO(x−Q−1c) 获得一个更简化的表达式:
(12.6.8)h(z)=12z⊤Λz+b′.
这里 b′=b−12c⊤Q−1c. 自从O只是一个正交矩阵,它不会以有意义的方式扰乱梯度。表示为 z梯度下降变成
(12.6.9)zt=zt−1−Λzt−1=(I−Λ)zt−1.
这个表达式中的重要事实是梯度下降不会在不同的特征空间之间混合。也就是说,当用以下的特征系统表示时Q优化问题以坐标方式进行。这也适用于
(12.6.10)vt=βvt−1+Λzt−1zt=zt−1−η(βvt−1+Λzt−1)=(I−ηΛ)zt−1−ηβvt−1.
在这样做时,我们只是证明了以下定理:凸二次函数有和没有动量的梯度下降分解为二次矩阵特征向量方向上的坐标优化。
12.6.3.2。标量函数
鉴于上述结果,让我们看看当我们最小化函数时会发生什么f(x)=λ2x2. 对于梯度下降,我们有
(12.6.11)xt+1=xt−ηλxt=(1−ηλ)xt.
每当|1−ηλ|<1这种优化以指数速率收敛,因为之后t我们的步骤 xt=(1−ηλ)tx0. 这显示了收敛速度最初是如何随着我们增加学习率而提高的 η直到ηλ=1. 除此之外,事情有所不同ηλ>2优化问题发散。
lambdas = [0.1, 1, 10, 19] eta = 0.1 d2l.set_figsize((6, 4)) for lam in lambdas: t = torch.arange(20).detach().numpy() d2l.plt.plot(t, (1 - eta * lam) ** t, label=f'lambda = {lam:.2f}') d2l.plt.xlabel('time') d2l.plt.legend();
lambdas = [0.1, 1, 10, 19] eta = 0.1 d2l.set_figsize((6, 4)) for lam in lambdas: t = np.arange(20).asnumpy() d2l.plt.plot(t, (1 - eta * lam) ** t, label=f'lambda = {lam:.2f}') d2l.plt.xlabel('time') d2l.plt.legend();
lambdas = [0.1, 1, 10, 19] eta = 0.1 d2l.set_figsize((6, 4)) for lam in lambdas: t = tf.range(20).numpy() d2l.plt.plot(t, (1 - eta * lam) ** t, label=f'lambda = {lam:.2f}') d2l.plt.xlabel('time') d2l.plt.legend();
为了分析动量情况下的收敛性,我们首先根据两个标量重写更新方程:一个用于x和一个速度v. 这产生:
(12.6.12)[vt+1xt+1]=[βλ−ηβ(1−ηλ)][vtxt]=R(β,η,λ)[vtxt].
我们用了R表示2×2治理收敛行为。后t步骤初始选择 [v0,x0]成为 R(β,η,λ)t[v0,x0]. 因此,这取决于特征值R来确定收敛速度。有关精彩动画,请参阅Goh ( 2017 )的Distill 帖子,以及有关详细分析的Flammarion 和 Bach ( 2015 ) 。可以证明0<ηλ<2+2β 速度收敛。与相比,这是更大范围的可行参数0<ηλ<2用于梯度下降。它还表明,一般来说,大值β是可取的。进一步的细节需要相当多的技术细节,我们建议有兴趣的读者查阅原始出版物。
12.6.4。概括
动量用过去梯度的泄漏平均值代替梯度。这显着加快了收敛速度。
无噪声梯度下降和(噪声)随机梯度下降都是可取的。
动量可防止优化过程停滞,而随机梯度下降更可能发生这种情况。
梯度的有效数量由下式给出 11−β由于过去数据的指数下降。
在凸二次问题的情况下,这可以明确地详细分析。
实现非常简单,但它需要我们存储一个额外的状态向量(速度v).
12.6.5。练习
使用动量超参数和学习率的其他组合,观察和分析不同的实验结果。
对具有多个特征值的二次问题尝试梯度下降和动量,即 f(x)=12∑iλixi2,例如, λi=2−i. 绘制值如何x减少初始化xi=1.
导出最小值和最小值 h(x)=12x⊤Qx+x⊤c+b.
当我们使用动量执行随机梯度下降时会发生什么变化?当我们使用带动量的小批量随机梯度下降时会发生什么?试验参数?
全部0条评论
快来发表一下你的评论吧 !