|
|
|
|
|
|
|
|
|
import math |
|
from functools import partial |
|
|
|
|
|
class LRScheduler: |
|
def __init__(self, name, lr, iters_per_epoch, total_epochs, **kwargs): |
|
""" |
|
Supported lr schedulers: [cos, warmcos, multistep] |
|
|
|
Args: |
|
lr (float): learning rate. |
|
iters_per_peoch (int): number of iterations in one epoch. |
|
total_epochs (int): number of epochs in training. |
|
kwargs (dict): |
|
- cos: None |
|
- warmcos: [warmup_epochs, warmup_lr_start (default 1e-6)] |
|
- multistep: [milestones (epochs), gamma (default 0.1)] |
|
""" |
|
|
|
self.lr = lr |
|
self.iters_per_epoch = iters_per_epoch |
|
self.total_epochs = total_epochs |
|
self.total_iters = iters_per_epoch * total_epochs |
|
|
|
self.__dict__.update(kwargs) |
|
|
|
self.lr_func = self._get_lr_func(name) |
|
|
|
def update_lr(self, iters): |
|
return self.lr_func(iters) |
|
|
|
def _get_lr_func(self, name): |
|
if name == "cos": |
|
lr_func = partial(cos_lr, self.lr, self.total_iters) |
|
elif name == "warmcos": |
|
warmup_total_iters = self.iters_per_epoch * self.warmup_epochs |
|
warmup_lr_start = getattr(self, "warmup_lr_start", 1e-6) |
|
lr_func = partial( |
|
warm_cos_lr, |
|
self.lr, |
|
self.total_iters, |
|
warmup_total_iters, |
|
warmup_lr_start, |
|
) |
|
elif name == "yoloxwarmcos": |
|
warmup_total_iters = self.iters_per_epoch * self.warmup_epochs |
|
no_aug_iters = self.iters_per_epoch * self.no_aug_epochs |
|
warmup_lr_start = getattr(self, "warmup_lr_start", 0) |
|
min_lr_ratio = getattr(self, "min_lr_ratio", 0.2) |
|
lr_func = partial( |
|
yolox_warm_cos_lr, |
|
self.lr, |
|
min_lr_ratio, |
|
self.total_iters, |
|
warmup_total_iters, |
|
warmup_lr_start, |
|
no_aug_iters, |
|
) |
|
elif name == "yoloxsemiwarmcos": |
|
warmup_lr_start = getattr(self, "warmup_lr_start", 0) |
|
min_lr_ratio = getattr(self, "min_lr_ratio", 0.2) |
|
warmup_total_iters = self.iters_per_epoch * self.warmup_epochs |
|
no_aug_iters = self.iters_per_epoch * self.no_aug_epochs |
|
normal_iters = self.iters_per_epoch * self.semi_epoch |
|
semi_iters = self.iters_per_epoch_semi * ( |
|
self.total_epochs - self.semi_epoch - self.no_aug_epochs |
|
) |
|
lr_func = partial( |
|
yolox_semi_warm_cos_lr, |
|
self.lr, |
|
min_lr_ratio, |
|
warmup_lr_start, |
|
self.total_iters, |
|
normal_iters, |
|
no_aug_iters, |
|
warmup_total_iters, |
|
semi_iters, |
|
self.iters_per_epoch, |
|
self.iters_per_epoch_semi, |
|
) |
|
elif name == "multistep": |
|
milestones = [ |
|
int(self.total_iters * milestone / self.total_epochs) |
|
for milestone in self.milestones |
|
] |
|
gamma = getattr(self, "gamma", 0.1) |
|
lr_func = partial(multistep_lr, self.lr, milestones, gamma) |
|
else: |
|
raise ValueError("Scheduler version {} not supported.".format(name)) |
|
return lr_func |
|
|
|
|
|
def cos_lr(lr, total_iters, iters): |
|
"""Cosine learning rate""" |
|
lr *= 0.5 * (1.0 + math.cos(math.pi * iters / total_iters)) |
|
return lr |
|
|
|
|
|
def warm_cos_lr(lr, total_iters, warmup_total_iters, warmup_lr_start, iters): |
|
"""Cosine learning rate with warm up.""" |
|
if iters <= warmup_total_iters: |
|
lr = (lr - warmup_lr_start) * iters / float( |
|
warmup_total_iters |
|
) + warmup_lr_start |
|
else: |
|
lr *= 0.5 * ( |
|
1.0 |
|
+ math.cos( |
|
math.pi |
|
* (iters - warmup_total_iters) |
|
/ (total_iters - warmup_total_iters) |
|
) |
|
) |
|
return lr |
|
|
|
|
|
def yolox_warm_cos_lr( |
|
lr, |
|
min_lr_ratio, |
|
total_iters, |
|
warmup_total_iters, |
|
warmup_lr_start, |
|
no_aug_iter, |
|
iters, |
|
): |
|
"""Cosine learning rate with warm up.""" |
|
min_lr = lr * min_lr_ratio |
|
if iters <= warmup_total_iters: |
|
|
|
lr = (lr - warmup_lr_start) * pow( |
|
iters / float(warmup_total_iters), 2 |
|
) + warmup_lr_start |
|
elif iters >= total_iters - no_aug_iter: |
|
lr = min_lr |
|
else: |
|
lr = min_lr + 0.5 * (lr - min_lr) * ( |
|
1.0 |
|
+ math.cos( |
|
math.pi |
|
* (iters - warmup_total_iters) |
|
/ (total_iters - warmup_total_iters - no_aug_iter) |
|
) |
|
) |
|
return lr |
|
|
|
|
|
def yolox_semi_warm_cos_lr( |
|
lr, |
|
min_lr_ratio, |
|
warmup_lr_start, |
|
total_iters, |
|
normal_iters, |
|
no_aug_iters, |
|
warmup_total_iters, |
|
semi_iters, |
|
iters_per_epoch, |
|
iters_per_epoch_semi, |
|
iters, |
|
): |
|
"""Cosine learning rate with warm up.""" |
|
min_lr = lr * min_lr_ratio |
|
if iters <= warmup_total_iters: |
|
|
|
lr = (lr - warmup_lr_start) * pow( |
|
iters / float(warmup_total_iters), 2 |
|
) + warmup_lr_start |
|
elif iters >= normal_iters + semi_iters: |
|
lr = min_lr |
|
elif iters <= normal_iters: |
|
lr = min_lr + 0.5 * (lr - min_lr) * ( |
|
1.0 |
|
+ math.cos( |
|
math.pi |
|
* (iters - warmup_total_iters) |
|
/ (total_iters - warmup_total_iters - no_aug_iters) |
|
) |
|
) |
|
else: |
|
lr = min_lr + 0.5 * (lr - min_lr) * ( |
|
1.0 |
|
+ math.cos( |
|
math.pi |
|
* ( |
|
normal_iters |
|
- warmup_total_iters |
|
+ (iters - normal_iters) |
|
* iters_per_epoch |
|
* 1.0 |
|
/ iters_per_epoch_semi |
|
) |
|
/ (total_iters - warmup_total_iters - no_aug_iters) |
|
) |
|
) |
|
return lr |
|
|
|
|
|
def multistep_lr(lr, milestones, gamma, iters): |
|
"""MultiStep learning rate""" |
|
for milestone in milestones: |
|
lr *= gamma if iters >= milestone else 1.0 |
|
return lr |
|
|