# ***************************************************************
# Copyright (c) 2023 Jittor. All Rights Reserved.
# Maintainers:
# Guowei Yang <471184555@qq.com>
# Dun Liang <randonlang@gmail.com>.
#
#
# This file is subject to the terms and conditions defined in
# file 'LICENSE.txt', which is part of this source code package.
# ***************************************************************
import jittor as jt
from jittor.optim import Optimizer
import math
[文档]
class ReduceLROnPlateau(object):
"""
在训练深度学习模型时,如果训练过程中的损失(Loss)一直不再降低则表明已进入平台期,将学习率 (Learning Rate) 降低可以有助于模型跳出局部最小值点。
ReduceLROnPlateau 类提供了这样一种策略:当损失在一定期限 (patience) 内没有降低时,降低学习率。在一定的慢冷期 (cooldown) 内,损失的变化不再触发学习率的减小。
参数:
- optimizer (``Optimizer``): 要使用的优化器.
- mode (``str``): 有两种模式 ``'min'`` 和 ``'max'``。默认为 ``'min'``,当损失不再降低时减小学习率。如果设置为 ``'max'``,当损失不再增加时减小学习率。
- factor (``float``): 学习率每次降低的倍数。默认为 ``0.1``
- patience (``int``): 当损失在这么多次迭代后没有下降时,学习率进行调整。默认为 ``10``
- verbose (``bool``): 设为 ``True`` 会在每次更新学习率时打印信息。默认为 ``False``
- threshold (``float``): 判断损失是否不再减少的阈值。只有损失减少的幅度大于该值才算作损失的减小。默认为 ``0.0001``
- threshold_mode (``str``): 损失阈值模式,``'rel'`` 或 ``'abs'`` 。在 ``'rel'`` 模式下,只要相较于上一次的损失减少了相对于上一次损失的 ``threshold`` 个百分比即算作损失的减小;在 'abs' 模式下,只有 相较于上一次的损失减少了 ``threshold`` 即算作损失的减小。默认为 ``'rel'``
- cooldown (``int``): 每次降低学习率后,会有一段慢冷期,在该期间内的损失变化不再触发学习率的减小。默认为 ``0``
- min_lr (``float`` or ``list``): 学习率的下限,可为单一浮点数或浮点数的列表。如果是浮点数,则所有参数组的学习率下限都是这个值,如果是列表,则每个参数组的学习率下限是列表中的对应值。默认为 ``0``
- eps (``float``): 学习率的最小减少值。如果新旧学习率之间的差异小于 ``eps`` ,则忽略这次更新。默认为 ``1e-8``
代码示例:
>>> import jittor as jt
>>> optimizer = jt.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
>>> scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10)
>>> for epoch in range(100):
... train(...)
... loss = validate(...)
... scheduler.step(loss)
"""
def __init__(self, optimizer, mode='min', factor=0.1, patience=10, verbose=False, threshold=1e-4, threshold_mode='rel', cooldown=0, min_lr=0, eps=1e-8):
assert factor < 1.0, "factor should be < 1.0."
assert isinstance(optimizer, Optimizer), '{} is not an Optimizer'.format(type(optimizer).__name__)
assert mode in {'min', 'max'}, 'mode ' + mode + ' is unknown!'
assert threshold_mode in {'rel', 'abs'}, 'threshold mode ' + threshold_mode + ' is unknown!'
if isinstance(min_lr, list) or isinstance(min_lr, tuple):
assert len(min_lr) == len(optimizer.param_groups), "expected {} min_lrs, got {}".format(len(optimizer.param_groups), len(min_lr))
self.min_lrs = list(min_lr)
else:
self.min_lrs = [min_lr] * len(optimizer.param_groups)
self.factor = factor
self.optimizer = optimizer
self.patience = patience
self.verbose = verbose
self.cooldown = cooldown
self.n_cd = 0
self.mode = mode
self.threshold = threshold
self.threshold_mode = threshold_mode
self.loss_best = None
self.n_bad = 0
self.eps = eps
self.last_epoch = 0
self.loss_best = math.inf if mode=="min" else -math.inf
def step(self, loss, epoch=None):
# convert `metrics` to float, in case it's a zero-dim Tensor
loss_now = float(loss)
if epoch is None:
epoch = self.last_epoch + 1
self.last_epoch = epoch
if self.better(loss_now, self.loss_best):
self.loss_best = loss_now
self.n_bad = 0
else:
self.n_bad += 1
if self.n_cd > 0:
self.n_cd -= 1
self.n_bad = 0
if self.n_bad > self.patience:
self.update_lr(epoch)
self.n_cd = self.cooldown
self.n_bad = 0
def update_lr(self, epoch):
for i, param_group in enumerate(self.optimizer.param_groups):
old_lr = float(param_group.get("lr", self.optimizer.lr))
new_lr = max(old_lr * self.factor, self.min_lrs[i])
if old_lr - new_lr > self.eps:
if param_group.get("lr")!=None:
param_group["lr"] = max(param_group["lr"] * self.factor, self.min_lrs[i])
else:
self.optimizer.lr = new_lr
if self.verbose:
print('Epoch {:5d}: reducing learning rate of group {} from {:.4e} to {:.4e}.'.format(epoch, i, old_lr, new_lr))
def better(self, a, b):
if self.mode == 'min' and self.threshold_mode == 'rel':
save = 1.0 - self.threshold
return a < b * save
elif self.mode == 'min' and self.threshold_mode == 'abs':
return a < b - self.threshold
elif self.mode == 'max' and self.threshold_mode == 'rel':
save = self.threshold + 1.0
return a > b * save
else:
return a > b + self.threshold
[文档]
class CosineAnnealingLR(object):
"""是用于实现余弦退火学习率调度策略的工具。该类实现了余弦退火学习率调度策略,其中第 :math:`t` 步的学习率计算过程如下:
.. math::
\\eta_t = \\eta_{min} + \\dfrac{1}{2}(\\eta_{max} - \\eta_{min})\\left(1+cos\\left(\\dfrac{T_{cur}}{T_{max}}\\pi\\right)\\right), &\\quad T_{cur} = (2k+1)T_{max};
\\eta_{t+1} = \\eta_{t} + \\dfrac{1}{2}(\\eta_{max} - \\eta_{min})\\left(1-cos\\left(\\dfrac{1}{T_{max}}\\pi\\right)\\right), &\\quad T_{cur} = (2k+1)T_{max}.
其中,:math:`\\eta_{max}` 是初始学习率,:math:`\\eta_{min}` 是学习率下限,:math:`T_{max}` 是最大周期数, :math:`T_{cur}` 是 SGDR 上一次重启后的周期(``epoch``)数,
通过 ``last_epoch`` 指定。特别地, ``last_epoch`` 为 ``-1``(默认)时,将学习率设置为优化器的初始学习率。
参数:
- optimizer (``Optimizer``): 优化器对象,其学习率将被调整。
- T_max (``int``): 用来计算学习率的最大周期数。
- eta_min (``float``, 可选): 学习率下限,默认为 ``0``
- last_epoch (``int``, 可选): 最后一次迭代的周期数,默认为 ``-1``
"""
def __init__(self, optimizer, T_max, eta_min=0, last_epoch=-1):
self.T_max = T_max
self.eta_min = eta_min
self.optimizer = optimizer
self.last_epoch = last_epoch
self.base_lr = optimizer.lr
self.base_lr_pg = [pg.get("lr") for pg in optimizer.param_groups]
#TODO set last_epoch is not ready
def get_lr(self, base_lr, now_lr):
if self.last_epoch == 0:
return base_lr
if (self.last_epoch - 1 - self.T_max) % (2 * self.T_max) == 0:
return (now_lr + (base_lr - self.eta_min) *
(1 - math.cos(math.pi / self.T_max)) / 2)
return ((1 + math.cos(math.pi * self.last_epoch / self.T_max)) /
(1 + math.cos(math.pi * (self.last_epoch - 1) / self.T_max)) *
(now_lr - self.eta_min) + self.eta_min)
def step(self):
self.last_epoch += 1
self.update_lr()
def update_lr(self):
self.optimizer.lr = self.get_lr(self.base_lr, self.optimizer.lr)
for i, param_group in enumerate(self.optimizer.param_groups):
if param_group.get("lr") != None:
param_group["lr"] = self.get_lr(self.base_lr_pg[i], param_group["lr"])
[文档]
class ExponentialLR(object):
"""
此类实现指数衰减学习率优化策略。在每个步骤中,将学习率乘以一个给定的系数 gamma。当 ``last_epoch`` 为 ``-1`` 时,将学习率设置为优化器的初始学习率。
参数:
- optimizer (``Optimizer``): 优化器对象,需要进行学习率衰减的优化器。
- gamma (``float``): 在每个步骤中乘以学习率的系数。
- last_epoch (``int``): 上一个周期(``epoch``)编号。默认值:``-1``
代码示例:
>>> optimizer = jt.optim.SGD(model.parameters(), lr=0.1)
>>> scheduler = ExponentialLR(optimizer, gamma=0.9)
>>> for epoch in range(100):
... train(...)
... validate(...)
... scheduler.step()
"""
def __init__(self, optimizer, gamma, last_epoch=-1):
self.optimizer = optimizer
self.gamma = gamma
self.last_epoch = last_epoch
self.base_lr = optimizer.lr
self.base_lr_pg = [pg.get("lr") for pg in optimizer.param_groups]
def get_lr(self, base_lr, now_lr):
if self.last_epoch == 0:
return base_lr
return base_lr * self.gamma ** self.last_epoch
def step(self):
self.last_epoch += 1
self.update_lr()
def update_lr(self):
self.optimizer.lr = self.get_lr(self.base_lr, self.optimizer.lr)
for i, param_group in enumerate(self.optimizer.param_groups):
if param_group.get("lr") != None:
param_group["lr"] = self.get_lr(self.base_lr_pg[i], param_group["lr"])
[文档]
class StepLR(object):
""" 实现了对学习率的周期性衰减调度,每达设定的步数时,对学习率进行一次衰减。
参数:
- optimizer (``Optimizer``) : 使用的优化器。
- step_size (``int``) : 设定的步数,即每过多少轮后,进行一次衰减。
- gamma (``float``, 可选) : 学习率衰减的系数, 默认为 ``0.1``
- last_epoch (``int``, 可选) : 上个周期(``epoch``)的编号,即初始的步数。默认为 ``-1`` ,表示从第一个周期开始调整。
代码示例:
>>> scheduler = StepLR(optimizer, step_size=30, gamma=0.1)
>>> for epoch in range(100):
... train(...)
... validate(...)
... scheduler.step()
"""
def __init__(self, optimizer, step_size, gamma=0.1, last_epoch=-1):
self.optimizer = optimizer
self.step_size = step_size
self.gamma = gamma
self.last_epoch = last_epoch
self.cur_epoch = 0
def get_gamma(self):
if self.last_epoch < 0:
if (self.cur_epoch != 0 and (self.cur_epoch + 1) % self.step_size == 0):
return self.gamma
else:
if (self.cur_epoch + 1 + self.last_epoch) % self.step_size == 0:
return self.gamma
return 1.
def get_lr(self):
return self.optimizer.lr
def step(self):
self.update_lr()
self.cur_epoch += 1
def update_lr(self):
gamma = self.get_gamma()
if gamma != 1.:
self.optimizer.lr = self.optimizer.lr * gamma
for i, param_group in enumerate(self.optimizer.param_groups):
if param_group.get("lr") != None:
param_group["lr"] = param_group["lr"] * gamma
[文档]
class MultiStepLR(object):
""" 实现了一个常见的学习率调节策略,即在预设的多个阶段(milestones)对学习率进行衰减。
参数:
- optimizer (``Optimizer``): 被包装的优化器实例。
- milestones (``list``): 预设的学习率衰减阶段。默认值:``[]``
- gamma (``float``): 学习率衰减系数,每次衰减时,学习率都会乘以这个系数。默认值:0.1
- last_epoch (``int``): 上一个 epoch 的标记,默认值:-1。
代码示例:
>>> # 当 epoch 达到 30 和 80 时学习率乘以 0.1
>>> scheduler = MultiStepLR(optimizer, milestones=[30,80], gamma=0.1)
>>> for epoch in range(100):
... train(...)
... validate(...)
... scheduler.step()
"""
def __init__(self, optimizer, milestones=[], gamma=0.1, last_epoch=-1):
self.optimizer = optimizer
self.milestones = milestones
self.gamma = gamma
self.last_epoch = last_epoch
#TODO set last_epoch is not ready
def get_gamma(self):
if (self.last_epoch in self.milestones):
return self.gamma
return 1.0
def get_lr(self):
now_lr = self.optimizer.lr
return now_lr * self.get_gamma()
def step(self):
self.last_epoch += 1
self.update_lr()
def update_lr(self):
gamma = self.get_gamma()
if gamma != 1.0:
self.optimizer.lr = self.optimizer.lr * gamma
for i, param_group in enumerate(self.optimizer.param_groups):
if param_group.get("lr") != None:
param_group["lr"] = param_group["lr"] * gamma