e93507a09c
Lockfile supply-chain audit / lockfile supply-chain audit (push) Has been cancelled
Windows Studio GGUF CI / GPU prebuilt resolves without Visual Studio (push) Has been cancelled
Windows Studio GGUF CI / setup.ps1 unit tests (VS 2026 / CMake guard) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2022) (push) Has been cancelled
Windows Studio GGUF CI / real-VS detection (VS 2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-2025-vs2026) (push) Has been cancelled
Windows Studio GGUF CI / VC++ runtime detect + install round-trip (windows-latest) (push) Has been cancelled
Windows Studio Update CI / Studio Updating Tests (push) Has been cancelled
Wheel CI / Wheel build + content sanity + import smoke (push) Has been cancelled
Lint CI / Source lint (Python + shell + YAML + JSON + safety nets) (push) Has been cancelled
MLX CI on Mac M1 / dispatch (push) Has been cancelled
Security audit / advisory audit (pip + npm + cargo) (push) Has been cancelled
Security audit / pip scan-packages :: extras (push) Has been cancelled
Security audit / pip scan-packages :: studio (push) Has been cancelled
Security audit / pip scan-packages :: hf-stack (push) Has been cancelled
Security audit / npm scan-packages (Studio frontend tarballs) (push) Has been cancelled
Security audit / workflow-trigger lint (pull_request_target / cache-poisoning) (push) Has been cancelled
Security audit / pytest tests/security (push) Has been cancelled
Security audit / npm provenance + new install-script diff (push) Has been cancelled
Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Backend CI / (Python 3.10) (push) Has been cancelled
Backend CI / (Python 3.11) (push) Has been cancelled
Backend CI / (Python 3.12) (push) Has been cancelled
Backend CI / (Python 3.13) (push) Has been cancelled
Backend CI / Repo tests (CPU) (push) Has been cancelled
Frontend CI / Frontend build + bundle sanity (push) Has been cancelled
Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Mac Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Mac Studio GGUF CI / JSON, images (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-14) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26) (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-15-intel) (push) Has been cancelled
Mac Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Mac Studio Install Matrix CI / Install + load (macos-26-intel) (push) Has been cancelled
Mac Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Tauri CI / Tauri Linux debug build (no codesign) (push) Has been cancelled
Mac Studio Update CI / Studio Updating Tests (push) Has been cancelled
Studio UI CI / Chat UI Tests (push) Has been cancelled
Windows Studio API CI / Studio API & Auth Tests (push) Has been cancelled
Windows Studio UI CI / Chat UI Tests (push) Has been cancelled
Studio Update CI / Studio Updating Tests (push) Has been cancelled
Core / Core (HF=default + TRL=default) (push) Has been cancelled
Core / Core (HF=4.57.6 + TRL<1) (push) Has been cancelled
Core / Core (HF=latest + TRL=latest) (push) Has been cancelled
Core / llama.cpp build + smoke (push) Has been cancelled
Windows Studio GGUF CI / OpenAI, Anthropic API tests (push) Has been cancelled
Windows Studio GGUF CI / Tool calling Tests (push) Has been cancelled
Windows Studio GGUF CI / JSON, images (push) Has been cancelled
Windows Studio GGUF CI / Studio install + inference without Visual Studio (push) Has been cancelled
Studio export capability / capability (macos-latest) (push) Has been cancelled
Studio export capability / capability (ubuntu-latest) (push) Has been cancelled
Studio export capability / capability (windows-latest) (push) Has been cancelled
Cross-platform parity / parity (macos-latest) (push) Has been cancelled
Cross-platform parity / parity (windows-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Studio load-orchestrator CI / test (push) Has been cancelled
1130 lines
35 KiB
Python
1130 lines
35 KiB
Python
# Copyright 2023-present Daniel Han-Chen & the Unsloth team. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import importlib
|
|
import triton
|
|
import ctypes
|
|
|
|
MAX_FUSED_SIZE: int = 65536
|
|
next_power_of_2 = triton.next_power_of_2
|
|
import functools
|
|
from typing import Optional
|
|
|
|
from ..device_type import (
|
|
is_hip,
|
|
get_device_type,
|
|
DEVICE_TYPE,
|
|
DEVICE_TYPE_TORCH,
|
|
DEVICE_COUNT,
|
|
ALLOW_PREQUANTIZED_MODELS,
|
|
)
|
|
from .fp8 import weight_dequant, fp8_linear
|
|
import functools
|
|
|
|
# torch.cuda.amp.custom_fwd is deprecated >= 2.4
|
|
import torch
|
|
|
|
torch_Tensor = torch.Tensor
|
|
from unsloth_zoo.utils import Version
|
|
|
|
if DEVICE_TYPE == "xpu" and Version(torch.__version__) < Version("2.6.0"):
|
|
raise RuntimeError("Intel xpu currently supports unsloth with torch.version >= 2.6.0")
|
|
|
|
if Version(torch.__version__) < Version("2.4.0"):
|
|
torch_amp_custom_fwd = torch.cuda.amp.custom_fwd
|
|
torch_amp_custom_bwd = torch.cuda.amp.custom_bwd
|
|
else:
|
|
torch_amp_custom_fwd = torch.amp.custom_fwd(device_type = "cuda")
|
|
torch_amp_custom_bwd = torch.amp.custom_bwd(device_type = "cuda")
|
|
|
|
if DEVICE_TYPE == "xpu":
|
|
torch_amp_custom_fwd = torch.amp.custom_fwd(device_type = "xpu")
|
|
torch_amp_custom_bwd = torch.amp.custom_bwd(device_type = "xpu")
|
|
|
|
|
|
# tl.math.tanh now is libdevice.tanh
|
|
import triton
|
|
import triton.language as tl
|
|
|
|
if Version(triton.__version__) >= Version("3.0.0"):
|
|
if DEVICE_TYPE == "xpu":
|
|
triton_tanh = tl.extra.intel.libdevice.tanh
|
|
else:
|
|
from triton.language.extra import libdevice
|
|
triton_tanh = libdevice.tanh
|
|
triton_cast = tl.cast
|
|
else:
|
|
triton_tanh = tl.math.tanh
|
|
|
|
# No casting in old Triton versions
|
|
@triton.jit
|
|
def triton_cast(x, dtype):
|
|
return x.to(dtype)
|
|
|
|
|
|
@functools.lru_cache(1)
|
|
def is_cdna():
|
|
return is_hip() and triton.runtime.driver.active.get_current_target().arch in (
|
|
"gfx940",
|
|
"gfx941",
|
|
"gfx942",
|
|
"gfx950", # CDNA4 (MI350/MI355X)
|
|
)
|
|
|
|
|
|
@functools.lru_cache(1)
|
|
def is_rdna():
|
|
"""Detect ROCm-supported RDNA consumer/workstation GPUs (RDNA2, RDNA3, RDNA3.5, RDNA4)."""
|
|
return is_hip() and triton.runtime.driver.active.get_current_target().arch in (
|
|
# RDNA2 (Navi 21-24)
|
|
"gfx1030",
|
|
"gfx1031",
|
|
"gfx1032",
|
|
"gfx1033",
|
|
"gfx1034",
|
|
"gfx1035",
|
|
"gfx1036",
|
|
# RDNA3 (Navi 31-33)
|
|
"gfx1100",
|
|
"gfx1101",
|
|
"gfx1102",
|
|
"gfx1103",
|
|
# RDNA3.5 (Strix Point / Strix Halo)
|
|
"gfx1150",
|
|
"gfx1151",
|
|
"gfx1152",
|
|
# RDNA4 (Navi 48-44)
|
|
"gfx1200",
|
|
"gfx1201",
|
|
)
|
|
|
|
|
|
def calculate_settings(
|
|
n: int,
|
|
) -> (
|
|
int,
|
|
int,
|
|
):
|
|
BLOCK_SIZE: int = next_power_of_2(n)
|
|
if BLOCK_SIZE > MAX_FUSED_SIZE:
|
|
raise RuntimeError(
|
|
f"Cannot launch Triton kernel since n = {n} exceeds "
|
|
f"the maximum CUDA blocksize = {MAX_FUSED_SIZE}."
|
|
)
|
|
num_warps: int = 4
|
|
if BLOCK_SIZE >= 32768:
|
|
num_warps = 32
|
|
elif BLOCK_SIZE >= 8192:
|
|
num_warps = 16
|
|
elif BLOCK_SIZE >= 2048:
|
|
num_warps = 8
|
|
return BLOCK_SIZE, num_warps
|
|
|
|
|
|
HAS_CUDA_STREAM = False
|
|
import bitsandbytes as bnb
|
|
|
|
# https://github.com/bitsandbytes-foundation/bitsandbytes/pull/1330/files
|
|
HAS_CUDA_STREAM = Version(bnb.__version__) > Version("0.43.3")
|
|
get_ptr = bnb.functional.get_ptr
|
|
|
|
if DEVICE_TYPE == "xpu":
|
|
HAS_XPU_STREAM = True
|
|
|
|
if DEVICE_COUNT > 1:
|
|
if DEVICE_TYPE in ("cuda", "hip"):
|
|
torch_gpu_device = torch.cuda.device
|
|
elif DEVICE_TYPE == "xpu":
|
|
torch_gpu_device = torch.xpu.device
|
|
else:
|
|
from contextlib import nullcontext
|
|
def torch_gpu_device(device):
|
|
return nullcontext()
|
|
|
|
|
|
# INTEL GPU Specific Logic
|
|
if DEVICE_TYPE == "xpu":
|
|
_gpu_getCurrentRawStream = torch._C._xpu_getCurrentRawStream
|
|
elif DEVICE_TYPE == "mlx":
|
|
|
|
def _gpu_getCurrentRawStream(_index = 0):
|
|
return 0
|
|
# NVIDIA GPU Default Logic
|
|
elif hasattr(torch._C, "_cuda_getCurrentRawStream"):
|
|
_gpu_getCurrentRawStream = torch._C._cuda_getCurrentRawStream
|
|
else:
|
|
# CPU-only torch wheel (no compiled CUDA backend). _get_tensor_stream
|
|
# is only invoked during real GPU work, so a no-op binding is safe.
|
|
def _gpu_getCurrentRawStream(_index = 0):
|
|
return 0
|
|
|
|
|
|
c_void_p = ctypes.c_void_p
|
|
|
|
|
|
def _get_tensor_stream(tensor: torch_Tensor) -> c_void_p:
|
|
return c_void_p(_gpu_getCurrentRawStream(tensor.device.index))
|
|
|
|
|
|
# Get array of CUDA streams and other buffers
|
|
global CUDA_STREAMS
|
|
global XPU_STREAMS
|
|
global WEIGHT_BUFFERS
|
|
global ABSMAX_BUFFERS
|
|
|
|
# DEVICE_COUNT == 0 = no visible accelerator (e.g. CPU-only CI runner).
|
|
# The consumer functions below only index these arrays during real GPU
|
|
# work, so empty containers are safe -- they just need to be defined so
|
|
# the module imports cleanly.
|
|
if DEVICE_TYPE == "xpu":
|
|
if DEVICE_COUNT > 0:
|
|
_XPU_STREAMS = {
|
|
(index := torch.xpu.device(i).idx): ctypes.c_void_p(
|
|
torch._C._xpu_getCurrentRawStream(index)
|
|
)
|
|
for i in range(DEVICE_COUNT)
|
|
}
|
|
XPU_STREAMS = [None] * (max(_XPU_STREAMS.keys()) + 1)
|
|
WEIGHT_BUFFERS = [None] * (max(_XPU_STREAMS.keys()) + 1)
|
|
ABSMAX_BUFFERS = [None] * (max(_XPU_STREAMS.keys()) + 1)
|
|
for k, v in _XPU_STREAMS.items():
|
|
XPU_STREAMS[k] = v
|
|
XPU_STREAMS = tuple(XPU_STREAMS)
|
|
del _XPU_STREAMS
|
|
else:
|
|
XPU_STREAMS = ()
|
|
WEIGHT_BUFFERS = []
|
|
ABSMAX_BUFFERS = []
|
|
elif DEVICE_TYPE == "mlx":
|
|
CUDA_STREAMS = ()
|
|
XPU_STREAMS = ()
|
|
WEIGHT_BUFFERS = []
|
|
ABSMAX_BUFFERS = []
|
|
else:
|
|
# NVIDIA GPU Default Logic
|
|
if DEVICE_COUNT > 0:
|
|
_CUDA_STREAMS = {
|
|
(index := torch.cuda.device(i).idx): ctypes.c_void_p(
|
|
torch._C._cuda_getCurrentRawStream(index)
|
|
)
|
|
for i in range(DEVICE_COUNT)
|
|
}
|
|
CUDA_STREAMS = [None] * (max(_CUDA_STREAMS.keys()) + 1)
|
|
WEIGHT_BUFFERS = [None] * (max(_CUDA_STREAMS.keys()) + 1)
|
|
ABSMAX_BUFFERS = [None] * (max(_CUDA_STREAMS.keys()) + 1)
|
|
for k, v in _CUDA_STREAMS.items():
|
|
CUDA_STREAMS[k] = v
|
|
CUDA_STREAMS = tuple(CUDA_STREAMS)
|
|
del _CUDA_STREAMS
|
|
else:
|
|
CUDA_STREAMS = ()
|
|
WEIGHT_BUFFERS = []
|
|
ABSMAX_BUFFERS = []
|
|
|
|
# Bitsandbytes operations
|
|
ctypes_c_int = ctypes.c_int
|
|
ctypes_c_int32 = ctypes.c_int32
|
|
cdequantize_blockwise_fp32 = bnb.functional.lib.cdequantize_blockwise_fp32
|
|
cdequantize_blockwise_fp16_nf4 = bnb.functional.lib.cdequantize_blockwise_fp16_nf4
|
|
cdequantize_blockwise_bf16_nf4 = bnb.functional.lib.cdequantize_blockwise_bf16_nf4
|
|
|
|
if DEVICE_TYPE == "xpu":
|
|
# https://github.com/bitsandbytes-foundation/bitsandbytes/blob/c3b8de268fdb55a88f92feada23fc811a1e6877a/bitsandbytes/backends/xpu/ops.py#L115
|
|
# for xpu, inference gemv using above link
|
|
cgemm_4bit_inference_naive_fp16 = bnb.functional.lib.cgemv_4bit_inference_fp16
|
|
cgemm_4bit_inference_naive_bf16 = bnb.functional.lib.cgemv_4bit_inference_bf16
|
|
else:
|
|
cgemm_4bit_inference_naive_fp16 = bnb.functional.lib.cgemm_4bit_inference_naive_fp16
|
|
cgemm_4bit_inference_naive_bf16 = bnb.functional.lib.cgemm_4bit_inference_naive_bf16
|
|
|
|
|
|
torch_device_stream = (
|
|
torch.xpu.current_stream if DEVICE_TYPE == "xpu" else torch.cuda.current_stream
|
|
)
|
|
|
|
torch_mm = torch.mm
|
|
torch_mv = torch.mv
|
|
torch_matmul = torch.matmul
|
|
torch_addmm = torch.addmm
|
|
torch_empty = torch.empty
|
|
torch_float32 = torch.float32
|
|
torch_float16 = torch.float16
|
|
torch_bfloat16 = torch.bfloat16
|
|
|
|
|
|
# Check whether torchao can be imported to get Float8Tensor
|
|
if importlib.util.find_spec("torchao") is not None:
|
|
try:
|
|
from torchao.quantization import Float8Tensor
|
|
except:
|
|
import torchao
|
|
if Version(torchao.__version__) >= Version("0.15.0"):
|
|
print(
|
|
f"Unsloth: `from torchao.quantization import Float8Tensor` failed on version={torchao.__version__}"
|
|
)
|
|
Float8Tensor = type(None)
|
|
else:
|
|
Float8Tensor = type(None)
|
|
|
|
|
|
def QUANT_STATE(W):
|
|
return getattr(W, "quant_state", None)
|
|
|
|
|
|
# fp8 weight dtypes. A `weight_scale` / `weight_scale_inv` should only be treated as a
|
|
# quant state when the weight itself is still fp8. compressed-tensors layers expose an
|
|
# already-dequantized bf16 weight at forward time while keeping a `weight_scale` around;
|
|
# reading that as a quant state routes a bf16 weight into the bitsandbytes fast_gemv /
|
|
# fast_dequantize path, which then reads a missing `absmax` and crashes.
|
|
_FP8_WEIGHT_DTYPES = tuple(
|
|
dtype
|
|
for dtype in (
|
|
getattr(torch, "float8_e4m3fn", None),
|
|
getattr(torch, "float8_e5m2", None),
|
|
)
|
|
if dtype is not None
|
|
)
|
|
|
|
|
|
def get_lora_parameters(proj):
|
|
"""Return (weight, weight quant_state, lora A, lora B, lora scale).
|
|
With QAT enabled, also fake-quantizes the base layer and lora weights.
|
|
"""
|
|
# For DPO or disabled adapters
|
|
base_layer = getattr(
|
|
proj, "base_layer", proj
|
|
) # (proj.base_layer if hasattr(proj, "base_layer") else proj)
|
|
W = base_layer.weight
|
|
|
|
# Optionally apply fake quantization to base layer weights for QAT
|
|
if hasattr(base_layer, "weight_fake_quantizer"):
|
|
weight_fake_quantizer = getattr(base_layer, "weight_fake_quantizer", None)
|
|
if weight_fake_quantizer is not None:
|
|
W = weight_fake_quantizer(W)
|
|
|
|
# Get quant state for 4bit or FP8. Only fall back to a weight_scale(_inv) when the
|
|
# weight is still fp8; a bf16 weight (e.g. a decompressed compressed-tensors layer)
|
|
# must not carry a scale as its quant state or fast_gemv will crash on it.
|
|
W_quant = getattr(W, "quant_state", None)
|
|
if W_quant is None and W.dtype in _FP8_WEIGHT_DTYPES:
|
|
W_quant = getattr(base_layer, "weight_scale_inv", None)
|
|
if W_quant is None:
|
|
W_quant = getattr(base_layer, "weight_scale", None)
|
|
|
|
if getattr(base_layer, "quant_method", None) == "fp8":
|
|
# we need to somehow store and pass this information :)
|
|
W.block_size = getattr(base_layer, "block_size", [128, 128])
|
|
W_quant.block_size = W.block_size
|
|
|
|
# if not hasattr(proj, "disable_adapters") or proj.disable_adapters or proj.merged:
|
|
if getattr(proj, "disable_adapters", True) or proj.merged:
|
|
return W, W_quant, None, None, None
|
|
|
|
adapter = getattr(proj, "active_adapters", None)
|
|
if adapter is None:
|
|
adapter = getattr(proj, "active_adapter", ("default"))
|
|
adapter = adapter[0]
|
|
|
|
# Optionally apply fake quantization to lora weights for QAT
|
|
lora_A_linear = proj.lora_A[adapter]
|
|
lora_B_linear = proj.lora_B[adapter]
|
|
A = lora_A_linear.weight
|
|
B = lora_B_linear.weight
|
|
if hasattr(lora_A_linear, "weight_fake_quantizer"):
|
|
lora_A_fake_quantizer = getattr(lora_A_linear, "weight_fake_quantizer", None)
|
|
if lora_A_fake_quantizer is not None:
|
|
A = lora_A_fake_quantizer(A)
|
|
if hasattr(lora_B_linear, "weight_fake_quantizer"):
|
|
lora_B_fake_quantizer = getattr(lora_B_linear, "weight_fake_quantizer", None)
|
|
if lora_B_fake_quantizer is not None:
|
|
B = lora_B_fake_quantizer(B)
|
|
|
|
return (
|
|
W,
|
|
W_quant,
|
|
A,
|
|
B,
|
|
proj.scaling[adapter],
|
|
)
|
|
|
|
|
|
def get_lora_parameters_bias(proj):
|
|
# For DPO or disabled adapters
|
|
base_layer = getattr(
|
|
proj, "base_layer", proj
|
|
) # (proj.base_layer if hasattr(proj, "base_layer") else proj)
|
|
W = base_layer.weight
|
|
|
|
# Get quant state for 4bit or FP8. Only fall back to a weight_scale(_inv) when the
|
|
# weight is still fp8; a bf16 weight (e.g. a decompressed compressed-tensors layer)
|
|
# must not carry a scale as its quant state or fast_gemv will crash on it.
|
|
W_quant = getattr(W, "quant_state", None)
|
|
if W_quant is None and W.dtype in _FP8_WEIGHT_DTYPES:
|
|
W_quant = getattr(base_layer, "weight_scale_inv", None)
|
|
if W_quant is None:
|
|
W_quant = getattr(base_layer, "weight_scale", None)
|
|
|
|
# if not hasattr(proj, "disable_adapters") or proj.disable_adapters or proj.merged:
|
|
if getattr(proj, "disable_adapters", True) or proj.merged:
|
|
return W, W_quant, None, None, None, base_layer.bias
|
|
|
|
if getattr(base_layer, "quant_method", None) == "fp8":
|
|
# we need to somehow store and pass this information :)
|
|
W.block_size = getattr(base_layer, "block_size", [128, 128])
|
|
W_quant.block_size = W.block_size
|
|
|
|
adapter = getattr(proj, "active_adapters", None)
|
|
if adapter is None:
|
|
adapter = getattr(proj, "active_adapter", ("default"))
|
|
adapter = adapter[0]
|
|
|
|
return (
|
|
W,
|
|
W_quant,
|
|
proj.lora_A[adapter].weight,
|
|
proj.lora_B[adapter].weight,
|
|
proj.scaling[adapter],
|
|
base_layer.bias,
|
|
)
|
|
|
|
|
|
def _maybe_fake_quantize_activations(X: torch.Tensor, proj: torch.nn.Module) -> torch.Tensor:
|
|
"""Fake-quantize input activations if QAT is enabled, else return as-is.
|
|
Weights are fake-quantized separately in `get_lora_parameters`.
|
|
"""
|
|
base_layer = getattr(proj, "base_layer", proj)
|
|
activation_fake_quantizer = getattr(base_layer, "activation_fake_quantizer", None)
|
|
if activation_fake_quantizer is not None:
|
|
X = activation_fake_quantizer(X)
|
|
return X
|
|
|
|
|
|
# INTEL GPU Specific Logic
|
|
if DEVICE_TYPE == "xpu" and HAS_XPU_STREAM:
|
|
|
|
@torch.inference_mode
|
|
def fast_dequantize(
|
|
W,
|
|
quant_state = None,
|
|
out = None,
|
|
use_global_buffer = False,
|
|
):
|
|
# TODO: After adding XPU BNB support, check this function
|
|
if isinstance(W, Float8Tensor):
|
|
return W.dequantize()
|
|
if quant_state is None:
|
|
return W
|
|
if W.dtype == torch.float8_e4m3fn:
|
|
return weight_dequant(W, quant_state)
|
|
if type(quant_state) is not list:
|
|
# New quant_state as a class
|
|
# https://github.com/TimDettmers/bitsandbytes/pull/763/files
|
|
absmax = quant_state.absmax
|
|
shape = quant_state.shape
|
|
dtype = quant_state.dtype
|
|
blocksize = quant_state.blocksize
|
|
offset = quant_state.offset
|
|
state2 = quant_state.state2
|
|
absmax2 = state2.absmax
|
|
code2 = state2.code
|
|
blocksize2 = state2.blocksize
|
|
else:
|
|
# Old quant_state as a list of lists
|
|
absmax, shape, dtype, blocksize, compressed_stats, _, _ = quant_state
|
|
offset, state2 = compressed_stats
|
|
absmax2, code2, blocksize2, _, _, _, _ = state2
|
|
global XPU_STREAMS
|
|
device = W.device
|
|
device_index = device.index
|
|
XPU_STREAM = XPU_STREAMS[device_index]
|
|
|
|
n_elements_absmax = absmax.numel()
|
|
# Create weight matrix
|
|
if use_global_buffer:
|
|
# Use same buffers for faster inference
|
|
size = shape[0] * shape[1]
|
|
global WEIGHT_BUFFERS
|
|
global ABSMAX_BUFFERS
|
|
WEIGHT_BUFFER = WEIGHT_BUFFERS[device_index]
|
|
ABSMAX_BUFFER = ABSMAX_BUFFERS[device_index]
|
|
if WEIGHT_BUFFER is None or WEIGHT_BUFFER.dtype != dtype:
|
|
WEIGHT_BUFFERS[device_index] = WEIGHT_BUFFER = torch_empty(
|
|
size, dtype = dtype, device = device, requires_grad = False
|
|
)
|
|
ABSMAX_BUFFERS[device_index] = ABSMAX_BUFFER = torch_empty(
|
|
n_elements_absmax,
|
|
dtype = torch.float32,
|
|
device = device,
|
|
requires_grad = False,
|
|
)
|
|
|
|
if size > WEIGHT_BUFFER.numel():
|
|
WEIGHT_BUFFER.resize_(size)
|
|
if n_elements_absmax > ABSMAX_BUFFER.numel():
|
|
ABSMAX_BUFFER.resize_(n_elements_absmax)
|
|
|
|
out = WEIGHT_BUFFER[:size].view(shape)
|
|
out_absmax = ABSMAX_BUFFER[:n_elements_absmax]
|
|
else:
|
|
if out is None:
|
|
out = torch_empty(shape, dtype = dtype, device = device, requires_grad = False)
|
|
else:
|
|
assert out.shape == shape
|
|
assert out.dtype == dtype
|
|
out_absmax = torch_empty(
|
|
n_elements_absmax,
|
|
dtype = torch_float32,
|
|
device = device,
|
|
requires_grad = False,
|
|
)
|
|
|
|
# NF4 dequantization of statistics
|
|
ptr_out_absmax = get_ptr(out_absmax)
|
|
with torch_gpu_device(device):
|
|
cdequantize_blockwise_fp32(
|
|
get_ptr(code2),
|
|
get_ptr(absmax),
|
|
get_ptr(absmax2),
|
|
ptr_out_absmax,
|
|
ctypes_c_int(blocksize2),
|
|
ctypes_c_int(n_elements_absmax),
|
|
XPU_STREAM,
|
|
)
|
|
out_absmax += offset
|
|
|
|
# Dequantize W
|
|
fx = (
|
|
cdequantize_blockwise_fp16_nf4
|
|
if dtype == torch_float16
|
|
else cdequantize_blockwise_bf16_nf4
|
|
)
|
|
fx(
|
|
get_ptr(None),
|
|
get_ptr(W),
|
|
ptr_out_absmax,
|
|
get_ptr(out),
|
|
ctypes_c_int(blocksize),
|
|
ctypes_c_int(out.numel()),
|
|
XPU_STREAM,
|
|
)
|
|
# Careful returning transposed data
|
|
is_transposed = True if W.shape[0] == 1 else False
|
|
return out.t() if is_transposed else out
|
|
|
|
# NVIDIA GPU Default Logic
|
|
elif DEVICE_TYPE in ("cuda", "hip") and HAS_CUDA_STREAM:
|
|
|
|
@torch.inference_mode
|
|
def fast_dequantize(
|
|
W,
|
|
quant_state = None,
|
|
out = None,
|
|
use_global_buffer = False,
|
|
):
|
|
if isinstance(W, Float8Tensor):
|
|
return W.dequantize()
|
|
if quant_state is None:
|
|
return W
|
|
if W.dtype == torch.float8_e4m3fn:
|
|
return weight_dequant(W, quant_state)
|
|
if type(quant_state) is not list:
|
|
# New quant_state as a class
|
|
# https://github.com/TimDettmers/bitsandbytes/pull/763/files
|
|
absmax = quant_state.absmax
|
|
shape = quant_state.shape
|
|
dtype = quant_state.dtype
|
|
blocksize = quant_state.blocksize
|
|
offset = quant_state.offset
|
|
state2 = quant_state.state2
|
|
absmax2 = state2.absmax
|
|
code2 = state2.code
|
|
blocksize2 = state2.blocksize
|
|
else:
|
|
# Old quant_state as a list of lists
|
|
absmax, shape, dtype, blocksize, compressed_stats, _, _ = quant_state
|
|
offset, state2 = compressed_stats
|
|
absmax2, code2, blocksize2, _, _, _, _ = state2
|
|
pass
|
|
global CUDA_STREAMS
|
|
device = W.device
|
|
device_index = device.index
|
|
CUDA_STREAM = CUDA_STREAMS[device_index]
|
|
|
|
n_elements_absmax = absmax.numel()
|
|
|
|
# Create weight matrix
|
|
if use_global_buffer:
|
|
# Use same buffers for faster inference
|
|
size = shape[0] * shape[1]
|
|
global WEIGHT_BUFFERS
|
|
global ABSMAX_BUFFERS
|
|
WEIGHT_BUFFER = WEIGHT_BUFFERS[device_index]
|
|
ABSMAX_BUFFER = ABSMAX_BUFFERS[device_index]
|
|
if WEIGHT_BUFFER is None or WEIGHT_BUFFER.dtype != dtype:
|
|
WEIGHT_BUFFERS[device_index] = WEIGHT_BUFFER = torch_empty(
|
|
size, dtype = dtype, device = device, requires_grad = False
|
|
)
|
|
ABSMAX_BUFFERS[device_index] = ABSMAX_BUFFER = torch_empty(
|
|
n_elements_absmax,
|
|
dtype = torch_float32,
|
|
device = device,
|
|
requires_grad = False,
|
|
)
|
|
|
|
if size > WEIGHT_BUFFER.numel():
|
|
WEIGHT_BUFFER.resize_(size)
|
|
if n_elements_absmax > ABSMAX_BUFFER.numel():
|
|
ABSMAX_BUFFER.resize_(n_elements_absmax)
|
|
|
|
out = WEIGHT_BUFFER[:size].view(shape)
|
|
out_absmax = ABSMAX_BUFFER[:n_elements_absmax]
|
|
else:
|
|
if out is None:
|
|
out = torch_empty(shape, dtype = dtype, device = device, requires_grad = False)
|
|
else:
|
|
assert out.shape == shape
|
|
assert out.dtype == dtype
|
|
out_absmax = torch_empty(
|
|
n_elements_absmax,
|
|
dtype = torch_float32,
|
|
device = device,
|
|
requires_grad = False,
|
|
)
|
|
pass
|
|
|
|
# NF4 dequantization of statistics
|
|
ptr_out_absmax = get_ptr(out_absmax)
|
|
with torch_gpu_device(device):
|
|
cdequantize_blockwise_fp32(
|
|
get_ptr(code2),
|
|
get_ptr(absmax),
|
|
get_ptr(absmax2),
|
|
ptr_out_absmax,
|
|
ctypes_c_int(blocksize2),
|
|
ctypes_c_int(n_elements_absmax),
|
|
CUDA_STREAM,
|
|
)
|
|
out_absmax += offset
|
|
|
|
# Dequantize W
|
|
fx = (
|
|
cdequantize_blockwise_fp16_nf4
|
|
if dtype == torch_float16
|
|
else cdequantize_blockwise_bf16_nf4
|
|
)
|
|
fx(
|
|
get_ptr(None),
|
|
get_ptr(W),
|
|
ptr_out_absmax,
|
|
get_ptr(out),
|
|
ctypes_c_int(blocksize),
|
|
ctypes_c_int(out.numel()),
|
|
CUDA_STREAM,
|
|
)
|
|
pass
|
|
# Careful returning transposed data
|
|
is_transposed = True if W.shape[0] == 1 else False
|
|
return out.t() if is_transposed else out
|
|
|
|
pass
|
|
else:
|
|
|
|
@torch.inference_mode
|
|
def fast_dequantize(
|
|
W,
|
|
quant_state = None,
|
|
out = None,
|
|
use_global_buffer = False,
|
|
):
|
|
if isinstance(W, Float8Tensor):
|
|
return W.dequantize()
|
|
if quant_state is None:
|
|
return W
|
|
if W.dtype == torch.float8_e4m3fn:
|
|
return weight_dequant(W, quant_state)
|
|
if type(quant_state) is not list:
|
|
# New quant_state as a class
|
|
# https://github.com/TimDettmers/bitsandbytes/pull/763/files
|
|
absmax = quant_state.absmax
|
|
shape = quant_state.shape
|
|
dtype = quant_state.dtype
|
|
blocksize = quant_state.blocksize
|
|
offset = quant_state.offset
|
|
state2 = quant_state.state2
|
|
absmax2 = state2.absmax
|
|
code2 = state2.code
|
|
blocksize2 = state2.blocksize
|
|
else:
|
|
# Old quant_state as a list of lists
|
|
absmax, shape, dtype, blocksize, compressed_stats, _, _ = quant_state
|
|
offset, state2 = compressed_stats
|
|
absmax2, code2, blocksize2, _, _, _, _ = state2
|
|
pass
|
|
|
|
n_elements_absmax = absmax.numel()
|
|
device = W.device
|
|
|
|
# Create weight matrix
|
|
if out is None:
|
|
out = torch_empty(shape, dtype = dtype, device = device, requires_grad = False)
|
|
else:
|
|
assert out.shape == shape
|
|
assert out.dtype == dtype
|
|
out_absmax = torch_empty(
|
|
n_elements_absmax, dtype = torch_float32, device = device, requires_grad = False
|
|
)
|
|
|
|
# Do dequantization
|
|
ptr_out_absmax = get_ptr(out_absmax)
|
|
cdequantize_blockwise_fp32(
|
|
get_ptr(code2),
|
|
get_ptr(absmax),
|
|
get_ptr(absmax2),
|
|
ptr_out_absmax,
|
|
ctypes_c_int(blocksize2),
|
|
ctypes_c_int(n_elements_absmax),
|
|
)
|
|
out_absmax += offset
|
|
|
|
fx = (
|
|
cdequantize_blockwise_fp16_nf4
|
|
if dtype == torch_float16
|
|
else cdequantize_blockwise_bf16_nf4
|
|
)
|
|
fx(
|
|
get_ptr(None),
|
|
get_ptr(W),
|
|
ptr_out_absmax,
|
|
get_ptr(out),
|
|
ctypes_c_int(blocksize),
|
|
ctypes_c_int(out.numel()),
|
|
)
|
|
|
|
# Careful returning transposed data
|
|
is_transposed = True if W.shape[0] == 1 else False
|
|
return out.t() if is_transposed else out
|
|
|
|
pass
|
|
|
|
|
|
# INTEL GPU Specific Logic
|
|
if DEVICE_TYPE == "xpu" and HAS_XPU_STREAM:
|
|
|
|
def fast_gemv(
|
|
X,
|
|
W,
|
|
quant_state,
|
|
out = None,
|
|
):
|
|
if quant_state is None:
|
|
return torch_matmul(X, W, out = out)
|
|
# For fast X @ W where seq_len == 1
|
|
# From https://github.com/TimDettmers/bitsandbytes/blob/main/bitsandbytes/functional.py#L1469
|
|
_, q_len, hd = X.shape
|
|
# assert(q_len == 1)
|
|
|
|
if type(quant_state) is not list:
|
|
# https://github.com/TimDettmers/bitsandbytes/pull/763/files
|
|
absmax = quant_state.absmax
|
|
shape = quant_state.shape
|
|
dtype = quant_state.dtype
|
|
blocksize = quant_state.blocksize
|
|
stats = quant_state.code
|
|
offset = quant_state.offset
|
|
state2 = quant_state.state2
|
|
absmax2 = state2.absmax
|
|
code2 = state2.code
|
|
blocksize2 = state2.blocksize
|
|
else:
|
|
absmax, shape, dtype, blocksize, compressed_stats, quant_type, stats = quant_state
|
|
offset, state2 = compressed_stats
|
|
absmax2, code2, blocksize2, _, _, _, _ = state2
|
|
global XPU_STREAMS
|
|
device = W.device
|
|
device_index = device.index
|
|
XPU_STREAM = XPU_STREAMS[device_index]
|
|
|
|
# assert(dtype == X.dtype)
|
|
bout = shape[0]
|
|
|
|
if out is None:
|
|
out = torch_empty(
|
|
(
|
|
1,
|
|
1,
|
|
bout,
|
|
),
|
|
dtype = dtype,
|
|
device = device,
|
|
)
|
|
# else:
|
|
# assert(out.shape == (1, 1, bout,))
|
|
# pass
|
|
|
|
if DEVICE_TYPE == "xpu":
|
|
m = 1
|
|
n = shape[0]
|
|
else:
|
|
n = 1
|
|
m = shape[0]
|
|
k = shape[1]
|
|
lda = shape[0]
|
|
ldc = shape[0]
|
|
ldb = (hd + 1) // 2
|
|
m = ctypes_c_int32(m)
|
|
n = ctypes_c_int32(n)
|
|
k = ctypes_c_int32(k)
|
|
lda = ctypes_c_int32(lda)
|
|
ldb = ctypes_c_int32(ldb)
|
|
ldc = ctypes_c_int32(ldc)
|
|
|
|
df = torch_empty(absmax.shape, dtype = torch_float32, device = device)
|
|
with torch_gpu_device(device):
|
|
cdequantize_blockwise_fp32(
|
|
get_ptr(code2),
|
|
get_ptr(absmax),
|
|
get_ptr(absmax2),
|
|
get_ptr(df),
|
|
ctypes_c_int(blocksize2),
|
|
ctypes_c_int(df.numel()),
|
|
XPU_STREAM,
|
|
)
|
|
df += offset
|
|
absmax = df
|
|
|
|
fx = (
|
|
cgemm_4bit_inference_naive_fp16
|
|
if dtype == torch_float16
|
|
else cgemm_4bit_inference_naive_bf16
|
|
)
|
|
|
|
blocksize = ctypes_c_int32(blocksize)
|
|
fx(
|
|
m,
|
|
n,
|
|
k,
|
|
get_ptr(X),
|
|
get_ptr(W),
|
|
get_ptr(absmax),
|
|
get_ptr(stats),
|
|
get_ptr(out),
|
|
lda,
|
|
ldb,
|
|
ldc,
|
|
blocksize,
|
|
XPU_STREAM,
|
|
)
|
|
|
|
return out
|
|
|
|
elif DEVICE_TYPE in ("cuda", "hip") and HAS_CUDA_STREAM:
|
|
|
|
def fast_gemv(
|
|
X,
|
|
W,
|
|
quant_state,
|
|
out = None,
|
|
):
|
|
if quant_state is None:
|
|
return torch_matmul(X, W, out = out)
|
|
# For fast X @ W where seq_len == 1
|
|
# From https://github.com/TimDettmers/bitsandbytes/blob/main/bitsandbytes/functional.py#L1469
|
|
_, q_len, hd = X.shape
|
|
# assert(q_len == 1)
|
|
|
|
if type(quant_state) is not list:
|
|
# https://github.com/TimDettmers/bitsandbytes/pull/763/files
|
|
absmax = quant_state.absmax
|
|
shape = quant_state.shape
|
|
dtype = quant_state.dtype
|
|
blocksize = quant_state.blocksize
|
|
stats = quant_state.code
|
|
offset = quant_state.offset
|
|
state2 = quant_state.state2
|
|
absmax2 = state2.absmax
|
|
code2 = state2.code
|
|
blocksize2 = state2.blocksize
|
|
else:
|
|
absmax, shape, dtype, blocksize, compressed_stats, quant_type, stats = quant_state
|
|
offset, state2 = compressed_stats
|
|
absmax2, code2, blocksize2, _, _, _, _ = state2
|
|
pass
|
|
global CUDA_STREAMS
|
|
device = W.device
|
|
device_index = device.index
|
|
CUDA_STREAM = CUDA_STREAMS[device_index]
|
|
|
|
# assert(dtype == X.dtype)
|
|
bout = shape[0]
|
|
|
|
if out is None:
|
|
out = torch_empty(
|
|
(
|
|
1,
|
|
1,
|
|
bout,
|
|
),
|
|
dtype = dtype,
|
|
device = device,
|
|
)
|
|
# else:
|
|
# assert(out.shape == (1, 1, bout,))
|
|
# pass
|
|
|
|
n = 1
|
|
m = shape[0]
|
|
k = shape[1]
|
|
lda = shape[0]
|
|
ldc = shape[0]
|
|
ldb = (hd + 1) // 2
|
|
m = ctypes_c_int32(m)
|
|
n = ctypes_c_int32(n)
|
|
k = ctypes_c_int32(k)
|
|
lda = ctypes_c_int32(lda)
|
|
ldb = ctypes_c_int32(ldb)
|
|
ldc = ctypes_c_int32(ldc)
|
|
|
|
df = torch_empty(absmax.shape, dtype = torch_float32, device = device)
|
|
with torch_gpu_device(device):
|
|
cdequantize_blockwise_fp32(
|
|
get_ptr(code2),
|
|
get_ptr(absmax),
|
|
get_ptr(absmax2),
|
|
get_ptr(df),
|
|
ctypes_c_int(blocksize2),
|
|
ctypes_c_int(df.numel()),
|
|
CUDA_STREAM,
|
|
)
|
|
df += offset
|
|
absmax = df
|
|
|
|
fx = (
|
|
cgemm_4bit_inference_naive_fp16
|
|
if dtype == torch_float16
|
|
else cgemm_4bit_inference_naive_bf16
|
|
)
|
|
|
|
blocksize = ctypes_c_int32(blocksize)
|
|
fx(
|
|
m,
|
|
n,
|
|
k,
|
|
get_ptr(X),
|
|
get_ptr(W),
|
|
get_ptr(absmax),
|
|
get_ptr(stats),
|
|
get_ptr(out),
|
|
lda,
|
|
ldb,
|
|
ldc,
|
|
blocksize,
|
|
CUDA_STREAM,
|
|
)
|
|
pass
|
|
|
|
return out
|
|
|
|
pass
|
|
else:
|
|
|
|
def fast_gemv(
|
|
X,
|
|
W,
|
|
quant_state,
|
|
out = None,
|
|
):
|
|
if quant_state is None:
|
|
return torch_matmul(X, W, out = out)
|
|
# For fast X @ W where seq_len == 1
|
|
# From https://github.com/TimDettmers/bitsandbytes/blob/main/bitsandbytes/functional.py#L1469
|
|
_, q_len, hd = X.shape
|
|
# assert(q_len == 1)
|
|
|
|
if type(quant_state) is not list:
|
|
# https://github.com/TimDettmers/bitsandbytes/pull/763/files
|
|
absmax = quant_state.absmax
|
|
shape = quant_state.shape
|
|
dtype = quant_state.dtype
|
|
blocksize = quant_state.blocksize
|
|
stats = quant_state.code
|
|
offset = quant_state.offset
|
|
state2 = quant_state.state2
|
|
absmax2 = state2.absmax
|
|
code2 = state2.code
|
|
blocksize2 = state2.blocksize
|
|
else:
|
|
absmax, shape, dtype, blocksize, compressed_stats, quant_type, stats = quant_state
|
|
offset, state2 = compressed_stats
|
|
absmax2, code2, blocksize2, _, _, _, _ = state2
|
|
pass
|
|
# assert(dtype == X.dtype)
|
|
bout = shape[0]
|
|
device = W.device
|
|
|
|
if out is None:
|
|
out = torch_empty(
|
|
(
|
|
1,
|
|
1,
|
|
bout,
|
|
),
|
|
dtype = dtype,
|
|
device = device,
|
|
)
|
|
# else:
|
|
# assert(out.shape == (1, 1, bout,))
|
|
# pass
|
|
|
|
n = 1
|
|
m = shape[0]
|
|
k = shape[1]
|
|
lda = shape[0]
|
|
ldc = shape[0]
|
|
ldb = (hd + 1) // 2
|
|
m = ctypes_c_int32(m)
|
|
n = ctypes_c_int32(n)
|
|
k = ctypes_c_int32(k)
|
|
lda = ctypes_c_int32(lda)
|
|
ldb = ctypes_c_int32(ldb)
|
|
ldc = ctypes_c_int32(ldc)
|
|
|
|
df = torch_empty(absmax.shape, dtype = torch_float32, device = device)
|
|
cdequantize_blockwise_fp32(
|
|
get_ptr(code2),
|
|
get_ptr(absmax),
|
|
get_ptr(absmax2),
|
|
get_ptr(df),
|
|
ctypes_c_int(blocksize2),
|
|
ctypes_c_int(df.numel()),
|
|
)
|
|
df += offset
|
|
absmax = df
|
|
|
|
fx = (
|
|
cgemm_4bit_inference_naive_fp16
|
|
if dtype == torch_float16
|
|
else cgemm_4bit_inference_naive_bf16
|
|
)
|
|
|
|
blocksize = ctypes_c_int32(blocksize)
|
|
fx(
|
|
m,
|
|
n,
|
|
k,
|
|
get_ptr(X),
|
|
get_ptr(W),
|
|
get_ptr(absmax),
|
|
get_ptr(stats),
|
|
get_ptr(out),
|
|
lda,
|
|
ldb,
|
|
ldc,
|
|
blocksize,
|
|
)
|
|
|
|
return out
|
|
|
|
pass
|
|
|
|
|
|
def fast_linear_forward(
|
|
proj,
|
|
X,
|
|
temp_lora = None,
|
|
out = None,
|
|
):
|
|
W, W_quant, lora_A, lora_B, lora_S, bias = get_lora_parameters_bias(proj)
|
|
bsz, q_len, in_dim = X.shape
|
|
if q_len != 1:
|
|
return matmul_lora(X, W, W_quant, lora_A, lora_B, lora_S)
|
|
|
|
if W_quant is None:
|
|
out = torch_matmul(X, W.t(), out = out)
|
|
elif W.dtype == torch.float8_e4m3fn:
|
|
out = fp8_linear(X, W, W_quant, bias)
|
|
elif bsz == 1 and q_len == 1:
|
|
out = fast_gemv(X, W, W_quant, out = out)
|
|
else:
|
|
W = fast_dequantize(W.t(), W_quant, use_global_buffer = True)
|
|
out = torch_matmul(X, W, out = out)
|
|
|
|
# Add in LoRA weights
|
|
if lora_A is not None:
|
|
out_dim = out.shape[2]
|
|
dtype = X.dtype
|
|
|
|
if not hasattr(lora_A, "_fast_lora"):
|
|
lora_A._fast_lora = lora_A.to(dtype)
|
|
lora_B._fast_lora = lora_B.to(dtype)
|
|
|
|
if bsz == 1:
|
|
out = out.view(out_dim)
|
|
temp_lora = torch_mv(lora_A._fast_lora, X.ravel(), out = temp_lora)
|
|
out.addmv_(lora_B._fast_lora, temp_lora, alpha = lora_S)
|
|
else:
|
|
out = out.view(bsz, out_dim)
|
|
temp_lora = torch_mm(X.view(bsz, in_dim), lora_A._fast_lora.t(), out = temp_lora)
|
|
out.addmm_(temp_lora, lora_B._fast_lora.t(), alpha = lora_S)
|
|
out = out.view(bsz, 1, out_dim)
|
|
|
|
if bias is not None:
|
|
out += bias
|
|
|
|
return out
|
|
|
|
|
|
def matmul_lora(
|
|
X,
|
|
W,
|
|
W_quant,
|
|
A,
|
|
B,
|
|
s,
|
|
out = None,
|
|
):
|
|
dtype = X.dtype
|
|
|
|
if X.dim() == 3:
|
|
batch, seq_len, d = X.shape
|
|
X = X.view(-1, X.shape[-1])
|
|
reshape = True
|
|
else:
|
|
reshape = False
|
|
|
|
if isinstance(W, Float8Tensor):
|
|
assert W.ndim == 2
|
|
if W.block_size[0] == W.shape[0] and W.block_size[1] == 1:
|
|
# Rowwise scaling becomes colwise after transpose, so this detects
|
|
# the backward pass. TODO: avoid calling matmul_lora in backward.
|
|
W = W.dequantize()
|
|
else:
|
|
W = W.contiguous()
|
|
out = torch_matmul(X, W.t(), out = out)
|
|
elif W.dtype == torch.float8_e4m3fn:
|
|
out = fp8_linear(X, W, W_quant)
|
|
else:
|
|
W = fast_dequantize(W, W_quant, use_global_buffer = True)
|
|
out = torch_matmul(X, W.t(), out = out)
|
|
if W_quant is not None:
|
|
del W
|
|
|
|
if A is not None:
|
|
# LoRA is enabled
|
|
A, B = A.t(), B.t()
|
|
XA = torch_matmul(X, A.to(dtype))
|
|
out.addmm_(XA, B.to(dtype), alpha = s)
|
|
# out += (X @ A.to(dtype)) @ (s * B.to(dtype))
|
|
|
|
return out.view(batch, seq_len, -1) if reshape else out
|