在前面的章节中,我们一直在训练过程中使用随机梯度下降,但是没有解释它为什么有效。为了阐明它,我们刚刚在第 12.3 节中描述了梯度下降的基本原理。在本节中,我们将继续 更详细地讨论随机梯度下降。
%matplotlib inline import math import torch from d2l import torch as d2l
%matplotlib inline import math from mxnet import np, npx from d2l import mxnet as d2l npx.set_np()
%matplotlib inline import math import tensorflow as tf from d2l import tensorflow as d2l
12.4.1。随机梯度更新
在深度学习中,目标函数通常是训练数据集中每个示例的损失函数的平均值。给定训练数据集n例子,我们假设 fi(x)是关于 index 训练样例的损失函数i, 在哪里x是参数向量。然后我们到达目标函数
(12.4.1)f(x)=1n∑i=1nfi(x).
目标函数的梯度在x被计算为
(12.4.2)∇f(x)=1n∑i=1n∇fi(x).
如果使用梯度下降,每次自变量迭代的计算成本为O(n), 线性增长 n. 因此,当训练数据集较大时,每次迭代的梯度下降代价会更高。
随机梯度下降 (SGD) 减少了每次迭代的计算成本。在随机梯度下降的每次迭代中,我们统一采样一个索引i∈{1,…,n}随机获取数据示例,并计算梯度∇fi(x)更新x:
(12.4.3)x←x−η∇fi(x),
在哪里η是学习率。我们可以看到每次迭代的计算成本从O(n) 梯度下降到常数O(1). 此外,我们要强调的是随机梯度 ∇fi(x)是完整梯度的无偏估计∇f(x)因为
(12.4.4)Ei∇fi(x)=1n∑i=1n∇fi(x)=∇f(x).
这意味着,平均而言,随机梯度是对梯度的良好估计。
现在,我们将通过向梯度添加均值为 0 和方差为 1 的随机噪声来模拟随机梯度下降,将其与梯度下降进行比较。
def f(x1, x2): # Objective function return x1 ** 2 + 2 * x2 ** 2 def f_grad(x1, x2): # Gradient of the objective function return 2 * x1, 4 * x2 def sgd(x1, x2, s1, s2, f_grad): g1, g2 = f_grad(x1, x2) # Simulate noisy gradient g1 += torch.normal(0.0, 1, (1,)).item() g2 += torch.normal(0.0, 1, (1,)).item() eta_t = eta * lr() return (x1 - eta_t * g1, x2 - eta_t * g2, 0, 0) def constant_lr(): return 1 eta = 0.1 lr = constant_lr # Constant learning rate d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=50, f_grad=f_grad))
epoch 50, x1: 0.014749, x2: 0.009829
def f(x1, x2): # Objective function return x1 ** 2 + 2 * x2 ** 2 def f_grad(x1, x2): # Gradient of the objective function return 2 * x1, 4 * x2 def sgd(x1, x2, s1, s2, f_grad): g1, g2 = f_grad(x1, x2) # Simulate noisy gradient g1 += np.random.normal(0.0, 1, (1,)) g2 += np.random.normal(0.0, 1, (1,)) eta_t = eta * lr() return (x1 - eta_t * g1, x2 - eta_t * g2, 0, 0) def constant_lr(): return 1 eta = 0.1 lr = constant_lr # Constant learning rate d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=50, f_grad=f_grad))
epoch 50, x1: -0.472513, x2: 0.110780
def f(x1, x2): # Objective function return x1 ** 2 + 2 * x2 ** 2 def f_grad(x1, x2): # Gradient of the objective function return 2 * x1, 4 * x2 def sgd(x1, x2, s1, s2, f_grad): g1, g2 = f_grad(x1, x2) # Simulate noisy gradient g1 += tf.random.normal([1], 0.0, 1) g2 += tf.random.normal([1], 0.0, 1) eta_t = eta * lr() return (x1 - eta_t * g1, x2 - eta_t * g2, 0, 0) def constant_lr(): return 1 eta = 0.1 lr = constant_lr # Constant learning rate d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=50, f_grad=f_grad))
epoch 50, x1: -0.103750, x2: 0.054715
正如我们所见,随机梯度下降中变量的轨迹比我们在12.3 节中观察到的梯度下降中的轨迹噪声大得多。这是由于梯度的随机性。也就是说,即使我们到达最小值附近,我们仍然受到瞬时梯度注入的不确定性的影响η∇fi(x). 即使在 50 步之后,质量仍然不是很好。更糟糕的是,它不会在额外的步骤后得到改善(我们鼓励您尝试更多的步骤来确认这一点)。这给我们留下了唯一的选择:改变学习率η. 但是,如果我们选择的太小,我们最初不会取得任何有意义的进展。另一方面,如果我们选择太大,我们将得不到好的解决方案,如上所示。解决这些相互冲突的目标的唯一方法是随着优化的进行动态降低学习率。
lr这也是在step函数中加入学习率函数的原因sgd。在上面的例子中,学习率调度的任何功能都处于休眠状态,因为我们将相关lr 函数设置为常量。
12.4.2。动态学习率
更换η具有随时间变化的学习率 η(t)增加了控制优化算法收敛的复杂性。特别是,我们需要弄清楚多快 η应该腐烂。如果太快,我们将过早地停止优化。如果我们减少它太慢,我们会在优化上浪费太多时间。以下是一些用于调整的基本策略η随着时间的推移(我们稍后会讨论更高级的策略):
(12.4.5)η(t)=ηiifti≤t≤ti+1piecewise constantη(t)=η0⋅e−λtexponential decayη(t)=η0⋅(βt+1)−αpolynomial decay
在第一个分段常数场景中,我们降低学习率,例如,每当优化进展停滞时。这是训练深度网络的常用策略。或者,我们可以通过指数衰减更积极地减少它。不幸的是,这通常会导致在算法收敛之前过早停止。一个流行的选择是多项式衰减α=0.5. 在凸优化的情况下,有许多证据表明该比率表现良好。
让我们看看指数衰减在实践中是什么样子的。
def exponential_lr(): # Global variable that is defined outside this function and updated inside global t t += 1 return math.exp(-0.1 * t) t = 1 lr = exponential_lr d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=1000, f_grad=f_grad))
epoch 1000, x1: -0.878960, x2: -0.023958
def exponential_lr(): # Global variable that is defined outside this function and updated inside global t t += 1 return math.exp(-0.1 * t) t = 1 lr = exponential_lr d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=1000, f_grad=f_grad))
epoch 1000, x1: -0.820458, x2: 0.004701
def exponential_lr(): # Global variable that is defined outside this function and updated inside global t t += 1 return math.exp(-0.1 * t) t = 1 lr = exponential_lr d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=1000, f_grad=f_grad))
epoch 1000, x1: -0.798681, x2: -0.067649
正如预期的那样,参数的方差显着减少。然而,这是以无法收敛到最优解为代价的x=(0,0). 即使在 1000 次迭代之后,我们仍然离最佳解决方案很远。实际上,该算法根本无法收敛。另一方面,如果我们使用多项式衰减,其中学习率随步数的平方根反比衰减,则收敛仅在 50 步后变得更好。
def polynomial_lr(): # Global variable that is defined outside this function and updated inside global t t += 1 return (1 + 0.1 * t) ** (-0.5) t = 1 lr = polynomial_lr d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=50, f_grad=f_grad))
epoch 50, x1: -0.060831, x2: 0.028779
def polynomial_lr(): # Global variable that is defined outside this function and updated inside global t t += 1 return (1 + 0.1 * t) ** (-0.5) t = 1 lr = polynomial_lr d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=50, f_grad=f_grad))
epoch 50, x1: 0.025029, x2: 0.115820
def polynomial_lr(): # Global variable that is defined outside this function and updated inside global t t += 1 return (1 + 0.1 * t) ** (-0.5) t = 1 lr = polynomial_lr d2l.show_trace_2d(f, d2l.train_2d(sgd, steps=50, f_grad=f_grad))
epoch 50, x1: 0.280001, x2: -0.037688
关于如何设置学习率,还有更多选择。例如,我们可以从一个小的速率开始,然后迅速上升,然后再次下降,尽管速度会更慢。我们甚至可以在更小和更大的学习率之间交替。存在多种此类时间表。现在让我们关注可以进行综合理论分析的学习率计划,即凸设置中的学习率。对于一般的非凸问题,很难获得有意义的收敛保证,因为通常最小化非线性非凸问题是 NP 难题。有关调查,请参见 Tibshirani 2015 的优秀讲义 。
12.4.3。凸目标的收敛性分析
以下针对凸目标函数的随机梯度下降的收敛分析是可选的,主要用于传达有关问题的更多直觉。我们将自己限制在最简单的证明之一(Nesterov 和 Vial,2000)。存在显着更高级的证明技术,例如,每当目标函数表现得特别好时。
假设目标函数 f(ξ,x)是凸的x 对全部ξ. 更具体地说,我们考虑随机梯度下降更新:
(12.4.6)xt+1=xt−ηt∂xf(ξt,x),
在哪里f(ξt,x)是关于训练样例的目标函数ξt 在步骤中从一些分布中提取t和x是模型参数。表示为
(12.4.7)R(x)=Eξ[f(ξ,x)]
预期的风险和R∗它的最低限度 x. 最后让x∗是最小化器(我们假设它存在于x被定义为)。在这种情况下,我们可以跟踪当前参数之间的距离xt在时间t和风险最小化 x∗看看它是否随着时间的推移而改善:
(12.4.8)‖xt+1−x∗‖2=‖xt−ηt∂xf(ξt,x)−x∗‖2=‖xt−x∗‖2+ηt2‖∂xf(ξt,x)‖2−2ηt〈xt−x∗,∂xf(ξt,x)〉.
我们假设ℓ2随机梯度范数 ∂xf(ξt,x)受一些常数的限制L, 因此我们有
(12.4.9)ηt2‖∂xf(ξt,x)‖2≤ηt2L2.
我们最感兴趣的是两者之间的距离 xt和x∗预期的变化。事实上,对于任何特定的步骤序列,距离很可能会增加,具体取决于哪个ξt我们遇到。因此我们需要绑定点积。因为对于任何凸函数f它认为 f(y)≥f(x)+〈f′(x),y−x〉 对全部x和y, 根据凸性我们有
(12.4.10)f(ξt,x∗)≥f(ξt,xt)+〈x∗−xt,∂xf(ξt,xt)〉.
将不等式(12.4.9)和 (12.4.10)代入(12.4.8)我们得到了时间参数之间距离的界限t+1如下:
(12.4.11)‖xt−x∗‖2−‖xt+1−x∗‖2≥2ηt(f(ξt,xt)−f(ξt,x∗))−ηt2L2.
这意味着只要当前损失与最优损失之间的差异大于ηtL2/2. 由于这种差异必然会收敛到零,因此学习率ηt也需要消失。
接下来我们对(12.4.11)进行期望。这产生
(12.4.12)E[‖xt−x∗‖2]−E[‖xt+1−x∗‖2]≥2ηt[E[R(xt)]−R∗]−ηt2L2.
最后一步涉及对不等式求和 t∈{1,…,T}. 由于总和望远镜并且通过删除较低的项我们获得
(12.4.13)‖x1−x∗‖2≥2(∑t=1Tηt)[E[R(xt)]−R∗]−L2∑t=1Tηt2.
请注意,我们利用了x1是给定的,因此可以放弃期望。最后定义
(12.4.14)x¯=def∑t=1Tηtxt∑t=1Tηt.
自从
(12.4.15)E(∑t=1TηtR(xt)∑t=1Tηt)=∑t=1TηtE[R(xt)]∑t=1Tηt=E[R(xt)],
由 Jensen 不等式(设置i=t, αi=ηt/∑t=1Tηt在 (12.2.3) ) 和凸性R它遵循E[R(xt)]≥E[R(x¯)], 因此
(12.4.16)∑t=1TηtE[R(xt)]≥∑t=1TηtE[R(x¯)].
将其代入不等式(12.4.13)得到边界
(12.4.17)[E[x¯]]−R∗≤r2+L2∑t=1Tηt22∑t=1Tηt,
在哪里 r2=def‖x1−x∗‖2 是参数的初始选择与最终结果之间距离的界限。简而言之,收敛速度取决于随机梯度的范数如何有界(L) 以及初始参数值离最优性有多远 (r). 请注意,边界是根据x¯而不是 xT. 这是因为x¯是优化路径的平滑版本。每当r,L, 和 T已知我们可以选择学习率 η=r/(LT). 这产生作为上限 rL/T. 也就是说,我们收敛于 rate O(1/T)到最优解。
12.4.4。随机梯度和有限样本
到目前为止,在谈到随机梯度下降时,我们玩得有点快和松了。我们假设我们绘制实例 xi,通常带有标签yi从一些分布 p(x,y)并且我们使用它以某种方式更新模型参数。特别地,对于有限的样本量,我们简单地认为离散分布 p(x,y)=1n∑i=1nδxi(x)δyi(y) 对于某些功能δxi和δyi允许我们对其执行随机梯度下降。
然而,这并不是我们所做的。在当前部分的玩具示例中,我们只是将噪声添加到其他非随机梯度中,即,我们假装有对(xi,yi). 事实证明,这是有道理的(详细讨论见习题)。更麻烦的是,在之前的所有讨论中,我们显然都没有这样做。相反,我们只对所有实例进行一次迭代。要了解为什么这样做更可取,请考虑相反的情况,即我们正在抽样n从带有替换的离散分布观察 。选择元素的概率i随机是1/n. 因此至少选择一次是
(12.4.18)P(choosei)=1−P(omiti)=1−(1−1/n)n≈1−e−1≈0.63.
类似的推理表明,恰好一次选择某个样本(即训练示例)的概率由下式给出
(12.4.19)(n1)1n(1−1n)n−1=nn−1(1−1n)n≈e−1≈0.37.
相对于无放回抽样,放回抽样会导致方差增加和数据效率降低。因此,在实践中我们执行后者(这是贯穿本书的默认选择)。最后请注意,重复通过训练数据集以不同的随机顺序遍历它。
12.4.5。概括
对于凸问题,我们可以证明,对于广泛选择的学习率,随机梯度下降将收敛到最优解。
对于深度学习,通常情况并非如此。然而,对凸问题的分析让我们对如何接近优化有了有用的认识,即逐步降低学习率,尽管不是太快。
学习率太小或太大时都会出现问题。在实践中,通常需要经过多次实验才能找到合适的学习率。
当训练数据集中有更多示例时,梯度下降的每次迭代计算成本更高,因此在这些情况下首选随机梯度下降。
随机梯度下降的最优性保证在非凸情况下通常不可用,因为需要检查的局部最小值的数量很可能是指数级的。
12.4.6。练习
试验随机梯度下降的不同学习率计划和不同的迭代次数。特别是,绘制与最佳解决方案的距离 (0,0)作为迭代次数的函数。
证明对于函数f(x1,x2)=x12+2x22 向梯度添加正常噪声相当于最小化损失函数 f(x,w)=(x1−w1)2+2(x2−w2)2 在哪里x从正态分布中提取。
当您从中采样时比较随机梯度下降的收敛性{(x1,y1),…,(xn,yn)}有更换和当您在没有更换的情况下取样时。
如果某个梯度(或者与其相关的某个坐标)始终大于所有其他梯度,您将如何更改随机梯度下降求解器?
假使,假设f(x)=x2(1+sinx). 有多少局部最小值f有?你能改变吗f以这样一种方式来最小化它需要评估所有局部最小值?
-
随机梯度下降
+关注
关注
0文章
4浏览量
968 -
pytorch
+关注
关注
2文章
808浏览量
13202
发布评论请先 登录
相关推荐
评论