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
|
File without changes
|
|
@@ -0,0 +1,632 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
from functools import cache, wraps
|
|
3
|
+
from math import prod, sqrt
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import torch
|
|
7
|
+
|
|
8
|
+
from ..._ops import register_kernel
|
|
9
|
+
from ..utils import _get_4bit_code
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _try_torch_compile(func=None, **compile_kwargs):
|
|
13
|
+
"""
|
|
14
|
+
Wrapper around torch.compile that falls back to the original function if compilation fails.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def decorator(fn):
|
|
18
|
+
try:
|
|
19
|
+
compiled_fn = torch.compile(fn, **compile_kwargs)
|
|
20
|
+
|
|
21
|
+
@wraps(fn)
|
|
22
|
+
def wrapper(*args, **kwargs):
|
|
23
|
+
try:
|
|
24
|
+
return compiled_fn(*args, **kwargs)
|
|
25
|
+
except Exception:
|
|
26
|
+
return fn(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
return wrapper
|
|
29
|
+
except Exception:
|
|
30
|
+
return fn
|
|
31
|
+
|
|
32
|
+
if func is None:
|
|
33
|
+
return decorator
|
|
34
|
+
else:
|
|
35
|
+
return decorator(func)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@register_kernel("bitsandbytes::int8_mm_dequant", "default")
|
|
39
|
+
def _(
|
|
40
|
+
A: torch.Tensor,
|
|
41
|
+
row_stats: torch.Tensor,
|
|
42
|
+
col_stats: torch.Tensor,
|
|
43
|
+
dtype: Optional[torch.dtype] = None,
|
|
44
|
+
bias: Optional[torch.Tensor] = None,
|
|
45
|
+
) -> torch.Tensor:
|
|
46
|
+
if A.dtype != torch.int32:
|
|
47
|
+
raise ValueError(f"A must be int32, got {A.dtype}")
|
|
48
|
+
if row_stats.dtype != torch.float32:
|
|
49
|
+
raise ValueError(f"row_stats must be float32, got {row_stats.dtype}")
|
|
50
|
+
if col_stats.dtype != torch.float32:
|
|
51
|
+
raise ValueError(f"col_stats must be float32, got {col_stats.dtype}")
|
|
52
|
+
|
|
53
|
+
A_calc = A.view(-1, A.shape[-1])
|
|
54
|
+
row_stats = row_stats.reshape(-1).unsqueeze(-1)
|
|
55
|
+
col_stats = col_stats.reshape(-1).unsqueeze(0)
|
|
56
|
+
|
|
57
|
+
out = A_calc * (row_stats * col_stats) * 6.200124e-05
|
|
58
|
+
if bias is not None:
|
|
59
|
+
out += bias
|
|
60
|
+
|
|
61
|
+
return out.to(dtype or torch.float16)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@register_kernel("bitsandbytes::int8_mixed_scaled_mm", "default")
|
|
65
|
+
def _(
|
|
66
|
+
A: torch.Tensor,
|
|
67
|
+
CA: torch.Tensor,
|
|
68
|
+
CB: torch.Tensor,
|
|
69
|
+
SCA: torch.Tensor,
|
|
70
|
+
SCB: torch.Tensor,
|
|
71
|
+
outlier_cols: Optional[torch.Tensor] = None,
|
|
72
|
+
bias: Optional[torch.Tensor] = None,
|
|
73
|
+
) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
|
|
74
|
+
subB = None
|
|
75
|
+
|
|
76
|
+
if outlier_cols is not None and outlier_cols.numel():
|
|
77
|
+
# Extract the inputs with outliers in original precision
|
|
78
|
+
subA = A[:, outlier_cols].contiguous()
|
|
79
|
+
|
|
80
|
+
# Dequantize the corresponding weight columns
|
|
81
|
+
subB = (
|
|
82
|
+
torch.ops.bitsandbytes.int8_vectorwise_dequant.default(CB[:, outlier_cols].contiguous(), SCB)
|
|
83
|
+
.to(A.dtype)
|
|
84
|
+
.t()
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# TODO: if state.has_fp16_weights: subB = B[:, outlier_cols].t()
|
|
88
|
+
|
|
89
|
+
else:
|
|
90
|
+
# Needed for torch.compile when there are no outliers.
|
|
91
|
+
subA = torch.empty(0, device=A.device, dtype=A.dtype)
|
|
92
|
+
|
|
93
|
+
# Int8 Matmul + Dequant + Bias
|
|
94
|
+
output = torch.ops.bitsandbytes.int8_scaled_mm.default(CA, CB, SCA, SCB, bias=bias, dtype=A.dtype)
|
|
95
|
+
|
|
96
|
+
if subB is not None:
|
|
97
|
+
# Add the outlier columns back to the output
|
|
98
|
+
output = output.addmm(subA, subB)
|
|
99
|
+
|
|
100
|
+
return output, subA
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@register_kernel("bitsandbytes::int8_scaled_mm", "default")
|
|
104
|
+
def _(
|
|
105
|
+
A: torch.Tensor,
|
|
106
|
+
B: torch.Tensor,
|
|
107
|
+
row_stats: torch.Tensor,
|
|
108
|
+
col_stats: torch.Tensor,
|
|
109
|
+
bias: Optional[torch.Tensor] = None,
|
|
110
|
+
dtype: Optional[torch.dtype] = None,
|
|
111
|
+
) -> torch.Tensor:
|
|
112
|
+
out_i32 = torch.ops.bitsandbytes.int8_linear_matmul.default(A, B)
|
|
113
|
+
return torch.ops.bitsandbytes.int8_mm_dequant.default(
|
|
114
|
+
out_i32,
|
|
115
|
+
row_stats,
|
|
116
|
+
col_stats,
|
|
117
|
+
dtype=dtype or torch.float16,
|
|
118
|
+
bias=bias,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@register_kernel("bitsandbytes::int8_linear_matmul", "default")
|
|
123
|
+
def _(A: torch.Tensor, B: torch.Tensor):
|
|
124
|
+
return _int8_linear_matmul_impl(A, B)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@register_kernel("bitsandbytes::int8_linear_matmul.out", "default")
|
|
128
|
+
def _(A: torch.Tensor, B: torch.Tensor, out: torch.Tensor):
|
|
129
|
+
if out.dtype != torch.int32:
|
|
130
|
+
raise ValueError(f"out must be int32, got {out.dtype}")
|
|
131
|
+
_int8_linear_matmul_impl(A, B, out)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _int8_linear_matmul_impl(A: torch.Tensor, B: torch.Tensor, out: Optional[torch.Tensor] = None):
|
|
135
|
+
# Naive implementation: perform matmul in fp32
|
|
136
|
+
result = torch.matmul(A.float(), B.float().t()).to(torch.int32)
|
|
137
|
+
if out is not None:
|
|
138
|
+
result = out.copy_(result)
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@register_kernel("bitsandbytes::int8_vectorwise_quant", "default")
|
|
143
|
+
def _(A: torch.Tensor, threshold=0.0):
|
|
144
|
+
rows = A.numel() // A.shape[-1]
|
|
145
|
+
outlier_cols = None
|
|
146
|
+
|
|
147
|
+
outlier_restore = None
|
|
148
|
+
|
|
149
|
+
if threshold > 0.0:
|
|
150
|
+
outliers = A.abs() >= threshold
|
|
151
|
+
|
|
152
|
+
if outliers.any():
|
|
153
|
+
# Determine which columns contain outliers, and zero out the
|
|
154
|
+
# outliers ahead of quantization. We need to keep a backup of these
|
|
155
|
+
# outliers to restore them after quantization.
|
|
156
|
+
outlier_cols = torch.argwhere(outliers.any(dim=0)).view(-1)
|
|
157
|
+
outlier_restore = A[outliers].clone()
|
|
158
|
+
A[outliers] = 0
|
|
159
|
+
else:
|
|
160
|
+
# Needed for torch.compile support.
|
|
161
|
+
outlier_cols = torch.empty(0, device=A.device, dtype=torch.int64)
|
|
162
|
+
|
|
163
|
+
# Get absmax for each row.
|
|
164
|
+
row_stats = torch.max(A.abs(), dim=1).values.float()
|
|
165
|
+
|
|
166
|
+
# Quantize row-wise to int8.
|
|
167
|
+
out_row = torch.round(A * (127.0 / row_stats.unsqueeze(-1))).to(torch.int8)
|
|
168
|
+
|
|
169
|
+
# Zero out values from outlier columns across all rows.
|
|
170
|
+
if rows > 1 and outlier_cols is not None:
|
|
171
|
+
out_row[:, outlier_cols] = 0
|
|
172
|
+
|
|
173
|
+
# Restore outliers.
|
|
174
|
+
if outlier_restore is not None:
|
|
175
|
+
A[outliers] = outlier_restore
|
|
176
|
+
|
|
177
|
+
return out_row, row_stats, outlier_cols
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
@register_kernel("bitsandbytes::quantize_blockwise", "default")
|
|
181
|
+
def _(A: torch.Tensor, code: torch.Tensor, blocksize: int) -> tuple[torch.Tensor, torch.Tensor]:
|
|
182
|
+
A_flat = A.reshape(-1).float()
|
|
183
|
+
n = A_flat.numel()
|
|
184
|
+
rem = n % blocksize
|
|
185
|
+
full = n - rem
|
|
186
|
+
blocks = full // blocksize
|
|
187
|
+
A_com = A_flat[:full].reshape(blocks, blocksize)
|
|
188
|
+
absmax = A_com.abs().max(dim=-1)[0]
|
|
189
|
+
scaled = torch.clamp(A_com * (1.0 / absmax.clamp(min=1e-38).view(-1, 1)), -1, 1).reshape(-1)
|
|
190
|
+
if rem:
|
|
191
|
+
am = A_flat[full:].abs().max().clamp(min=1e-38)
|
|
192
|
+
absmax = torch.cat([absmax, am.unsqueeze(0)])
|
|
193
|
+
scaled = torch.cat([scaled, torch.clamp(A_flat[full:] / am, -1, 1)])
|
|
194
|
+
bounds = (code[:-1] + code[1:]) / 2 # code is always sorted (same assumption as CUDA kernel)
|
|
195
|
+
q = torch.bucketize(scaled, bounds, out_int32=True).to(torch.uint8)
|
|
196
|
+
return q.reshape(A.shape), absmax
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@_try_torch_compile(dynamic=False)
|
|
200
|
+
def _dequantize_blockwise_compute(
|
|
201
|
+
A_flat: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype
|
|
202
|
+
):
|
|
203
|
+
n = A_flat.numel()
|
|
204
|
+
out = code[A_flat.to(torch.int64)]
|
|
205
|
+
rem = n % blocksize
|
|
206
|
+
if rem == 0:
|
|
207
|
+
out = (out.reshape(-1, blocksize) * absmax.view(-1, 1)).reshape(n)
|
|
208
|
+
else:
|
|
209
|
+
full = n - rem
|
|
210
|
+
blocks = full // blocksize
|
|
211
|
+
out = torch.cat(
|
|
212
|
+
[
|
|
213
|
+
(out[:full].reshape(blocks, blocksize) * absmax[:blocks].view(-1, 1)).reshape(full),
|
|
214
|
+
out[full:] * absmax[blocks],
|
|
215
|
+
]
|
|
216
|
+
)
|
|
217
|
+
return out.to(dtype)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@register_kernel("bitsandbytes::dequantize_blockwise", "default")
|
|
221
|
+
def _(A: torch.Tensor, absmax: torch.Tensor, code: torch.Tensor, blocksize: int, dtype: torch.dtype) -> torch.Tensor:
|
|
222
|
+
return _dequantize_blockwise_compute(A.reshape(-1), absmax, code, blocksize, dtype).reshape(A.shape)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@cache
|
|
226
|
+
def _get_4bit_quantize_bounds(quant_type: str, device: torch.device):
|
|
227
|
+
code = _get_4bit_code(quant_type, device)
|
|
228
|
+
order = torch.argsort(code)
|
|
229
|
+
midpoints = (code[order[:-1]] + code[order[1:]]) / 2
|
|
230
|
+
return midpoints, order # NF4 order is identity (sorted); FP4 needs remap
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@register_kernel("bitsandbytes::quantize_4bit", "default")
|
|
234
|
+
def _(
|
|
235
|
+
A: torch.Tensor, blocksize: int, quant_type: str, quant_storage: torch.dtype
|
|
236
|
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
237
|
+
bounds, order = _get_4bit_quantize_bounds(quant_type, A.device)
|
|
238
|
+
A_flat = A.reshape(-1).float()
|
|
239
|
+
n = A_flat.numel()
|
|
240
|
+
rem = n % blocksize
|
|
241
|
+
full = n - rem
|
|
242
|
+
blocks = full // blocksize
|
|
243
|
+
A_com = A_flat[:full].reshape(blocks, blocksize)
|
|
244
|
+
absmax = A_com.abs().max(dim=-1)[0]
|
|
245
|
+
scaled = torch.clamp(A_com * (1.0 / absmax.clamp(min=1e-38).view(-1, 1)), -1, 1).reshape(-1)
|
|
246
|
+
if rem:
|
|
247
|
+
am = A_flat[full:].abs().max().clamp(min=1e-38)
|
|
248
|
+
absmax = torch.cat([absmax, am.unsqueeze(0)])
|
|
249
|
+
scaled = torch.cat([scaled, torch.clamp(A_flat[full:] / am, -1, 1)])
|
|
250
|
+
if scaled.numel() % 2:
|
|
251
|
+
scaled = torch.nn.functional.pad(scaled, (0, 1))
|
|
252
|
+
q = torch.bucketize(scaled, bounds, out_int32=True)
|
|
253
|
+
if quant_type != "nf4":
|
|
254
|
+
q = order[q]
|
|
255
|
+
q8 = q.to(torch.uint8)
|
|
256
|
+
packed = ((q8[::2] << 4) | q8[1::2]).unsqueeze(1)
|
|
257
|
+
if quant_storage != torch.uint8:
|
|
258
|
+
packed = packed.squeeze().view(quant_storage).unsqueeze(1)
|
|
259
|
+
return packed, absmax
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
@_try_torch_compile(dynamic=False)
|
|
263
|
+
def _dequantize_4bit_compute(
|
|
264
|
+
A_flat: torch.Tensor,
|
|
265
|
+
absmax: torch.Tensor,
|
|
266
|
+
code: torch.Tensor,
|
|
267
|
+
blocksize: int,
|
|
268
|
+
shape: Sequence[int],
|
|
269
|
+
dtype: torch.dtype,
|
|
270
|
+
):
|
|
271
|
+
n = prod(shape)
|
|
272
|
+
out_dq = torch.empty(A_flat.size(0) * 2, dtype=torch.int32, device=A_flat.device)
|
|
273
|
+
out_dq[1::2] = A_flat & 0xF
|
|
274
|
+
out_dq[::2] = A_flat >> 4
|
|
275
|
+
out_dq = code[out_dq][:n] # stays fp32, matches C++ / CUDA behavior
|
|
276
|
+
rem = n % blocksize
|
|
277
|
+
if rem:
|
|
278
|
+
full = n - rem
|
|
279
|
+
blocks = full // blocksize
|
|
280
|
+
out = torch.empty(n, dtype=torch.float32, device=A_flat.device)
|
|
281
|
+
out[:full] = (out_dq[:full].view(-1, blocksize) * absmax[:blocks].view(-1, 1)).reshape(full)
|
|
282
|
+
out[full:] = out_dq[full:] * absmax[blocks]
|
|
283
|
+
else:
|
|
284
|
+
out = (out_dq.view(-1, blocksize) * absmax.view(-1, 1)).reshape(n)
|
|
285
|
+
return out.reshape(-1, *shape[1:]).to(dtype)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
@register_kernel("bitsandbytes::dequantize_4bit", "default")
|
|
289
|
+
def _(
|
|
290
|
+
A: torch.Tensor,
|
|
291
|
+
absmax: torch.Tensor,
|
|
292
|
+
blocksize: int,
|
|
293
|
+
quant_type: str,
|
|
294
|
+
shape: Sequence[int],
|
|
295
|
+
dtype: torch.dtype,
|
|
296
|
+
) -> torch.Tensor:
|
|
297
|
+
if A.dtype != torch.uint8:
|
|
298
|
+
A = A.view(torch.uint8)
|
|
299
|
+
code = _get_4bit_code(quant_type, A.device)
|
|
300
|
+
return _dequantize_4bit_compute(A.reshape(-1), absmax, code, blocksize, shape, dtype)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
@register_kernel("bitsandbytes::gemv_4bit", "default")
|
|
304
|
+
def _(
|
|
305
|
+
A: torch.Tensor,
|
|
306
|
+
B: torch.Tensor,
|
|
307
|
+
shapeB: Sequence[int],
|
|
308
|
+
absmax: torch.Tensor,
|
|
309
|
+
code: torch.Tensor,
|
|
310
|
+
blocksize: int,
|
|
311
|
+
) -> torch.Tensor:
|
|
312
|
+
# Applied from dequantize_4bit
|
|
313
|
+
quant_type = "fp4" if code[1] > 0 else "nf4"
|
|
314
|
+
B_dq = torch.ops.bitsandbytes.dequantize_4bit.default(B, absmax, blocksize, quant_type, shapeB, A.dtype)
|
|
315
|
+
|
|
316
|
+
return torch.nn.functional.linear(
|
|
317
|
+
A,
|
|
318
|
+
B_dq,
|
|
319
|
+
bias=None,
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def _gemm_4bit_default_impl(
|
|
324
|
+
A: torch.Tensor,
|
|
325
|
+
B: torch.Tensor,
|
|
326
|
+
shapeB: Sequence[int],
|
|
327
|
+
absmax: torch.Tensor,
|
|
328
|
+
blocksize: int,
|
|
329
|
+
quant_type: str,
|
|
330
|
+
bias: Optional[torch.Tensor] = None,
|
|
331
|
+
absmax_8bit: Optional[torch.Tensor] = None,
|
|
332
|
+
absmax_code: Optional[torch.Tensor] = None,
|
|
333
|
+
absmax_offset: Optional[torch.Tensor] = None,
|
|
334
|
+
) -> torch.Tensor:
|
|
335
|
+
# When nested, per-block scale = absmax_code[absmax_8bit[i]] * absmax[i // 256] + absmax_offset
|
|
336
|
+
if absmax_8bit is not None:
|
|
337
|
+
absmax = (
|
|
338
|
+
torch.ops.bitsandbytes.dequantize_blockwise.default(absmax_8bit, absmax, absmax_code, 256, torch.float32)
|
|
339
|
+
+ absmax_offset
|
|
340
|
+
)
|
|
341
|
+
B_dq = torch.ops.bitsandbytes.dequantize_4bit.default(B, absmax, blocksize, quant_type, shapeB, A.dtype)
|
|
342
|
+
return torch.nn.functional.linear(A, B_dq, bias)
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
register_kernel("bitsandbytes::gemm_4bit", "default")(_gemm_4bit_default_impl)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
MOMENTUM = 0
|
|
349
|
+
RMSPROP = 1
|
|
350
|
+
ADAGRAD = 2
|
|
351
|
+
ADAM = 3
|
|
352
|
+
# LION should be larger than MOMENTUM, RMSPROP, ADAGRAD due to comparison in kernels
|
|
353
|
+
LION = 4
|
|
354
|
+
ADEMAMIX = 5
|
|
355
|
+
|
|
356
|
+
name2optimizer_id = {
|
|
357
|
+
"momentum": MOMENTUM,
|
|
358
|
+
"lars": MOMENTUM,
|
|
359
|
+
"rmsprop": RMSPROP,
|
|
360
|
+
"adagrad": ADAGRAD,
|
|
361
|
+
"adam": ADAM,
|
|
362
|
+
"lamb": ADAM,
|
|
363
|
+
"lion": LION,
|
|
364
|
+
"ademamix": ADEMAMIX,
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@_try_torch_compile
|
|
369
|
+
def _optimizer_precondition_32bit(
|
|
370
|
+
g: torch.Tensor,
|
|
371
|
+
p: torch.Tensor,
|
|
372
|
+
state1: torch.Tensor,
|
|
373
|
+
state2: Optional[torch.Tensor],
|
|
374
|
+
unorm_vec: torch.Tensor,
|
|
375
|
+
beta1: float,
|
|
376
|
+
beta2: float,
|
|
377
|
+
eps: float,
|
|
378
|
+
weight_decay: float,
|
|
379
|
+
step: int,
|
|
380
|
+
lr: float,
|
|
381
|
+
gnorm_scale: float,
|
|
382
|
+
optimizer_id: int,
|
|
383
|
+
):
|
|
384
|
+
"""Preprocessing optimizer, computing update norm"""
|
|
385
|
+
|
|
386
|
+
g_vals = gnorm_scale * g
|
|
387
|
+
|
|
388
|
+
if optimizer_id == 3: # ADAM
|
|
389
|
+
correction1 = 1.0 / (1.0 - beta1**step)
|
|
390
|
+
correction2 = 1.0 / (1.0 - beta2**step)
|
|
391
|
+
|
|
392
|
+
s1_vals = state1 * beta1 + (1.0 - beta1) * g_vals
|
|
393
|
+
s2_vals = state2 * beta2 + (1.0 - beta2) * g_vals * g_vals
|
|
394
|
+
|
|
395
|
+
s1_vals = s1_vals * correction1
|
|
396
|
+
s2_vals = s2_vals * correction2
|
|
397
|
+
|
|
398
|
+
update_vals = s1_vals / (torch.sqrt(s2_vals) + eps)
|
|
399
|
+
update_norm = update_vals * update_vals
|
|
400
|
+
|
|
401
|
+
elif optimizer_id == 5: # ADEMAMIX
|
|
402
|
+
update_norm = state1
|
|
403
|
+
|
|
404
|
+
elif optimizer_id == 0: # MOMENTUM
|
|
405
|
+
if step == 1:
|
|
406
|
+
s1_vals = g_vals
|
|
407
|
+
else:
|
|
408
|
+
s1_vals = state1 * beta1 + g_vals
|
|
409
|
+
update_norm = s1_vals * s1_vals
|
|
410
|
+
|
|
411
|
+
elif optimizer_id == 4: # LION
|
|
412
|
+
s1_vals = state1 * beta2 + (1.0 - beta2) * g_vals
|
|
413
|
+
update_norm = s1_vals
|
|
414
|
+
|
|
415
|
+
elif optimizer_id == 1: # RMSPROP
|
|
416
|
+
s1_vals = state1 * beta1 + (1.0 - beta1) * g_vals * g_vals
|
|
417
|
+
update_vals = g_vals / (torch.sqrt(s1_vals) + eps)
|
|
418
|
+
update_norm = update_vals * update_vals
|
|
419
|
+
|
|
420
|
+
elif optimizer_id == 2: # ADAGRAD
|
|
421
|
+
s1_vals = state1 + g_vals * g_vals
|
|
422
|
+
update_vals = g_vals / (torch.sqrt(s1_vals) + eps)
|
|
423
|
+
update_norm = update_vals * update_vals
|
|
424
|
+
|
|
425
|
+
total_norm = torch.sum(update_norm)
|
|
426
|
+
unorm_vec.add_(total_norm)
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
@_try_torch_compile
|
|
430
|
+
def _optimizer_update_32bit(
|
|
431
|
+
g: torch.Tensor,
|
|
432
|
+
p: torch.Tensor,
|
|
433
|
+
state1: torch.Tensor,
|
|
434
|
+
state2: Optional[torch.Tensor],
|
|
435
|
+
unorm_vec: Optional[torch.Tensor],
|
|
436
|
+
max_unorm: float,
|
|
437
|
+
param_norm: float,
|
|
438
|
+
beta1: float,
|
|
439
|
+
beta2: float,
|
|
440
|
+
beta3: float,
|
|
441
|
+
alpha: float,
|
|
442
|
+
eps: float,
|
|
443
|
+
weight_decay: float,
|
|
444
|
+
step: int,
|
|
445
|
+
lr: float,
|
|
446
|
+
gnorm_scale: float,
|
|
447
|
+
optimizer_id: int,
|
|
448
|
+
):
|
|
449
|
+
"""Unified optimizer update kernel"""
|
|
450
|
+
|
|
451
|
+
p_vals = p.float()
|
|
452
|
+
g_vals = (gnorm_scale * g).float()
|
|
453
|
+
# Coupled (L2) weight decay: fold wd into the gradient. This is correct for
|
|
454
|
+
# MOMENTUM/RMSPROP/ADAGRAD, but NOT for LION (id 4), which uses *decoupled*
|
|
455
|
+
# (AdamW-style) weight decay applied to the param directly (see the LION branch
|
|
456
|
+
# below and Chen et al. 2023). LION is intentionally excluded here.
|
|
457
|
+
if optimizer_id in [0, 1, 2] and weight_decay > 0.0:
|
|
458
|
+
g_vals = g_vals + p_vals * weight_decay
|
|
459
|
+
|
|
460
|
+
update_scale = 1.0
|
|
461
|
+
if max_unorm > 0.0:
|
|
462
|
+
current_unorm = torch.sqrt(unorm_vec)
|
|
463
|
+
if optimizer_id in [0, 1, 2, 4]: # 1-state optimizers
|
|
464
|
+
if current_unorm > max_unorm * param_norm + eps:
|
|
465
|
+
update_scale = (max_unorm * param_norm + eps) / current_unorm
|
|
466
|
+
else: # 2-state optimizers
|
|
467
|
+
if current_unorm > max_unorm * param_norm:
|
|
468
|
+
update_scale = (max_unorm * param_norm) / current_unorm
|
|
469
|
+
|
|
470
|
+
if optimizer_id == 3: # ADAM
|
|
471
|
+
s1_vals = state1 * beta1 + (1.0 - beta1) * g_vals
|
|
472
|
+
s2_vals = state2 * beta2 + (1.0 - beta2) * g_vals * g_vals
|
|
473
|
+
|
|
474
|
+
correction1 = 1.0 - beta1**step
|
|
475
|
+
correction2 = sqrt(1.0 - beta2**step)
|
|
476
|
+
step_size = -lr * correction2 / correction1
|
|
477
|
+
|
|
478
|
+
if weight_decay > 0.0:
|
|
479
|
+
p_vals = p_vals * (1.0 - lr * weight_decay)
|
|
480
|
+
|
|
481
|
+
update_val = update_scale * step_size * (s1_vals / (torch.sqrt(s2_vals) + eps * correction2))
|
|
482
|
+
p_vals = p_vals + update_val
|
|
483
|
+
|
|
484
|
+
state1.copy_(s1_vals)
|
|
485
|
+
state2.copy_(s2_vals)
|
|
486
|
+
|
|
487
|
+
elif optimizer_id == 5: # ADEMAMIX
|
|
488
|
+
s1_vals = state1[0]
|
|
489
|
+
s3_vals = state1[1]
|
|
490
|
+
s2_vals = state2
|
|
491
|
+
|
|
492
|
+
m1 = s1_vals * beta1 + (1.0 - beta1) * g_vals
|
|
493
|
+
m2 = s3_vals * beta3 + (1.0 - beta3) * g_vals
|
|
494
|
+
nu = s2_vals * beta2 + (1.0 - beta2) * g_vals * g_vals
|
|
495
|
+
|
|
496
|
+
correction1 = 1.0 - beta1**step
|
|
497
|
+
correction2 = sqrt(1.0 - beta2**step)
|
|
498
|
+
|
|
499
|
+
if weight_decay > 0.0:
|
|
500
|
+
p_vals = p_vals * (1.0 - lr * weight_decay)
|
|
501
|
+
|
|
502
|
+
mixed_momentum = (m1 / correction1) + (alpha * m2)
|
|
503
|
+
adaptive_term = (torch.sqrt(nu) / correction2) + eps
|
|
504
|
+
p_vals = p_vals - lr * (mixed_momentum / adaptive_term)
|
|
505
|
+
|
|
506
|
+
state1[0].copy_(m1)
|
|
507
|
+
state1[1].copy_(m2)
|
|
508
|
+
state2.copy_(nu)
|
|
509
|
+
|
|
510
|
+
elif optimizer_id == 0: # MOMENTUM
|
|
511
|
+
if step == 1:
|
|
512
|
+
s1_vals = g_vals
|
|
513
|
+
else:
|
|
514
|
+
s1_vals = state1 * beta1 + g_vals
|
|
515
|
+
|
|
516
|
+
update_val = update_scale * (-lr * s1_vals)
|
|
517
|
+
p_vals = p_vals + update_val
|
|
518
|
+
|
|
519
|
+
state1.copy_(s1_vals)
|
|
520
|
+
|
|
521
|
+
elif optimizer_id == 4: # LION
|
|
522
|
+
# Lion uses decoupled weight decay: shrink the param directly (p *= 1 - lr*wd)
|
|
523
|
+
# rather than folding wd into the gradient. Matches the cpu backend, the CUDA
|
|
524
|
+
# 8-bit blockwise kernel, and the Lion paper (Chen et al. 2023).
|
|
525
|
+
if weight_decay > 0.0:
|
|
526
|
+
p_vals = p_vals * (1.0 - lr * weight_decay)
|
|
527
|
+
|
|
528
|
+
momentum_update = state1 * beta1 + (1.0 - beta1) * g_vals
|
|
529
|
+
update_val = update_scale * lr * torch.sign(momentum_update)
|
|
530
|
+
p_vals = p_vals - update_val
|
|
531
|
+
|
|
532
|
+
s1_vals = state1 * beta2 + (1.0 - beta2) * g_vals
|
|
533
|
+
state1.copy_(s1_vals)
|
|
534
|
+
|
|
535
|
+
elif optimizer_id == 1: # RMSPROP
|
|
536
|
+
s1_vals = state1 * beta1 + (1.0 - beta1) * g_vals * g_vals
|
|
537
|
+
update_val = update_scale * lr * g_vals / (torch.sqrt(s1_vals) + eps)
|
|
538
|
+
p_vals = p_vals - update_val
|
|
539
|
+
|
|
540
|
+
state1.copy_(s1_vals)
|
|
541
|
+
|
|
542
|
+
elif optimizer_id == 2: # ADAGRAD
|
|
543
|
+
s1_vals = state1 + g_vals * g_vals
|
|
544
|
+
update_val = lr * g_vals / (torch.sqrt(s1_vals) + eps)
|
|
545
|
+
p_vals = p_vals - update_val
|
|
546
|
+
|
|
547
|
+
state1.copy_(s1_vals)
|
|
548
|
+
|
|
549
|
+
p.copy_(p_vals)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
@register_kernel("bitsandbytes::optimizer_update_32bit", "default")
|
|
553
|
+
def _(
|
|
554
|
+
optimizer_name: str,
|
|
555
|
+
g: torch.Tensor,
|
|
556
|
+
p: torch.Tensor,
|
|
557
|
+
state1: torch.Tensor,
|
|
558
|
+
state2: Optional[torch.Tensor],
|
|
559
|
+
unorm_vec: Optional[torch.Tensor],
|
|
560
|
+
max_unorm: float,
|
|
561
|
+
param_norm: float,
|
|
562
|
+
beta1: float,
|
|
563
|
+
beta2: float,
|
|
564
|
+
beta3: float,
|
|
565
|
+
alpha: float,
|
|
566
|
+
eps: float,
|
|
567
|
+
weight_decay: float,
|
|
568
|
+
step: int,
|
|
569
|
+
lr: float,
|
|
570
|
+
gnorm_scale: float = 1.0,
|
|
571
|
+
skip_zeros=False,
|
|
572
|
+
) -> None:
|
|
573
|
+
"""
|
|
574
|
+
32-bit optimizer implemented by PyTorch with @torch.compile
|
|
575
|
+
"""
|
|
576
|
+
if skip_zeros:
|
|
577
|
+
raise NotImplementedError("skip_zeros is not supported yet")
|
|
578
|
+
|
|
579
|
+
optimizer_id = name2optimizer_id[optimizer_name]
|
|
580
|
+
|
|
581
|
+
if optimizer_name == "lion":
|
|
582
|
+
_optimizer_update_32bit(
|
|
583
|
+
g,
|
|
584
|
+
p,
|
|
585
|
+
state1,
|
|
586
|
+
state2,
|
|
587
|
+
unorm_vec,
|
|
588
|
+
max_unorm,
|
|
589
|
+
param_norm,
|
|
590
|
+
beta1,
|
|
591
|
+
beta2,
|
|
592
|
+
beta3,
|
|
593
|
+
alpha,
|
|
594
|
+
eps,
|
|
595
|
+
weight_decay,
|
|
596
|
+
step,
|
|
597
|
+
lr,
|
|
598
|
+
gnorm_scale,
|
|
599
|
+
optimizer_id,
|
|
600
|
+
)
|
|
601
|
+
|
|
602
|
+
if max_unorm > 0.0:
|
|
603
|
+
unorm_vec.zero_()
|
|
604
|
+
_optimizer_precondition_32bit(
|
|
605
|
+
g, p, state1, state2, unorm_vec, beta1, beta2, eps, weight_decay, step, lr, gnorm_scale, optimizer_id
|
|
606
|
+
)
|
|
607
|
+
else:
|
|
608
|
+
if max_unorm > 0.0:
|
|
609
|
+
unorm_vec.zero_()
|
|
610
|
+
_optimizer_precondition_32bit(
|
|
611
|
+
g, p, state1, state2, unorm_vec, beta1, beta2, eps, weight_decay, step, lr, gnorm_scale, optimizer_id
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
_optimizer_update_32bit(
|
|
615
|
+
g,
|
|
616
|
+
p,
|
|
617
|
+
state1,
|
|
618
|
+
state2,
|
|
619
|
+
unorm_vec,
|
|
620
|
+
max_unorm,
|
|
621
|
+
param_norm,
|
|
622
|
+
beta1,
|
|
623
|
+
beta2,
|
|
624
|
+
beta3,
|
|
625
|
+
alpha,
|
|
626
|
+
eps,
|
|
627
|
+
weight_decay,
|
|
628
|
+
step,
|
|
629
|
+
lr,
|
|
630
|
+
gnorm_scale,
|
|
631
|
+
optimizer_id,
|
|
632
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from collections.abc import Sequence
|
|
2
|
+
import math
|
|
3
|
+
|
|
4
|
+
import torch
|
|
5
|
+
|
|
6
|
+
from ..._ops import register_kernel
|
|
7
|
+
from ..utils import GAUDI_SW_VER
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# convert btw standard 4-bit compression format and ipex compression format
|
|
11
|
+
# needed for backward compatibility with older versions of gaudi sw
|
|
12
|
+
def _reverse_4bit_compress_format(weight: torch.Tensor):
|
|
13
|
+
out_1 = (weight & 0xF0) >> 4
|
|
14
|
+
out_2 = (weight & 0xF) << 4
|
|
15
|
+
out = out_1 | out_2
|
|
16
|
+
return out
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@register_kernel("bitsandbytes::dequantize_4bit", "hpu")
|
|
20
|
+
def _(
|
|
21
|
+
A: torch.Tensor,
|
|
22
|
+
absmax: torch.Tensor,
|
|
23
|
+
blocksize: int,
|
|
24
|
+
quant_type: str,
|
|
25
|
+
shape: Sequence[int],
|
|
26
|
+
dtype: torch.dtype,
|
|
27
|
+
) -> torch.Tensor:
|
|
28
|
+
if quant_type != "nf4":
|
|
29
|
+
raise ValueError(f"HPU backend only supports quant_type 'nf4', got {quant_type!r}")
|
|
30
|
+
if A.dtype not in (torch.bfloat16, torch.uint8):
|
|
31
|
+
raise ValueError(f"HPU backend only supports uint8 or bfloat16 storage, got {A.dtype}")
|
|
32
|
+
|
|
33
|
+
# Enable non uint8 dtype
|
|
34
|
+
if A.dtype != torch.uint8:
|
|
35
|
+
A = A.view(torch.uint8)
|
|
36
|
+
|
|
37
|
+
A = A.reshape(-1)
|
|
38
|
+
|
|
39
|
+
if GAUDI_SW_VER and (GAUDI_SW_VER.major < 1 or GAUDI_SW_VER.minor < 22):
|
|
40
|
+
A = _reverse_4bit_compress_format(A)
|
|
41
|
+
|
|
42
|
+
# HPU dequantization function for NF4 quantized tensors.
|
|
43
|
+
out_dq = torch.ops.hpu.dequantize_nf4(
|
|
44
|
+
A,
|
|
45
|
+
absmax.to(dtype),
|
|
46
|
+
blocksize,
|
|
47
|
+
out_shape=(math.prod(shape),),
|
|
48
|
+
out_dtype=dtype,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
output = out_dq.reshape(shape)
|
|
52
|
+
|
|
53
|
+
return output
|
|
File without changes
|