美文网首页鱼的深度学习
深度学习(六)损失函数和优化器

深度学习(六)损失函数和优化器

作者: 升不上三段的大鱼 | 来源:发表于2020-07-31 20:28 被阅读0次

    思考一下,神经网络是靠什么来判断得到的结果是好还是坏呢?

    答案是通过比较神经网络自己预测的输出与真实标签的差距,也就是Loss函数。从数学上推导loss函数可以看这里

    为了找到最小的loss,通常采用的是梯度下降(Gradient Descent)的方法。

    记loss为 L(w) = \frac{1}{M}\sum_{m=1}^{M} L(w,x_m,y_m), M 是样本的数量。

    一、梯度下降 Gradient Descent

    梯度下降的思想是计算loss的梯度, 并取当前点的梯度值的负数,由梯度的定义可以直到,负的梯度指向区域最小值,如果是正值就是区域最大值。步长由学习率\eta决定,k+1次的权重值就是第k次的权重减去学习率乘梯度:
    w^{k+1} = w^k - \eta\nabla L(w^{k},x,y)

    By Gradient_descent.png: The original uploader was Olegalexandrov at English Wikipedia.derivative work: Zerodamage - This file was derived from: Gradient descent.png:, Public Domain, https://commons.wikimedia.org/w/index.php?curid=20569355

    1. 批梯度下降 Batch gradient descent

    上面的公式使用了数据集里的所有的样本来计算梯度,这种方法被称为批梯度下降(Batch gradient descent)。
    如果我们每次迭代只计算一次梯度,那整体梯度下降的就太慢了,并且可能因为数据量太大,内存放不下。不过对于凸误差面,批梯度下降可以保证收敛到全局最小值,对于非凸面,可以保证收敛到局部最小值。
    w = w- \eta\nabla_w L(w)

    2. 随机梯度下降 Stochastic gradient descent

    随机梯度下降每次只用一个样本更新参数,因此梯度下降的非常快,但不是很稳定。SGD不会像批梯度下降一样向最优点逼近,可能会在最优点附近震荡。不过也有人说如果逐渐降低学习率,SGD也会想BGD一样收敛到全局最小值或者局部最小值。
    w = w- \eta\nabla_w L(w;x_i, y_i)

    3. Mini-batch gradient descent

    Mini-batch gradient descent是每次用B个样本来更新参数,B=1的时候是SGD,B=M的时候就是BGD。这样做可以得到一个更加稳定的梯度下降,同时也减少了需要计算的参数,提高了优化效率。一般来说B会取2的幂,比如2,4,8,16,32,64,128等,有利于GPU的加速。
    w = w- \eta (\frac{1}{B}\nabla_w \sum_{b=1}^B L(w;x_b, y_b))
    Mini-batch gradient descent 是现在最常用的方法,通常说的SGD指的也就是Mini-batch gradient descent。SGD 的缺点是选择合适的学习率可能很困难。学习率太小会导致收敛缓慢,而学习率太大会阻碍收敛,并导致损失函数在最小值附近波动甚至发散。

    二、 动量 Gradient Descent With Momentum

    1. 动量 Momentum

    上面提到了SGD的一个缺点,就是在每个更新参数的时候,不一定是朝着梯度下降的方向,比如下面左边的图,我们更希望在水平方向上下降的快一点,而在垂直方向上的变化量小一点。Momentum的作用就是在相关方向上加速SGD并且抑制震荡。
    如果我们增加一个变量来储存上一步的更新向量:
    v^{(k)} = \mu v^{k-1} - \eta \nabla L(w^{k})
    w^{k+1}=w^{k}+v^{k}
    通常取\mu={0.9,0.95,0.99}

    SGD with &without momentum
    接下来详细讲一下momentum的作用:




    梯度向量
    从图上可以看到蓝色的箭头是不加动量时参数要指向的方向, 红色箭头是上一次的梯度下降指向的方向,如果是区域最小值在水平方向的右边,那么绿色箭头会比蓝色箭头更快到达区域最小值。
    Momentum的优点在于可以抑制震荡,加速收敛,缺点是需要调整学习率。

    2. Nesterov Accelerated Gradient

    Nesterov Accelerated Gradient 在momentum上做了一些改变, 计算了下一次参数的loss,计算了梯度的二阶梯度,所以比SGD+momentum收敛地更快[5]。
    v^k = \mu v^{k-1}- \eta \nabla L(w^k + \mu v^{k-1})
    w^{k+1}=w^k+v^k
    这块数学没怎么懂,总之它收敛比momentum更快。

    3. AdaGrad

    上面的优化方法都用的是固定的学习率,缺点就是需要自己调参数找最适合的学习率,所以我们要想办法,让学习率在优化过程中一起优化了。
    AdaGrad 全称Adpative Gradient,方法如下, 这里的*代表的是矩阵元素间的乘法:
    g^k = \nabla L(w^k)
    r^k =r^{k-1}+g^k * g^k
    w^{k+1}=w_{k}- \frac{\eta}{\sqrt{r^k}+ \varepsilon} * g^k
    可以看到与SGD的更新规则相比,上面公式里与学习率相同作用的,是一个基于梯度和上一次迭代里学习率的值。
    Adagrad的主要好处之一是,它无需手动调整学习速度。 大多数实现使用默认值0.01并将其保留为默认值。缺点就是学习率下降的太快了。

    4. RMSprop

    g^k = \nabla L(w^k)
    r^k = \rho r^{k-1}+(1- \rho)g^k * g^k
    w^{k+1}=w_{k}- \frac{\eta}{\sqrt{r^k}+ \varepsilon} * g^k
    通常取\rho = 0.9, \eta=0.001
    RMSprop的优点是不会让学习率单调变小。

    5. Adam

    Adaptive Moment Estimation
    g_k = \nabla L(w^k)
    v_k=\mu v^{k-1}+(1- \mu)g^k
    r^k = \rho r^{k-1} +(1-\rho)g^k * g^k
    通常\mu = 0.9, \rho=0.999, \eta=0.001
    Adam 看起来是RMSprop和 momentum的结合版, 一般推荐使用Adam来训练。

    6. AMSGrad

    Adam 有时候被观察到不能收敛到最优解,于是就有人提出了解决方案:
    v^{k}=max(v^{k-1}, v^k)
    是不是比Adam更好还不确定。

    小结

    最常用的两种优化方法是SGD+momentum 和Adam,两种方法各有优缺点:

    • SGD + Nesterov momentum + 学习率衰减
      优点: 可以稳定的收敛,现在依旧被许多paper使用
      缺点:需要调整学习率衰减
    • Adam
      优点:自适应的学习率
      缺点:loss曲线很难解释

    三、Pytorch实现

    pytorch实现SGD和Adam

    import torch.optim as optim
    
    optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
    # optimizer = optim.Adam(model.parameters(), lr=learning_rate)
    
    for input, target in dataset:
        # set zero gradient
        optimizer.zero_grad()
        output = model(input)
        loss = loss_fn(output, target)
        loss.backward()
        # take optimization step
        optimizer.step()
    

    如果想只优化特定的一些参数:

    params_to_update = []
    for name, param in self.model.named_parameters():
        if param.requires_grad == True:
            params_to_update.append(param)
    

    为不同参数设置不同的学习率

    optimizer = torch.optim.SGD([
                {'params': model.parameters()},
                {'params': model.s, 'lr':1e-3}
            ], lr=learning_rate)
    

    为不同的参数设置不同的优化器:

    class MultipleOptimizer(object):
        def __init__(self, *op):
            self.optimizers = op
    
        def zero_grad(self):
            for op in self.optimizers:
                op.zero_grad()
    
        def step(self):
            for op in self.optimizers:
                op.step()
    
     optimizer = MultipleOptimizer(optim.SGD([model.s], lr=0.01),
                  optim.Adam(model.parameters(), lr=learning_rate))
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    

    如果想要根据训练的表现调节学习率,可以使用torch.optim.lr_scheduler里的函数,可以根据各种条件调节学习率的变化。

    Reference

    1. 几种梯度下降方法对比
    2. An overview of gradient descent optimization algorithms
    3. Gradient Descent with Momentum
    4. pytorch文档
    5. 比Momentum更快:揭开Nesterov Accelerated Gradient的真面目

    相关文章

      网友评论

        本文标题:深度学习(六)损失函数和优化器

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