[C2W2] Improving Deep Neural Networks : Optimization algorithms

第二周:优化算法(Optimization algorithms)

Mini-batch 梯度下降(Mini-batch gradient descent)

本周将学习优化算法,这能让你的神经网络运行得更快。机器学习的应用是一个高度依赖经验的过程,伴随着大量迭代的过程,你需要训练诸多模型,才能找到合适的那一个,所以,优化算法能够帮助你快速训练模型。

我们希望可以利用一个巨大的数据集来训练神经网络,而深度学习没有在大数据领域发挥最大的效果其中一个难点在于,在巨大的数据集基础上进行训练速度很慢。因此,你会发现,使用更好的优化算法能够大大提高你和团队的效率,那么,我们首先来谈谈 mini-batch 梯度下降算法。

你之前学过,向量化能够让你有效地对所有 (m) 个样本进行计算,允许你处理整个训练集,而无需某个明确的公式。所以我们要把训练样本放入巨大的矩阵 (X) 当中去,(X= lbrack x^{(1)} x^{(2)} x^{(3)}ldotsldots x^{(m)} brack)(Y) 也是如此,(Y= lbrack y^{(1)} y^{(2)} y^{(3)}ldots ldots y^{(m)} brack),所以 (X) 的维数是 ((n_{x},m))(Y) 的维数是 ((1,m)),向量化能够让你相对较快地处理所有 (m) 个样本。如果 (m) 很大的话,处理速度仍然缓慢。比如说,如果 (m) 是 500 万或 5000 万或者更大的一个数,在对整个训练集执行梯度下降法时,你要做的是,你必须处理整个训练集,然后才能进行一步梯度下降法,然后你需要再重新处理 500 万个训练样本,才能进行下一步梯度下降法。所以如果你在处理完整个 500 万个样本的训练集之前,先让梯度下降法处理一部分,你的算法速度会更快。

准确地说,你可以把训练集分割为小一点的子集来训练,这些子集被取名为 mini-batch,假设每一个子集中只有 1000 个样本,那么把其中的 (x^{(1)})(x^{(1000)}) 取出来,将其称为第一个子训练集,也叫做 mini-batch,然后你再取出接下来的 1000 个样本,从 (x^{(1001)})(x^{(2000)}),然后再取 1000 个样本,以此类推。

接下来我要说一个新的符号,把 (x^{(1)})(x^{(1000)}) 称为 (X^{{1}})(x^{(1001)})(x^{(2000)}) 称为 (X^{{2}}),如果你的训练样本一共有 500 万个,每个 mini-batch 都有 1000 个样本,也就是说,你有 5000 个 mini-batch,因为 5000 乘以 1000 就是 5000 万。你共有 5000 个 mini-batch,所以最后得到是 (X^{{ 5000}})

(Y) 也要进行相同处理,你也要相应地拆分 (Y) 的训练集,所以从 (y^{(1)})(y^{(1000)})(Y^{{1}}),然后从 (y^{(1001)})(y^{(2000)}),这个叫 (Y^{{2}}),一直到 (Y^{{ 5000}})

在继续课程之前,先确定一下我的符号,之前我们使用了上角小括号 ((i)) 表示训练集里的值,所以 (x^{(i)}) 是第 (i) 个训练样本。我们用了上角中括号 ([l]) 来表示神经网络的层数,(z^{[l]}) 表示神经网络中第 (l) 层的 (z) 值,我们现在引入了大括号 ({t}) 来代表不同的 mini-batch,所以我们有 (X^{{t}})(Y^{{t}}),检查一下自己是否理解无误。

(X^{{t}})(Y^{{t}}) 的维数:如果 (X^{{1}}) 是一个有 1000 个样本的训练集,所以维数应该是 ((n_{x},1000))(X^{{2}}) 的维数应该是 ((n_{x},1000)),以此类推,因此所有的子集维数都是 ((n_{x},1000)),即 (Y^{{t}}) 的维数都是 ((n_{x},1000))

解释一下这个算法的名称,batch 梯度下降法指的是我们之前讲过的梯度下降法算法,就是同时处理整个训练集,这个名字就是来源于能够同时看到整个 batch 训练集的样本被处理,这个名字不怎么样,但就是这样叫它。

相比之下,mini-batch 梯度下降法,指的是你每次同时处理一个 mini-batch 的 (X^{{t}})(Y^{{t}}),而不是同时处理全部的 (X)(Y) 训练集。

那么究竟 mini-batch 梯度下降法的原理是什么?看下面的伪代码 ...

repeat for epoch {
(;;;;;;;;)for t = 1,...,5000{
(;;;;;;;;)(;;;;;;;;)forward propagation on (X^{{t}}) { # here we use 1000 examples
(;;;;;;;;)(;;;;;;;;)(;;;;;;;;)(z^{[1]}=w^{[1]}X^{{t}} + b^{[1]})
(;;;;;;;;)(;;;;;;;;)(;;;;;;;;)(A^{[1]}=g^{[1]}(Z^{[1]}))
(;;;;;;;;)(;;;;;;;;)(;;;;;;;;)(;;;;;;;;)(vdots)
(;;;;;;;;)(;;;;;;;;)(;;;;;;;;)(A^{[L]}=g^{[L]}(Z^{[L]}))
(;;;;;;;;)(;;;;;;;;)}
(;;;;;;;;)(;;;;;;;;)compute cost (J^{{t}}=frac{1}{1000} ; sumlimits_{i=1}^{l} ; mathcal{L}(hat{y}^{(i)}, y^{(i)}) + frac{lambda}{2 cdot 1000} ; sumlimits_{l} ; ||w^{[l]}||^2_F)
(;;;;;;;;)(;;;;;;;;)back propagation to compute gradient of (J^{{t}}) (using ((X^{{t}},Y^{{t}})))
(;;;;;;;;)(;;;;;;;;)(;;;;;;;;)(w^{[l]}:=w^{[l]} - alpha ; mathrm{d} w^{[l]})
(;;;;;;;;)(;;;;;;;;)(;;;;;;;;)(b^{[l]}:=b^{[l]} - alpha ; mathrm{d} b^{[l]})
(;;;;;;;;)}
}

使用 batch 梯度下降算法,一次遍历训练集只能让你做一个梯度下降,而使用 mini-batch 梯度下降法,一次遍历训练集,能让你做 5000 个梯度下降。当然正常来说你想要多次遍历训练集,还需要为另一个 for 循环来控制遍历训练集的次数。所以你可以一直处理遍历训练集,直到最后你能收敛到一个合适的精度。

如果你有一个巨大的训练集,mini-batch 梯度下降法比 batch 梯度下降法运行地更快,所以几乎每个研习深度学习的人在训练巨大的数据集时都会用到,下节课中,我们将进一步深度讨论 mini-batch 梯度下降法,你也会因此更好地理解它的作用和原理。

理解 Mini-batch 梯度下降(Understanding Mini-batch gradient descent)

在上周的课程中,你知道了如何利用 mini-batch 梯度下降法来开始处理训练集和开始梯度下降,即使你只处理了部分训练集,即使你是第一次处理,本节课中,我们将进一步学习如何执行梯度下降法,更好地理解其作用和原理。

见上方左侧图:使用 batch 梯度下降法时,每次迭代你都需要历遍整个训练集,可以预期每次迭代成本都会下降,所以如果成本函数 (J) 是迭代次数的一个函数,它应该会随着每次迭代而减少,如果 (J) 在某次迭代中增加了,那肯定出了问题,也许你的学习率太大。

见上方右侧图:使用 mini-batch 梯度下降法,如果你作出成本函数在整个过程中的图,则并不是每次迭代都是下降的,特别是在每次迭代中,你要处理的是 (X^{{t}})(Y^{{t}}),如果要作出成本函数 (J^{{t}}) 的图,而 (J^{{t}}) 只和 (X^{{t}})(Y^{{t}}) 有关,也就是每次迭代下你都在训练不同的样本集或者说训练不同的 mini-batch,如果你要作出成本函数 (J) 的图,你很可能会得到上面右侧图的结果,走向朝下,但有更多的噪声。即使没有每次迭代都下降是不要紧的,但走势应该向下,噪声产生的原因也许可能是因为 (X^{{1}})(Y^{{1}}) 是比较容易计算的 mini-batch,因此成本会低一些。不过也许出于偶然,(X^{{2}})(Y^{{2}}) 是比较难运算的 mini-batch,或许你需要一些残缺的样本,这样一来,成本会更高一些,因为你是在运行 mini-batch 梯度下降法作出的成本函数图,所以才会出现这些摆动。

if mini-batch size == m

你需要决定的变量之一是 mini-batch 的大小,(m) 就是训练集的大小,极端情况下,如果 mini-batch 的大小等于 (m),其实就是 batch 梯度下降法,在这种极端情况下,你就有了 mini-batch (X^{{1}})(Y^{{1}}),并且该 mini-batch 等于整个训练集,所以把 mini-batch 大小设为 (m) 可以得到 batch 梯度下降法。

if mini-batch size == 1

另一个极端情况,假设 mini-batch 大小为 1,就有了新的算法,叫做 随机梯度下降法,每个样本都是独立的 mini-batch,当你有第一个 mini-batch,也就是 (X^{{1}})(Y^{{1}}),如果 mini-batch 大小为 1,那么它就是你的第一个训练样本。接着再看第二个 mini-batch,也就是第二个训练样本,采取梯度下降步骤,然后是第三个训练样本,以此类推,一次只处理一个。

对比不同 mini-batch 大小时,成本函数的优化情况

蓝色线)如上图,假设椭圆是最小化的成本函数的轮廓,最小值在中心点那里,batch 梯度下降法从某处开始,相对噪声低些,幅度也大一些,你可以继续找最小值。

紫色线)相反,在 随机梯度下降法 中,从某一点开始,每次迭代,只对一个样本进行梯度下降,大部分时候它向着全局最小值靠近,有时它会远离最小值,因为可能某个样本恰好给你指的方向不对,因此随机梯度下降法是有很多噪声的,平均来看,它最终会靠近最小值,不过有时候也会方向错误。也因此,随机梯度下降法永远不会收敛,而是会一直在最小值附近波动,但它并不会达到最小值并停留在最小值处。

绿色线)实际上你选择的 mini-batch 大小应该在 1 和 (m) 之间,因为 1 太小了,而 (m) 又太大了,原因在于如果使用 batch 梯度下降法,mini-batch 的大小为 (m),每个迭代需要处理大量训练样本,该算法的主要弊端在于特别是在训练样本数量巨大的时候,单次迭代耗时太长。但如果训练样本不大,那么 batch 梯度下降法也会运行地很好。相反,如果使用随机梯度下降法,如果你只需要处理一个样本,那这个方法很好,这样做没有问题,通过减小学习率,噪声会被改善或有所减小,但随机梯度下降法的一大缺点是,你会失去所有向量化带给你的加速,因为一次性只处理了一个训练样本,这样效率过于低下,所以实践中最好选择不大不小的 mini-batch 尺寸。你会发现两个好处,一方面,你得到了大量向量化,上一节中我们用过的例子中,如果 mini-batch 大小为 1000 个样本,你就可以对 1000 个样本向量化,比你一次只处理一个样本快得多。另一方面,你也不需要等待整个训练集被处理完就可以开始进行后续工作,再用一下上节中的数字,每遍历一次训练集允许我们采取 5000 个梯度下降步骤,所以实际上一些位于 1 和 (m) 中间的 mini-batch 大小效果最好。用 mini-batch 梯度下降法,它也不会总朝向最小值靠近,但它比随机梯度下降要更持续地靠近最小值的方向,它也不一定在很小的范围内收敛或者波动,但如果出现这个问题,可以慢慢减少学习率,我们在下节会讲到学习率衰减,也就是如何减小学习率。

如果 mini-batch 大小既不是 1 也不是 (m),应该取中间值,那应该怎么选择呢?其实是有指导原则的。

首先,如果训练集较小,直接使用 batch 梯度下降法,样本集较小就没必要使用 mini-batch 梯度下降法,你可以快速处理整个训练集,所以使用 batch 梯度下降法也很好,这里的少是说小于 2000 个样本,这样比较适合使用 batch 梯度下降法。如果样本数目较大的话,就可以使用 mini-batch 梯度下降法,一般的 mini-batch 大小为 64 到 512,考虑到电脑内存设置和使用的方式,如果 mini-batch 大小是 2 的 (n) 次方,代码会运行的快一些,64 就是 2 的 6 次方,以此类推,128 是 2 的 7 次方,256 是 2 的 8 次方,512 是 2 的 9 次方。所以我经常把 mini-batch 大小设成 2 的次方。在上节中,我将 mini-batch 大小设为了 1000,建议你可以试一下 1024,也就是 2 的 10 次方,不过比较少见,64 到 512 的 mini-batch 大小比较常见。

最后需要注意的是在你的 mini-batch 中,要确保 (X^{{t}}​)(Y^{{t}}​) 符合 CPU / GPU / 内存,这取决于你的应用方向以及训练集的大小。如果你处理的 mini-batch 和 CPU / GPU / 内存不相符,那么不管你用什么方法处理数据,你会注意到算法的表现急转直下变得惨不忍睹,所以我希望你对一般人们使用的 mini-batch 大小有一个直观了解。事实上 mini-batch 大小是另一个重要的变量,你需要做一个快速尝试,才能找到能够最有效地减少成本函数的那个,我一般会尝试几个不同的值,几个不同的 2 次方,然后看能否找到一个让梯度下降优化算法最高效的大小。希望这些能够指导你如何开始找到这一数值。

你学会了如何执行 mini-batch 梯度下降,令算法运行得更快,特别是在训练样本数目较大的情况下。不过还有个更高效的算法,比梯度下降法和 mini-batch 梯度下降法都要高效的多,我们在接下来的章节中将为大家一一讲解。

指数加权移动平均(Exponential Weighted Moving Average)

接下来会展示几个优化算法,它们比梯度下降法快,要理解这些算法,你需要用到指数加权平均,在统计中也叫做 指数加权移动平均,我们首先讲这个,然后再来讲更复杂的优化算法。

如上图,以伦敦一年中每天的温度数据为例,绘制散点图(每个蓝点代表一天的温度)。如果想要计算趋势(红色线)的话,也就是温度的局部平均值,或者说移动平均值。那么可以使用下面的 指数加权移动平均 的计算公式:

(v_t = eta ; v_{t-1} + (1-eta) ; heta_t)

其中:

  • (t) 表示时刻,此处是某一天
  • (v_t) 表示时刻 (t) 时的加权平均值
  • (eta)(1-eta) 表示加权的权值
  • ( heta_t) 表示时刻 (t) 时的实际温度,此处表示某一天当天的真实温度值

以上面伦敦温度为例,代入公式,看看如何计算 (v_t)

首先,令 (v_{0} =0),加权值 (eta=0.9),那么接下来会有:

(v_{1} = 0.9 ; v_0 + 0.1 ; heta_1)
(v_{2} = 0.9 ; v_1 + 0.1 ; heta_2)
(v_{3} = 0.9 ; v_2 + 0.1 ; heta_3)
(vdots)
(v_{t} = 0.9 ; v_{t-1} + 0.1 ; heta_t)

直观理解,就是某一天的 (v) 等于前一天 (v) 值的 0.9 倍,再加上当日温度的 0.1 倍。如此计算,然后作图的话,就可以得到上图中的红色线,即温度的趋势变化,而且是使用 指数加权移动平均 方法计算出来的。

直觉上来讲,你可以理解为 (v_{t}) 大概是平均了 (frac{1}{(1 -eta)}) 天的温度值,后面章节会详细说明这一点。

那么上面例子中 (eta) 是 0.9,(frac{1}{(1 - 0.9)} = 10),你会想,这是十天的平均值,也就是上图中红色线部分。

再试试别的,将 (eta) 设置为接近 1 的一个值,比如 0.98,计算 (frac{1}{(1 - 0.98)} = 50),这就是粗略平均了过去 50 天的温度,这时作图可以得到上图中的绿色线。

你会发现,相对来讲,使用这个高一点的 (eta) 值(0.98),你得到的曲线会比使用 0.9 得到的曲线更平坦一些,原因在于你多平均了几天的温度,所以这个曲线波动更小,更加平坦,缺点是曲线向右偏移了许多,因为现在平均的温度值更多,所以指数加权平均公式在温度变化时,适应的也要更缓慢一些,从而会导致出现一定的延迟。当 (eta=0.98) 时,这相当于给前一天的值加了太多权重,只有 0.02 的权重给了当日的值,所以当温度上下起伏变化时,较大一些的 (eta),会让指数加权平均值适应的更缓慢一些。

我们可以再换一个值试一试,如果 (eta = 0.5),计算 (frac{1}{(1-0.5)} = 2),所以这是平均了两天的温度。如下图中的黄色线所示:

由于仅平均了两天的温度,平均的数据太少,所以得到的曲线有更多的噪声,有可能出现异常值,但是这个曲线能够更快适应温度变化。

指数加权平均数经常被使用,再次强调,它在统计学中被称为 指数加权移动平均,我们简称为 指数加权平均。通过调整参数 (eta)(在讲到后面的算法时,你会发现这是一个很重要的参数),可以取得稍微不同的效果,往往中间有某个值效果最好,类似于上面例子中 (eta) 取中间值 (0.9)时得到的红色曲线,比起绿线和黄线更好地平均了温度。

现在你知道计算指数加权平均数的基本原理,下一节我们会聊聊它的本质作用。

理解指数加权平均(Understanding Exponentially weighted averages)

上节中,我们讲到了指数加权平均数,这是后面几个优化算法中的关键一环,而这几个优化算法能帮助你训练神经网络。接下来,我们进一步探讨算法的本质作用。

指数加权移动平均 公式回顾:
(v_t = eta ; v_{t-1} + (1-eta) ; heta_t)

继续上节中伦敦气温的例子来讲,同样令 (eta = 0.9),但我们改变下公式中两个加和项的顺序,并且从后往前写:

(v_{100} = 0.1 ; heta_{100} + 0.9 ; v_{99})
(v_{99} = 0.1 ; heta_{99} + 0.9 ; v_{98})
(v_{98} = 0.1 ; heta_{98} + 0.9 ; v_{97})
(vdots)

可以看到,公式都是嵌套的,如果我们将 (v_{99}) 代入到 (v_{100}) 的式子中:

(v_{100} = 0.1 ; heta_{100} + 0.9 ; (0.1 ; heta_{99} + 0.9 ; v_{98}))

如果我们继续将 (v_{98}) 代入式子中:

(v_{100} = 0.1 ; heta_{100} + 0.9 ; left(0.1 ; heta_{99} + 0.9 ; (0.1 ; heta_{98} + 0.9 ; v_{97}) ight))

(vdots)

以此类推,如果你把余下的式子全部代入,并且把这些括号全部都展开,那么你会得到:

(v_{100} = 0.1 imes 0.9^0 heta_{100} + 0.1 imes 0.9^1 heta_{99} + 0.1 imes {(0.9)}^{2} heta_{98} + ldots + 0.1 imes {(0.9)}^{98} heta_{2} + 0.1 imes {(0.9)}^{99} heta_{1})

也就是:(v_{100} = 0.1 * sumlimits_{i=1}^{100} ; 0.9^{100 - i} * heta_i)

所以公式 (v_t = eta ; v_{t-1} + (1-eta) ; heta_t) 展开后的通项公式为:(v_t = (1-eta) * sumlimits_{i=1}^{t} ; eta^{t - i} * heta_i)

可以看出,首先它是 100 天数据的加和,并且做了平均,但是平均的时候在每一天的数据前面都添加了权值,同时你可以观察这些有规律的权值,随着权值项中 0.9 的指数的增加(本例中从 0 ~ 100),整个权值会逐渐变小,所以这个 权值 是一个 呈指数形式衰减的函数

从直观上理解,当 (eta = 0.9) 时,这意味着大约是平均了 10 天的数据。上面展开式中每一项面前都有一个 (1-eta),本例中等于 0.1,也就是 (frac{1}{10})。可以理解为取前 10 项相加,再除以 10 取平均,这不就意味着平均最近 10 天的数据么。但是前 10 项中除第一项 ( heta_{100}) 以外的其他 9 项中的当日真实温度值(( heta))都开始因为权值的缘故而开始衰减,所以这个 10 天的平均值是偏小的,但是后面还有 90 项,不过这 90 项的权值已经经过了前面 10 次的指数衰减,所以它们越来越小,把它们全部相加后,在和前面的 10 天平均值相加,正好可以对前面求出的,偏小一些的 10 日平均值做一些修正。所以说这意味着是平均了 10 天的数据,但是它是一个近似的大约值。最准确的当然是直接取近期 10 天的数据,然后除以 10,这是最准确和最直接的平均求法了。所以结论是你可以理解为平均了 (frac{1}{1-eta}) 天的数据。

当然,也可以从另一个角度来理解到底平均了多少天的数据。这里需要用到自然常数 (e)

(e = limlimits_{x oinfty}left(1+frac{1}{x} ight)^x),其数值约为(小数点后100位):(e approx) 2.71828 18284 59045 23536 02874 71352 66249 77572 47093 69995 95749 66967 62772 40766 30353 54759 45713 82178 52516 64274。

本例中,(eta = 0.9),令 (varepsilon = 1 - eta),则 (varepsilon = 0.1)

${(1-varepsilon)}^{(frac{1}{varepsilon})} = (0.9)^{10} = $ 0.3486784401

(frac{1}{e} approx) 0.3678794411

所以 ({(1-varepsilon)}^{(frac{1}{varepsilon})} approx frac{1}{e}),换言之,当权值经过 10 次指数形式衰减之后,也就是 10 天之后,权重的呈指数形式衰减的函数曲线将下降到峰值的 (frac{1}{e}),相当于三分之一处,这是一个很小的数值,可以理解为权重已经很小很小。

再来看一下,当 (eta = 0.98) 时的情况,此时 (varepsilon = 1 - eta = 0.02 = frac{1}{50})

而 ${(1-varepsilon)}^{(frac{1}{varepsilon})} = (0.98)^{50} = $ 0.3641696800871117 (approx frac{1}{e})

当然也可以直接代入公式 (frac{1}{(1-eta)} = frac{1}{(1-0.98)} = 50),即大约平均了 50 天的数据。

以上都只是思考的大致方向,并不是正式的数学证明。

代码中实现方式为:

v_theta := 0
v_theta := beta v_theta + (1 - beta) theta_1
v_theta := beta v_theta + (1 - beta) theta_2
v_theta := beta v_theta + (1 - beta) theta_3
...
v_theta := beta v_theta + (1 - beta) theta_t

(v) 加下标,来表示 (v) 是用来计算数据的指数加权平均数。整体来讲,就是先初始化 (v_{ heta} =0),然后每拿到第 (t) 天的数据,就把 (v_{ heta}) 更新为(v_{ heta} := eta ; v_{ heta} + (1 - eta) ; heta_{t})

指数加权平均数公式的好处之一在于,它占用极少内存,电脑内存中只占用一行数字而已,然后把最新数据代入公式,不断覆盖就可以了,正因为这个原因,其效率很高,基本上只占用一行代码,计算指数加权平均数也只占用单行数字的存储和内存,当然它并不是最好的,也不是最精准的计算平均数的方法。

如果你要计算移动平均,你直接算出过去 10 天的总和,或过去 50 天的总和,然后除以 10 和 50 就好了,如此往往会得到更好的估测。但缺点是,如果保存所有近期的温度数据,以及过去 10 和 50 天的总和,必须占用更多的内存,执行更加复杂,计算成本也更加高昂。

在接下来的章节中,我们会计算多个变量的平均值,从计算和内存效率来说,这都是一个有效的方法,所以在机器学习中会经常使用,更不用说只要一行代码,这也是一个优势。

现在你学会了计算指数加权平均数,你还需要知道一个专业概念,叫做 偏差修正,下一节我们会讲到它,接着你就可以用它构建更好的优化算法,而不是简单直接的梯度下降法。

指数加权平均的偏差修正(Bias correction in exponentially weighted averages)

你学过了如何计算指数加权平均数,有一个技术名词叫做偏差修正,可以让平均数运算更加准确,来看看它是怎么运行的。

指数加权移动平均 公式回顾:(v_t=eta ; v_{t-1} + (1-eta) ; heta_t)

在之前的例子中,这个(红色)曲线对应 (eta) 的值为 0.9,这个(绿色)曲线对应的 (eta) 的值为 0.98,如果你执行上面的公式,在 (eta) 等于 0.98 时,得到的并不是绿色的曲线,而是紫色的曲线,你可以注意到紫色曲线的起点较低,我们来看看怎么处理。

计算移动平均数的时候,初始化 (v_{0} = 0)(v_{1} = 0.98 v_{0} +0.02 heta_{1}),但是 (v_{0} =0),所以这部分没有了((0.98 v_{0})),所以 (v_{1} = 0.02 heta_{1}),所以如果一天温度是 40 华氏度,那么 (v_{1} = 0.02 heta_{1} = 0.02 imes 40 = 8),因此得到的值会小很多,所以第一天温度的估测不准。

(v_{2} = 0.98 v_{1} + 0.02 heta_{2}),如果代入 (v_{1}),然后相乘,所以 (v_{2} = 0.98 imes 0.02 heta_{1} + 0.02 heta_{2} = 0.0196 heta_{1} +0.02 heta_{2}),假设 ( heta_{1})( heta_{2}) 都是正数,计算后 (v_{2}) 要远小于 ( heta_{1})( heta_{2}),所以 (v_{2}) 不能很好估测出这一年前两天的温度。

有个办法可以修改这一估测,让估测变得更好,更准确,特别是在估测初期,也就是不用 (v_{t}),而是用 (frac{v_{t}}{1- eta^{t}}),t 就是现在的天数。举个具体例子,当 (t=2) 时,(1 - eta^{t} = 1 - {0.98}^{2} = 0.0396),因此对第二天温度的估测变成了 (frac{v_{2}}{0.0396} = frac{0.0196 heta_{1} + 0.02 heta_{2}}{0.0396}),也就是 ( heta_{1})( heta_{2}) 的加权平均数,并去除了偏差。你会发现随着 (t) 增加, (eta^{t}) 接近于 0,所以当 (t) 很大的时候,偏差修正几乎没有作用,因此当 (t) 较大的时候,绿线基本和紫线重合了。不过在开始阶段,偏差修正可以帮助你更好预测温度,偏差修正可以帮助你使结果从紫线变成绿线。另外一方面,紫色线开始是有偏差的,为什么之后就会变得无偏差了,或者说偏差很小了呢?因为随着天数增加,前面数据的权重呈指数形式衰减,有偏差的那些数据影响力越来越不重要了,而后面的数据都是无偏差的,而且权重大,所以慢慢的估测就会趋近于准确。

在机器学习中,在计算指数加权平均数的大部分时候,大家可能不在乎是否执行偏差修正,因为大部分人宁愿熬过初始时期,拿到具有偏差的估测,然后继续计算下去。如果你关心初始时期的偏差,在刚开始计算指数加权移动平均数的时候,偏差修正能帮助你在早期获取更好的估测。

所以你已经学会了计算指数加权移动平均数,我们接着用它来构建更好的优化算法吧!

动量梯度下降(Gradient descent with momentum)

还有一种算法叫做 Momentum,或者叫做动量梯度下降法,运行速度几乎总是快于标准的梯度下降算法,简而言之,基本的想法就是计算梯度的指数加权平均数,并利用该梯度更新你的权重,下面来了解下细节。

假设,上面的等高线图是你要优化的成本函数,红色圆点即最小值位置。你进行梯度下降(蓝色线),你会发现,无论是 batch 或 mini-batch,都需要很多计算步骤,慢慢的摆动才能到达最小值。这种上下摆动减慢了梯度下降法的速度,但假如你选择使用较大的学习率(紫色的线段和箭头),那么结果可能会偏离函数的范围,为了避免摆动过大,你还是得选用一个较小的学习率。

另一个看待问题的角度是,在纵轴上,你会希望摆动幅度小一些,而在横轴上, 你会希望学习的速度快一些,尽快的到达圆心的最小值。动量梯度下降可以帮上忙。如上图中的蓝色线,在纵轴上,它大幅度的上下波动,如果对它计算 指数加权移动平均 得到的均值,就会产生正负抵消的情况,其结果会更接近于 0 ,这就到达了我们的目的,也就意味着在纵轴上的摆动幅度被削弱了。而在横轴方向,所有的微分都指向横轴方向,因此横轴方向的斜率经过平均后仍保持大体上不变的状态,但总体而言,算法走了一条更加直接的路径,在抵达最小值的路上减少了摆动。所以,相比于之前的标准梯度下降法,效率有所提升。

Implementation details

On iteration t:
(;;;;;;;;)Compute (dW, db) on the current mini-batch
(;;;;;;;;)(v_{dW} = eta v_{dW} + (1 - eta) dW)
(;;;;;;;;)(v_{db} = eta v_{db} + (1 - eta) db)
(;;;;;;;;)(W := W - alpha v_{dW},;; b := b - alpha v_{db})

Hyperparameters: (alpha, eta ;;;;;;;; eta = 0.9)

所以你有两个超参数,学习率 (alpha) 以及参数 (eta)(eta) 控制着指数加权平均数。(eta) 最常用的值是 0.9,我们之前平均了过去十天的温度,所以现在平均了前十次迭代的梯度。实际上 (eta) 为 0.9 时,效果不错,你可以尝试不同的值,可以做一些超参数的研究,不过 0.9 是很棒的鲁棒数。那么关于偏差修正,所以你要拿 (v_{dW})(v_{db}) 除以 (1-eta^{t}),实际上人们不这么做,因为 10 次迭代之后,因为你的移动平均已经过了初始阶段。实际中,在使用梯度下降法或动量梯度下降法时,人们不会受到偏差修正的困扰。当然 (v_{{dW}}) 初始值是 0,要注意到这是和 (dW) 拥有相同维数的零矩阵,也就是跟 (W) 拥有相同的维数,(v_{db}) 的初始值也是向量零,所以和 (db) 拥有相同的维数,也就是和 (b) 是同一维数。

最后要说一点,如果你查阅了动量梯度下降法相关资料,你经常会看到 (1-eta) 被删除了,最后得到的是 (v_{dW} = eta v_{{dW}} +dW),图中紫色线所示。所以 (v_{{dW}}) 缩小了 (1-eta) 倍,相当于乘以 (frac{1}{1- eta}),所以你要用梯度下降最新值的话,(alpha) 要根据 (frac{1}{1 -eta}) 相应变化。实际上,二者效果都不错,只是会影响到学习率 (alpha) 的最佳值。我觉得这个公式用起来没有那么自然,因为有一个影响,如果你最后要调整超参数 (eta),就会影响到 (v_{{dW}})(v_{db}),你也许还要修改学习率 (alpha),所以我更喜欢保留了 (1-eta) 的这个公式,但是两个公式都将 (eta) 设置为 0.9,是超参数的常见选择,只是在这两个公式中,学习率 (alpha) 的调整会有所不同。

所以这就是动量梯度下降法,这个算法肯定要好于没有 Momentum 的梯度下降算法,我们还可以做别的事情来加快学习算法,我们将在接下来的章节中探讨这些问题。

RMSprop —— root mean square prop(RMSprop)

我们已经知道了动量(Momentum)可以加快梯度下降,还有一个叫做 RMSprop 的算法,全称是 root mean square prop 算法,它也可以加速梯度下降,我们来看看它是如何运作的。

Momentum 梯度下降中心思想是对梯度使用指数加权移动平均,然后再更新。这样会整体上减小梯度的巨幅波动。而 RMSprop 的中心思想是,对于梯度,让该减小的减小(比如例子中的纵轴方向),让该增大的增大(比如例子中的横轴方向),形成两个反方向的改变。

Implementation details

On iteration t:
(;;;;;;;;)Compute (dW, db) on the current mini-batch
(;;;;;;;;)(S_{dW} = eta S_{dW} + (1 - eta) dW^2)
(;;;;;;;;)(S_{db} = eta S_{db} + (1 - eta) db^2)
(;;;;;;;;)(W := W - alpha frac{dW}{sqrt{S_{dW}}},;; b := b - alpha frac{db}{sqrt{S_{db}}})

Hyperparameters: (alpha, eta ;;;;;;;; eta = 0.999)

回忆一下我们之前的例子,如果你执行梯度下降,虽然横轴方向正在推进,但纵轴方向会有大幅度摆动,为了分析这个例子,假设纵轴代表参数 (b),横轴代表参数 (W),可能有 (W_{1})(W_{2}) 或者其它重要的参数,为了便于理解,被称为 (b)(W)。所以,你想减缓 (b) 方向的学习,即纵轴方向,同时加快,至少不是减缓横轴方向的学习。

RMSprop 会使用 (frac{dW}{sqrt{S_{dW}}})(frac{db}{sqrt{S_{db}}}) 来更新参数值 ,我们来理解一下其原理。

你看这些微分,因为函数的倾斜程度,垂直方向的要比水平方向的大得多,所以斜率在 (b) 方向特别大,在 (W) 方向较小。

所以:

(db) 较大,(db^2) 较大,(S_{db}) 较大,(sqrt{S_{db}}) 也会较大。
(dW) 较小,(dW^2) 较小,(S_{dW}) 较小,(sqrt{S_{dW}}) 也会较小。

结果就是纵轴上的更新要被一个较大的数相除,就能消除摆动,而水平方向的更新则被较小的数相除。

另,(dW^2)(db^2) 可以确保 (S_{dW})(S_{db}) 都是正数。且该平方操作是指 element-wise,如此会有:

如果当 (dW > 0)(0< sqrt{S_{dW}} < 1) 时,(frac{dW}{sqrt{S_{dW}}}) 会比 (dW) 大。
如果当 (dW > 0)(sqrt{S_{dW}} > 1) 时,(frac{dW}{sqrt{S_{dW}}}) 会比 (dW) 小。
如果当 (dW < 0)(0< sqrt{S_{dW}} < 1) 时,(frac{dW}{sqrt{S_{dW}}}) 会比 (dW) 小。
如果当 (dW < 0)(sqrt{S_{dW}} > 1) 时,(frac{dW}{sqrt{S_{dW}}}) 会比 (dW) 大。

RMSprop 的影响就是你的更新最后会变成这样(绿色线),纵轴方向上摆动较小,而横轴方向继续推进。还有个影响就是,你可以用一个更大学习率 (alpha),然后加快学习,而无须在纵轴上垂直方向偏离。

要说明一点,我一直把纵轴和横轴方向分别称为 (b)(W),只是为了方便展示而已。实际中,你会处于参数的高维度空间,所以需要消除摆动的垂直维度,实际上是参数 (W_1)(W_2) 等的合集,水平维度可能 (W_3)(W_4) 等等,因此把 (W)(b) 分开只是方便说明。实际中 (dW) 是一个高维度的参数向量,(db) 也是一个高维度参数向量,但是你的直觉是,在你要消除摆动的维度中,最终你要计算出并除以一个更大的微分的平方的指数加权平均值的平方根,所以你最后去掉了那些有摆动的方向。所以这就是 RMSprop,全称是均方根,因为你将微分进行平方,然后最后使用平方根。

如果 (S_{dW}) 的平方根趋近于 0 怎么办?那么得到的答案就非常大,为了确保数值稳定,在实际操作时,你需要在分母上加上一个很小很小的 (varepsilon)(varepsilon) 是多少没关系,(10^{-8}) 是个不错的选择,这只是保证数值能稳定一些,无论什么原因,你都不会除以一个很小很小的数。

所以 RMSprop 跟 Momentum 有很相似的一点,可以消除梯度下降中的摆动,包括 mini-batch 梯度下降,并允许你使用一个更大的学习率 (alpha),从而加快你的算法学习速度。但也许你已经发现了,RMSprop 中用于更新的梯度项的分子使用的是 (dW)(db),它没有被指数加权平均过,所以可能也会产生波动。所以 RMSprop 和 Momentum 结合就会解决这个问题,下一节我们详细讲如何将他们结合起来,到时你会得到一个更好的优化算法。

Adam 优化算法(Adam optimization algorithm)

在深度学习的历史上,包括许多知名研究者在内,提出了优化算法,并很好地解决了一些问题,但随后这些优化算法被指出并不能一般化,并不适用于多种神经网络,时间久了,深度学习圈子里的人开始多少有些质疑全新的优化算法,很多人都觉得动量(Momentum)梯度下降法很好用,很难再想出更好的优化算法。所以 RMSprop 以及 Adam 优化算法,就是少有的经受住人们考验的两种算法,已被证明适用于不同的深度学习结构,这个 Adam 算法我会毫不犹豫地推荐给你,因为很多人都试过,并且用它很好地解决了许多问题。

Adam优化算法基本上就是将 MomentumRMSprop 结合在一起,那么来看看如何使用 Adam 算法。

开始之前,首先要说明下,在 Momentum 和 RMSprop 算法中都有一个超参数 (eta),为了避免混淆和方便区分,现在将 Momentum 中的 (eta) 变为 (eta_{1}),将 RMSprop 中的 (eta) 变为 (eta_{2})

另外,一般使用 Adam 算法的时候,要计算 偏差修正

Adam (Adaptive Moment Estimation) optimization algorithm implementation details

(v_{dW} = 0, ;;S_{dW} = 0, ;;v_{db} = 0, ;;S_{db} = 0)

On iteration t:

(;;;;;;;;)Compute (dW, db) on the current mini-batch

(;;;;;;;;)(v_{dW} = eta_1 v_{dW} + (1 - eta_1) dW,;;v_{db} = eta_1 v_{db} + (1 - eta_1) db ;;;;;;;;gets) "Momentum" (eta_1)

(;;;;;;;;)(S_{dW} = eta_2 S_{dW} + (1 - eta_2) dW^2,;;S_{db} = eta_2 S_{db} + (1 - eta_2) db^2 ;;;;;;;;gets) "RMSprop" (eta_1)

(;;;;;;;;)(v_{dW}^{ ext{corrected}}= frac{v_{dW}}{1 - eta_{1}^{t}}, ;; v_{db}^{ ext{corrected}} =frac{v_{db}}{1 -eta_{1}^{t}})

(;;;;;;;;)(S_{dW}^{ ext{corrected}} =frac{S_{dW}}{1 - eta_{2}^{t}}, ;; S_{db}^{ ext{corrected}} =frac{S_{db}}{1 - eta_{2}^{t}})

(;;;;;;;;)(W := W - alpha frac{v_{dW}^{ ext{corrected}}}{sqrt{S_{dW}^{ ext{corrected}}} +varepsilon},;; b := b - alpha frac{v_{ ext{db}}^{ ext{corrected}}}{sqrt{S_{ ext{db}}^{ ext{corrected}}} +varepsilon})

Hyperparameters choice : (alpha ext{ needs to be tune}, ;; eta_1 = 0.9, ;; eta_2 = 0.999, ;; varepsilon = 10^{-8})

所以 Adam 算法结合了 Momentum 和 RMSprop 梯度下降法,并且是一种极其常用的学习算法,被证明能有效适用于不同神经网络,适用于广泛的结构。

本算法中有很多超参数,超参数学习率 (alpha) 很重要,也经常需要调试,你可以尝试一系列值,然后看哪个有效。(eta_{1}) 常用的缺省值为 0.9,这是在计算 (dW) 以及 (db) 的移动加权平均值,它是 Momentum 涉及的项。至于超参数 (eta_{2}),Adam 论文作者,也就是 Adam 算法的发明者,推荐使用 0.999,这是在计算 ({(dW)}^{2}) 以及 ({(db)}^{2}) 的移动加权平均值,关于 (varepsilon) 的选择其实没那么重要,Adam 论文的作者建议 (varepsilon)(10^{-8}),但你并不需要设置它,因为它并不会影响算法表现。但是在使用 Adam 的时候,人们往往使用缺省值即可,(eta_{1})(eta_{2})(varepsilon) 都是如此,我觉得没人会去调整 (varepsilon),然后尝试不同的 (alpha) 值,看看哪个效果最好。你也可以调整 (eta_{1})(eta_{2}),但我认识的业内人士很少这么干。

为什么这个算法叫做 Adam ?Adam 代表的是 Adaptive Moment Estimation,(eta_{1}) 用于计算这个微分((dW)),叫做第一矩,(eta_{2}) 用来计算平方数的指数加权平均数(({(dW)}^{2})),叫做第二矩,所以 Adam 的名字由此而来,但是大家都简称 Adam 权威算法。

这就是关于 Adam 优化算法的全部内容,有了它,你可以更加快速地训练神经网络,在结束本周课程之前,我们还要讲一下超参数调整,以及更好地理解神经网络的优化问题有哪些。下一节,我们将讲讲学习率衰减。

学习率衰减(Learning rate decay)

加快学习算法的一个办法就是随时间慢慢减少学习率,我们将之称为学习率衰减。

首先通过一个例子看看,为什么要计算学习率衰减。假设你要使用 mini-batch 梯度下降法,mini-batch 数量不大,大概 64 或者 128 个样本,在迭代过程中会有噪音(蓝色线),梯度下降朝向中心的最小值,但是不会精确地收敛,所以你的算法最后会在最小值附近摆动,并不会真正收敛,因为你用的 (alpha) 是固定值。

如果慢慢减少学习率 (alpha) 的话,在初期的时候,(alpha) 学习率还比较大,你的学习还是相对较快,但随着 (alpha) 变小,你的步伐也会变慢变小,所以最后你的曲线(绿色线)会在最小值附近的一小块区域里摆动,而不是在训练过程中,大幅度在最小值附近摆动。

所以慢慢减少 (alpha) 的本质在于,在学习初期,你能承受较大的步伐,但当开始收敛的时候,小一些的学习率能让你步伐变小,从而更靠近最小值。

具体做法是,使用 mini-batch 法,每一次迭代都遍历一次整个数据集,然后使用公式:

(alpha= frac{1}{1 + ext{ decay_rate } * ext{ epoch_num }} cdot alpha_0) 更新 (alpha) 之后,在继续迭代。

其中,decay_rate 称为衰减率,epoch_num 为迭代次数,(alpha_{0}) 为初始学习率。衰减率是另一个你需要调整的超参数。

具体例子,假设 (alpha_{0} = 0.2),衰减率 decay_rate = 1,那么:

第一次迭代,(alpha = 0.1)
第二次迭代,(alpha = 0.67)
第三次迭代,(alpha = 0.5)
第四次迭代,(alpha = 0.4)
...
根据上述公式,你的学习率 (alpha) 呈递减趋势。如果你想用学习率衰减,要做的是要去尝试不同的值,包括超参数 (alpha_{0}),以及超参数衰退率 decay_rate,找到合适的值。

除了这个学习率衰减的公式,人们还会用其它的公式。

Other learning rate decay methods

  • (alpha = 0.95^{ ext{epoch_num}} cdot alpha_0), 指数衰减,学习率呈指数下降。

  • (alpha = frac{k}{sqrt{ ext{epoch_num}}} cdot alpha_0)

  • (alpha = frac{k}{sqrt{ ext{mini-batch size}}} cdot alpha_0)

  • 有时人们也会用一个离散下降的学习率,也就是某个步骤有某个学习率,一会之后,学习率减少了一半,一会儿减少一半,一会儿又一半,这就是离散下降(discrete stair cease)的意思。

  • 人们有时候还会做一件事,手动衰减。如果你一次只训练一个模型,如果你要花上数小时或数天来训练,有些人的确会这么做,看看自己的模型训练,耗上数日,然后他们觉得,学习速率变慢了,我把 (alpha) 调小一点。手动控制 (alpha) 当然有用,时复一时,日复一日地手动调整 (alpha),只有模型数量小的时候有用。

所以现在你有了多个选择来控制学习率 (alpha)。你可能会想,好多超参数,究竟我应该做哪一个选择,我觉得,现在担心为时过早。下一周,我们会讲到,如何系统选择超参数。对我而言,学习率衰减并不是我尝试的要点,设定一个固定的 (alpha),然后好好调整,会有很大的影响,学习率衰减的确大有裨益,有时候可以加快训练,但它并不是我会率先尝试的内容,但下周我们将涉及超参数调整,你能学到更多系统的办法来管理所有的超参数,以及如何高效搜索超参数。

这就是学习率衰减,最后我还要讲讲神经网络中的局部最优以及鞍点,所以能更好理解在训练神经网络过程中,你的算法正在解决的优化问题,下一节我们就好好聊聊这些问题。

局部最优问题(The problem of local optima)

在深度学习研究早期,人们总是担心优化算法会困在极差的局部最优,不过随着深度学习理论不断发展,我们对局部最优的理解也发生了改变。我向你展示一下现在我们怎么看待局部最优以及深度学习中的优化问题。

如上图,这是曾经人们在想到局部最优时脑海里会出现的图,也许你想优化一些参数,我们把它们称之为 (W_{1})(W_{2}),平面的高度就是损失函数。在图中似乎各处都分布着局部最优(蓝色点)。梯度下降法或者某个算法可能困在一个局部最优中,而不会抵达全局最优。如果你在低维的情况,比如说两个维度,你作出图,就容易出现有多个不同局部最优的图,而这些低维的图曾经影响了我们的理解,但是这些理解并不正确。事实上,如果你要创建一个神经网络,通常梯度为零的点并不是这个图中的局部最优点(蓝色点),实际上成本函数的零梯度点,通常是鞍点。如下图中的蓝色点:

一个具有高维度空间的函数,如果梯度为 0,那么在该为 0 的点的不同方向,它既可能是凸函数,也可能是凹函数,如上图中的马鞍式图形。所以,在 2 万维空间中,假如你想要得到局部最优,那么所有的 2 万个方向都需要是凸,或都是凹的,但这发生的机率也许很小,可能是 (2^{-20000})。相反,鞍点的情况才是你你更有可能遇到,它在同一个点时,有些方向的曲线会向上弯曲,另一些方向曲线向下弯曲,而不是所有的都向上或向下弯曲,因此在高维度空间,你更可能碰到鞍点。就像上图中蓝色点的那种:而不会碰到局部最优。

至于为什么会把一个曲面叫做鞍点,如上图,你想象一下,就像是放在马背上的马鞍一样,然后你是骑马的人,要坐在马鞍上,因此上图中绿色的点,就是导数为 0 的点,这个点叫做鞍点。

所以我们从深度学习历史中学到的一课就是,我们对低维度空间的大部分直觉,并不能应用到高维度空间中。适用于其它算法,因为如果你有 2 万个参数,那么 (J) 函数有 2 万个维度向量,你更可能遇到鞍点,而不是局部最优点。

如果局部最优不是问题,那么问题是什么?

问题就是在下降的平稳段会减缓算法学习速度,如下图,平稳段是一块区域(蓝色的线段和箭头指示的区域),其中导数长时间接近于 0,如果你下降到此区域,梯度会沿着曲面从上向下下降,因为梯度等于或接近 0,曲面很平坦,你得花上很长时间慢慢抵达平稳段的鞍点,然后你的算法才能够走出平稳段(红色线段和箭头指示的区域)。

本节讲述的要点是:

  • Unlikely to get stuck in a bad local optima,你不太可能困在极差的局部最优中,条件是你在训练较大的神经网络,存在大量参数,并且成本函数 (J) 被定义在较高的维度空间。

  • Plateaus can make learning slow,平稳段是一个问题,它使得学习十分缓慢,这也是像 Momentum 或是 RMSprop,Adam 这样的算法,能够加速学习算法的地方。在这些情况下,更成熟的优化算法,如 Adam 算法,能够加快速度,让你尽早往下走出平稳段。说实话,要面临如此之高的维度空间,我觉得没有人有那么好的直觉,知道这些空间长什么样,而且我们对它们的理解还在不断发展,不过我希望这一点能够让你更好地理解优化算法所面临的问题。

原文地址:https://www.cnblogs.com/kershaw/p/11160403.html