人工智能梯度下降的优化器SGD、Momentum、AdaGrad、Adam的数学原理以及无框架实现

系列文章目录

人工智能—梯度下降的原理和手写实现



前言

       我们在系列文章 人工智能—梯度下降的原理和手写实现中已经知道了什么是梯度下降,且证明了梯度下降的原理和可行性。但梯度下降中只是指明了梯度是下降的方向,但梯度不是下降的度量。
       在 人工智能–机器学习线性回归数学原理及实现中我们采用的是用梯度与引入的步长的概念相结合进行下降指导,其实这就是我们今天要说的优化器的概念了,而步长与梯度的直接结合便是最简单的一个优化——SGD,同时我们今天还会给大家详细介绍Momentum、AdaGrad、Adam等更多的优化器.


一、梯度下降优化器是什么?

       对于梯度下降,我们可以理解成站在山谷谷顶,蒙上眼睛寻找谷底的一个过程。将其形象抽象在函数上我们可以理解为,面对复杂的函数曲线/曲面的任意位置,我们在不知道这个函数图像的前提下,摸索出此函数的最小值的一个过程,因为当我们没有画出函数的图像时,在这个函数上摸索的过程也就像我们蒙上眼在山顶找山谷的过程,我们能凭借的只有脚下山坡的一个抖度来猜测是在上山还是在下山。

在这里插入图片描述
       于是我们能想到的第一个方法就是顺着下坡的方向一小步一小步的挪,而这个一小步的长度其实就是对应我们梯度下降中步长的概念,这个概念在我们之前的章节中已经详细描述过了,所以他不是我们今天的主角,我们今天的任务是怎么能让我们不要太迷茫的摸索,或者说让我们摸索下山的速度变快,路线变平整。这就涉及到梯度下降的优化方法了。

二、SGD优化方法

1.SGD是什么

       站在山坡上,我们有360°的方向可以去走,而朝向任意一步我们感受到的坡度都是不一样的,也就是抖度不一样,SGD就是选择这无数个方向中最抖的那个方向,每迈出一步都会重新做一次判断,SGD相信每一次都选择最优的一步,一定能到达那个“最深处”。其实也可以理解为一种贪心的算法。

2.SGD的数学原理

       SGD中有两个概念,第一个是步长 l r ( l e a r n i n g   r a t e ) lr(learning\ rate) lr(learning rate)第二个是梯度 ( g r a d i e n t ) (gradient) (gradient)一个决定试探的长度,一个指导试探的方向。
       所以SGD的数学表达式便是:
W ← W − η ∂ L ∂ W W\leftarrow W-\eta\frac{\partial L}{\partial W} WWηWL
       在梯度和步长的共同作用下,当前的 W W W会逐渐的逼近期望值。

3.SGD的实现

class SGD:

    def __init__(self, lr=0.01):
        self.lr = lr
        
    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key] 

4.SGD的缺陷

       在上一章节中,我们使用SGD在 f ( x 0 , x 1 ) = x 0 2 + x 1 2 f(x_0,x_1)=x_0^2+x_1^2 f(x0,x1)=x02+x12中进行梯度下降,似乎表现良好,那是因为 f ( x 0 , x 1 ) = x 0 2 + x 1 2 f(x_0,x_1)=x_0^2+x_1^2 f(x0,x1)=x02+x12是均向的,而什么是均向呢?我们来看 f ( x 0 , x 1 ) = x 0 2 + x 1 2 f(x_0,x_1)=x_0^2+x_1^2 f(x0,x1)=x02+x12的梯度图:
在这里插入图片描述
       所有的梯度不仅指向下降的方向,同时指向最低处,那这种图像我们便称之为均向的。可能有些同学很费解,指向下降的方向不就是指向最低处?那非均向的图像是什么样的呢?我们对 f ( x 0 , x 1 ) = x 0 2 + x 1 2 f(x_0,x_1)=x_0^2+x_1^2 f(x0,x1)=x02+x12进行一个简单的修改可得到:
f ( x 0 , x 1 ) = x 0 2 20 + x 1 2 f(x_0,x_1)=\frac{x_0^2}{20}+x_1^2 f(x0,x1)=20x02+x12
那他的图像可以相应的画出:
在这里插入图片描述

那他的梯度图也可以相应的画出:
在这里插入图片描述
       这里我们就能很形象的发现,两边的梯度虽然也在下降但他并不是指向最低的位置,但这个函数其实是有最小值的,那就是 f ( x 0 , x 1 ) m i n = f ( 0 , 0 ) = 0 f(x_0,x_1)_{min}=f(0,0)=0 f(x0,x1)min=f(0,0)=0。那么他的移动轨迹会是什么样呢?因为这个函数是没有其他极值点的,所以用SGD是一定能得走到最低处的,我们可以用python来进行简单的描绘他的运行轨迹。
在这里插入图片描述
       可以看到他SGD算法在以一种“之”字形的轨迹前进,对于算法自己来说这是不可避免的“最优路径”,但是对于知道函数图像的上帝视角的我们来说,这种走法是非常浪费时间的。

三、Momentum优化方法

1.Momentum是什么

        M o m e n t u m Momentum Momentum本身的意思是“动量”,其实是一个物理种的概念,描述的是物体运动趋势的变化,其实也很形象的能比喻我们在寻找最低点的过程中来回折返的一个过程。动量是存在于万物之间的,有运动就有动量,他是平滑变化的,但SGD的折返是僵硬的,也可以简单的说是直线的,我们希望能引入自然界的动量来让我们的折返变的更加平滑,从而达到优化的过程。

2.Momentum的数学原理

        M o m e n t u m Momentum Momentum指的是运动物体的作用效果,表示为物体的质量和速度的乘积,所以我们需要引入新的变量—— v 、 α v、\alpha vα。于是动量在我们的梯度下降中,用数学的描述方法便为:
v ← α v − η ∂ L ∂ W v\leftarrow \alpha v-\eta\frac{\partial L}{\partial W} vαvηWL
W ← W + v W\leftarrow W+v WW+v

3.Momentum的实现

class Momentum:

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None
        
    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():                                
                self.v[key] = np.zeros_like(val)
                
        for key in params.keys():
            self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] 
            params[key] += self.v[key]

4.Momentum的优势

       这个新变量 v v v的意义表示了物体在这个梯度方向上受的力,在这个力的作用下,物体表现出相应的速度,并更新相应的梯度,这个梯度的更新路径就像小球在光滑的碗底滚动一样,和SGD相比,“之”字形的程度大大的降低了。这是因为 x x x轴方向的力小,但一直是同一个方向的力,所以会有一定的加速,但 y y y方向是在谷底两侧反复变化的,两个方向的力相互抵消,但又不均匀,速度就会变缓,那么“之”字的幅度就会变小,拐角就会变圆。
让我们来看看Momentum的实际效果:
在这里插入图片描述
和SGD的对比之下确实达到了我们需要的效果。

四、AdaGrad优化方法

1.AdaGrad是什么

       在前面的几种方法里,我们做的优化都是争对 W W W的,那我们还有什么能值得优化的方向吗?想一下我们最基础的模型里还有哪个参数?那就是我们的 学习率 η \eta η。所以AdaGrad就是一种针对 η \eta η的优化方法。
       在有关学习率的优化中,其中一种就是学习率衰减 ( l e a r n i n g   r a t e   d e c a y ) (learning\ rate\ decay) (learning rate decay)。他的思路是一开始训练的时候因为离“山谷”较远,所以迈的步子可以大一点,扩张点说,可以适当的跑一下,而不用担心过头。但随着向山谷的前进后,离山谷越来越近,那步子就要越来越小,谨防越过山谷了。

2.AdaGrad的数学原理

       AdaGrad会为每一个权重 W W W适当的调整学习率,而调整的依据就是我们离山谷还有多远,当然由于我们目前不知道准确的山谷位置,所以我们猜测离山谷的距离肯定不能用 当 前 位 置 − 谷 底 { 当前位置}-谷底
       所以我们只能记住以前走过的距离,假设我们是正确的向着谷底走的,那我们走的距离和越大,那就离谷底越近。
       这就需要我们又引入一个新的变量 h h h来登记我们走过的路程。于是AdaGrad的数学表述为:
h ← h + ∂ L ∂ W ⊙ ∂ L ∂ W h\leftarrow h+\frac{\partial L}{\partial W}\odot \frac{\partial L}{\partial W} hh+WLWL
W ← W − η 1 h + 1 0 − 7 ∂ L ∂ W W\leftarrow W-\eta\frac1{\sqrt h+10^{-7}}\frac{\partial L}{\partial W} WWηh +1071WL
        h h h中保留了以往全部权重的一个平方和,在更新权重时,步伐大小取决于学习率 η \eta η 1 h \frac1{\sqrt h} h 1。这就意味着在权重的更新中,变动大的权重就会有较大的 h h h而较大的 h h h就会使 η 1 h ∂ L ∂ W \eta\frac1{\sqrt h}\frac{\partial L}{\partial W} ηh 1WL变小,也就相当于减小了学习率。

3.AdaGrad的实现

class AdaGrad:

    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None
        
    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

4.AdaGrad的优势

       让我们来看一下我们的AdaGrad的实际效果:
在这里插入图片描述
       由图可知,函数的取值高效的向着最小值移动。由于 y y y轴方向上的梯度较大,因此第一次的梯度变动较大,但他很快就收到了来自 h h h的制约,接下来在 y y y上的变动越来越小。

五、Adam优化方法

1.Adam是什么

       Adam是相对于AdaGrad提出的,他对于AdaGrad的意义就像Momentum对于SGD的意义一样,为了让僵硬的前者变的更为的平滑。所以你可以简单的理解成Adam是AdaGrad与Momentum的一个结合体。

2.Adam的数学原理

       因为我们已经在上面详细的介绍了AdaGrad和Momentum,所以这里就不再赘述直接描述Adam的数学表达形式:
i t e r = i t e r + 1 , l r t = l r t ∗ 1 − β 2 i t e r 1 − β 1 i t e r iter=iter+1,lr_t = lr_t*\frac{\sqrt {1-\beta_2^{iter}}}{1-\beta_1^{iter}} iter=iter+1,lrt=lrt1β1iter1β2iter
m = m + ( 1 − β 1 ) ∗ ( ∂ L ∂ W − m ) m = m+(1-\beta_1)*(\frac{\partial L}{\partial W}-m) m=m+(1β1)(WLm)
v = v + ( 1 − β 2 ) ∗ ( ∂ L ∂ W ⊙ ∂ L ∂ W − v ) v = v+(1-\beta_2)*(\frac{\partial L}{\partial W}\odot\frac{\partial L}{\partial W}-v) v=v+(1β2)(WLWLv)
W ← W − l r t m v + 1 0 − 7   W\leftarrow W-lr_t\frac{m}{\sqrt v+10^{-7}}\ WWlrtv +107m 
{ β 2 = 0.999 β 1 = 0.9 \bigg\{^{\beta_1=0.9}_{\beta_2=0.999} {β2=0.999β1=0.9

3.Adam的实现

class Adam:

    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.iter = 0
        self.m = None
        self.v = None
        
    def update(self, params, grads):
        if self.m is None:
            self.m, self.v = {}, {}
            for key, val in params.items():
                self.m[key] = np.zeros_like(val)
                self.v[key] = np.zeros_like(val)
        
        self.iter += 1
        lr_t  = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)         
        
        for key in params.keys():
            self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
            self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])
            params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

4.Adam的优势

       让我们来看一下我们的Adam的实际效果:
在这里插入图片描述
       在AdaGrad中融入了Momentum后,也起到了对AdaGrad摆动幅度和拐角圆滑度的一个极大提高。

六、汇总对比

       在今天的教学中,我们较好的实现了4个常用优化器,下面是他们四个的直观比较。
在这里插入图片描述
       虽然看起来这4个算法一个比一个好,但其实他们各自有自己适应的一个场景以及他们的优势所在,所以不能简单的根据函数寻道的一个轨迹长度来判断优化器的好坏。
       当然为了能更好的给大家直观感受几个优化器在不同的场景中的好坏,我这边选取了一个手写数字识别的算法进行代入几个优化器来描述优化器的好坏。
在这里插入图片描述
可以看到在这个场景中,AdaGrad的效果是最好的,而不是我们猜测的Adam。


总结

       优化器是神经网络训练中非常重要的一个部分,因为网络的训练其实就是一个调整权重的过程,而一个神经网络最多的运行时间都是花在梯度更新上,一个好的优化器和一个较不适应的优化器的选择会直接导致训练精度、时间的极大幅度的变化。
       而很可惜的是现在还并不存在一个万能的优化器,也没有选择何种优化器的准确定义,所以对优化器的选择更多的是凭借读者自己的经验和尝试

  • 6
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Foxerity

看心情就好啦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值