• 狗都会用的余弦退火(CosineAnnealingLR)学习率调节算法讲解


    今天刚好有空,来讲讲deep learning中学习率调节的常见技巧:余弦退火算法。

    学习率调整

    学习率是最影响性能的超参数之一,在我们的大多数的炼丹过程中,遇到 loss 变成 NaN 的情况大多数是由于学习率选择不当引起的(当然也有可能是loss实现过程有问题)。

    过大的学习率容易造成loss NaN,太小的学习率会导致训练过程非常缓慢。所以在实际训练中,我们都会采用学习率调整的方法。调节的策略有很多,但都大致都遵从一个原则,即学习率在开始训练时很大,在训练过程中逐渐变小,在结束时达到一个最小值

    常见的方法,如:StepLR,设置好固定的训练节点,到达这个节点就将学习率下降一个固定值。LinearLR,以一个初始值和一个结束值做线性的下降。

    Warm up

    那么我们今天要介绍的是,余弦先退火算法CosineAnnealingLR。它的思路不同于以往的学习率调整策略。作者认为神经网络在刚开始训练时,并非如同理想的情况(见下图)一样,只需要确定一个方向即可。模型参数在初始化时,是非常不稳定的,因此在刚开始时需要选用小的学习率。

    在这里插入图片描述

    但是小的学习率会让训练过程非常缓慢,因此这里会采用以较低学习率逐渐增大至较高学习率的方式实现网络训练的“热身”阶段,称为 warm up stage。

    CosineAnnealingLR

    如果我们使得网络训练的 loss 最小,那么一直使用较高学习率是不合适的,因为它会使得权重的梯度一直来回震荡,很难使训练的损失值达到全局最低谷。所以学习率还是需要下降,可以通过余弦函数来降低学习率。余弦函数中随着x的增加余弦值首先缓慢下降,然后加速下降,再次缓慢下降。这种下降模式能和学习率配合,以一种十分有效的计算方式来产生很好的效果。

    上述过程就称为余弦退火。

    在这里插入图片描述

    代码实现

    pytorch实现

    import torch.optim as optim
    
    class CosineAnnealingWarmupRestarts(optim.lr_scheduler._LRScheduler):
        """
            optimizer (Optimizer): Wrapped optimizer.
            first_cycle_steps (int): First cycle step size.
            cycle_mult(float): Cycle steps magnification. Default: -1.
            max_lr(float): First cycle's max learning rate. Default: 0.1.
            min_lr(float): Min learning rate. Default: 0.001.
            warmup_steps(int): Linear warmup step size. Default: 0.
            gamma(float): Decrease rate of max learning rate by cycle. Default: 1.
            last_epoch (int): The index of last epoch. Default: -1.
        """
    
        def __init__(self,
                     optimizer: torch.optim.Optimizer,
                     first_cycle_steps: int,
                     cycle_mult: float = 1.,
                     max_lr: float = 0.1,
                     min_lr: float = 0.001,
                     warmup_steps: int = 0,
                     gamma: float = 1.,
                     last_epoch: int = -1
                     ):
            assert warmup_steps < first_cycle_steps
    
            self.first_cycle_steps = first_cycle_steps  # first cycle step size
            self.cycle_mult = cycle_mult    # cycle steps magnification
            self.base_max_lr = max_lr   # first max learning rate
            self.max_lr = max_lr    # max learning rate in the current cycle
            self.min_lr = min_lr    # min learning rate
            self.warmup_steps = warmup_steps    # warmup step size
            self.gamma = gamma  # decrease rate of max learning rate by cycle
    
            self.cur_cycle_steps = first_cycle_steps    # first cycle step size
            self.cycle = 0  # cycle count
            self.step_in_cycle = last_epoch     # step size of the current cycle
    
            super(CosineAnnealingWarmupRestarts, self).__init__(optimizer, last_epoch)
    
            # set learning rate min_lr
            self.init_lr()
    
        def init_lr(self):
            self.base_lrs = []
            for param_group in self.optimizer.param_groups:
                param_group['lr'] = self.min_lr
                self.base_lrs.append(self.min_lr)
    
        def get_lr(self):
            if self.step_in_cycle == -1:
                return self.base_lrs
            elif self.step_in_cycle < self.warmup_steps:
                return [(self.max_lr - base_lr)*self.step_in_cycle / self.warmup_steps + base_lr for base_lr in self.base_lrs]
            else:
                return [base_lr + (self.max_lr - base_lr) \
                        * (1 + math.cos(math.pi * (self.step_in_cycle-self.warmup_steps) \
                                        / (self.cur_cycle_steps - self.warmup_steps))) / 2
                        for base_lr in self.base_lrs]
    
        def step(self, epoch=None):
            if epoch is None:
                epoch = self.last_epoch + 1
                self.step_in_cycle = self.step_in_cycle + 1
                if self.step_in_cycle >= self.cur_cycle_steps:
                    self.cycle += 1
                    self.step_in_cycle = self.step_in_cycle - self.cur_cycle_steps
                    self.cur_cycle_steps = int((self.cur_cycle_steps - self.warmup_steps) * self.cycle_mult) + self.warmup_steps
            else:
                if epoch >= self.first_cycle_steps:
                    if self.cycle_mult == 1.:
                        self.step_in_cycle = epoch % self.first_cycle_steps
                        self.cycle = epoch // self.first_cycle_steps
                    else:
                        n = int(math.log((epoch / self.first_cycle_steps * (self.cycle_mult - 1) + 1), self.cycle_mult))
                        self.cycle = n
                        self.step_in_cycle = epoch - int(self.first_cycle_steps * (self.cycle_mult ** n - 1) / (self.cycle_mult - 1))
                        self.cur_cycle_steps = self.first_cycle_steps * self.cycle_mult ** (n)
                else:
                    self.cur_cycle_steps = self.first_cycle_steps
                    self.step_in_cycle = epoch
    
            self.max_lr = self.base_max_lr * (self.gamma**self.cycle)
            self.last_epoch = math.floor(epoch)
            for param_group, lr in zip(self.optimizer.param_groups, self.get_lr()):
                param_group['lr'] = lr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    tensorflow实现

    from tensorflow.keras import optimizers
    import numpy as np
    
    class CosineAnnealingLRScheduler(optimizers.schedules.LearningRateSchedule):
        def __init__(self, total_step, lr_max, lr_min=0, warmth_rate=0):
            super(CosineAnnealingLRScheduler, self).__init__()
            self.total_step = total_step
    
            if warmth_rate == 0:
                self.warm_step = 1
            else:
                self.warm_step = int(self.total_step * warmth_rate)
    
            self.lr_max = lr_max
            self.lr_min = lr_min
    
        @tf.function
        def __call__(self, step):
            if step < self.warm_step:
                lr = self.lr_max / self.warm_step * step
            else:
                lr = self.lr_min + 0.5 * (self.lr_max - self.lr_min) * (1.0 + tf.cos((step - self.warm_step) / self.total_step * np.pi))
    
            return lr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
  • 相关阅读:
    编程学:关于同类词的等长拼写问题
    1218. 最长定差子序列
    在 Elasticsearch 中使用 PyTorch 进行现代自然语言处理的介绍
    四旋翼无人机学习第8节--OpenMV电路分析
    【python初学者日记】用PIL批量给HEIC格式的照片,添加拍摄日期、拍摄地点的水印戳
    如果忘记了 iPhone 密码
    再次登顶 GitHub,阿里大牛用 758 页讲清微服务 K8S 响应式的文案,真的太香了
    软件项目管理 6.3.用例点估算法
    IntelliJ IDEA 开发Vue项目 ElementUI 未知HTML标记 提示问题的解决
    设计模式-01 设计模式简介之分类
  • 原文地址:https://blog.csdn.net/weixin_42392454/article/details/127766771