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,491 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
import logging
|
|
3
|
+
from math import prod
|
|
4
|
+
from typing import Optional
|
|
5
|
+
import warnings
|
|
6
|
+
from warnings import warn
|
|
7
|
+
|
|
8
|
+
import torch
|
|
9
|
+
|
|
10
|
+
import bitsandbytes.functional as F
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
# The inverse transformation for the colTuring and colAmpere format were contributed by Alex Borzunov:
|
|
15
|
+
# https://github.com/bigscience-workshop/petals/blob/main/src/petals/utils/linear8bitlt_patch.py
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
This class pools outlier dimensions across layers.
|
|
20
|
+
This is particularly important for small models where outlier features
|
|
21
|
+
are less systematic and occur with low frequency.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GlobalOutlierPooler:
|
|
26
|
+
_instance = None
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
raise RuntimeError("Call get_instance() instead")
|
|
30
|
+
|
|
31
|
+
def initialize(self):
|
|
32
|
+
self.outliers = set()
|
|
33
|
+
self.model_dim = None
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def get_instance(cls):
|
|
37
|
+
if cls._instance is None:
|
|
38
|
+
cls._instance = cls.__new__(cls)
|
|
39
|
+
cls._instance.initialize()
|
|
40
|
+
return cls._instance
|
|
41
|
+
|
|
42
|
+
def add_outliers(self, outlier_idx, feature_dim):
|
|
43
|
+
if self.model_dim is None:
|
|
44
|
+
self.model_dim = feature_dim
|
|
45
|
+
if feature_dim != self.model_dim:
|
|
46
|
+
return # we do not encode outliers for the 2nd FFN layer
|
|
47
|
+
|
|
48
|
+
self.outliers.update(outlier_idx.tolist())
|
|
49
|
+
|
|
50
|
+
def get_current_outlier_idx(self):
|
|
51
|
+
return torch.Tensor(list(self.outliers)).to(torch.int64)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
_is_compiling = torch.compiler.is_compiling
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class MatmulLtState:
|
|
59
|
+
force_no_igemmlt: bool = False
|
|
60
|
+
|
|
61
|
+
CB: Optional[torch.Tensor] = None
|
|
62
|
+
SB: Optional[torch.Tensor] = None
|
|
63
|
+
SCB: Optional[torch.Tensor] = None
|
|
64
|
+
|
|
65
|
+
SBt: Optional[torch.Tensor] = None
|
|
66
|
+
CBt: Optional[torch.Tensor] = None
|
|
67
|
+
|
|
68
|
+
subB: Optional[torch.Tensor] = None
|
|
69
|
+
|
|
70
|
+
outlier_pool: Optional[GlobalOutlierPooler] = None
|
|
71
|
+
has_accumulated_gradients = False
|
|
72
|
+
threshold = 0.0
|
|
73
|
+
idx: Optional[torch.Tensor] = None
|
|
74
|
+
is_training = True
|
|
75
|
+
has_fp16_weights = True
|
|
76
|
+
use_pool = False
|
|
77
|
+
|
|
78
|
+
# Deprecated attributes kept for downstream compatibility (TGI, vLLM).
|
|
79
|
+
# These are always None and will be fully removed in the next release.
|
|
80
|
+
_deprecated_fields = frozenset({"CxB", "CxBt", "formatB", "_tile_indices"})
|
|
81
|
+
|
|
82
|
+
def __getattr__(self, name):
|
|
83
|
+
if name in MatmulLtState._deprecated_fields:
|
|
84
|
+
warnings.warn(
|
|
85
|
+
f"MatmulLtState.{name} is deprecated and will be removed in the next bitsandbytes release.",
|
|
86
|
+
FutureWarning,
|
|
87
|
+
stacklevel=2,
|
|
88
|
+
)
|
|
89
|
+
return None
|
|
90
|
+
raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
|
|
91
|
+
|
|
92
|
+
def reset_grads(self):
|
|
93
|
+
self.CB = None
|
|
94
|
+
self.SB = None
|
|
95
|
+
self.SCB = None
|
|
96
|
+
|
|
97
|
+
self.SBt = None
|
|
98
|
+
self.CBt = None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class MatMul8bitLt(torch.autograd.Function):
|
|
102
|
+
@staticmethod
|
|
103
|
+
def forward(
|
|
104
|
+
ctx: torch.autograd.function.FunctionCtx,
|
|
105
|
+
A: torch.Tensor,
|
|
106
|
+
B: torch.Tensor,
|
|
107
|
+
out: Optional[torch.Tensor] = None,
|
|
108
|
+
bias: Optional[torch.Tensor] = None,
|
|
109
|
+
state: Optional[MatmulLtState] = None,
|
|
110
|
+
):
|
|
111
|
+
state = state or MatmulLtState()
|
|
112
|
+
|
|
113
|
+
# default of pytorch behavior if inputs are empty
|
|
114
|
+
ctx.is_empty = False
|
|
115
|
+
if prod(A.shape) == 0:
|
|
116
|
+
ctx.is_empty = True
|
|
117
|
+
ctx.A = A
|
|
118
|
+
ctx.B = B
|
|
119
|
+
ctx.bias = bias
|
|
120
|
+
if A.shape[-1] == B.shape[0]:
|
|
121
|
+
return torch.empty(A.shape[:-1] + B.shape[1:], dtype=A.dtype, device=A.device)
|
|
122
|
+
else:
|
|
123
|
+
return torch.empty(A.shape[:-1] + B.shape[:1], dtype=A.dtype, device=A.device)
|
|
124
|
+
|
|
125
|
+
input_shape = A.shape
|
|
126
|
+
|
|
127
|
+
# Cast A to fp16
|
|
128
|
+
if A.dtype != torch.float16 and not _is_compiling():
|
|
129
|
+
logger.warning("MatMul8bitLt: inputs will be cast from %s to float16 during quantization", A.dtype)
|
|
130
|
+
|
|
131
|
+
if len(A.shape) == 3:
|
|
132
|
+
A = A.reshape(-1, A.shape[-1])
|
|
133
|
+
|
|
134
|
+
# 1. Quantize A. Note that as a side-effect, outliers are suppressed in CA/CAt.
|
|
135
|
+
if ctx.needs_input_grad[1]:
|
|
136
|
+
# Slower path
|
|
137
|
+
CA, CAt, SCA, SCAt, outlier_cols = F.int8_double_quant(A.to(torch.float16), threshold=state.threshold)
|
|
138
|
+
else:
|
|
139
|
+
# Fast path
|
|
140
|
+
CA, SCA, outlier_cols = F.int8_vectorwise_quant(A.to(torch.float16), threshold=state.threshold)
|
|
141
|
+
CAt = SCAt = None
|
|
142
|
+
|
|
143
|
+
has_grad = False
|
|
144
|
+
|
|
145
|
+
if state.has_fp16_weights or state.CB is None:
|
|
146
|
+
has_grad = getattr(B, "grad", None) is not None
|
|
147
|
+
is_transposed = not B.is_contiguous() and B.shape[0] == B.stride(1)
|
|
148
|
+
if is_transposed:
|
|
149
|
+
B = B.contiguous()
|
|
150
|
+
|
|
151
|
+
if (state.is_training and not has_grad) or state.CB is None or state.SCB is None:
|
|
152
|
+
state.reset_grads()
|
|
153
|
+
|
|
154
|
+
# 2. Quantize B
|
|
155
|
+
state.CB, state.SCB, _ = F.int8_vectorwise_quant(B.to(torch.float16))
|
|
156
|
+
|
|
157
|
+
# Handle sparse decomposition
|
|
158
|
+
if state.threshold > 0.0:
|
|
159
|
+
state.idx = outlier_cols
|
|
160
|
+
|
|
161
|
+
# Mixed Int8 Matmul + Dequant + Bias
|
|
162
|
+
output, subA = torch.ops.bitsandbytes.int8_mixed_scaled_mm(
|
|
163
|
+
A,
|
|
164
|
+
CA,
|
|
165
|
+
state.CB,
|
|
166
|
+
SCA,
|
|
167
|
+
state.SCB,
|
|
168
|
+
outlier_cols,
|
|
169
|
+
bias,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
else:
|
|
173
|
+
# Int8 Matmul + Dequant + Bias
|
|
174
|
+
output = torch.ops.bitsandbytes.int8_scaled_mm.default(
|
|
175
|
+
CA, state.CB, SCA, state.SCB, bias=bias, dtype=A.dtype
|
|
176
|
+
)
|
|
177
|
+
subA = None
|
|
178
|
+
|
|
179
|
+
# 5. Save state
|
|
180
|
+
ctx.state = state
|
|
181
|
+
|
|
182
|
+
ctx.grad_shape = input_shape
|
|
183
|
+
ctx.dtype_A = A.dtype
|
|
184
|
+
ctx.dtype_bias = None if bias is None else bias.dtype
|
|
185
|
+
|
|
186
|
+
if any(ctx.needs_input_grad[:2]):
|
|
187
|
+
ctx.tensors = (CAt, subA, A)
|
|
188
|
+
ctx.tensor_states = (SCAt, state.idx)
|
|
189
|
+
else:
|
|
190
|
+
ctx.tensors = [None, None, None]
|
|
191
|
+
ctx.tensor_states = (None, None)
|
|
192
|
+
ctx.save_for_backward(None, None)
|
|
193
|
+
|
|
194
|
+
output_shape = (*input_shape[:-1], state.CB.shape[0])
|
|
195
|
+
|
|
196
|
+
if len(input_shape) == 3:
|
|
197
|
+
return output.reshape(output_shape)
|
|
198
|
+
|
|
199
|
+
return output
|
|
200
|
+
|
|
201
|
+
@staticmethod
|
|
202
|
+
def backward(ctx: torch.autograd.function.FunctionCtx, grad_output: torch.Tensor):
|
|
203
|
+
if ctx.is_empty:
|
|
204
|
+
bias_grad = None if ctx.bias is None else torch.zeros_like(ctx.bias)
|
|
205
|
+
return torch.zeros_like(ctx.A), torch.zeros_like(ctx.B), None, bias_grad, None
|
|
206
|
+
|
|
207
|
+
req_gradA, req_gradB, _, req_gradBias, _ = ctx.needs_input_grad
|
|
208
|
+
CAt, subA, _A = ctx.tensors
|
|
209
|
+
SCAt, idx = ctx.tensor_states
|
|
210
|
+
state: MatmulLtState = ctx.state
|
|
211
|
+
grad_A = grad_B = grad_bias = None
|
|
212
|
+
|
|
213
|
+
if req_gradBias:
|
|
214
|
+
# compute grad_bias first before changing grad_output dtype
|
|
215
|
+
grad_bias = grad_output.sum(0, dtype=ctx.dtype_bias)
|
|
216
|
+
|
|
217
|
+
# Cast grad_output to fp16
|
|
218
|
+
if len(grad_output.shape) == 3:
|
|
219
|
+
grad_output = grad_output.reshape(-1, grad_output.shape[-1]).contiguous()
|
|
220
|
+
|
|
221
|
+
if req_gradB:
|
|
222
|
+
Cgrad, _, _, SCgradt, _ = F.int8_double_quant(grad_output.to(torch.float16))
|
|
223
|
+
|
|
224
|
+
grad_B = torch.ops.bitsandbytes.int8_scaled_mm.default(
|
|
225
|
+
Cgrad.t().contiguous(),
|
|
226
|
+
CAt.t(),
|
|
227
|
+
SCgradt,
|
|
228
|
+
SCAt,
|
|
229
|
+
dtype=torch.float16,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if state.threshold > 0.0 and subA is not None and subA.numel() > 0:
|
|
233
|
+
grad_B[:, idx] += torch.matmul(grad_output.t(), subA)
|
|
234
|
+
|
|
235
|
+
if req_gradA:
|
|
236
|
+
if state.CB is not None:
|
|
237
|
+
CB = state.CB.to(ctx.dtype_A, copy=True).mul_(state.SCB.unsqueeze(1).mul(1.0 / 127.0))
|
|
238
|
+
grad_A = torch.matmul(grad_output.to(ctx.dtype_A), CB).view(ctx.grad_shape)
|
|
239
|
+
else:
|
|
240
|
+
raise Exception("State must contain CB matrix for backward")
|
|
241
|
+
|
|
242
|
+
return grad_A, grad_B, None, grad_bias, None
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class MatMul8bitFp(torch.autograd.Function):
|
|
246
|
+
# For Intel CPU and XPU MatMul8bitFp is much faster (~3x) than MatMul8bitLt in finetune.
|
|
247
|
+
# Because the MatMul8bitLt has more mechanisms in computing grad.
|
|
248
|
+
# We don't have fast kernel for quant/dequant 8bit in CPU/XPU, so it's very slow.
|
|
249
|
+
# We'd like to use dequant + matmul to run finetune with good performance.
|
|
250
|
+
|
|
251
|
+
@staticmethod
|
|
252
|
+
def forward(ctx, A, B, out=None, bias=None, state=MatmulLtState):
|
|
253
|
+
if state.has_fp16_weights or state.CB is None:
|
|
254
|
+
has_grad = getattr(B, "grad", None) is not None
|
|
255
|
+
is_transposed = not B.is_contiguous() and B.shape[0] == B.stride(1)
|
|
256
|
+
if is_transposed:
|
|
257
|
+
B = B.contiguous()
|
|
258
|
+
|
|
259
|
+
if (state.is_training and not has_grad) or state.CB is None or state.SCB is None:
|
|
260
|
+
state.reset_grads()
|
|
261
|
+
state.CB, state.SCB, _ = F.int8_vectorwise_quant(B.to(torch.float16))
|
|
262
|
+
B = state.CB
|
|
263
|
+
|
|
264
|
+
CB = state.CB.data.to(A.dtype).mul_(state.SCB.unsqueeze(1).mul(1.0 / 127.0))
|
|
265
|
+
output = torch.nn.functional.linear(A, CB, bias)
|
|
266
|
+
ctx.state = state
|
|
267
|
+
ctx.dtype_A = A.dtype
|
|
268
|
+
ctx.grad_shape = A.shape
|
|
269
|
+
ctx.A = A
|
|
270
|
+
ctx.dtype_bias = None if bias is None else bias.dtype
|
|
271
|
+
return output
|
|
272
|
+
|
|
273
|
+
@staticmethod
|
|
274
|
+
def backward(ctx, grad_output):
|
|
275
|
+
req_gradA, req_gradB, _, req_gradBias, _ = ctx.needs_input_grad
|
|
276
|
+
A = ctx.A
|
|
277
|
+
state = ctx.state
|
|
278
|
+
grad_A = grad_B = grad_bias = None
|
|
279
|
+
if req_gradBias:
|
|
280
|
+
# compute grad_bias first before changing grad_output dtype
|
|
281
|
+
grad_bias = grad_output.sum(0, dtype=ctx.dtype_bias)
|
|
282
|
+
|
|
283
|
+
# Cast grad_output to fp16
|
|
284
|
+
if len(grad_output.shape) == 3:
|
|
285
|
+
grad_output = grad_output.reshape(-1, grad_output.shape[-1]).contiguous()
|
|
286
|
+
|
|
287
|
+
if req_gradB:
|
|
288
|
+
grad_B = torch.matmul(A.t(), grad_output).t()
|
|
289
|
+
|
|
290
|
+
if req_gradA:
|
|
291
|
+
if state.CB is not None:
|
|
292
|
+
CB = state.CB.to(ctx.dtype_A, copy=True).mul_(state.SCB.unsqueeze(1).mul(1.0 / 127.0))
|
|
293
|
+
grad_A = torch.matmul(grad_output.to(ctx.dtype_A), CB).view(ctx.grad_shape)
|
|
294
|
+
else:
|
|
295
|
+
raise Exception("State must contain CB matrix for backward")
|
|
296
|
+
|
|
297
|
+
return grad_A, grad_B, None, grad_bias, None
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
class MatMul4Bit(torch.autograd.Function):
|
|
301
|
+
# forward is the same, but we added the fallback for pre-turing GPUs
|
|
302
|
+
|
|
303
|
+
@staticmethod
|
|
304
|
+
def forward(ctx, A, B, out=None, bias=None, quant_state: Optional[F.QuantState] = None):
|
|
305
|
+
# default of pytorch behavior if inputs are empty
|
|
306
|
+
ctx.is_empty = False
|
|
307
|
+
if A.numel() == 0:
|
|
308
|
+
ctx.is_empty = True
|
|
309
|
+
ctx.A = A
|
|
310
|
+
ctx.B = B
|
|
311
|
+
ctx.bias = bias
|
|
312
|
+
B_shape = quant_state.shape
|
|
313
|
+
if A.shape[-1] == B_shape[0]:
|
|
314
|
+
return torch.empty(A.shape[:-1] + B_shape[1:], dtype=A.dtype, device=A.device)
|
|
315
|
+
else:
|
|
316
|
+
return torch.empty(A.shape[:-1] + B_shape[:1], dtype=A.dtype, device=A.device)
|
|
317
|
+
|
|
318
|
+
# Normalize to canonical [(N*K+1)//2, 1]. Packed weights are always contiguous
|
|
319
|
+
# in this orientation (B.t() callers get strides [1,1], still compatible).
|
|
320
|
+
# quant_state.shape is the source of truth for N and K.
|
|
321
|
+
B = B.view(-1, 1)
|
|
322
|
+
|
|
323
|
+
if not quant_state.nested:
|
|
324
|
+
output = torch.ops.bitsandbytes.gemm_4bit.default(
|
|
325
|
+
A,
|
|
326
|
+
B,
|
|
327
|
+
quant_state.shape,
|
|
328
|
+
quant_state.absmax,
|
|
329
|
+
quant_state.blocksize,
|
|
330
|
+
quant_state.quant_type,
|
|
331
|
+
bias=bias,
|
|
332
|
+
)
|
|
333
|
+
elif quant_state.state2.blocksize == 256:
|
|
334
|
+
output = torch.ops.bitsandbytes.gemm_4bit.default(
|
|
335
|
+
A,
|
|
336
|
+
B,
|
|
337
|
+
quant_state.shape,
|
|
338
|
+
quant_state.state2.absmax,
|
|
339
|
+
quant_state.blocksize,
|
|
340
|
+
quant_state.quant_type,
|
|
341
|
+
bias=bias,
|
|
342
|
+
absmax_8bit=quant_state.absmax,
|
|
343
|
+
absmax_code=quant_state.state2.code,
|
|
344
|
+
absmax_offset=quant_state.offset,
|
|
345
|
+
)
|
|
346
|
+
else:
|
|
347
|
+
raise NotImplementedError("nested quantization with state2.blocksize != 256 is not supported")
|
|
348
|
+
|
|
349
|
+
if out is not None:
|
|
350
|
+
out.copy_(output)
|
|
351
|
+
output = out
|
|
352
|
+
|
|
353
|
+
# 3. Save state
|
|
354
|
+
ctx.state = quant_state
|
|
355
|
+
ctx.dtype_A, ctx.dtype_B, ctx.dtype_bias = A.dtype, B.dtype, None if bias is None else bias.dtype
|
|
356
|
+
|
|
357
|
+
if any(ctx.needs_input_grad[:2]):
|
|
358
|
+
ctx.tensors = (None, B)
|
|
359
|
+
else:
|
|
360
|
+
ctx.tensors = (None, None)
|
|
361
|
+
|
|
362
|
+
return output
|
|
363
|
+
|
|
364
|
+
@staticmethod
|
|
365
|
+
def backward(ctx, grad_output):
|
|
366
|
+
if ctx.is_empty:
|
|
367
|
+
bias_grad = None if ctx.bias is None else torch.zeros_like(ctx.bias)
|
|
368
|
+
return torch.zeros_like(ctx.A), torch.zeros_like(ctx.B), None, bias_grad, None
|
|
369
|
+
|
|
370
|
+
req_gradA, _, _, req_gradBias, _ = ctx.needs_input_grad
|
|
371
|
+
_, B = ctx.tensors
|
|
372
|
+
|
|
373
|
+
grad_A, grad_B, grad_bias = None, None, None
|
|
374
|
+
|
|
375
|
+
if req_gradBias:
|
|
376
|
+
# compute grad_bias first before changing grad_output dtype
|
|
377
|
+
grad_bias = grad_output.sum(0, dtype=ctx.dtype_bias)
|
|
378
|
+
|
|
379
|
+
# not supported by PyTorch. TODO: create work-around
|
|
380
|
+
# if req_gradB: grad_B = torch.matmul(grad_output.t(), A)
|
|
381
|
+
if req_gradA:
|
|
382
|
+
# B in ctx.tensors is already in canonical [(N*K+1)//2, 1] form (normalized in forward).
|
|
383
|
+
# dequantize returns [N, K]; matmul(grad_output[M,N], [N,K]) = grad_A[M,K].
|
|
384
|
+
grad_A = torch.matmul(grad_output, F.dequantize_4bit(B, ctx.state).to(grad_output.dtype))
|
|
385
|
+
|
|
386
|
+
return grad_A, grad_B, None, grad_bias, None
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def matmul(
|
|
390
|
+
A: torch.Tensor,
|
|
391
|
+
B: torch.Tensor,
|
|
392
|
+
out: Optional[torch.Tensor] = None,
|
|
393
|
+
state: Optional[MatmulLtState] = None,
|
|
394
|
+
threshold=0.0,
|
|
395
|
+
bias: Optional[torch.Tensor] = None,
|
|
396
|
+
):
|
|
397
|
+
state = state or MatmulLtState()
|
|
398
|
+
if threshold > 0.0:
|
|
399
|
+
state.threshold = threshold
|
|
400
|
+
# MatMul8bitLt is slower because no fast kernel for quant/dequant 8bit in CPU/XPU
|
|
401
|
+
if state.is_training:
|
|
402
|
+
if A.device.type in ("cpu", "xpu"):
|
|
403
|
+
return MatMul8bitFp.apply(A, B, out, bias, state)
|
|
404
|
+
return MatMul8bitLt.apply(A, B, out, bias, state)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def matmul_4bit(
|
|
408
|
+
A: torch.Tensor,
|
|
409
|
+
B: torch.Tensor,
|
|
410
|
+
quant_state: F.QuantState,
|
|
411
|
+
out: Optional[torch.Tensor] = None,
|
|
412
|
+
bias: Optional[torch.Tensor] = None,
|
|
413
|
+
):
|
|
414
|
+
if quant_state is None:
|
|
415
|
+
raise ValueError("quant_state is required")
|
|
416
|
+
if len(quant_state.shape) != 2:
|
|
417
|
+
raise ValueError("matmul_4bit: quant_state.shape must be 2D [N, K]")
|
|
418
|
+
|
|
419
|
+
# packing_format_for_cpu uses a different memory layout optimized for AVX512BF16.
|
|
420
|
+
# This flag is only set for inference (weight conversion happens at eval time).
|
|
421
|
+
# The underlying kernel supports any M via tiled GEMM despite the gemv name.
|
|
422
|
+
if A.device.type == "cpu" and getattr(quant_state, "packing_format_for_cpu", False):
|
|
423
|
+
result = F.gemv_4bit(A, B, out=out, state=quant_state)
|
|
424
|
+
if bias is not None:
|
|
425
|
+
result += bias
|
|
426
|
+
return result
|
|
427
|
+
|
|
428
|
+
# Normalize B to canonical [(N*K+1)//2, 1]. Packed weights are always contiguous
|
|
429
|
+
# in this orientation (B.t() callers get strides [1,1], still compatible).
|
|
430
|
+
# quant_state.shape is the source of truth for N and K.
|
|
431
|
+
B = B.view(-1, 1)
|
|
432
|
+
|
|
433
|
+
K = A.shape[-1]
|
|
434
|
+
|
|
435
|
+
# Weight is in [K, N] orientation when A's inner dim matches shape[0] not shape[1].
|
|
436
|
+
# Square weights (K==N) are ambiguous and treated as [N, K].
|
|
437
|
+
if K == quant_state.shape[0] and K != quant_state.shape[1]:
|
|
438
|
+
if not _is_compiling():
|
|
439
|
+
warn(
|
|
440
|
+
f"matmul_4bit: weight was quantized from a [K, N] tensor (quant_state.shape={list(quant_state.shape)}). "
|
|
441
|
+
"Re-quantize from the weight in [N, K] (out_features, in_features) orientation. "
|
|
442
|
+
"This will be an error in a future version.",
|
|
443
|
+
DeprecationWarning,
|
|
444
|
+
stacklevel=2,
|
|
445
|
+
)
|
|
446
|
+
B_dq = F.dequantize_4bit(B, quant_state).to(A.dtype)
|
|
447
|
+
result = torch.nn.functional.linear(A, B_dq.t(), bias)
|
|
448
|
+
if out is not None:
|
|
449
|
+
out.copy_(result)
|
|
450
|
+
return out
|
|
451
|
+
return result
|
|
452
|
+
|
|
453
|
+
needs_grad = torch.is_grad_enabled() and (A.requires_grad or (bias is not None and bias.requires_grad))
|
|
454
|
+
if not needs_grad:
|
|
455
|
+
A_numel = A.numel()
|
|
456
|
+
if A_numel == 0:
|
|
457
|
+
if out is not None:
|
|
458
|
+
return out
|
|
459
|
+
return torch.empty((*A.shape[:-1], quant_state.shape[0]), dtype=A.dtype, device=A.device)
|
|
460
|
+
|
|
461
|
+
if not quant_state.nested:
|
|
462
|
+
result = torch.ops.bitsandbytes.gemm_4bit.default(
|
|
463
|
+
A,
|
|
464
|
+
B,
|
|
465
|
+
quant_state.shape,
|
|
466
|
+
quant_state.absmax,
|
|
467
|
+
quant_state.blocksize,
|
|
468
|
+
quant_state.quant_type,
|
|
469
|
+
bias=bias,
|
|
470
|
+
)
|
|
471
|
+
elif quant_state.state2.blocksize == 256:
|
|
472
|
+
result = torch.ops.bitsandbytes.gemm_4bit.default(
|
|
473
|
+
A,
|
|
474
|
+
B,
|
|
475
|
+
quant_state.shape,
|
|
476
|
+
quant_state.state2.absmax,
|
|
477
|
+
quant_state.blocksize,
|
|
478
|
+
quant_state.quant_type,
|
|
479
|
+
bias=bias,
|
|
480
|
+
absmax_8bit=quant_state.absmax,
|
|
481
|
+
absmax_code=quant_state.state2.code,
|
|
482
|
+
absmax_offset=quant_state.offset,
|
|
483
|
+
)
|
|
484
|
+
else:
|
|
485
|
+
raise NotImplementedError("nested quantization with state2.blocksize != 256 is not supported")
|
|
486
|
+
if out is not None:
|
|
487
|
+
out.copy_(result)
|
|
488
|
+
return out
|
|
489
|
+
return result
|
|
490
|
+
|
|
491
|
+
return MatMul4Bit.apply(A, B, out, bias, quant_state)
|
|
File without changes
|
|
File without changes
|