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/utils.py ADDED
@@ -0,0 +1,249 @@
1
+ import os
2
+ from typing import Any, overload, Union, Optional, Literal
3
+ from random import Random
4
+
5
+ import torch
6
+ from torch import distributed as dist
7
+ from torch.utils.data import get_worker_info
8
+ from torch import Tensor
9
+ import atexit
10
+
11
+ from typing import TypeVar
12
+ import random
13
+ import logging
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ T = TypeVar('T')
18
+
19
+ def get_world_size() -> int:
20
+ if 'WORLD_SIZE' in os.environ:
21
+ return int(os.environ['WORLD_SIZE'])
22
+ else:
23
+ return 1
24
+
25
+ def get_global_rank() -> int:
26
+ if 'RANK' in os.environ:
27
+ return int(os.environ['RANK'])
28
+ else:
29
+ return 0
30
+
31
+ def get_local_rank() -> int:
32
+ if "LOCAL_RANK" in os.environ:
33
+ return int(os.environ["LOCAL_RANK"])
34
+ else:
35
+ # Provide a default or handle the case of non-distributed training
36
+ return 0
37
+
38
+ def dist_init() -> None:
39
+ if dist.is_initialized():
40
+ return
41
+
42
+ device_type = get_default_device_type()
43
+
44
+ if device_type == 'cuda':
45
+ dist.init_process_group(backend='nccl')
46
+ torch.cuda.set_device(get_local_rank())
47
+ elif device_type == 'npu':
48
+ dist.init_process_group(backend='hccl')
49
+ else:
50
+ dist.init_process_group(backend='mpi')
51
+
52
+ atexit.register(dist.destroy_process_group)
53
+
54
+ def weighted_random_choice(
55
+ elements: list[T],
56
+ probabilities: list[float],
57
+ rand: Optional[Random] = None
58
+ ) -> T:
59
+ """
60
+ Choose a random element from a list based on specified probabilities.
61
+
62
+ Args:
63
+ elements (List[T]): A list of elements to choose from.
64
+ probabilities (List[float]): A list of probabilities associated with each element. Must sum to 1.
65
+ rand (Optional[Random]): The random number generator.
66
+
67
+ Returns:
68
+ T: A randomly chosen element from the list.
69
+ """
70
+ if len(elements) != len(probabilities):
71
+ raise ValueError("Elements and probabilities must have the same length.")
72
+ if not all(0 <= p <= 1 for p in probabilities):
73
+ raise ValueError("Probabilities must be non-negative and non-greater than 1.")
74
+ if not abs(sum(probabilities) - 1.0) < 1e-6:
75
+ raise ValueError("Probabilities must sum to 1.")
76
+
77
+ if rand is None:
78
+ index = random.choices(list(range(len(elements))), probabilities)[0]
79
+ else:
80
+ index = rand.choices(list(range(len(elements))), probabilities)[0]
81
+
82
+ return elements[index]
83
+
84
+ def partition_list(lst: list[T], num_shards: int, index: int) -> list[T]:
85
+ # Ensure the number of shards is positive and index is valid
86
+ if num_shards <= 0 or index >= num_shards or index < 0:
87
+ raise ValueError("Invalid number of shards or index, "
88
+ f"number of shards: {num_shards};"
89
+ f"index: {index}.")
90
+
91
+ # Calculate the size of each shard
92
+ shard_size = len(lst) // num_shards
93
+ remainder = len(lst) % num_shards
94
+
95
+ # Calculate the start and end indices for the partition
96
+ start = index * shard_size + min(index, remainder)
97
+ end = (index + 1) * shard_size + min(index + 1, remainder)
98
+
99
+ return lst[start:end]
100
+
101
+ DEVICE_TYPE: Optional[Literal['cuda', 'npu', 'cpu']] = None
102
+ def get_default_device_type() -> Literal['cuda', 'npu', 'cpu']:
103
+ global DEVICE_TYPE
104
+ if DEVICE_TYPE is not None:
105
+ return DEVICE_TYPE
106
+
107
+ device = os.environ.get('HURRICANE_DEVICE')
108
+ if device is not None:
109
+ DEVICE_TYPE = device # type: ignore
110
+ return DEVICE_TYPE # type: ignore
111
+
112
+ if torch.cuda.is_available():
113
+ DEVICE_TYPE = 'cuda'
114
+ return DEVICE_TYPE
115
+
116
+ try:
117
+ __import__('torch_npu')
118
+ if torch_npu.npu.is_available(): # type: ignore
119
+ DEVICE_TYPE = 'npu'
120
+ return DEVICE_TYPE
121
+ except ImportError:
122
+ ...
123
+
124
+ DEVICE_TYPE = 'cpu'
125
+
126
+ return DEVICE_TYPE
127
+
128
+ DEVICE: Optional[str] = None
129
+ def get_default_device() -> str | int:
130
+ """
131
+ Get the default device for the current process.
132
+ """
133
+ global DEVICE
134
+ if DEVICE is not None:
135
+ return DEVICE
136
+
137
+ device_type = get_default_device_type()
138
+ if device_type == 'cpu':
139
+ return -1
140
+ if device_type == 'cuda':
141
+ return get_local_rank()
142
+
143
+ return f'{device_type}:{get_local_rank()}'
144
+
145
+ @overload
146
+ def dist_avg(value: Tensor) -> Tensor: ...
147
+ @overload
148
+ def dist_avg(value: int) -> float: ...
149
+ @overload
150
+ def dist_avg(value: float) -> float: ...
151
+ def dist_avg(value: Union[torch.Tensor, int, float]) -> Union[torch.Tensor, float]:
152
+ if not dist.is_initialized():
153
+ return value
154
+
155
+ if isinstance(value, torch.Tensor):
156
+ return_tensor = True
157
+ value = value.to(get_default_device())
158
+ else:
159
+ return_tensor = False
160
+ value = torch.tensor(value, device=get_default_device(), dtype=torch.float32)
161
+
162
+ dist.all_reduce(value, dist.ReduceOp.SUM)
163
+ dist.barrier()
164
+
165
+ if return_tensor:
166
+ return value / dist.get_world_size()
167
+
168
+ return value.item() / dist.get_world_size()
169
+
170
+
171
+ def gather_object(local_object: T) -> list[T]:
172
+ if not dist.is_initialized():
173
+ return [local_object]
174
+ world_size = dist.get_world_size()
175
+
176
+ gathered = [None for _ in range(world_size)]
177
+ dist.all_gather_object(gathered, local_object)
178
+
179
+ if isinstance(gathered[0], list):
180
+ results = []
181
+ for l in gathered:
182
+ results += l # type: ignore
183
+ gathered = results
184
+
185
+ return gathered # type: ignore
186
+
187
+ def tensor_all_gather(tensor: Tensor) -> Tensor:
188
+ if get_world_size() <= 1:
189
+ return tensor
190
+ results = [torch.empty_like(tensor, device=get_default_device()) for _ in range(dist.get_world_size())]
191
+
192
+ dist.all_gather(results, tensor.contiguous().to(get_default_device()))
193
+
194
+ return torch.cat(results, dim=0).to(tensor.device)
195
+
196
+ def batch_all_gather(batch: dict[str, Any]) -> dict[str, Any]:
197
+ """把各个rank的同一批batch数据汇总到rank0,方便计算metric或者刷库等等。
198
+ 不是分布式的话原样返回
199
+
200
+ Args:
201
+ batch (Dict[str, Union[Tensor, List[Any]]]): 一个batch的数据,注意一个key对应一个list
202
+
203
+ Returns:
204
+ Dict[Dict[str, Union[Tensor, List[Any]]]]: 汇总后的batch
205
+ """
206
+ logger.info(f'Begin all gather on rank {get_global_rank()}')
207
+ if not dist.is_initialized():
208
+ return batch
209
+
210
+ gathered = {}
211
+ for k, v_list in batch.items():
212
+ logger.info(f'Begin gather key {k} on rank {get_global_rank()}')
213
+ if isinstance(v_list, list):
214
+ logger.info(f'Key {k} with length {len(v_list)} on rank {get_global_rank()}')
215
+ logger.info(f'after barrier on rank {get_global_rank()}')
216
+ if isinstance(v_list, Tensor):
217
+ gathered[k] = tensor_all_gather(v_list)
218
+ else:
219
+ if isinstance(v_list[0], Tensor):
220
+ v_tensor = torch.cat(v_list, dim=0)
221
+ gathered[k] = tensor_all_gather(v_tensor)
222
+ else:
223
+ gathered[k] = gather_object(v_list)
224
+
225
+ return gathered
226
+
227
+ def cal_acc_num(batch_size: int, sub_batch_size: int, world_size: int) -> int:
228
+ acc_num = batch_size // sub_batch_size // world_size
229
+ an = batch_size / sub_batch_size / world_size
230
+ if abs(acc_num - an) > 1e-8:
231
+ raise ValueError(
232
+ f'Batchsize无法被平分!请根据显卡数合理设置“BatchSize”与“SubBatchSize”'
233
+ )
234
+ return acc_num
235
+
236
+
237
+ class MethodOverideChecker:
238
+ def is_overridden(self, method_name: str) -> bool:
239
+ """
240
+ Check if the method `method_name` is overridden in this instance's class
241
+ compared to the Parent class.
242
+ """
243
+ cls = self.__class__
244
+ for parent in self.__class__.__bases__:
245
+ if not hasattr(parent, method_name):
246
+ continue
247
+ return getattr(cls, method_name, None) is not getattr(parent, method_name, None)
248
+
249
+ raise ValueError(f'No such method {method_name} in parent classes')
@@ -0,0 +1,30 @@
1
+ Metadata-Version: 2.4
2
+ Name: LMFuser
3
+ Version: 0.0.1
4
+ Summary: The LMFuser training framework.
5
+ Project-URL: Homepage, https://github.com/TYTTYTTYT/LMFuser
6
+ Project-URL: Documentation, https://github.com/TYTTYTTYT/LMFuser
7
+ Project-URL: Repository, https://github.com/TYTTYTTYT/LMFuser
8
+ Project-URL: Bug Tracker, https://github.com/TYTTYTTYT/LMFuser
9
+ Project-URL: Changelog, https://github.com/TYTTYTTYT/LMFuser
10
+ Author-email: Yintao Tai <tai.yintao@gmail.com>
11
+ Maintainer-email: Yintao Tai <tai.yintao@gmail.com>
12
+ License-File: LICENSE
13
+ Keywords: machine learning,pytorch
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Topic :: Utilities
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: hyperargs>=0.1.3
20
+ Requires-Dist: lmfuser-data
21
+ Requires-Dist: numpy
22
+ Requires-Dist: pandas
23
+ Requires-Dist: pyarrow
24
+ Requires-Dist: torch
25
+ Requires-Dist: tqdm
26
+ Requires-Dist: wandb
27
+ Description-Content-Type: text/markdown
28
+
29
+ # LMFuser
30
+ A robust, multi-task PyTorch framework for training versatile Language Models at scale.
@@ -0,0 +1,13 @@
1
+ lmfuser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ lmfuser/model_loader.py,sha256=Y3Rm_hjdDYvKZuvAnblJ7URcDozOwe-QqBniyXrlzLs,1188
3
+ lmfuser/optimizers.py,sha256=9Mp4-_WuKdEcSweSumm0eGgL8l2nUUlLbcgMPPEbYPU,5476
4
+ lmfuser/schedulers.py,sha256=g5Can4dOOeO9YiK0NLiDZt1dAmGRXN6sFKKIcTFTGR8,8035
5
+ lmfuser/task.py,sha256=3OBLuDnwlqe-ctWqh9WBzS0bbxRoj5gx_PibHBkWhHU,9825
6
+ lmfuser/utils.py,sha256=lIXp1CAtTKqoCMdcU8-bSZxS36AMmBZ8a8j_xDHzoI4,7829
7
+ lmfuser/runners/__init__.py,sha256=azfFi3bGPMDG2SEMFiIxKJgA-JGS6vuRlipkJcB7O90,51
8
+ lmfuser/runners/ddp_runner.py,sha256=y3sIkEtzqrbVQzMy1O5iZbprGTagYCTM98uEiO7vvMU,23995
9
+ lmfuser/runners/runner.py,sha256=L6R1Vp62mpx_73kxAByQrNfIXc-kKv6Vq5eT8kF-fhM,1297
10
+ lmfuser-0.0.1.dist-info/METADATA,sha256=t1ielj85-3NNopKxPvj8G2ovAIBW_Q6c75kRZjLjVPQ,1104
11
+ lmfuser-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
+ lmfuser-0.0.1.dist-info/licenses/LICENSE,sha256=4cDSUqshGiBlu4cAQNp8fFvQvy0N5xHX8VdJ0pzbPNU,1067
13
+ lmfuser-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Yintao Tai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.