简易机器学习笔记(六)不同优化算法器

发布时间 2024-01-02 23:38:55作者: 轩先生。

前言

我们之前不是说了有关梯度下降公式的事嘛,就是那个
在这里插入图片描述

这样梯度下降公式涉及两个问题,一是梯度下降的策略,二是涉及到参数的选择,如果我们选择固定步长的时候,就会发现我们求的值一直在最小值左右震荡,很难选择到我们期望的值。

假设上图中,x0为我们期望的极小值,yB = xA - yA'xA的时候,xB实际上蹿到了极小值的右侧去了,当我们yC = xB - yB'xB的时候,我们求的yC又跑到极小值的左边去了,反正就是一直会在极小值x0附近震荡。

这时候我们有几种常见的优化算法,这也是我们在之前的代码中看到的SGD(随机梯度下降)优化器的由来。

来自Paddlepaddle的官方文档,图示如下:

  1. SGD(随机梯度下降)
    在这里插入图片描述
  2. Momentum
    引入物理“动量”的概念,累积速度,减少震荡,使参数更新的方向更稳定。

在这里插入图片描述

每个批次的数据含有抽样误差,导致梯度更新的方向波动较大。如果我们引入物理动量的概念,给梯度下降的过程加入一定的“惯性”累积,就可以减少更新路径上的震荡,即每次更新的梯度由“历史多次梯度的累积方向”和“当次梯度”加权相加得到。历史多次梯度的累积方向往往是从全局视角更正确的方向,这与“惯性”的物理概念很像,也是为何其起名为“Momentum”的原因。类似不同品牌和材质的篮球有一定的重量差别,街头篮球队中的投手(擅长中远距离投篮)喜欢稍重篮球的比例较高。一个很重要的原因是,重的篮球惯性大,更不容易受到手势的小幅变形或风吹的影响。

  1. AdaGrad
    根据不同参数距离最优解的远近,动态调整学习率。学习率逐渐下降,依据各参数变化大小调整学习率。

在这里插入图片描述
通过调整学习率的实验可以发现:当某个参数的现值距离最优解较远时(表现为梯度的绝对值较大),我们期望参数更新的步长大一些,以便更快收敛到最优解。当某个参数的现值距离最优解较近时(表现为梯度的绝对值较小),我们期望参数的更新步长小一些,以便更精细的逼近最优解。类似于打高尔夫球,专业运动员第一杆开球时,通常会大力打一个远球,让球尽量落在洞口附近。当第二杆面对离洞口较近的球时,他会更轻柔而细致的推杆,避免将球打飞。与此类似,参数更新的步长应该随着优化过程逐渐减少,减少的程度与当前梯度的大小有关。根据这个思想编写的优化算法称为“AdaGrad”,Ada是Adaptive的缩写,表示“适应环境而变化”的意思。RMSProp是在AdaGrad基础上的改进,学习率随着梯度变化而适应,解决AdaGrad学习率急剧下降的问题。

  1. Adam
    在这里插入图片描述

由于动量和自适应学习率两个优化思路是正交的,因此可以将两个思路结合起来,这就是当前广泛应用的算法。

很显然,除开我们常用的随机梯度下降方法,每种优化的方式都有自己的优势。这主要看目标场景和数据集类型和数值的好坏。

实际代码

实际上在开发中,在Paddlepaddle库只需要修改优化器的选型即可,库本身已经做好了这些方案的分装

#仅优化算法的设置有所差别
def train(model):
    model.train()
    #调用加载数据的函数
    train_loader = load_data('train')
    
    #四种优化算法的设置方案,可以逐一尝试效果
    opt = paddle.optimizer.SGD(learning_rate=0.01, parameters=model.parameters())
    # opt = paddle.optimizer.Momentum(learning_rate=0.01, momentum=0.9, parameters=model.parameters())
    # opt = paddle.optimizer.Adagrad(learning_rate=0.01, parameters=model.parameters())
    # opt = paddle.optimizer.Adam(learning_rate=0.01, parameters=model.parameters())
    
    EPOCH_NUM = 3
    for epoch_id in range(EPOCH_NUM):
        for batch_id, data in enumerate(train_loader()):
            #准备数据
            images, labels = data
            images = paddle.to_tensor(images)
            labels = paddle.to_tensor(labels)
            
            #前向计算的过程
            predicts = model(images)
            
            #计算损失,取一个批次样本损失的平均值
            loss = F.cross_entropy(predicts, labels)
            avg_loss = paddle.mean(loss)
            
            #每训练了100批次的数据,打印下当前Loss的情况
            if batch_id % 200 == 0:
                print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))
            
            #后向传播,更新参数的过程
            avg_loss.backward()
            # 最小化loss,更新参数
            opt.step()
            # 清除梯度
            opt.clear_grad()

    #保存模型参数
    paddle.save(model.state_dict(), 'mnist.pdparams')
    
#创建模型    
model = MNIST()
#启动训练过程
train(model)