Add initial support for gpt-oss (#8824)
This commit is contained in:
@@ -4,6 +4,7 @@ import torch
|
||||
|
||||
from sglang.srt.layers.quantization import deep_gemm_wrapper
|
||||
from sglang.srt.layers.quantization.fp8_kernel import sglang_per_token_group_quant_fp8
|
||||
from sglang.srt.layers.quantization.mxfp4_tensor import MXFP4QuantizeUtil
|
||||
from sglang.srt.layers.utils import is_sm100_supported
|
||||
|
||||
try:
|
||||
@@ -26,6 +27,7 @@ from sglang.srt.layers.quantization.fp8_kernel import (
|
||||
)
|
||||
from sglang.srt.utils import (
|
||||
align,
|
||||
ceil_div,
|
||||
get_bool_env_var,
|
||||
get_cuda_version,
|
||||
get_device_capability,
|
||||
@@ -307,6 +309,33 @@ def triton_w8a8_block_fp8_linear(
|
||||
return output.to(dtype=input_2d.dtype).view(*output_shape)
|
||||
|
||||
|
||||
def dequant_mxfp4(
|
||||
w_block: torch.Tensor,
|
||||
w_scale: torch.Tensor,
|
||||
out_dtype,
|
||||
) -> torch.Tensor:
|
||||
"""
|
||||
:param w_block: (batch, n, k, 16), uint8, pack two mxfp4 into one byte
|
||||
:param w_scale: (batch, n, k), uint8
|
||||
:return: (batch, n, k * 32), float32
|
||||
"""
|
||||
|
||||
assert w_block.dtype == torch.uint8
|
||||
assert w_scale.dtype == torch.uint8
|
||||
|
||||
batch, n, k, pack_dim = w_block.shape
|
||||
batch_, n_, k_ = w_scale.shape
|
||||
assert pack_dim == 16
|
||||
assert batch == batch_
|
||||
assert n == n_
|
||||
assert k == k_
|
||||
|
||||
out_raw = MXFP4QuantizeUtil.dequantize(
|
||||
quantized_data=w_block, scale=w_scale, dtype=out_dtype, block_sizes=[32]
|
||||
)
|
||||
return out_raw.reshape(batch, n, k * 32)
|
||||
|
||||
|
||||
def input_to_float8(
|
||||
x: torch.Tensor, dtype: torch.dtype = fp8_dtype
|
||||
) -> Tuple[torch.Tensor, torch.Tensor]:
|
||||
|
||||
133
python/sglang/srt/layers/quantization/mxfp4_tensor.py
Normal file
133
python/sglang/srt/layers/quantization/mxfp4_tensor.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# 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 torch
|
||||
|
||||
|
||||
# https://github.com/NVIDIA/TensorRT-Model-Optimizer/blob/main/modelopt/torch/quantization/qtensor/mxfp4_tensor.py
|
||||
class MXFP4QuantizeUtil:
|
||||
E2M1_max = 6.0
|
||||
|
||||
E2M1_values = [0, 0.5, 1, 1.5, 2, 3, 4, 6]
|
||||
E2M1_bounds = torch.tensor([0.25, 0.75, 1.25, 1.75, 2.5, 3.5, 5])
|
||||
|
||||
@classmethod
|
||||
def quantize(cls, input: torch.Tensor, block_size: int | None) -> tuple:
|
||||
"""Converting a tensor to a quantized format based on MXFP4 quantization. Only E4M3 is supported.
|
||||
Args:
|
||||
input (torch.Tensor): The input tensor to be quantized.
|
||||
block_sizes (dict | None): The block sizes for quantization.
|
||||
"""
|
||||
|
||||
def cast_fp4(x):
|
||||
sign = torch.sign(x)
|
||||
sign_bit = (2 - sign) // 2
|
||||
ord_ = torch.sum(
|
||||
(x.abs().unsqueeze(-1) - cls.E2M1_bounds.to(x.device)) > 0, dim=-1
|
||||
)
|
||||
fp4_val = (sign_bit * 0b1000 + ord_).to(torch.uint8)
|
||||
return fp4_val
|
||||
|
||||
def fuse_uint4_to_uint8(x):
|
||||
# If the last dimension is odd, pad with zeros
|
||||
# If this behavior is not desired, please modify the code accordingly
|
||||
left_side = x[..., 0::2] # Even indices (0, 2, 4...)
|
||||
right_side = x[..., 1::2] # Odd indices (1, 3, 5...)
|
||||
new_data = (
|
||||
right_side.clone() << 4
|
||||
) # Put odd indices (higher addresses) in high bits
|
||||
new_data[
|
||||
..., : left_side.shape[-1]
|
||||
] += left_side # Put even indices in low bits
|
||||
return new_data
|
||||
|
||||
if block_size is None:
|
||||
block_size = 32
|
||||
|
||||
original_shape = input.shape
|
||||
original_dtype = input.dtype
|
||||
input = input.view(-1, block_size)
|
||||
# get scales
|
||||
input_amax = input.abs().max(dim=-1, keepdim=True).values
|
||||
descale = input_amax / cls.E2M1_max
|
||||
min_value = torch.tensor(-127.0, device=descale.device)
|
||||
e8m0_scale = torch.ceil(torch.maximum(torch.log2(descale), min_value))
|
||||
|
||||
input = (input / torch.exp2(e8m0_scale)).view(original_shape)
|
||||
input_q = cast_fp4(input)
|
||||
input_q = fuse_uint4_to_uint8(input_q)
|
||||
e8m0_scale = (e8m0_scale + 127).to(torch.uint8)
|
||||
return cls(original_shape, original_dtype, input_q), e8m0_scale
|
||||
|
||||
@classmethod
|
||||
def dequantize(cls, quantized_data, dtype: torch.dtype, scale, block_sizes):
|
||||
"""Dequantze MXFP4 packed tensor to a target dtype."""
|
||||
|
||||
def unfuse_uint8_to_uint4(x):
|
||||
"""Unfuse uint8 values back to uint4 values.
|
||||
This is the inverse operation of fuse_uint4_to_uint8.
|
||||
"""
|
||||
# Extract the lower 4 bits (even indices)
|
||||
left_side = x & 0x0F
|
||||
|
||||
# Extract the upper 4 bits (odd indices)
|
||||
right_side = (x >> 4) & 0x0F
|
||||
|
||||
# Create a new tensor with alternating values
|
||||
shape = list(x.shape)
|
||||
shape[-1] = shape[-1] * 2
|
||||
result = torch.zeros(shape, dtype=torch.uint8, device=x.device)
|
||||
|
||||
# Fill in the values - even indices get low bits, odd indices get high bits
|
||||
result[..., 0::2] = left_side # Even indices from low bits
|
||||
result[..., 1::2] = right_side # Odd indices from high bits
|
||||
|
||||
return result
|
||||
|
||||
e8m0_scale = scale
|
||||
block_size = block_sizes[-1]
|
||||
|
||||
# Unfuse the uint8 values back to uint4
|
||||
x_unfused = unfuse_uint8_to_uint4(quantized_data)
|
||||
# Extract sign and magnitude
|
||||
sign = 1 - 2 * ((x_unfused & 0b1000) >> 3).to(
|
||||
torch.float32
|
||||
) # Extract sign bit and convert to +1/-1
|
||||
magnitude = x_unfused & 0b0111 # Extract magnitude bits
|
||||
magnitude = magnitude.to(torch.long)
|
||||
|
||||
# Create a tensor with the E2M1 values
|
||||
values = torch.tensor(cls.E2M1_values, device=quantized_data.device)
|
||||
|
||||
# Use gather to index the values tensor properly
|
||||
# We need to reshape magnitude to match the dimensions we want to gather along
|
||||
original_shape = magnitude.shape
|
||||
x_float = values[magnitude.reshape(-1)].reshape(original_shape)
|
||||
|
||||
# Apply sign and scale
|
||||
x_float = sign.float() * x_float
|
||||
|
||||
# Reshape to apply block-wise scaling
|
||||
x_float = x_float.reshape(-1, block_size)
|
||||
|
||||
# Apply the E8M0 scale
|
||||
scale_factor = torch.exp2(e8m0_scale.float() - 127)
|
||||
scale_factor = scale_factor.reshape(-1, 1) # Reshape for proper broadcasting
|
||||
|
||||
# Apply scaling and reshape back to original shape
|
||||
x_float = x_float * scale_factor
|
||||
|
||||
# Reshape back to the original shape
|
||||
return x_float.reshape(original_shape).to(dtype)
|
||||
@@ -126,17 +126,23 @@ class UnquantizedLinearMethod(LinearMethodBase):
|
||||
class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
|
||||
"""MoE method without quantization."""
|
||||
|
||||
def __init__(self, use_triton_kernels: bool = False):
|
||||
def __init__(self, use_triton_kernels: bool = False, with_bias: bool = False):
|
||||
super().__init__()
|
||||
self.use_triton_kernels = use_triton_kernels
|
||||
self.with_bias = with_bias
|
||||
|
||||
self.triton_kernel_moe_forward = None
|
||||
self.triton_kernel_moe_with_bias_forward = None
|
||||
if torch.cuda.is_available() and has_triton_kernels:
|
||||
from sglang.srt.layers.moe.fused_moe_triton.triton_kernels_moe import (
|
||||
triton_kernel_moe_forward as _tk_forward,
|
||||
)
|
||||
from sglang.srt.layers.moe.fused_moe_triton.triton_kernels_moe import (
|
||||
triton_kernel_moe_with_bias_forward as _tk_with_bias_forward,
|
||||
)
|
||||
|
||||
self.triton_kernel_moe_forward = _tk_forward
|
||||
self.triton_kernel_moe_with_bias_forward = _tk_with_bias_forward
|
||||
|
||||
def create_weights(
|
||||
self,
|
||||
@@ -158,6 +164,14 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
|
||||
layer.register_parameter("w13_weight", w13_weight)
|
||||
set_weight_attrs(w13_weight, extra_weight_attrs)
|
||||
|
||||
if self.with_bias:
|
||||
w13_weight_bias = torch.nn.Parameter(
|
||||
torch.empty(num_experts, 2 * intermediate_size, dtype=torch.float32),
|
||||
requires_grad=False,
|
||||
)
|
||||
layer.register_parameter("w13_weight_bias", w13_weight_bias)
|
||||
set_weight_attrs(w13_weight_bias, extra_weight_attrs)
|
||||
|
||||
# down_proj (row parallel)
|
||||
w2_weight_n, w2_weight_k = (
|
||||
hidden_size,
|
||||
@@ -172,6 +186,14 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
|
||||
layer.register_parameter("w2_weight", w2_weight)
|
||||
set_weight_attrs(w2_weight, extra_weight_attrs)
|
||||
|
||||
if self.with_bias:
|
||||
w2_weight_bias = torch.nn.Parameter(
|
||||
torch.empty(num_experts, hidden_size, dtype=torch.float32),
|
||||
requires_grad=False,
|
||||
)
|
||||
layer.register_parameter("w2_weight_bias", w2_weight_bias)
|
||||
set_weight_attrs(w2_weight_bias, extra_weight_attrs)
|
||||
|
||||
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
|
||||
if _use_aiter:
|
||||
layer.w13_weight = torch.nn.Parameter(
|
||||
@@ -202,7 +224,14 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
|
||||
inplace: bool = True,
|
||||
no_combine: bool = False,
|
||||
routed_scaling_factor: Optional[float] = None,
|
||||
activation_alpha: Optional[float] = None,
|
||||
swiglu_limit: Optional[float] = None,
|
||||
) -> torch.Tensor:
|
||||
kwargs = {}
|
||||
if activation_alpha is not None:
|
||||
kwargs["activation_alpha"] = activation_alpha
|
||||
if swiglu_limit is not None:
|
||||
kwargs["swiglu_limit"] = swiglu_limit
|
||||
|
||||
return self.forward(
|
||||
x=x,
|
||||
@@ -213,6 +242,7 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
|
||||
inplace=inplace,
|
||||
no_combine=no_combine,
|
||||
routed_scaling_factor=routed_scaling_factor,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def forward_cuda(
|
||||
@@ -226,15 +256,30 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
|
||||
inplace: bool = True,
|
||||
no_combine: bool = False,
|
||||
routed_scaling_factor: Optional[float] = None,
|
||||
activation_alpha: Optional[float] = None,
|
||||
swiglu_limit: Optional[float] = None,
|
||||
) -> torch.Tensor:
|
||||
|
||||
if self.use_triton_kernels:
|
||||
return self.triton_kernel_moe_forward(
|
||||
hidden_states=x,
|
||||
w1=layer.w13_weight,
|
||||
w2=layer.w2_weight,
|
||||
topk_output=topk_output,
|
||||
)
|
||||
if self.with_bias:
|
||||
return self.triton_kernel_moe_with_bias_forward(
|
||||
hidden_states=x,
|
||||
w1=layer.w13_weight,
|
||||
w2=layer.w2_weight,
|
||||
b1=layer.w13_weight_bias,
|
||||
b2=layer.w2_weight_bias,
|
||||
topk_output=topk_output,
|
||||
activation=activation,
|
||||
activation_alpha=activation_alpha,
|
||||
swiglu_limit=swiglu_limit,
|
||||
)
|
||||
else:
|
||||
return self.triton_kernel_moe_forward(
|
||||
hidden_states=x,
|
||||
w1=layer.w13_weight,
|
||||
w2=layer.w2_weight,
|
||||
topk_output=topk_output,
|
||||
)
|
||||
else:
|
||||
if _use_aiter:
|
||||
assert not no_combine, "unsupported"
|
||||
|
||||
Reference in New Issue
Block a user