123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- #!/usr/bin/env python3
- # -*- coding:utf-8 -*-
- # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
-
- 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": # cosine lr schedule
- 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": # stepwise lr schedule
- 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) * iters / float(warmup_total_iters) + warmup_lr_start
- 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) * iters / float(warmup_total_iters) + warmup_lr_start
- 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
|