70 lines
2.0 KiB
Python
70 lines
2.0 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
import torch
|
|
|
|
from vllm._custom_ops import scaled_fp4_quant
|
|
from vllm.model_executor.layers.quantization.utils.nvfp4_utils import (
|
|
slice_nvfp4_output,
|
|
swizzle_blockscale,
|
|
)
|
|
from vllm.utils.import_utils import has_fbgemm_gpu
|
|
|
|
from .base import NvFp4LinearKernel, NvFp4LinearLayerConfig
|
|
|
|
|
|
class FbgemmNvFp4LinearKernel(NvFp4LinearKernel):
|
|
"""NVFP4 GEMM via FBGEMM."""
|
|
|
|
@classmethod
|
|
def is_supported(
|
|
cls, compute_capability: int | None = None
|
|
) -> tuple[bool, str | None]:
|
|
if has_fbgemm_gpu():
|
|
return True, None
|
|
return False, "fbgemm_gpu required"
|
|
|
|
@classmethod
|
|
def can_implement(cls, config: NvFp4LinearLayerConfig) -> tuple[bool, str | None]:
|
|
return True, None
|
|
|
|
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
|
|
swizzled = swizzle_blockscale(layer.weight_scale.data)
|
|
layer.weight_scale = torch.nn.Parameter(
|
|
swizzled.view(-1).view(torch.uint8), requires_grad=False
|
|
)
|
|
|
|
def apply_weights(
|
|
self,
|
|
layer: torch.nn.Module,
|
|
x: torch.Tensor,
|
|
bias: torch.Tensor | None = None,
|
|
) -> torch.Tensor:
|
|
import fbgemm_gpu # noqa: F401 - registers torch.ops.fbgemm.*
|
|
|
|
output_size = layer.output_size_per_partition
|
|
output_dtype = x.dtype
|
|
output_shape = [*x.shape[:-1], output_size]
|
|
|
|
x_fp4, x_blockscale = scaled_fp4_quant(
|
|
x,
|
|
layer.input_global_scale_inv,
|
|
is_sf_swizzled_layout=True,
|
|
backend="fbgemm",
|
|
)
|
|
|
|
out = torch.ops.fbgemm.f4f4bf16(
|
|
x_fp4,
|
|
layer.weight,
|
|
x_blockscale.view(-1).view(torch.uint8),
|
|
layer.weight_scale,
|
|
layer.alpha,
|
|
use_mx=False,
|
|
).to(output_dtype)
|
|
|
|
out = slice_nvfp4_output(out, output_size)
|
|
|
|
if bias is not None:
|
|
out = out + bias
|
|
return out.view(*output_shape)
|