bitsandbytes 0.50.0__py3-none-win_arm64.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.
Files changed (54) hide show
  1. bitsandbytes/__init__.py +78 -0
  2. bitsandbytes/__main__.py +4 -0
  3. bitsandbytes/_ops.py +510 -0
  4. bitsandbytes/autograd/__init__.py +0 -0
  5. bitsandbytes/autograd/_functions.py +491 -0
  6. bitsandbytes/backends/__init__.py +0 -0
  7. bitsandbytes/backends/cpu/__init__.py +0 -0
  8. bitsandbytes/backends/cpu/ops.py +580 -0
  9. bitsandbytes/backends/cuda/__init__.py +0 -0
  10. bitsandbytes/backends/cuda/ops.py +1199 -0
  11. bitsandbytes/backends/default/__init__.py +0 -0
  12. bitsandbytes/backends/default/ops.py +632 -0
  13. bitsandbytes/backends/hpu/__init__.py +0 -0
  14. bitsandbytes/backends/hpu/ops.py +53 -0
  15. bitsandbytes/backends/mps/__init__.py +0 -0
  16. bitsandbytes/backends/mps/ops.py +277 -0
  17. bitsandbytes/backends/triton/__init__.py +0 -0
  18. bitsandbytes/backends/triton/kernels_4bit.py +577 -0
  19. bitsandbytes/backends/triton/kernels_8bit_quant.py +195 -0
  20. bitsandbytes/backends/triton/kernels_optim.py +1177 -0
  21. bitsandbytes/backends/triton/ops.py +304 -0
  22. bitsandbytes/backends/utils.py +94 -0
  23. bitsandbytes/backends/xpu/__init__.py +0 -0
  24. bitsandbytes/backends/xpu/ops.py +305 -0
  25. bitsandbytes/cextension.py +405 -0
  26. bitsandbytes/consts.py +12 -0
  27. bitsandbytes/cuda_specs.py +111 -0
  28. bitsandbytes/diagnostics/__init__.py +0 -0
  29. bitsandbytes/diagnostics/cuda.py +193 -0
  30. bitsandbytes/diagnostics/main.py +134 -0
  31. bitsandbytes/diagnostics/utils.py +12 -0
  32. bitsandbytes/functional.py +1810 -0
  33. bitsandbytes/libbitsandbytes_cpu.dll +0 -0
  34. bitsandbytes/nn/__init__.py +19 -0
  35. bitsandbytes/nn/modules.py +1220 -0
  36. bitsandbytes/nn/parametrize.py +206 -0
  37. bitsandbytes/optim/__init__.py +22 -0
  38. bitsandbytes/optim/adagrad.py +187 -0
  39. bitsandbytes/optim/adam.py +346 -0
  40. bitsandbytes/optim/adamw.py +337 -0
  41. bitsandbytes/optim/ademamix.py +410 -0
  42. bitsandbytes/optim/lamb.py +190 -0
  43. bitsandbytes/optim/lars.py +259 -0
  44. bitsandbytes/optim/lion.py +266 -0
  45. bitsandbytes/optim/optimizer.py +756 -0
  46. bitsandbytes/optim/rmsprop.py +170 -0
  47. bitsandbytes/optim/sgd.py +152 -0
  48. bitsandbytes/py.typed +0 -0
  49. bitsandbytes/utils.py +208 -0
  50. bitsandbytes-0.50.0.dist-info/METADATA +288 -0
  51. bitsandbytes-0.50.0.dist-info/RECORD +54 -0
  52. bitsandbytes-0.50.0.dist-info/WHEEL +5 -0
  53. bitsandbytes-0.50.0.dist-info/licenses/LICENSE +21 -0
  54. bitsandbytes-0.50.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,756 @@
1
+ # Copyright (c) Facebook, Inc. and its affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+ from collections import abc as container_abcs, defaultdict
6
+ from copy import deepcopy
7
+ from itertools import chain
8
+ import logging
9
+ from typing import Optional
10
+ import warnings
11
+
12
+ import torch
13
+
14
+ import bitsandbytes.functional as F
15
+ from bitsandbytes.utils import sync_gpu
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class MockArgs:
21
+ def __init__(self, initial_data):
22
+ for key in initial_data:
23
+ setattr(self, key, initial_data[key])
24
+
25
+
26
+ class GlobalOptimManager:
27
+ """
28
+ A global optimizer manager for enabling custom optimizer configs.
29
+ """
30
+
31
+ _instance = None
32
+
33
+ def __init__(self):
34
+ raise RuntimeError("Call get_instance() instead")
35
+
36
+ def initialize(self):
37
+ self.pid2config = {}
38
+ self.index2config = {}
39
+ self.optimizer = None
40
+ self.uses_config_override = False
41
+ self.module_weight_config_triple = []
42
+
43
+ @classmethod
44
+ def get_instance(cls):
45
+ if cls._instance is None:
46
+ cls._instance = cls.__new__(cls)
47
+ cls._instance.initialize()
48
+ return cls._instance
49
+
50
+ def register_parameters(self, params):
51
+ param_groups = list(params)
52
+ if not isinstance(param_groups[0], dict):
53
+ param_groups = [{"params": param_groups}]
54
+
55
+ for group_index, group in enumerate(param_groups):
56
+ for p_index, p in enumerate(group["params"]):
57
+ if id(p) in self.pid2config:
58
+ self.index2config[(group_index, p_index)] = self.pid2config[id(p)]
59
+
60
+ def override_config(self, parameters, key=None, value=None, key_value_dict=None):
61
+ """
62
+ Override initial optimizer config with specific hyperparameters.
63
+
64
+ The key-values of the optimizer config for the input parameters are overridden
65
+ This can be both, optimizer parameters like `betas` or `lr`, or it can be
66
+ 8-bit specific parameters like `optim_bits`.
67
+
68
+ Arguments:
69
+ parameters (`torch.Tensor` or `list(torch.Tensors)`):
70
+ The input parameters.
71
+ key (`str`):
72
+ The hyperparameter to override.
73
+ value:
74
+ The hyperparameter value.
75
+ key_value_dict (`dict`):
76
+ A dictionary with multiple key-values to override.
77
+
78
+ Example:
79
+
80
+ ```py
81
+ import torch
82
+ import bitsandbytes as bnb
83
+
84
+ mng = bnb.optim.GlobalOptimManager.get_instance()
85
+
86
+ model = MyModel()
87
+ mng.register_parameters(model.parameters()) # 1. register parameters while still on CPU
88
+
89
+ model = model.cuda()
90
+ # use 8-bit optimizer states for all parameters
91
+ adam = bnb.optim.Adam(model.parameters(), lr=0.001, optim_bits=8)
92
+
93
+ # 2. override: the parameter model.fc1.weight now uses 32-bit Adam
94
+ mng.override_config(model.fc1.weight, 'optim_bits', 32)
95
+ ```
96
+ """
97
+ self.uses_config_override = True
98
+ if isinstance(parameters, torch.nn.Parameter):
99
+ parameters = [parameters]
100
+ if isinstance(parameters, torch.Tensor):
101
+ parameters = [parameters]
102
+ if key is not None and value is not None:
103
+ assert key_value_dict is None
104
+ key_value_dict = {key: value}
105
+
106
+ if key_value_dict is not None:
107
+ for p in parameters:
108
+ if id(p) in self.pid2config:
109
+ self.pid2config[id(p)].update(key_value_dict)
110
+ else:
111
+ self.pid2config[id(p)] = key_value_dict
112
+
113
+ def register_module_override(self, module, param_name, config):
114
+ self.module_weight_config_triple.append((module, param_name, config))
115
+
116
+
117
+ class Optimizer8bit(torch.optim.Optimizer):
118
+ _FSDP_WRAPPED_QUANT_STATE_KEY = "__bnb_optimizer_quant_state__"
119
+
120
+ def __init__(self, params, defaults, optim_bits=32, is_paged=False):
121
+ """
122
+ Base 8-bit optimizer class.
123
+
124
+ Arguments:
125
+ params (`torch.Tensor`):
126
+ The input parameters to optimize.
127
+ optim_bits (`int`, defaults to 32):
128
+ The number of bits of the optimizer state.
129
+ is_paged (`bool`, defaults to `False`):
130
+ Whether the optimizer is a paged optimizer or not.
131
+ """
132
+ super().__init__(params, defaults)
133
+ self.initialized = False
134
+ self.name2qmap = {}
135
+ self.is_paged = is_paged
136
+ self.page_mng = F.GlobalPageManager.get_instance()
137
+
138
+ self.mng = GlobalOptimManager.get_instance()
139
+ self.non_castable_tensor_keys = {
140
+ "qmap1",
141
+ "qmap2",
142
+ "max1",
143
+ "max2",
144
+ "new_max1",
145
+ "new_max2",
146
+ "state1",
147
+ "state2",
148
+ "gnorm_vec",
149
+ "absmax1",
150
+ "absmax2",
151
+ "unorm_vec",
152
+ }
153
+
154
+ if optim_bits == 8:
155
+ self.fill_qmap()
156
+
157
+ def fill_qmap(self):
158
+ self.name2qmap["dynamic"] = F.create_dynamic_map(signed=True)
159
+ self.name2qmap["udynamic"] = F.create_dynamic_map(signed=False)
160
+
161
+ def state_dict(self):
162
+ """Return optimizer state, wrapping quantization tensors for FSDP compatibility.
163
+
164
+ FSDP's full_optim_state_dict gathers all tensor states across ranks.
165
+ Quantization states (state1, state2, absmax, etc.) have different shapes
166
+ than model parameters, causing gather operations to fail. By wrapping
167
+ these tensors in a nested dict, FSDP skips them during gathering.
168
+ """
169
+ state_dict = super().state_dict()
170
+
171
+ # Deep copy the state to avoid modifying the original optimizer state
172
+ # PyTorch's state_dict() only does a shallow copy
173
+ state_dict["state"] = {
174
+ k: {kk: vv for kk, vv in v.items()} if isinstance(v, dict) else v for k, v in state_dict["state"].items()
175
+ }
176
+
177
+ # Wrap quantization-specific tensors in a nested dict to hide from FSDP
178
+ for param_state in state_dict["state"].values():
179
+ if isinstance(param_state, dict):
180
+ quant_state = {}
181
+ keys_to_wrap = [k for k in param_state if k in self.non_castable_tensor_keys]
182
+ for key in keys_to_wrap:
183
+ quant_state[key] = param_state.pop(key)
184
+ if quant_state:
185
+ param_state[self._FSDP_WRAPPED_QUANT_STATE_KEY] = quant_state
186
+
187
+ return state_dict
188
+
189
+ def __setstate__(self, state):
190
+ super().__setstate__(state)
191
+
192
+ def load_state_dict(self, state_dict, move_to_device=True):
193
+ """Load an optimizer state.
194
+
195
+ Arguments:
196
+ state_dict (`dict`):
197
+ An optimizer state (should be returned from a call to `state_dict`) to load.
198
+ move_to_device (`bool`, defaults to `True`):
199
+ Whether to move the optimizer's state to the device.
200
+ """
201
+ # deepcopy, to be consistent with module API
202
+ state_dict = deepcopy(state_dict)
203
+
204
+ # Unwrap quantization states that were wrapped for FSDP compatibility
205
+ for param_state in state_dict["state"].values():
206
+ if isinstance(param_state, dict) and self._FSDP_WRAPPED_QUANT_STATE_KEY in param_state:
207
+ quant_state = param_state.pop(self._FSDP_WRAPPED_QUANT_STATE_KEY)
208
+ param_state.update(quant_state)
209
+
210
+ # Validate the state_dict
211
+ groups = self.param_groups
212
+ saved_groups = state_dict["param_groups"]
213
+
214
+ if len(groups) != len(saved_groups):
215
+ raise ValueError("loaded state dict has a different number of parameter groups")
216
+ param_lens = (len(g["params"]) for g in groups)
217
+ saved_lens = (len(g["params"]) for g in saved_groups)
218
+ if any(p_len != s_len for p_len, s_len in zip(param_lens, saved_lens)):
219
+ raise ValueError(
220
+ "loaded state dict contains a parameter group that doesn't match the size of optimizer's group",
221
+ )
222
+
223
+ # Update the state
224
+ id_map = {
225
+ old_id: p
226
+ for old_id, p in zip(
227
+ chain.from_iterable(g["params"] for g in saved_groups),
228
+ chain.from_iterable(g["params"] for g in groups),
229
+ )
230
+ }
231
+
232
+ def cast(param, value):
233
+ r"""Make a deep copy of value, casting all tensors to device of param."""
234
+ if isinstance(value, torch.Tensor):
235
+ # Floating-point types are a bit special here. They are the only ones
236
+ # that are assumed to always match the type of params.
237
+ if param.is_floating_point() and value.dtype != torch.uint8:
238
+ value = value.to(param.dtype)
239
+ return value
240
+ elif isinstance(value, dict):
241
+ for k, v in value.items():
242
+ if k in self.non_castable_tensor_keys:
243
+ if move_to_device:
244
+ value[k] = v.to(param.device)
245
+ else:
246
+ value[k] = cast(param, v)
247
+
248
+ return value
249
+ elif isinstance(value, container_abcs.Iterable):
250
+ return type(value)(cast(param, v) for v in value)
251
+ else:
252
+ return value
253
+
254
+ # Copy state assigned to params (and cast tensors to appropriate types).
255
+ # State that is not assigned to params is copied as is (needed for
256
+ # backward compatibility).
257
+ state = defaultdict(dict)
258
+ for k, v in state_dict["state"].items():
259
+ if k in id_map:
260
+ param = id_map[k]
261
+ state[param] = cast(param, v)
262
+ else:
263
+ state[k] = v
264
+
265
+ # Update parameter groups, setting their 'params' value
266
+ def update_group(group, new_group):
267
+ new_group["params"] = group["params"]
268
+ return new_group
269
+
270
+ param_groups = [update_group(g, ng) for g, ng in zip(groups, saved_groups)]
271
+ self.__setstate__({"state": state, "param_groups": param_groups})
272
+
273
+ def to_gpu(self):
274
+ for gindex, group in enumerate(self.param_groups):
275
+ for pindex, p in enumerate(group["params"]):
276
+ if p.device.type == "cpu":
277
+ continue
278
+ if p in self.state:
279
+ values = self.state[p]
280
+ for k, v in values.items():
281
+ if isinstance(v, torch.Tensor):
282
+ is_paged = getattr(v, "is_paged", False)
283
+ if not is_paged:
284
+ self.state[p][k] = v.to(p.device)
285
+
286
+ def check_overrides(self):
287
+ for module, attr, config in self.mng.module_weight_config_triple:
288
+ pmodule = getattr(module, attr)
289
+ assert pmodule is not None
290
+ assert isinstance(pmodule, torch.Tensor) or isinstance(pmodule, torch.Parameter)
291
+ found = False
292
+ for gindex, group in enumerate(self.param_groups):
293
+ if found:
294
+ break
295
+ for pindex, p in enumerate(group["params"]):
296
+ if found:
297
+ break
298
+ if id(p) == id(pmodule):
299
+ # found the matching parameter
300
+ # init override
301
+ self.mng.pid2config[id(p)] = config
302
+ self.mng.index2config[(gindex, pindex)] = self.mng.pid2config[id(p)]
303
+ found = True
304
+
305
+ @torch.no_grad()
306
+ def step(self, closure=None):
307
+ """Perform a single optimization step.
308
+
309
+ Arguments:
310
+ closure (`Callable`, *optional*, defaults to `None`):
311
+ A closure that reevaluates the model and returns the loss.
312
+ """
313
+ loss = None
314
+ if closure is not None:
315
+ with torch.enable_grad():
316
+ loss = closure()
317
+
318
+ if not self.initialized:
319
+ self.check_overrides()
320
+ self.to_gpu() # needed for fairseq pure fp16 training
321
+ self.initialized = True
322
+
323
+ # if self.is_paged: self.page_mng.prefetch_all()
324
+ p = None
325
+ for gindex, group in enumerate(self.param_groups):
326
+ for pindex, p in enumerate(group["params"]):
327
+ if p.grad is None:
328
+ continue
329
+ state = self.state[p]
330
+ if len(state) == 0:
331
+ self.init_state(group, p, gindex, pindex)
332
+
333
+ self.prefetch_state(p)
334
+ self.update_step(group, p, gindex, pindex)
335
+ sync_gpu(p)
336
+ if self.is_paged and p is not None:
337
+ # all paged operations are asynchronous, we need
338
+ # to sync to make sure all tensors are in the right state
339
+ sync_gpu(p)
340
+
341
+ return loss
342
+
343
+ def get_config(self, gindex, pindex, group):
344
+ config = {}
345
+ config["betas"] = group["betas"]
346
+ config["eps"] = group["eps"]
347
+ config["weight_decay"] = group["weight_decay"]
348
+ config["lr"] = group["lr"]
349
+ config["alpha"] = group.get("alpha", 0.0)
350
+ config["t_alpha"] = group.get("t_alpha", None)
351
+ config["t_beta3"] = group.get("t_beta3", None)
352
+ config["optim_bits"] = self.args.optim_bits
353
+ config["min_8bit_size"] = self.args.min_8bit_size
354
+ config["max_unorm"] = self.args.max_unorm
355
+ config["skip_zeros"] = self.args.skip_zeros
356
+
357
+ if (gindex, pindex) in self.mng.index2config:
358
+ config.update(self.mng.index2config[(gindex, pindex)])
359
+
360
+ # Also check pid2config as a fallback so that override_config works
361
+ # regardless of whether it was called before or after register_parameters.
362
+ p = self.param_groups[gindex]["params"][pindex]
363
+ if id(p) in self.mng.pid2config:
364
+ config.update(self.mng.pid2config[id(p)])
365
+
366
+ return config
367
+
368
+ def init_state(self, group, p, gindex, pindex):
369
+ raise NotImplementedError("init_state method needs to be overridden")
370
+
371
+ def update_step(self, group, p, gindex, pindex):
372
+ raise NotImplementedError("The update_step method needs to be overridden")
373
+
374
+ def get_state_buffer(self, p, dtype=torch.float32):
375
+ if p.device.type == "cpu":
376
+ if self.is_paged and not getattr(self, "_cpu_paged_warned", False):
377
+ warnings.warn(
378
+ "Paged optimizers are not supported on CPU. Falling back to non-paged optimizer behavior.",
379
+ stacklevel=2,
380
+ )
381
+ self._cpu_paged_warned = True
382
+ return torch.zeros_like(p, dtype=dtype, device=p.device)
383
+ if not self.is_paged or p.numel() < 1e5:
384
+ return torch.zeros_like(p, dtype=dtype, device=p.device)
385
+ else:
386
+ # > 1 MB
387
+ buff = F.get_paged(*p.shape, dtype=dtype, device=p.device)
388
+ F.fill(buff, 0)
389
+ self.page_mng.paged_tensors.append(buff)
390
+ return buff
391
+
392
+ def prefetch_state(self, p):
393
+ if self.is_paged:
394
+ state = self.state[p]
395
+ s1 = state["state1"]
396
+ is_paged = getattr(s1, "is_paged", False)
397
+ if is_paged:
398
+ F.prefetch_tensor(state["state1"])
399
+ if "state2" in state:
400
+ F.prefetch_tensor(state["state2"])
401
+
402
+
403
+ class Optimizer2State(Optimizer8bit):
404
+ def __init__(
405
+ self,
406
+ optimizer_name,
407
+ params,
408
+ lr=1e-3,
409
+ betas=(0.9, 0.999),
410
+ eps=1e-8,
411
+ weight_decay=0.0,
412
+ optim_bits=32,
413
+ args=None,
414
+ min_8bit_size=4096,
415
+ max_unorm=0.0,
416
+ skip_zeros=False,
417
+ is_paged=False,
418
+ alpha=0.0,
419
+ t_alpha: Optional[int] = None,
420
+ t_beta3: Optional[int] = None,
421
+ ):
422
+ """
423
+ Base 2-state update optimizer class.
424
+
425
+ Arguments:
426
+ optimizer_name (`str`):
427
+ The name of the optimizer.
428
+ params (`torch.Tensor`):
429
+ The input parameters to optimize.
430
+ lr (`float`, defaults to 1e-3):
431
+ The learning rate.
432
+ betas (`tuple`, defaults to (0.9, 0.999)):
433
+ The beta values for the optimizer.
434
+ eps (`float`, defaults to 1e-8):
435
+ The epsilon value for the optimizer.
436
+ weight_decay (`float`, defaults to 0.0):
437
+ The weight decay value for the optimizer.
438
+ optim_bits (`int`, defaults to 32):
439
+ The number of bits of the optimizer state.
440
+ args (`object`, defaults to `None`):
441
+ An object with additional arguments.
442
+ min_8bit_size (`int`, defaults to 4096):
443
+ The minimum number of elements of the parameter tensors for 8-bit optimization.
444
+ max_unorm (`float`, defaults to 0.0):
445
+ The maximum value to normalize each block with.
446
+ skip_zeros (`bool`, defaults to `False`):
447
+ Whether to skip zero values for sparse gradients and models to ensure correct updates.
448
+ is_paged (`bool`, defaults to `False`):
449
+ Whether the optimizer is a paged optimizer or not.
450
+ alpha (`float`, defaults to 0.0):
451
+ The alpha value for the AdEMAMix optimizer.
452
+ t_alpha (`Optional[int]`, defaults to `None`):
453
+ Number of iterations for alpha scheduling with AdEMAMix.
454
+ t_beta3 (`Optional[int]`, defaults to `None`):
455
+ Number of iterations for beta scheduling with AdEMAMix.
456
+
457
+ """
458
+ if not 0.0 <= lr:
459
+ raise ValueError(f"Invalid learning rate: {lr}")
460
+ if not 0.0 <= eps:
461
+ raise ValueError(f"Invalid epsilon value: {eps}")
462
+ if isinstance(betas, str):
463
+ # format: '(beta1, beta2)'
464
+ betas = betas.replace("(", "").replace(")", "").strip().split(",")
465
+ betas = [float(b) for b in betas]
466
+ for i in range(len(betas)):
467
+ if not 0.0 <= betas[i] < 1.0:
468
+ raise ValueError(f"Invalid beta parameter at index {i}: {betas[i]}")
469
+ if not 0.0 <= weight_decay:
470
+ raise ValueError(f"Invalid weight_decay value: {weight_decay}")
471
+
472
+ defaults = dict(
473
+ lr=lr, betas=betas, eps=eps, weight_decay=weight_decay, alpha=alpha, t_alpha=t_alpha, t_beta3=t_beta3
474
+ )
475
+
476
+ super().__init__(params, defaults, optim_bits, is_paged)
477
+
478
+ if args is None:
479
+ args = {}
480
+ args["optim_bits"] = optim_bits
481
+ args["min_8bit_size"] = min_8bit_size
482
+ args["max_unorm"] = max_unorm
483
+ args["skip_zeros"] = skip_zeros
484
+
485
+ self.args = MockArgs(args)
486
+ else:
487
+ self.args = args
488
+
489
+ self.optimizer_name = optimizer_name
490
+
491
+ @torch.no_grad()
492
+ def init_state(self, group, p, gindex, pindex):
493
+ config = self.get_config(gindex, pindex, group)
494
+
495
+ if config["optim_bits"] == 32:
496
+ dtype = torch.float32
497
+ elif config["optim_bits"] == 8:
498
+ dtype = torch.uint8
499
+ else:
500
+ raise NotImplementedError(f"Amount of optimizer bits not supported: {config['optim_bits']}")
501
+
502
+ if p.numel() < config["min_8bit_size"]:
503
+ dtype = torch.float32
504
+
505
+ state = self.state[p]
506
+ state["step"] = 0
507
+
508
+ if dtype == torch.float32:
509
+ state["state1"] = self.get_state_buffer(p, dtype=torch.float32)
510
+ state["state2"] = self.get_state_buffer(p, dtype=torch.float32)
511
+ elif dtype == torch.uint8:
512
+ if state["step"] == 0:
513
+ if "dynamic" not in self.name2qmap:
514
+ self.fill_qmap()
515
+ self.name2qmap["dynamic"] = self.name2qmap["dynamic"].to(p.device)
516
+ self.name2qmap["udynamic"] = self.name2qmap["udynamic"].to(p.device)
517
+
518
+ state["state1"] = self.get_state_buffer(p, dtype=torch.uint8)
519
+ state["qmap1"] = self.name2qmap["dynamic"]
520
+
521
+ state["state2"] = self.get_state_buffer(p, dtype=torch.uint8)
522
+ state["qmap2"] = self.name2qmap["udynamic"]
523
+
524
+ blocksize = 256
525
+ n = p.numel()
526
+ blocks = (n // blocksize) + bool(n % blocksize)
527
+
528
+ state["absmax1"] = torch.zeros((blocks,), dtype=torch.float32, device=p.device)
529
+ state["absmax2"] = torch.zeros((blocks,), dtype=torch.float32, device=p.device)
530
+
531
+ if config["max_unorm"] > 0.0:
532
+ state["unorm_vec"] = torch.zeros((1,), device=p.device)
533
+
534
+ @torch.no_grad()
535
+ def update_step(self, group, p, gindex, pindex):
536
+ # avoid update error from non-contiguous memory layout
537
+ p.data = p.data.contiguous()
538
+ p.grad = p.grad.contiguous()
539
+
540
+ state = self.state[p]
541
+ grad = p.grad
542
+
543
+ config = self.get_config(gindex, pindex, group)
544
+
545
+ state["step"] += 1
546
+ step = state["step"]
547
+
548
+ if state["state1"].dtype == torch.float:
549
+ F.optimizer_update_32bit(
550
+ self.optimizer_name,
551
+ grad,
552
+ p,
553
+ state["state1"],
554
+ config["betas"][0],
555
+ config["eps"],
556
+ step,
557
+ config["lr"],
558
+ state["state2"],
559
+ config["betas"][1],
560
+ config["betas"][2] if len(config["betas"]) >= 3 else 0.0,
561
+ config.get("alpha", 0.0),
562
+ config["weight_decay"],
563
+ 1.0,
564
+ state["unorm_vec"] if config["max_unorm"] > 0.0 else None,
565
+ max_unorm=config["max_unorm"],
566
+ skip_zeros=config["skip_zeros"],
567
+ )
568
+
569
+ elif state["state1"].dtype == torch.uint8:
570
+ F.optimizer_update_8bit_blockwise(
571
+ self.optimizer_name,
572
+ grad,
573
+ p,
574
+ state["state1"],
575
+ state["state2"],
576
+ config["betas"][0],
577
+ config["betas"][1],
578
+ config["betas"][2] if len(config["betas"]) >= 3 else 0.0,
579
+ config.get("alpha", 0.0),
580
+ config["eps"],
581
+ step,
582
+ config["lr"],
583
+ state["qmap1"],
584
+ state["qmap2"],
585
+ state["absmax1"],
586
+ state["absmax2"],
587
+ config["weight_decay"],
588
+ gnorm_scale=1.0,
589
+ skip_zeros=config["skip_zeros"],
590
+ )
591
+
592
+
593
+ class Optimizer1State(Optimizer8bit):
594
+ def __init__(
595
+ self,
596
+ optimizer_name,
597
+ params,
598
+ lr=1e-3,
599
+ betas=(0.9, 0.0),
600
+ eps=1e-8,
601
+ weight_decay=0.0,
602
+ optim_bits=32,
603
+ args=None,
604
+ min_8bit_size=4096,
605
+ max_unorm=0.0,
606
+ skip_zeros=False,
607
+ is_paged=False,
608
+ ):
609
+ """
610
+ Base 1-state update optimizer class.
611
+
612
+ Arguments:
613
+ optimizer_name (`str`):
614
+ The name of the optimizer.
615
+ params (`torch.Tensor`):
616
+ The input parameters to optimize.
617
+ lr (`float`, defaults to 1e-3):
618
+ The learning rate.
619
+ betas (`tuple`, defaults to (0.9, 0.0)):
620
+ The beta values for the optimizer.
621
+ eps (`float`, defaults to 1e-8):
622
+ The epsilon value for the optimizer.
623
+ weight_decay (`float`, defaults to 0.0):
624
+ The weight decay value for the optimizer.
625
+ optim_bits (`int`, defaults to 32):
626
+ The number of bits of the optimizer state.
627
+ args (`object`, defaults to `None`):
628
+ An object with additional arguments.
629
+ min_8bit_size (`int`, defaults to 4096):
630
+ The minimum number of elements of the parameter tensors for 8-bit optimization.
631
+ max_unorm (`float`, defaults to 0.0):
632
+ The maximum value to normalize each block with.
633
+ skip_zeros (`bool`, defaults to `False`):
634
+ Whether to skip zero values for sparse gradients and models to ensure correct updates.
635
+ is_paged (`bool`, defaults to `False`):
636
+ Whether the optimizer is a paged optimizer or not.
637
+ """
638
+ if not 0.0 <= lr:
639
+ raise ValueError(f"Invalid learning rate: {lr}")
640
+ if not 0.0 <= eps:
641
+ raise ValueError(f"Invalid epsilon value: {eps}")
642
+ for i in range(len(betas)):
643
+ if not 0.0 <= betas[i] < 1.0:
644
+ raise ValueError(f"Invalid beta parameter at index {i}: {betas[i]}")
645
+ if not 0.0 <= weight_decay:
646
+ raise ValueError(f"Invalid weight_decay value: {weight_decay}")
647
+ defaults = dict(lr=lr, betas=betas, eps=eps, weight_decay=weight_decay)
648
+ super().__init__(params, defaults, optim_bits, is_paged)
649
+
650
+ if args is None:
651
+ args = {}
652
+ args["optim_bits"] = optim_bits
653
+ args["min_8bit_size"] = min_8bit_size
654
+ args["max_unorm"] = max_unorm
655
+ args["skip_zeros"] = skip_zeros
656
+
657
+ self.args = MockArgs(args)
658
+ else:
659
+ self.args = args
660
+
661
+ self.optimizer_name = optimizer_name
662
+
663
+ @torch.no_grad()
664
+ def init_state(self, group, p, gindex, pindex):
665
+ config = self.get_config(gindex, pindex, group)
666
+
667
+ if config["optim_bits"] == 32:
668
+ dtype = torch.float32
669
+ elif config["optim_bits"] == 8:
670
+ dtype = torch.uint8
671
+ else:
672
+ raise NotImplementedError(f"Amount of optimizer bits not supported: {config['optim_bits']}")
673
+
674
+ if p.numel() < config["min_8bit_size"]:
675
+ dtype = torch.float32
676
+
677
+ state = self.state[p]
678
+ state["step"] = 0
679
+
680
+ if dtype == torch.float32:
681
+ state["state1"] = self.get_state_buffer(p, dtype=torch.float32)
682
+ elif dtype == torch.uint8:
683
+ if state["step"] == 0:
684
+ if "dynamic" not in self.name2qmap:
685
+ self.fill_qmap()
686
+ self.name2qmap["dynamic"] = self.name2qmap["dynamic"].to(p.device)
687
+
688
+ state["state1"] = self.get_state_buffer(p, dtype=torch.uint8)
689
+ state["qmap1"] = self.name2qmap["dynamic"]
690
+
691
+ blocksize = 256
692
+ n = p.numel()
693
+ blocks = (n // blocksize) + bool(n % blocksize)
694
+
695
+ state["absmax1"] = torch.zeros((blocks,), dtype=torch.float32, device=p.device)
696
+
697
+ if config["max_unorm"] > 0.0:
698
+ state["unorm_vec"] = torch.zeros((1,), device=p.device)
699
+
700
+ @torch.no_grad()
701
+ def update_step(self, group, p, gindex, pindex):
702
+ # avoid update error from non-contiguous memory layout
703
+ p.data = p.data.contiguous()
704
+ p.grad = p.grad.contiguous()
705
+
706
+ state = self.state[p]
707
+ grad = p.grad
708
+
709
+ config = self.get_config(gindex, pindex, group)
710
+
711
+ state["step"] += 1
712
+ step = state["step"]
713
+
714
+ if state["state1"].dtype == torch.float:
715
+ F.optimizer_update_32bit(
716
+ self.optimizer_name,
717
+ grad,
718
+ p,
719
+ state["state1"],
720
+ config["betas"][0],
721
+ config["eps"],
722
+ step,
723
+ config["lr"],
724
+ None,
725
+ config["betas"][1],
726
+ 0.0,
727
+ 0.0,
728
+ config["weight_decay"],
729
+ 1.0,
730
+ state["unorm_vec"] if config["max_unorm"] > 0.0 else None,
731
+ max_unorm=config["max_unorm"],
732
+ skip_zeros=config["skip_zeros"],
733
+ )
734
+
735
+ elif state["state1"].dtype == torch.uint8:
736
+ F.optimizer_update_8bit_blockwise(
737
+ self.optimizer_name,
738
+ grad,
739
+ p,
740
+ state["state1"],
741
+ None,
742
+ config["betas"][0],
743
+ config["betas"][1],
744
+ 0.0,
745
+ 0.0,
746
+ config["eps"],
747
+ step,
748
+ config["lr"],
749
+ state["qmap1"],
750
+ None,
751
+ state["absmax1"],
752
+ None,
753
+ config["weight_decay"],
754
+ gnorm_scale=1.0,
755
+ skip_zeros=config["skip_zeros"],
756
+ )