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.
- bitsandbytes/__init__.py +78 -0
- bitsandbytes/__main__.py +4 -0
- bitsandbytes/_ops.py +510 -0
- bitsandbytes/autograd/__init__.py +0 -0
- bitsandbytes/autograd/_functions.py +491 -0
- bitsandbytes/backends/__init__.py +0 -0
- bitsandbytes/backends/cpu/__init__.py +0 -0
- bitsandbytes/backends/cpu/ops.py +580 -0
- bitsandbytes/backends/cuda/__init__.py +0 -0
- bitsandbytes/backends/cuda/ops.py +1199 -0
- bitsandbytes/backends/default/__init__.py +0 -0
- bitsandbytes/backends/default/ops.py +632 -0
- bitsandbytes/backends/hpu/__init__.py +0 -0
- bitsandbytes/backends/hpu/ops.py +53 -0
- bitsandbytes/backends/mps/__init__.py +0 -0
- bitsandbytes/backends/mps/ops.py +277 -0
- bitsandbytes/backends/triton/__init__.py +0 -0
- bitsandbytes/backends/triton/kernels_4bit.py +577 -0
- bitsandbytes/backends/triton/kernels_8bit_quant.py +195 -0
- bitsandbytes/backends/triton/kernels_optim.py +1177 -0
- bitsandbytes/backends/triton/ops.py +304 -0
- bitsandbytes/backends/utils.py +94 -0
- bitsandbytes/backends/xpu/__init__.py +0 -0
- bitsandbytes/backends/xpu/ops.py +305 -0
- bitsandbytes/cextension.py +405 -0
- bitsandbytes/consts.py +12 -0
- bitsandbytes/cuda_specs.py +111 -0
- bitsandbytes/diagnostics/__init__.py +0 -0
- bitsandbytes/diagnostics/cuda.py +193 -0
- bitsandbytes/diagnostics/main.py +134 -0
- bitsandbytes/diagnostics/utils.py +12 -0
- bitsandbytes/functional.py +1810 -0
- bitsandbytes/libbitsandbytes_cpu.dll +0 -0
- bitsandbytes/nn/__init__.py +19 -0
- bitsandbytes/nn/modules.py +1220 -0
- bitsandbytes/nn/parametrize.py +206 -0
- bitsandbytes/optim/__init__.py +22 -0
- bitsandbytes/optim/adagrad.py +187 -0
- bitsandbytes/optim/adam.py +346 -0
- bitsandbytes/optim/adamw.py +337 -0
- bitsandbytes/optim/ademamix.py +410 -0
- bitsandbytes/optim/lamb.py +190 -0
- bitsandbytes/optim/lars.py +259 -0
- bitsandbytes/optim/lion.py +266 -0
- bitsandbytes/optim/optimizer.py +756 -0
- bitsandbytes/optim/rmsprop.py +170 -0
- bitsandbytes/optim/sgd.py +152 -0
- bitsandbytes/py.typed +0 -0
- bitsandbytes/utils.py +208 -0
- bitsandbytes-0.50.0.dist-info/METADATA +288 -0
- bitsandbytes-0.50.0.dist-info/RECORD +54 -0
- bitsandbytes-0.50.0.dist-info/WHEEL +5 -0
- bitsandbytes-0.50.0.dist-info/licenses/LICENSE +21 -0
- 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
|
+
)
|