LMFuser 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
lmfuser/schedulers.py ADDED
@@ -0,0 +1,210 @@
1
+ from functools import partial
2
+ import math
3
+
4
+ from torch.optim.lr_scheduler import LambdaLR, LRScheduler
5
+ from torch.optim import Optimizer
6
+
7
+ from hyperargs import Conf, IntArg, FloatArg, OptionArg, monitor_on, add_dependency
8
+
9
+ def _get_linear_schedule_with_warmup_lr_lambda(
10
+ current_step: int,
11
+ *,
12
+ num_warmup_steps: int,
13
+ num_training_steps: int
14
+ ):
15
+ if current_step < num_warmup_steps:
16
+ return float(current_step) / float(max(1, num_warmup_steps))
17
+ return max(
18
+ 0.0,
19
+ float(num_training_steps - current_step) / float(
20
+ max(1, num_training_steps - num_warmup_steps))
21
+ )
22
+
23
+ def get_linear_schedule_with_warmup(
24
+ optimizer: Optimizer,
25
+ num_warmup_steps: int,
26
+ num_training_steps: int,
27
+ last_epoch=-1
28
+ ) -> LRScheduler:
29
+ """
30
+ Create a schedule with a learning rate that decreases linearly from the initial lr set in the optimizer to 0, after
31
+ a warmup period during which it increases linearly from 0 to the initial lr set in the optimizer.
32
+
33
+ Args:
34
+ optimizer ([`~torch.optim.Optimizer`]):
35
+ The optimizer for which to schedule the learning rate.
36
+ num_warmup_steps (`int`):
37
+ The number of steps for the warmup phase.
38
+ num_training_steps (`int`):
39
+ The total number of training steps.
40
+ last_epoch (`int`, *optional*, defaults to -1):
41
+ The index of the last epoch when resuming training.
42
+
43
+ Return:
44
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
45
+ """
46
+
47
+ lr_lambda = partial(
48
+ _get_linear_schedule_with_warmup_lr_lambda,
49
+ num_warmup_steps=num_warmup_steps,
50
+ num_training_steps=num_training_steps,
51
+ )
52
+ return LambdaLR(optimizer, lr_lambda, last_epoch)
53
+
54
+ def _get_constant_schedule_with_warmup_lr_lambda(
55
+ current_step: int,
56
+ *,
57
+ num_warmup_steps: int
58
+ ):
59
+ if current_step < num_warmup_steps:
60
+ return float(current_step) / float(max(1.0, num_warmup_steps))
61
+ return 1.0
62
+
63
+ def get_constant_schedule_with_warmup(
64
+ optimizer: Optimizer,
65
+ num_warmup_steps: int,
66
+ last_epoch: int = -1
67
+ ) -> LRScheduler:
68
+ """
69
+ Create a schedule with a constant learning rate preceded by a warmup period during which the learning rate
70
+ increases linearly between 0 and the initial lr set in the optimizer.
71
+
72
+ Args:
73
+ optimizer ([`~torch.optim.Optimizer`]):
74
+ The optimizer for which to schedule the learning rate.
75
+ num_warmup_steps (`int`):
76
+ The number of steps for the warmup phase.
77
+ last_epoch (`int`, *optional*, defaults to -1):
78
+ The index of the last epoch when resuming training.
79
+
80
+ Return:
81
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
82
+ """
83
+
84
+ lr_lambda = partial(_get_constant_schedule_with_warmup_lr_lambda, num_warmup_steps=num_warmup_steps)
85
+ return LambdaLR(optimizer, lr_lambda, last_epoch=last_epoch)
86
+
87
+ def _get_cosine_schedule_with_warmup_lr_lambda(
88
+ current_step: int, *, num_warmup_steps: int, num_training_steps: int, num_cycles: float
89
+ ):
90
+ if current_step < num_warmup_steps:
91
+ return float(current_step) / float(max(1, num_warmup_steps))
92
+ progress = float(current_step - num_warmup_steps) / float(max(1, num_training_steps - num_warmup_steps))
93
+ return max(0.0, 0.5 * (1.0 + math.cos(math.pi * float(num_cycles) * 2.0 * progress)))
94
+
95
+
96
+ def get_cosine_schedule_with_warmup(
97
+ optimizer: Optimizer, num_warmup_steps: int, num_training_steps: int, num_cycles: float = 0.5, last_epoch: int = -1
98
+ ) -> LRScheduler:
99
+ """
100
+ Create a schedule with a learning rate that decreases following the values of the cosine function between the
101
+ initial lr set in the optimizer to 0, after a warmup period during which it increases linearly between 0 and the
102
+ initial lr set in the optimizer.
103
+
104
+ Args:
105
+ optimizer ([`~torch.optim.Optimizer`]):
106
+ The optimizer for which to schedule the learning rate.
107
+ num_warmup_steps (`int`):
108
+ The number of steps for the warmup phase.
109
+ num_training_steps (`int`):
110
+ The total number of training steps.
111
+ num_cycles (`float`, *optional*, defaults to 0.5):
112
+ The number of waves in the cosine schedule (the defaults is to just decrease from the max value to 0
113
+ following a half-cosine).
114
+ last_epoch (`int`, *optional*, defaults to -1):
115
+ The index of the last epoch when resuming training.
116
+
117
+ Return:
118
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
119
+ """
120
+
121
+ lr_lambda = partial(
122
+ _get_cosine_schedule_with_warmup_lr_lambda,
123
+ num_warmup_steps=num_warmup_steps,
124
+ num_training_steps=num_training_steps,
125
+ num_cycles=num_cycles,
126
+ )
127
+ return LambdaLR(optimizer, lr_lambda, last_epoch)
128
+
129
+
130
+ class LRSchedulerConfigBase(Conf):
131
+
132
+ def init_lr_scheduler(self, optimizer: Optimizer, **kwargs) -> LRScheduler:
133
+ raise NotImplementedError(
134
+ 'Please Implement this method in child classes!'
135
+ )
136
+
137
+
138
+ class ConstantScheduleWithWarmupConfig(LRSchedulerConfigBase):
139
+
140
+ num_warmup_steps= IntArg(0, min_value=0)
141
+ last_epoch= IntArg(-1, min_value=-1)
142
+
143
+ def init_lr_scheduler(self, optimizer: Optimizer, **kwargs) -> LRScheduler:
144
+ return get_constant_schedule_with_warmup(
145
+ optimizer=optimizer,
146
+ num_warmup_steps=self.num_warmup_steps.value(), # type: ignore
147
+ last_epoch=self.last_epoch.value() # type: ignore
148
+ )
149
+
150
+
151
+ class LienarScheduleWithWarmup(LRSchedulerConfigBase):
152
+
153
+ num_warmup_steps= IntArg(0, min_value=0)
154
+ num_training_steps= IntArg(10000, min_value=1)
155
+ last_epoch= IntArg(-1, min_value=-1)
156
+
157
+ def init_lr_scheduler(self, optimizer: Optimizer, **kwargs) -> LRScheduler:
158
+ return get_linear_schedule_with_warmup(
159
+ optimizer=optimizer,
160
+ num_warmup_steps=self.num_warmup_steps.value(), # type: ignore
161
+ num_training_steps=self.num_training_steps.value(), # type: ignore
162
+ last_epoch=self.last_epoch.value() # type: ignore
163
+ )
164
+
165
+
166
+ class CosineScheduleWithWarmu(LRSchedulerConfigBase):
167
+
168
+ num_warmup_steps= IntArg(0, min_value=0)
169
+ num_training_steps= IntArg(10000, min_value=1)
170
+ num_cycles= FloatArg(0.5, min_value=0.0, max_value=1.0)
171
+ last_epoch= IntArg(-1, min_value=-1)
172
+
173
+ def init_lr_scheduler(self, optimizer: Optimizer, **kwargs) -> LRScheduler:
174
+ return get_cosine_schedule_with_warmup(
175
+ optimizer=optimizer,
176
+ num_warmup_steps=self.num_warmup_steps.value(), # type: ignore
177
+ num_training_steps=self.num_training_steps.value(), # type: ignore
178
+ num_cycles=self.num_cycles.value(), # type: ignore
179
+ last_epoch=self.last_epoch.value() # type: ignore
180
+ )
181
+
182
+
183
+ @add_dependency('type', 'scheduler')
184
+ class LRSchedulerConfig(LRSchedulerConfigBase):
185
+
186
+ type = OptionArg(
187
+ options=[
188
+ 'constant_schedule_with_warmup',
189
+ 'linear_schedule_with_warmup',
190
+ 'cosine_schedule_with_warmup'
191
+ ],
192
+ default='constant_schedule_with_warmup',
193
+ )
194
+ scheduler = ConstantScheduleWithWarmupConfig()
195
+
196
+ def init_lr_scheduler(self, optimizer: Optimizer, **kwargs) -> LRScheduler:
197
+ return self.scheduler.init_lr_scheduler(optimizer, **kwargs)
198
+
199
+ @monitor_on('type')
200
+ def set_scheduler(self) -> None:
201
+ if self.type.value() == 'constant_schedule_with_warmup' and not isinstance(self.scheduler, ConstantScheduleWithWarmupConfig):
202
+ self.scheduler = ConstantScheduleWithWarmupConfig()
203
+ elif self.type.value() == 'linear_schedule_with_warmup' and not isinstance(self.scheduler, LienarScheduleWithWarmup):
204
+ self.scheduler = LienarScheduleWithWarmup()
205
+ elif self.type.value() == 'cosine_schedule_with_warmup' and not isinstance(self.scheduler, CosineScheduleWithWarmu):
206
+ self.scheduler = CosineScheduleWithWarmu()
207
+
208
+ if __name__ == '__main__':
209
+ conf = LRSchedulerConfig.parse_command_line()
210
+ print(conf)
lmfuser/task.py ADDED
@@ -0,0 +1,287 @@
1
+ from abc import ABC
2
+ from typing import TypeVar, Any, Callable
3
+ from collections.abc import Iterable
4
+
5
+ import torch
6
+ from torch import nn
7
+ from lmfuser_data.interfaces import Batch, Row
8
+ from lmfuser_data.scanners import Scanner
9
+ from lmfuser_data import DataLoader
10
+ from lmfuser_data.interfaces import SubclassTracer
11
+ from hyperargs import Conf, StrArg, FloatArg, IntArg, OptionArg, add_dependency, monitor_on
12
+
13
+
14
+ def scanner_type_list() -> list[str]:
15
+ return list(Scanner.all_subclass_names())
16
+
17
+
18
+ @add_dependency('num_train_data_path', 'train_data_path_list')
19
+ @add_dependency('num_train_data_path', 'train_data_weights')
20
+ class TaskBase(Conf, SubclassTracer):
21
+ num_train_data_path = IntArg(1, min_value=0)
22
+ train_data_path_list = [StrArg('Enther the path to the data file.')]
23
+ train_data_weights = [FloatArg(1.0, min_value=0.0, max_value=1.0)]
24
+
25
+ num_eval_data_path = IntArg(1, min_value=0)
26
+ eval_data_path_list = [StrArg('Enther the path to the data file.')]
27
+ eval_data_weights = [FloatArg(1.0, min_value=0.0, max_value=1.0)]
28
+
29
+ scanner_type = OptionArg(default='C4Scanner', option_fn=scanner_type_list)
30
+
31
+ _train_dataloader: DataLoader | None = None
32
+ _eval_dataloader: DataLoader | None = None
33
+
34
+ @monitor_on('num_train_data_path')
35
+ def set_train_path_list(self) -> None:
36
+ num = self.num_train_data_path.value()
37
+ assert isinstance(num, int)
38
+ if len(self.train_data_path_list) > num:
39
+ self.train_data_path_list = self.train_data_path_list[:num]
40
+ self.train_data_weights = self.train_data_weights[:num]
41
+ elif len(self.train_data_path_list) < num:
42
+ self.train_data_path_list += [StrArg('Enther the path to the data file.')] * (num - len(self.train_data_path_list))
43
+ self.train_data_weights += [FloatArg(1.0, min_value=0.0, max_value=1.0)] * (num - len(self.train_data_weights))
44
+
45
+ @monitor_on('num_eval_data_path')
46
+ def set_eval_path_list(self) -> None:
47
+ num = self.num_eval_data_path.value()
48
+ assert isinstance(num, int)
49
+ if len(self.eval_data_path_list) > num:
50
+ self.eval_data_path_list = self.eval_data_path_list[:num]
51
+ self.eval_data_weights = self.eval_data_weights[:num]
52
+ elif len(self.eval_data_path_list) < num:
53
+ self.eval_data_path_list += [StrArg('Enther the path to the data file.')] * (num - len(self.train_data_path_list))
54
+ self.eval_data_weights += [FloatArg(1.0, min_value=0.0, max_value=1.0)] * (num - len(self.train_data_weights))
55
+
56
+ def _get_train_dataloader(
57
+ self,
58
+ batch_size: int,
59
+ seed: int,
60
+ shuffle: bool,
61
+ prefetch_factor: int,
62
+ ignore_error: bool,
63
+ qps: float | None,
64
+ instruct_timeout: float,
65
+ worker_timeout: float,
66
+ num_workers: int,
67
+ rank: int,
68
+ world_size: int
69
+ ) -> DataLoader | None:
70
+ if self.num_train_data_path.value() == 0:
71
+ return None
72
+ if self._train_dataloader is not None:
73
+ return self._train_dataloader
74
+ path_list = [p.value() for p in self.train_data_path_list]
75
+ weight_list = [w.value() for w in self.train_data_weights]
76
+ scanner_type = self.scanner_type.value()
77
+ assert scanner_type is not None, 'scanner_type is None'
78
+
79
+ self._train_dataloader = DataLoader(
80
+ batch_size=batch_size,
81
+ path_list=path_list, # type: ignore
82
+ distributor_weights=weight_list, # type: ignore
83
+ scanner_type=Scanner.get_subclass(scanner_type),
84
+ seed=seed,
85
+ shuffle=shuffle,
86
+ pre_fetch_factor=prefetch_factor,
87
+ ignore_error=ignore_error,
88
+ qps=qps,
89
+ instruct_timeout=instruct_timeout,
90
+ worker_timeout=worker_timeout,
91
+ num_workers=num_workers,
92
+ map_fn=self.get_row_processor(),
93
+ flow_fn=self.get_flow_processor(),
94
+ batch_map_fn=self.get_batch_processor(),
95
+ rank_idx=rank,
96
+ num_ranks=world_size,
97
+ )
98
+
99
+ return self._train_dataloader
100
+
101
+ def _get_eval_dataloader(
102
+ self,
103
+ batch_size: int,
104
+ seed: int,
105
+ shuffle: bool,
106
+ prefetch_factor: int,
107
+ ignore_error: bool,
108
+ qps: float | None,
109
+ instruct_timeout: float,
110
+ worker_timeout: float,
111
+ num_workers: int,
112
+ rank: int,
113
+ world_size: int
114
+ ) -> DataLoader | None:
115
+ if self.num_eval_data_path.value() == 0:
116
+ return None
117
+ if self._eval_dataloader is not None:
118
+ return self._eval_dataloader
119
+ path_list = [p.value() for p in self.eval_data_path_list]
120
+ weight_list = [w.value() for w in self.eval_data_weights]
121
+ scanner_type = self.scanner_type.value()
122
+ assert scanner_type is not None, 'scanner_type is None'
123
+ self._eval_dataloader = DataLoader(
124
+ batch_size=batch_size,
125
+ path_list=path_list, # type: ignore
126
+ distributor_weights=weight_list, # type: ignore
127
+ scanner_type=Scanner.get_subclass(scanner_type),
128
+ seed=seed,
129
+ shuffle=shuffle,
130
+ pre_fetch_factor=prefetch_factor,
131
+ ignore_error=ignore_error,
132
+ qps=qps,
133
+ instruct_timeout=instruct_timeout,
134
+ worker_timeout=worker_timeout,
135
+ num_workers=num_workers,
136
+ map_fn=self.get_row_processor(),
137
+ flow_fn=self.get_flow_processor(),
138
+ batch_map_fn=self.get_batch_processor(),
139
+ rank_idx=rank,
140
+ num_ranks=world_size,
141
+ )
142
+
143
+ return self._eval_dataloader
144
+
145
+ def train_step(
146
+ self, model: nn.Module,
147
+ batch: Batch,
148
+ step: int,
149
+ device: Any,
150
+ acc_step: int,
151
+ **kwargs: Any
152
+ ) -> Batch | torch.Tensor:
153
+ raise NotImplementedError('Please implement this method in child class')
154
+
155
+ def eval_step(
156
+ self,
157
+ model: nn.Module,
158
+ batch: Batch,
159
+ step: int,
160
+ device: Any,
161
+ **kwargs: Any
162
+ ) -> dict[str, list[Any]]:
163
+ raise NotImplementedError('Please implement this method in child class')
164
+
165
+ def cal_dev_metric(self, eval_outputs: dict[str, list[Any]]) -> dict[str, Any]:
166
+ raise NotImplementedError('Please implement this method in child class')
167
+
168
+ def get_row_processor(self) -> Callable[[Row], Row] | None:
169
+ return None
170
+
171
+ def get_flow_processor(self) -> Callable[[Iterable[Row]], Iterable[Row]] | None:
172
+ return None
173
+
174
+ def get_batch_processor(self) -> Callable[[Batch], Batch] | None:
175
+ return None
176
+
177
+
178
+ class Task(TaskBase):
179
+ pass
180
+
181
+ def task_list() -> list[str]:
182
+ return list(TaskBase.all_subclass_names())
183
+
184
+
185
+ @add_dependency('conf', 'task_name')
186
+ class TaskSelector(Conf):
187
+ task_name = OptionArg(default='Task', option_fn=task_list)
188
+ conf: TaskBase = Task()
189
+
190
+ @monitor_on('task_name')
191
+ def change_conf(self) -> None:
192
+ name = self.task_name.value()
193
+ if name is None:
194
+ self.conf = Task()
195
+
196
+ elif name != self.conf.__class__.__name__:
197
+ self.conf = TaskBase.all_subclass_map()[name]()
198
+
199
+
200
+ @add_dependency('num_tasks', 'tasks')
201
+ class Tasks(Conf):
202
+ num_tasks = IntArg(1, min_value=1)
203
+ tasks = [TaskSelector()]
204
+ task_weights = [FloatArg(1.0, min_value=0.0, max_value=1.0)]
205
+
206
+ @monitor_on('num_tasks')
207
+ def change_task_list(self) -> None:
208
+ num = self.num_tasks.value()
209
+ assert num is not None, 'num_tasks is None'
210
+
211
+ if len(self.tasks) > num:
212
+ self.tasks = self.tasks[:num]
213
+ self.task_weight = self.task_weight[:num]
214
+ elif len(self.tasks) < num:
215
+ self.tasks += [TaskSelector() for _ in range(num - len(self.tasks))]
216
+ self.task_weight += [
217
+ FloatArg(1.0, min_value=0.0, max_value=1.0) for _ in range(num - len(self.task_weight))
218
+ ]
219
+
220
+ def get_train_dataloaders(
221
+ self,
222
+ batch_size: int,
223
+ seed: int,
224
+ shuffle: bool,
225
+ prefetch_factor: int,
226
+ ignore_error: bool,
227
+ qps: float | None,
228
+ instruct_timeout: float,
229
+ worker_timeout: float,
230
+ num_workers: int,
231
+ rank: int,
232
+ world_size: int
233
+ ) -> list[DataLoader | None]:
234
+ return [
235
+ task.conf._get_train_dataloader(
236
+ batch_size=batch_size,
237
+ seed=seed,
238
+ shuffle=shuffle,
239
+ prefetch_factor=prefetch_factor,
240
+ ignore_error=ignore_error,
241
+ qps=qps,
242
+ instruct_timeout=instruct_timeout,
243
+ worker_timeout=worker_timeout,
244
+ num_workers=num_workers,
245
+ rank=rank,
246
+ world_size=world_size,
247
+ )
248
+ for task in self.tasks
249
+ ]
250
+
251
+ def get_eval_dataloaders(
252
+ self,
253
+ batch_size: int,
254
+ seed: int,
255
+ shuffle: bool,
256
+ prefetch_factor: int,
257
+ ignore_error: bool,
258
+ qps: float | None,
259
+ instruct_timeout: float,
260
+ worker_timeout: float,
261
+ num_workers: int,
262
+ rank: int,
263
+ world_size: int
264
+ ) -> list[DataLoader | None]:
265
+ return [
266
+ task.conf._get_eval_dataloader(
267
+ batch_size=batch_size,
268
+ seed=seed,
269
+ shuffle=shuffle,
270
+ prefetch_factor=prefetch_factor,
271
+ ignore_error=ignore_error,
272
+ qps=qps,
273
+ instruct_timeout=instruct_timeout,
274
+ worker_timeout=worker_timeout,
275
+ num_workers=num_workers,
276
+ rank=rank,
277
+ world_size=world_size,
278
+ )
279
+ for task in self.tasks
280
+ ]
281
+
282
+ if __name__ == '__main__':
283
+ class TaskTemp(Task):
284
+ pass
285
+
286
+ conf = Tasks.parse_command_line()
287
+ print(conf)