Spaces:
Runtime error
Runtime error
# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
import math | |
from paddle.optimizer.lr import LRScheduler | |
class CyclicalCosineDecay(LRScheduler): | |
def __init__(self, | |
learning_rate, | |
T_max, | |
cycle=1, | |
last_epoch=-1, | |
eta_min=0.0, | |
verbose=False): | |
""" | |
Cyclical cosine learning rate decay | |
A learning rate which can be referred in https://arxiv.org/pdf/2012.12645.pdf | |
Args: | |
learning rate(float): learning rate | |
T_max(int): maximum epoch num | |
cycle(int): period of the cosine decay | |
last_epoch (int, optional): The index of last epoch. Can be set to restart training. Default: -1, means initial learning rate. | |
eta_min(float): minimum learning rate during training | |
verbose(bool): whether to print learning rate for each epoch | |
""" | |
super(CyclicalCosineDecay, self).__init__(learning_rate, last_epoch, | |
verbose) | |
self.cycle = cycle | |
self.eta_min = eta_min | |
def get_lr(self): | |
if self.last_epoch == 0: | |
return self.base_lr | |
reletive_epoch = self.last_epoch % self.cycle | |
lr = self.eta_min + 0.5 * (self.base_lr - self.eta_min) * \ | |
(1 + math.cos(math.pi * reletive_epoch / self.cycle)) | |
return lr | |
class OneCycleDecay(LRScheduler): | |
""" | |
One Cycle learning rate decay | |
A learning rate which can be referred in https://arxiv.org/abs/1708.07120 | |
Code refered in https://pytorch.org/docs/stable/_modules/torch/optim/lr_scheduler.html#OneCycleLR | |
""" | |
def __init__(self, | |
max_lr, | |
epochs=None, | |
steps_per_epoch=None, | |
pct_start=0.3, | |
anneal_strategy='cos', | |
div_factor=25., | |
final_div_factor=1e4, | |
three_phase=False, | |
last_epoch=-1, | |
verbose=False): | |
# Validate total_steps | |
if epochs <= 0 or not isinstance(epochs, int): | |
raise ValueError( | |
"Expected positive integer epochs, but got {}".format(epochs)) | |
if steps_per_epoch <= 0 or not isinstance(steps_per_epoch, int): | |
raise ValueError( | |
"Expected positive integer steps_per_epoch, but got {}".format( | |
steps_per_epoch)) | |
self.total_steps = epochs * steps_per_epoch | |
self.max_lr = max_lr | |
self.initial_lr = self.max_lr / div_factor | |
self.min_lr = self.initial_lr / final_div_factor | |
if three_phase: | |
self._schedule_phases = [ | |
{ | |
'end_step': float(pct_start * self.total_steps) - 1, | |
'start_lr': self.initial_lr, | |
'end_lr': self.max_lr, | |
}, | |
{ | |
'end_step': float(2 * pct_start * self.total_steps) - 2, | |
'start_lr': self.max_lr, | |
'end_lr': self.initial_lr, | |
}, | |
{ | |
'end_step': self.total_steps - 1, | |
'start_lr': self.initial_lr, | |
'end_lr': self.min_lr, | |
}, | |
] | |
else: | |
self._schedule_phases = [ | |
{ | |
'end_step': float(pct_start * self.total_steps) - 1, | |
'start_lr': self.initial_lr, | |
'end_lr': self.max_lr, | |
}, | |
{ | |
'end_step': self.total_steps - 1, | |
'start_lr': self.max_lr, | |
'end_lr': self.min_lr, | |
}, | |
] | |
# Validate pct_start | |
if pct_start < 0 or pct_start > 1 or not isinstance(pct_start, float): | |
raise ValueError( | |
"Expected float between 0 and 1 pct_start, but got {}".format( | |
pct_start)) | |
# Validate anneal_strategy | |
if anneal_strategy not in ['cos', 'linear']: | |
raise ValueError( | |
"anneal_strategy must by one of 'cos' or 'linear', instead got {}". | |
format(anneal_strategy)) | |
elif anneal_strategy == 'cos': | |
self.anneal_func = self._annealing_cos | |
elif anneal_strategy == 'linear': | |
self.anneal_func = self._annealing_linear | |
super(OneCycleDecay, self).__init__(max_lr, last_epoch, verbose) | |
def _annealing_cos(self, start, end, pct): | |
"Cosine anneal from `start` to `end` as pct goes from 0.0 to 1.0." | |
cos_out = math.cos(math.pi * pct) + 1 | |
return end + (start - end) / 2.0 * cos_out | |
def _annealing_linear(self, start, end, pct): | |
"Linearly anneal from `start` to `end` as pct goes from 0.0 to 1.0." | |
return (end - start) * pct + start | |
def get_lr(self): | |
computed_lr = 0.0 | |
step_num = self.last_epoch | |
if step_num > self.total_steps: | |
raise ValueError( | |
"Tried to step {} times. The specified number of total steps is {}" | |
.format(step_num + 1, self.total_steps)) | |
start_step = 0 | |
for i, phase in enumerate(self._schedule_phases): | |
end_step = phase['end_step'] | |
if step_num <= end_step or i == len(self._schedule_phases) - 1: | |
pct = (step_num - start_step) / (end_step - start_step) | |
computed_lr = self.anneal_func(phase['start_lr'], | |
phase['end_lr'], pct) | |
break | |
start_step = phase['end_step'] | |
return computed_lr | |
class TwoStepCosineDecay(LRScheduler): | |
def __init__(self, | |
learning_rate, | |
T_max1, | |
T_max2, | |
eta_min=0, | |
last_epoch=-1, | |
verbose=False): | |
if not isinstance(T_max1, int): | |
raise TypeError( | |
"The type of 'T_max1' in 'CosineAnnealingDecay' must be 'int', but received %s." | |
% type(T_max1)) | |
if not isinstance(T_max2, int): | |
raise TypeError( | |
"The type of 'T_max2' in 'CosineAnnealingDecay' must be 'int', but received %s." | |
% type(T_max2)) | |
if not isinstance(eta_min, (float, int)): | |
raise TypeError( | |
"The type of 'eta_min' in 'CosineAnnealingDecay' must be 'float, int', but received %s." | |
% type(eta_min)) | |
assert T_max1 > 0 and isinstance( | |
T_max1, int), " 'T_max1' must be a positive integer." | |
assert T_max2 > 0 and isinstance( | |
T_max2, int), " 'T_max1' must be a positive integer." | |
self.T_max1 = T_max1 | |
self.T_max2 = T_max2 | |
self.eta_min = float(eta_min) | |
super(TwoStepCosineDecay, self).__init__(learning_rate, last_epoch, | |
verbose) | |
def get_lr(self): | |
if self.last_epoch <= self.T_max1: | |
if self.last_epoch == 0: | |
return self.base_lr | |
elif (self.last_epoch - 1 - self.T_max1) % (2 * self.T_max1) == 0: | |
return self.last_lr + (self.base_lr - self.eta_min) * ( | |
1 - math.cos(math.pi / self.T_max1)) / 2 | |
return (1 + math.cos(math.pi * self.last_epoch / self.T_max1)) / ( | |
1 + math.cos(math.pi * (self.last_epoch - 1) / self.T_max1)) * ( | |
self.last_lr - self.eta_min) + self.eta_min | |
else: | |
if (self.last_epoch - 1 - self.T_max2) % (2 * self.T_max2) == 0: | |
return self.last_lr + (self.base_lr - self.eta_min) * ( | |
1 - math.cos(math.pi / self.T_max2)) / 2 | |
return (1 + math.cos(math.pi * self.last_epoch / self.T_max2)) / ( | |
1 + math.cos(math.pi * (self.last_epoch - 1) / self.T_max2)) * ( | |
self.last_lr - self.eta_min) + self.eta_min | |
def _get_closed_form_lr(self): | |
if self.last_epoch <= self.T_max1: | |
return self.eta_min + (self.base_lr - self.eta_min) * (1 + math.cos( | |
math.pi * self.last_epoch / self.T_max1)) / 2 | |
else: | |
return self.eta_min + (self.base_lr - self.eta_min) * (1 + math.cos( | |
math.pi * self.last_epoch / self.T_max2)) / 2 | |