• 鱼书学习笔记:参数优化


    神经网络的学习的目的是找到使用使损失函数的值尽可能小的参数。这是寻找最优参数的问题,解决这个问题的过程称为最优化(optimization)。遗憾的是,神经网络的最优化问题非常难。这是因为参数空间非常复杂,无法轻易找到最优解(无法使用那种通过解数学式一下子就求得最小值的方法)。而且,在深度神经网络中,参数的数量非常庞大,导致最优化问题更加复杂。

    在之前,为了找到最优参数,鱼书使用参数的梯度(导数),沿梯度方向更新参数,并重复这个步骤多次,从而逐渐靠近最优参数,这个过程称为随机梯度下降法。

    随机梯度下降法(SGD)

    class SGD:
    
        """随机梯度下降法(Stochastic Gradient Descent)"""
    
        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] 

    lr表示学习率,这个学习率会保存为实例变量(一般事先定义为0.01或0.001)。参数params和grads是字典变量,按params['W1'],grads['W1']的形式,分别保存了权重参数和它们的梯度。

    SGD缺点:如果函数的形状非均向(anisotropic),比如呈延伸状,搜索的路径就会非常低效。SGD低效的根本原因是,梯度的方向并没有指向最小值的方向

    Momentum

    class Momentum:
    
        """Momentum SGD"""
    
        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]

    momentum*v这一项承担使物体逐渐减速的任务,对应物理上的地面摩擦或空气阻力。实例变量v会保存物体的速度。初始化时,v中什么都不保存,但当第一次调用update()时,v会以字典型变量的形式保存与参数结构相同的数据。

    和SGD相比,Momentum添加了一个阻力变量,使得梯度收敛于一个固定的方向,从而稳定了参数更新的过程

    AdaGrad

    在神经网络的学习中,学习率的值很重要。学习率过小,会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散而不能正确进行。

    在关于学习率的有效技巧中,有一种被称为学习率衰减(learning rate decay)的方法,即随着学习的进行,使学习率逐渐减小。

    逐渐减小学习率的想法,相当于将“全体”参数的学习率值一起降低。而AdaGrade[1]进一步发展了这个想法,针对“一个一个”的参数,赋予其“定制”的值

    class AdaGrad:
    
        """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)

    这里新出现了变量h,它保存了以前的所有梯度值的平方和。然后,在更新参数时,通过乘以1/sqrt(h),就可以调整学习的尺度。这意味着,参数的元素中变动较大(被大幅更新)的元素的学习率将变小。也就是说,可以按参数的元素进行学习率衰减,使变动大的参数的学习率逐渐减小。

    AdaGrad会记录过去所有梯度的平方和。因此,学习越深入,更新的幅度就越小。实际上,如果无止境地学习,更新量就会变为0,完全不再更新。为了改善这个问题,可以使用RMSPro[2]方法。

    RMSPro

    class RMSprop:
    
        """RMSprop"""
    
        def __init__(self, lr=0.01, decay_rate = 0.99):
            self.lr = lr
            self.decay_rate = decay_rate
            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] *= self.decay_rate
                self.h[key] += (1 - self.decay_rate) * grads[key] * grads[key]
                params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

    RMSPro方法并不是将过去所有的梯度一视同仁地相加,而是逐渐地遗忘过去的梯度,在做加法运算时将新梯度的信息更多地反映出来。这种操作从专业上讲,称为“指数移动平均”。

    Adam

    Momentum参照小球在碗中滚动的物理规则进行移动,AdaGrad为参数的每个元素适当地调整更新步伐。如果将这两个方法融合在一起会怎么样呢?,这就是Adam[3]方法的基本思路。

    Adam是2015年提出的新方法。它的理论有些复杂,直观地讲,就是融合了Momentum和AdaGrad的方法。通过组合前面两个方法的优点,有望实现参数空间的高效搜索。此外,进行超参数的“偏置校正”也是Adam的特征。

    class Adam:
    
        """Adam (http://arxiv.org/abs/1412.6980v8)"""
    
        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] = self.beta1*self.m[key] + (1-self.beta1)*grads[key]
                #self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2)
                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)
                
                #unbias_m += (1 - self.beta1) * (grads[key] - self.m[key]) # correct bias
                #unbisa_b += (1 - self.beta2) * (grads[key]*grads[key] - self.v[key]) # correct bias
                #params[key] += self.lr * unbias_m / (np.sqrt(unbisa_b) + 1e-7)

    参考资料:

    [1]Adaptive Subgradient Methods forOnline Learning and Stochastic Optimization

    https://www.jmlr.org/papers/volume12/duchi11a/duchi11a.pdf

    [2]Neural    Networks    for    Machine    Learning:divide the gradient by a running average of its recent magnitude

    http://www.cs.toronto.edu/~hinton/coursera/lecture6/lec6.pdf

    [3]Adam: A Method for Stochastic Optimization

    https://arxiv.org/abs/1412.6980

  • 相关阅读:
    作业20170928--2 单元测试
    作业20170928—1代码规范,结对要求
    第三周PSP
    20170922-3 功能测试作业
    第二周PSP
    采用c#实现功能1
    linux下搭建生成HLS所需的.ts和.m3u8文件
    android编译ffmpeg+x264
    ubuntu.sh: 113: ubuntu.sh: Syntax error: "(" unexpected
    解决:[WARNING] fpm_children_bury(), line 215: child 2736 (pool default) exited on signal 15 SIGTERM after 59.588363 seconds from start
  • 原文地址:https://www.cnblogs.com/J14nWe1/p/14580911.html
Copyright © 2020-2023  润新知