美文网首页
[机器学习] Gradient descent (Adagrad

[机器学习] Gradient descent (Adagrad

作者: 只爱学习的Gcy | 来源:发表于2020-09-10 16:52 被阅读0次

    前言

      这篇文章是李宏毅的《机器学习》课程的笔记,主要目的是让我自己梳理一遍课程的内容,加深理解,找到课上没弄懂的地方,并将新的知识点与我以前的一些认知结合起来。如有写错的地方或者理解有问题的地方希望能得到纠正,欢迎相关的问题。

    正文

      回顾在前面线性回归处使用的梯度下降来寻找损失函数 J (或记为 L) 最小时的参数 \boldsymbol\theta ,我们的目标函数是:
    \boldsymbol \theta^*=\arg \min_{\boldsymbol \theta} J(\boldsymbol \theta)
      其中, \boldsymbol \theta^* 是最优条件下的参数 \boldsymbol \theta 值。

      梯度下降的方法的过程就是随机选取一个起始点 \boldsymbol \theta^{0}= \begin{bmatrix} \theta^0_1 \\ \theta^0_2\end{bmatrix} ,然后计算在这一点的梯度向量 \nabla J(\boldsymbol \theta^0) = \begin{bmatrix} \partial J(\boldsymbol \theta^0_1) / \partial \theta_1 \\ \partial J(\boldsymbol \theta^0_2) / \partial \theta_2 \end{bmatrix} ,并设定一个学习率参数 \eta ,然后更新参数 \boldsymbol \theta 得到一个新的 \boldsymbol \theta^1
    \boldsymbol \theta^1 = \boldsymbol \theta^0 - \eta \nabla J(\boldsymbol \theta^0)
      然后不断重复这个过程,直到找到 J 最小的点。上面这个是 \boldsymbol \theta 只有两个参数的情况,也就是二维的情况,如果把 J\boldsymbol \theta 的关系图以等高线的形式画在图上,那么梯度的方向向量其实就是那一点等高线的垂线方向。梯度下降就是不断重复找当前所在位置的等高线的梯度方向,然后往其反方向走一步的过程,过程如图所示,红线是梯度方向,蓝线是移动方向,图中 L 就是上面的 J 表示损失函数。(PS: 学习率在李宏毅的课程中是记为 \eta 的,而在吴恩达 Coursera 上是记为 \alpha ,我是先看的吴恩达的课,所以习惯用 \alpha ,不过貌似好像 \eta 作为学习率更常用,所以以后都写成 \eta)

    Learning rate

    学习率的设置

      在上面所说的梯度下降的方法中,学习率是一个不变的常数,那么如何来设置这个参数其实是一个很重要的问题,因为如果设的太大的话,可能因为每次更新参数的幅度太大了而无法收敛到最低点(下图绿线),甚至变得发散了(下图黄线)。如果设的太小的话可能收敛的速度会很慢(下图蓝线)。

      上面这个图是在参数为一维的情况下才能画出来的,如果参数大于等于三维,其实是没有办法可视化画出 \boldsymbol \thetaJ 之间的关系图的,所以也就没法从图中观察到我的学习率时候设置的太小了或者太大了。但是我们可以画出每次迭代时的当前 J 的图,如下图所示,可以看到当学习率过大而损失值发散的时候会像黄线那样,当太小的时候会像蓝线那样。所以画迭代次数与损失值的关系图可以帮我们了解到我们的学习率 \eta 是否合适。

    自适应学习率 (Adaptive learning rates)

      上面所说的学习率是不变的,那么我们想让我们的学习率能够自适应迭代的过程要怎么办呢?一个大的原则是希望随着参数的不断更新,学习率会变的越来越小,以便更好的收敛。那么很容易可以想到可以让学习率随着迭代的次数而衰变,例如第 t 次迭代时的学习率 \eta^t 为:
    \eta^t = \frac \eta {\sqrt{t+1}}
      使用上面这种学习率自适应方法的梯度下降叫做 vanilla gradient descent,至此为止,学习率对于每一个参数 \{\theta_1, \theta_2 ,...,\theta_n\}都是一样的,但是应该为不同的参数设定不同的学习率。所以有另一种学习率自适应的算法,叫做 Adagrad

    Adagrad

      Adagrad 每次的学习率不是只关于迭代次数 t 来衰减,还与这 t 次迭代时每次的微分有关。记任意一个参数为 w ,改参数第 t 次的微分为 g^t 。则 使用 Adagrad 进行梯度下降的参数更新方法如下:
    w^{t+1} \leftarrow w^t - \frac {\eta^t} {\sigma^t} g^t
      其中 \eta^t = \frac \eta {\sqrt{t+1}}\sigma^t = \sqrt {\frac 1 {t+1} \sum _{i=0}^t(g^i)^2}g^t =\partial J(\boldsymbol \theta^t) / \partial w 。可以发现,分子分母都有 \frac 1 {\sqrt{t+1}} ,所以约掉后变成:
    w^{t+1} \leftarrow w^t - \frac {\eta} {\sqrt { \sum _{i=0}^t(g^i)^2}} g^t

      对比 vanilla gradient descent 和 Adagrad ,当在某一点偏微项 g^t 算出来比较大时,vanilla gradient descent 也会更新的比较大,而 Adagrad 的 g^t 变大时, \sqrt { \sum _{i=0}^t(g^i)^2} 会导致更新的一步变小,这一个变大时另一个会变小,相乘后不一定会变大,怎样来理解这种设定呢?

      一种理解是 Adagrad 想要考虑的是这个梯度的反差有多大,就是如果前面的梯度算出来都很小,突然来了一个比原来的大了几个数量级的就会觉得这个特别大,这样就会更新一个接近 \eta 的一步。如果原来都很大,突然来了个小一两个数量级的梯度,那么就会基本不更新。所以是把原来的依据绝对的微分的大小决定更新的大小变成了现在依据相对的微分的大小绝对更新的大小了。就像下面这个表格,有两个参数比如说是 \theta_1\theta_2 在第五次迭代微分(偏导)算出来都是 0.1 ,如果用 vanilla gradient descent 来更新的话,两个参数更新的大小会是一样的,但是使用了 Adagrad 后,\theta_1 会更新的很大,接近 \eta ,而 \theta_2 则基本不更新。那么为什么要这么更新呢?

      这就要从数学上来理解了。我们在更新参数 \theta 时,都会有一种直觉,就是当微分比较大时,那么离最小值点会比较远,而微分比较大时,会离最小值点比较近。例如在一个二次曲线上,a 点微分比 b 点大,离最小值点也比 b 点远。

      但是这在多维上就不一定成立了,比如在一个二维的损失值与 \theta (图中的 w ) 的图上,分别用两个平面去切,得到两个曲线,虽然在 c 点的微分会比在 a 点的更大,但实际是 c 点离的更远,更应该进行大的参数更新。那么为什么 Adagrad 能让蓝线中的 a 点更新的比 c 点更多一点呢?我们先来看一下另一个问题。

      假如我们有一个二次曲线 y = ax^2+bx +c,还有一个起始点 x_0 ,那么我们想要更新以后能够到达最小值点,最好的一步应该是这一点到最小值点的距离 |x_0+\frac b {2a}| ,不管这个曲线是像上面蓝色的那样缓慢变化的,还是像绿线那样陡峭变化的,最好的更新的距离都是 |x_0+\frac b {2a}| ,当然各自的 a, b, x_0 是不一样的。那么怎么得到这个 |x_0+\frac b {2a}| 呢?把他变成一个分式,分子其实是 y 的一阶导数,即在这一点的微分值,而分母是二次微分。所以我们知道了,在这种情况下,最好的一步不是函数的一次微分(vanilla gradient descent 的更新方式),而是一次微分除以二次微分。

      那么这和 Adagrad 有什么关系呢?难道 \sqrt { \sum _{i=0}^t(g^i)^2} 是在那一点的二次微分?没错, \sqrt { \sum _{i=0}^t(g^i)^2} 其实就是二次微分的估计。那么是怎么估计的呢? Adagrad 所做的其实就是在一次微分上采样,在比较平滑的曲线上,一次微分会比较小,在比较陡峭的曲线上,一次微分会比较大,采多个点求平方和在开根号就能够反映二次微分的大小,因为比较平滑的取消上的 \sqrt { \sum _{i=0}^t(g^i)^2} 会比比较陡峭的 \sqrt { \sum _{i=0}^t(g^i)^2} 大,二次微分也是这样的。

      那么不可避免的会问,\sqrt { \sum _{i=0}^t(g^i)^2} 和二次微分应该还是不一样的,这应该只能反映不同参数上二次微分的大小关系,为什么不直接计算二次微分呢?其实是因为在数据比较大的时候,往往是不能忍受需要额外计算二次微分的计算开销的,而 Adagrad 没有增加过多的运算,用的是原来已经算好的一次微分来尽可能达到同样的效果,所以是很有效的。

    Stochastic Gradient Descent

      在原来的梯度下降中,我们每次迭代用到了所有样本的,因为原来的损失函数是:
    J(\boldsymbol \theta) = \frac 1 {2m}\sum_{i=1}^m(h_{\boldsymbol \theta}(\boldsymbol x^{(i)})-y^{(i)})^2
      Stochastic Gradient Descent 说,我不要用这个多样本,每次我只用一个,然后依次取训练集的所有样本来更新。那么使用第 i 个数据进行梯度下降时的损失函数就变成了:
    J(\boldsymbol \theta) = (h_{\boldsymbol \theta}(\boldsymbol x^{(i)})-y^{(i)})^2
      那么原来扫描一遍数据只能更新 1 次,现在扫描一遍能更新 m 次了,下图是 m=20 时的两种梯度下降的对比,(其中 w,b 等同于我所写的 \theta_1,\theta_0 ) 。可以看到虽然每次更新不一定会朝着总体损失减小的方向进行,但大部分更新还是会朝着损失减小的方向进行的,同样扫描一遍训练集,Stochastic Gradient Descent 离最优点更近一些。

    Feature scaling

      通常,在进行梯度下降的时候,需要将不同属性的数据缩放到相近的取值范围,缩放的方式可以采取减均值再除以方差的方式进行,公式如下:
    x_j^r \leftarrow \frac {x^r_j-m_j} {\sigma_j^2}
      其中,x_j^r 是第 r 个数据的第 j 个属性的取值, m_j 是所有数据的第 j 的属性数据的均值, m_j = \sum_{i=1}^mx_j^m\sigma_j^2 是 所有数据的第 j 的属性数据的方差。缩放后,所有属性的均值为 0 ,方差为 1 。

      那么为什么要将不同属性的数据缩放到类似大小的范围呢?让我们来分别看下缩放前和缩放后的效果,假设有一个函数是 y=b+w_1x_1+w_2x_2 (这里的 b,w_1,w_2 分别对应前面的 \theta_0,\theta_1,\theta_2 ),如果不缩放的话,就如下图左图那样,y 的等值线是一个椭圆。缩放后就像右边那样是一个原。我们知道梯度下降每次的方向是等值线的发现方向,那么在椭圆上,法线方向一般不是指向圆心的,如果进行梯度下降,就会像图中那样绕个大弯,如果是一个圆的话,无论在哪里法线都是指向圆心的,只需要更少的次数就能收敛。所以特征缩放可以加速梯度下降收敛的速度,更快的找到最小值点。

    Theory (梯度下降的数学原理)

      梯度下降的基本原理是在当前所处位置的周围很小的范围内(下图红圈内)看一看哪一点是最低的,然后朝着那个方向走一步,直到走到最低点,那么怎么才能找到哪一点是红圈里最低的点呢?

    图片18.png

      让我们先来回顾一下泰勒展开式,泰勒展开说,当 f(x)x_0n 阶可导的时候,那么可以写成:
    \begin{aligned} f(x) &= \sum_{k=0}^{n+1} \frac {f^{(k)}(x_0)} {k!} (x-x_0)^k + 0(x-x_0)\\ &=f(x_0) + f'(x_0)(x-x_0)+\frac {f''(x_0)} {2!}(x-x_0)^2 + \cdots \end{aligned}
      所以当 xx_0 很接近的时候,f(x) \approx f(x_0) + f'(x_0)(x-x_0) ,因为后面二次一个很小的数约等于 0 ,更高次就更小了。从一个 sin 函数的展开可以直观的看到这一点。将一个正弦函数在 \pi/4 位置展开,画出各次项的曲线。0 次的时候就是一条水平线,1 次的时候是一条斜线,更高次更拟合橙色的正弦曲线,在 \pi/4 周围很小的范围里,一次项是可以拟合正弦曲线的。

      二元的泰勒展开是下面这样的:
    \begin{aligned} f(x,y) = &f(x_0,y_0) + \frac {\partial f(x_0,y_0)}{\partial x}(x-x_0) + \frac {\partial f(x_0,y_0)}{\partial y}(y-y_0) \\ &+ \text {something related to $(x-x_0)^2$ and $(y-y_0)^2$} + ... \end{aligned}
      所以可以 f(x,y) \approx f(x_0,y_0) + \frac {\partial f(x_0,y_0)}{\partial x}(x-x_0) + \frac {\partial f(x_0,y_0)}{\partial y}(y-y_0) 。让我们回到上面那个二维情况下的梯度下降中,当红圈足够的小的时候,我们可以用一次的泰勒展开去近似原函数。假设当前所在的位置是 (a,b) ,则在这个点周围的那个红圈里:
    J(\boldsymbol \theta) \approx J(a,b) + \frac {\partial J(\boldsymbol \theta)} {\partial \theta_1}(\theta_1-a)+ \frac {\partial J(\boldsymbol \theta)} {\partial \theta_2}(\theta_2-b)
      其中 J(a,b) 其实是个常数,后面两项其实可以看作两个向量的内积,第一个向量是 \begin{bmatrix} \frac {\partial J(\boldsymbol \theta)} {\partial \theta_1} \\\frac {\partial J(\boldsymbol \theta)} {\partial \theta_2} \end{bmatrix} ,另一个是 \begin{bmatrix} \theta_1 -a \\ \theta_2 - b\end{bmatrix}\begin{bmatrix} \Delta \theta_1 \\ \Delta \theta_2\end{bmatrix} 。第一个其实就是在这一点的梯度方向,而要使 J(\boldsymbol \theta ) 最小,第二个向量最好是第一个向量的反方向最长的位置,即梯度反方向的射线与红圈的交点是最小值点。 这从数学上解释了参数为什么要这么更新,以及为什么有的时候会导致更新后的 J 反而变大,因为更新的步子太大了而使的红圈变得太大了而不满足一次泰勒展开的近似关系了。

    More Limitation of Gradient Descent

      我们前面就知道了,梯度下降会有落入局部最小的风险,然后事情并没有这么简单。梯度下降是按照梯度的大小来进行更新的,但并不是只有最小值点的梯度可能是 0 ,鞍点 (saddle point) 的梯度也可能是 0 ,比如 y = x^3 在 0 处的导数是 0 ,但并不是极值点,所以我们可能会落入鞍点。除此之外,我们在具体实现的时候,并不是梯度严格等于 0 的时候才停止迭代,当梯度约等于 0 时,就会停,那么就会有一种风险,可能只是函数在某一段比较平缓,其实离极值点还很远。这三种情况分别对应了下图三个点:

    相关文章

      网友评论

          本文标题:[机器学习] Gradient descent (Adagrad

          本文链接:https://www.haomeiwen.com/subject/lvvpektx.html