forked from PaddlePaddle/PaddleOCR
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lr_scheduler.py
240 lines (217 loc) · 8.8 KB
/
lr_scheduler.py
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# 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.0,
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
)