深度学习optimizer:SGD,Momentum,AdaGrad,RMSProp,Adam源代码自编写及pytorch.optimizer介绍

        随着深度学习的兴起,其算法的核心:梯度下降算法正不断发展,本文将简要介绍几种主流的optimizer:SGD(Stochastic Gradient Descent),MomentumAdaGrad(Adaptive Gradient Algorithm),RMSProp(Root Mean Square prop )和Adam(Adaptive Moment Estimation)。对于每一种optimizer,将从四个方面展开:

        ①简要介绍:算法的基本思路,优缺点等;

        ②计算过程:主要以数学表达式的形式呈现;

        ③Python原生实现:不借助任何外部库,实现各种optimizer

        ④Pytorch对应介绍:每一种optimizer在深度学习框架pytorch中都有与之对应的命令,可以直接调用,将对之进行详细介绍

        在开始之前,首先要讲清楚的一件事情是:无论是任何optimizer,其核心都是对神经网络中参数(需要进行学习的参数,例如全连接神经网络中的权重参数和偏置参数)的梯度进行不同方式的使用,从而更新参数。这里不同方式的使用就是不同optimizer的区别所在。因此,所有的opitmizer都是在已知参数梯度信息(grads)的前提下展开的。

        再啰嗦一嘴梯度的意义:从数学上的直观理解是,梯度的正负指明了在当前参数下,优化模型应该前进的方向,梯度的大小反映了在当前参数下,模型优化的快慢。

        接下来进入正文:

1.SGD(Stochastic Gradient Descent)

①基本介绍

        SGD(Stochastic Gradient Descent)全称:随机梯度下降法。实际上,SGD对梯度(grads,后文全部用grads代替梯度)的使用非常简单,仅仅是使用梯度本身,再乘以learning rate直接来更新参数。(这里我不想过分的去探究SGD优化器背后的数学意义,而仅仅描述优化器本身对梯度的使用情况)

②计算过程

        假设需要更新的参数为W,则SGD的做法如下所示:

W\leftarrow W-\eta \frac{\partial L}{\partial W}

③Python原生实现

       源代码:        

class SGD:
    def __init__(self, lr=0.01): # 默认的learning rate为0.01
        self.lr = lr

    def update(self, params, grads): #  需要更新的参数为Params,params的梯度信息保存在grads中
        for keys in params.keys():
            params[keys] -= self.lr * grads[keys] #  更新参数

  将SGD以类的方式实现,默认的learning rate为0.01,更新参数时的调用方式为:

SGD.updata(params, grads)

④Pytorch对应介绍

在Pytorch中调用SGD的方式为:

torch.optim.SGD(params, lr=0.01, momentum=0, dampening=0, weight_dacay=0, nesterov=False)

这里与SGD有关的仅仅是前两个参数,后边的保持默认就与③中的源代码作用相同了。

参数介绍:

params:所搭建的神经网络模型中需要更新的参数。

lr:学习率

对于参数:momentumdampeningweight_dacaynesterov这里不做介绍,原因是:在Pytorch框架中,SGD优化器不仅仅是我们上述介绍的简单的SGD,它将优化其momentum,nesterov都融合进了一个类,只是命名时,将这个大类命名为了SGD,在后续的介绍中,再介绍momentum。

2.Momentum

①基本介绍

    Momentum的翻译是动量,算法momentum的思想就是模拟物理学中的动量,让参数的更新不仅仅与当前的梯度更新方向有关,同时与上一步梯度更新方向有关。虽然这里使用了动量,但仅仅是使用了动量的思想,仔细观察算法的核心计算过程,会发现其与物理学上的动量P=Mv没有任何关系。

②计算过程

        第一步,获取参数需要更新的大小,实际上是获取更新参数的方向和大小,为上一次更新方向和梯度向量的向量和,\alpha的大小衡量上一步更新在此次更新中的重要程度。

    v\leftarrow \alpha v-\eta \cdot grads

        第二步:更新神经网络模型中的参数

W\leftarrow W + v

③Python原生实现

        源代码:

class Momentum:
    def __init__(self, lr=0.1):
        self.lr = lr
        self.alpha = 0.9 #  默认α为0.9
        self.v = None # 用来记录上一步更新的方向和大小

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for keys, values in params.items():
                self.v[keys] = np.zeros_like(values) # 根据网络中参数的形状,对V进行初始化

        for keys in params.keys(): # 更新网络模型中需要学习的参数
            self.v[keys] = self.alpha * self.v[keys] - self.lr * grads[keys]
            params[keys] += self.v[keys]

        将momentum以python中类(class)的形式实现。

        超参数:learning_rate=0.1,\alpha=0.9。

        调用momentum进行模型参数更新时的调用格式:

Momentum.updata(params, grads)

④Pytorch对应介绍

  在Pytorch框架中没有专门的momentum优化器,而是将其写入到了SGD优化器内。

torch.optim.SGD(params, lr=0.01, momentum=0, dampening=0, weight_dacay=0, nesterov=False)

这里与momentum有关的是前三个参数,之后的参数保持默认。

参数介绍:

params:所搭建的神经网络模型中需要更新的参数。

lr:学习率

momentum:相当于上述源代码中的\alpha,表示上一步梯度在此次参数更新过程的重要程度。在momentum=0时,退化为SGD。

3.AdaGrad(Adaptive Gradient Algorithm)

①基本介绍

       之前介绍的两种optimizer:SGD和momentum,在整个神经网络的训练过程中,learning rate始终保持不变。这样带来的问题是,当梯度值非常小时,采用小的学习率,会使网络的训练非常的低效。AdaGrad从learning rate的角度来考虑问题,基本的思路是:当梯度的值较大时,采用较小的学习率,当梯度的值较小时,采用较大的学习率,从而使得神经网络的训练过程变得高效。AdaGrad在Adaptive Subgradient Methods for Online Learning and Stochastic Optimization中提出,有兴趣的读者可以研究原文。

②计算过程

        第一步,计算h,对于神经网络中的每一个参数,h为整个训练过程此参数梯度的平方和。

 h \leftarrow h + grads\bigodot grads   

        第二步:更新神经网络模型中的参数

W\leftarrow W - \frac{lr}{\sqrt{h}}\cdot grads

③Python原生实现

        源代码:

class AdaGrad:
    def __init__(self, lr=0.01):
        self.lr = lr
        self.r = None

    def update(self, params, grads):
        eps = 1e-7 # 引入一个极小值,防止被除数为零报错
        if self.r is None:
            self.r = {} # 将r保存为字典型变量
            for keys, values in params.items():
                self.r[keys] = np.zeros_like(values) # 根据神经网络中需要被更新的参数的形状初始化r

        for keys in params.keys(): # 更新参数
            self.r[keys] = self.r[keys] + grads[keys]**2
            params[keys] -= self.lr * grads[keys] / (np.sqrt(self.r[keys]) + eps)

        AdaGrad中的超参数只有learning rate,这里默认为0.01。使用AdaGrad更新参数时,调用格式与前述的SGD与Momentum没有任何区别,这里不再赘述。

④Pytorch对应介绍

 在Pytorch中调用AdaGrad的方式为:

torch.optim.Adagrad(params, lr=0.01, lr_decay=0, weight_decay=0, initial_accumulator_value=0, eps=1e-10)

参数介绍:

params:神经网络中需要更新的参数。

lr:学习率,需给定

lr_decay(可选):有关学习率衰减的参数。

weight_decay(可选):权值衰减系数。

initial_accumulator_value(可选):初始的累加h(②中的h)值。

eps:一个极小值,为了防止分母为0而引入。

4.RMSProp(Root Mean Square prop )

①基本介绍

        RMSProp的提出有意思的是,这种优化器的设计并不是发表在某一篇论文或者杂志上的。他是由Hinton教授在自己的课堂上提出的。算法的每一步都对之前的梯度进行一种累计的加权平均。正式版首次出现在: Adaptive Subgradient Methods for Online Learning and Stochastic Optimization

②计算过程

        第一步,计算r,对神经网络中的每一个参数计算累计加权平均。

 r\leftarrow \alpha r+(1-\alpha )\cdot g^{2}   

        第二步:更新神经网络模型中的参数:

W\leftarrow W-\frac{lr}{\sqrt{r}}\cdot g

③Python原生实现

        源代码:

class RMSProp:
    def __init__(self, lr=0.01): # 默认学习率为0.01
        self.lr = lr
        self.r = None
        self.alpha = 0.9 # 超参数,设为0.9

    def update(self, params, grads):
        eps = 1e-7 # 为防止分母为零对分母加上一个极小值
        if self.r is None: # 根据神经网络中参数的形状初始化r
            self.r = {}
            for keys, values in params.items():
                self.r[keys] = np.zeros_like(values)

        for keys in params.keys(): # 更新参数
            self.r[keys] = self.alpha * self.r[keys] + (1 - self.alpha) * grads[keys]**2
            params[keys] -= self.lr * grads[keys] / (np.sqrt(self.r[keys]) + eps)

        这里的调用方式与之前介绍的优化器没有任何区别,这里不再赘述。

④Pytorch对应介绍

 在Pytorch中调用RMSProp的方式为:

torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)

参数介绍:

params:神经网络中需要学习的参数;

lr:学习率;

alpha:平滑参数;

eps:极小值

weight_decay:权值衰减参数

momentum,centered与此处的RMSProp没有任何关系,这里不做介绍。

5.Adam(Adaptive Moment Estimation)

①基本介绍

        AdamRMSProp的基础上进行了速度滑动平均和偏差修正。具体的内容可以参考原始的论文:《Adam:A Method for Stochastic Optimization》

②计算过程

        第一步:计算动量平均和速度平均:

m_{t}\leftarrow \beta _{1}\cdot m_{t-1}+\left ( 1-\beta _{1} \right )\cdot g_{t}

v_{t}\leftarrow \beta _{2}\cdot v_{t-1}+(1-\beta _{2})\cdot g_{2}^{t}

        第二步:修正过程

\hat{m_{t}}\leftarrow m_{t}/(1-\beta _{t}^{1})

\hat{v}_{t}\leftarrow v_{t}/(1-\beta _{t}^{2})

        第三步:更新参数

\theta _{t}\leftarrow \theta _{t-1}-\alpha \cdot \hat{m}_{t}/(\sqrt{\hat{v}_{t}})

③Python原生实现

        源代码:

class Adam:
    def __init__(self, lr=0.001):
        self.lr = lr
        self.beta1 = 0.9
        self.beta2 = 0.999
        self.eps = 1e-7
        self.m = None
        self.v = None
        self.step = 0

    def update(self, params, grads):
        if self.m is None:
            self.m = {}
            self.v = {}
            for keys, values in params.items():
                self.m[keys] = np.zeros_like(values)
                self.v[keys] = np.zeros_like(values)

        self.step += 1
        lr_t = self.lr * np.sqrt(1.0 - self.beta2**self.step) / (1.0 - self.beta1**self.step)
        for keys in params.keys():
            self.m[keys] = (self.beta1 * self.m[keys] + (1 - self.beta1) * grads[keys])
            self.v[keys] = (self.beta2 * self.v[keys] + (1 - self.beta2) * grads[keys]**2)
            params[keys] -= lr_t * self.m[keys] / (np.sqrt(self.v[keys]) + self.eps)

        这里的调用方式与之前介绍的优化器没有任何区别,这里不再赘述。

④Pytorch对应介绍

         在Pytorch中调用Adam的方式为:

torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0, amsgrad=False)

参数介绍:

params:神经网络中需要学习的参数;

lr:学习率

betas:平滑常数\beta _{1}\beta _{2}

eps:极小值,防止分母为零;

weight_decay:权值衰减

amsgrad:与Adam没有关系,不做解释。

你可能感兴趣的:(深度学习,机器学习,神经网络,python,pytorch)