Support fp4 fp8 non gated moe (#13794)
Co-authored-by: Roi Koren <roik@nvidia.com> Co-authored-by: Tomer Natan <tbarnatan@computelab-frontend-8.nvidia.com>
This commit is contained in:
co-authored by
Roi Koren
Tomer Natan
parent
eb5008846a
commit
02af51e4fc
@@ -281,7 +281,10 @@ class FusedMoE(torch.nn.Module):
|
|||||||
# We have to keep the weight scales of w1 and w3 because
|
# We have to keep the weight scales of w1 and w3 because
|
||||||
# we need to re-quantize w1/w3 weights after weight loading.
|
# we need to re-quantize w1/w3 weights after weight loading.
|
||||||
idx = 0 if shard_id == "w1" else 1
|
idx = 0 if shard_id == "w1" else 1
|
||||||
param_data[expert_id][idx] = loaded_weight
|
if self.moe_runner_config.is_gated:
|
||||||
|
param_data[expert_id][idx] = loaded_weight
|
||||||
|
else:
|
||||||
|
param_data[expert_id] = loaded_weight
|
||||||
# If we are in the row parallel case (down_proj)
|
# If we are in the row parallel case (down_proj)
|
||||||
elif shard_id == "w2":
|
elif shard_id == "w2":
|
||||||
param_data[expert_id] = loaded_weight
|
param_data[expert_id] = loaded_weight
|
||||||
@@ -345,7 +348,6 @@ class FusedMoE(torch.nn.Module):
|
|||||||
tp_rank: int,
|
tp_rank: int,
|
||||||
is_bias: bool = False,
|
is_bias: bool = False,
|
||||||
):
|
):
|
||||||
|
|
||||||
# Index the loaded weight for tp sharding.
|
# Index the loaded weight for tp sharding.
|
||||||
# gate_up_proj: "MergedColumnParallel", so tp sharding on output_dim
|
# gate_up_proj: "MergedColumnParallel", so tp sharding on output_dim
|
||||||
assert shard_id in {"w1", "w3", "w13"}
|
assert shard_id in {"w1", "w3", "w13"}
|
||||||
@@ -481,7 +483,6 @@ class FusedMoE(torch.nn.Module):
|
|||||||
loaded_weight: torch.Tensor,
|
loaded_weight: torch.Tensor,
|
||||||
tp_rank: int,
|
tp_rank: int,
|
||||||
):
|
):
|
||||||
|
|
||||||
if shard_id == "w2":
|
if shard_id == "w2":
|
||||||
self._load_w2(
|
self._load_w2(
|
||||||
shard_id=shard_id,
|
shard_id=shard_id,
|
||||||
@@ -518,7 +519,6 @@ class FusedMoE(torch.nn.Module):
|
|||||||
shard_id: str,
|
shard_id: str,
|
||||||
expert_id: Optional[int],
|
expert_id: Optional[int],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
# if expert_id is None, then
|
# if expert_id is None, then
|
||||||
# all the experts are loaded at the same time
|
# all the experts are loaded at the same time
|
||||||
if (
|
if (
|
||||||
@@ -612,7 +612,6 @@ class FusedMoE(torch.nn.Module):
|
|||||||
shard_id: str,
|
shard_id: str,
|
||||||
expert_id: int,
|
expert_id: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|
||||||
tp_rank = self.moe_tp_rank
|
tp_rank = self.moe_tp_rank
|
||||||
|
|
||||||
# compressed-tensors checkpoints with packed weights are stored flipped
|
# compressed-tensors checkpoints with packed weights are stored flipped
|
||||||
@@ -925,7 +924,6 @@ class FusedMoE(torch.nn.Module):
|
|||||||
ckpt_up_proj_name: str,
|
ckpt_up_proj_name: str,
|
||||||
num_experts: int,
|
num_experts: int,
|
||||||
) -> List[Tuple[str, str, int, str]]:
|
) -> List[Tuple[str, str, int, str]]:
|
||||||
|
|
||||||
return [
|
return [
|
||||||
# (param_name, weight_name, expert_id, shard_id)
|
# (param_name, weight_name, expert_id, shard_id)
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
from enum import IntEnum
|
||||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
@@ -85,9 +86,16 @@ except ImportError:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from flashinfer.fused_moe import cutlass_fused_moe as flashinfer_cutlass_fused_moe
|
from flashinfer.fused_moe import cutlass_fused_moe as flashinfer_cutlass_fused_moe
|
||||||
|
from flashinfer.fused_moe.core import ActivationType
|
||||||
except ImportError:
|
except ImportError:
|
||||||
flashinfer_cutlass_fused_moe = None
|
flashinfer_cutlass_fused_moe = None
|
||||||
|
|
||||||
|
# Define a minimal ActivationType enum if flashinfer is not available
|
||||||
|
class ActivationType(IntEnum):
|
||||||
|
Swiglu = 3
|
||||||
|
Relu2 = 6
|
||||||
|
|
||||||
|
|
||||||
# Initialize logger for the module
|
# Initialize logger for the module
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -145,6 +153,11 @@ FLASHINFER_FP4_GEMM_BACKEND = envs.SGLANG_FLASHINFER_FP4_GEMM_BACKEND.get()
|
|||||||
# Supported activation schemes for the current configuration
|
# Supported activation schemes for the current configuration
|
||||||
ACTIVATION_SCHEMES = ["static"]
|
ACTIVATION_SCHEMES = ["static"]
|
||||||
|
|
||||||
|
ACT_STR_TO_TYPE_MAP = {
|
||||||
|
"silu": ActivationType.Swiglu, # This is the default
|
||||||
|
"relu2": ActivationType.Relu2,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ModelOptQuantConfig(QuantizationConfig):
|
class ModelOptQuantConfig(QuantizationConfig):
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -443,11 +456,12 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
|
|||||||
else params_dtype
|
else params_dtype
|
||||||
)
|
)
|
||||||
weight_loader = extra_weight_attrs.get("weight_loader")
|
weight_loader = extra_weight_attrs.get("weight_loader")
|
||||||
|
num_shards = 2 if layer.moe_runner_config.is_gated else 1
|
||||||
|
intermediate_size = num_shards * intermediate_size_per_partition
|
||||||
w13_weight = ModelWeightParameter(
|
w13_weight = ModelWeightParameter(
|
||||||
data=torch.empty(
|
data=torch.empty(
|
||||||
num_experts,
|
num_experts,
|
||||||
2 * intermediate_size_per_partition,
|
intermediate_size,
|
||||||
hidden_size,
|
hidden_size,
|
||||||
dtype=weight_dtype,
|
dtype=weight_dtype,
|
||||||
),
|
),
|
||||||
@@ -474,9 +488,10 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
|
|||||||
# WEIGHT SCALES - Per-tensor scaling for ModelOpts
|
# WEIGHT SCALES - Per-tensor scaling for ModelOpts
|
||||||
# Allocate 2 scales for w1 and w3 respectively.
|
# Allocate 2 scales for w1 and w3 respectively.
|
||||||
# They will be combined to a single scale after weight loading.
|
# They will be combined to a single scale after weight loading.
|
||||||
|
w13_scale_shape = (num_experts, num_shards)
|
||||||
w13_weight_scale = PerTensorScaleParameter(
|
w13_weight_scale = PerTensorScaleParameter(
|
||||||
data=torch.full(
|
data=torch.full(
|
||||||
(num_experts, 2),
|
w13_scale_shape,
|
||||||
torch.finfo(torch.float32).min,
|
torch.finfo(torch.float32).min,
|
||||||
dtype=torch.float32,
|
dtype=torch.float32,
|
||||||
),
|
),
|
||||||
@@ -528,10 +543,13 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
|
|||||||
# Requantize each expert's weights using the combined scale
|
# Requantize each expert's weights using the combined scale
|
||||||
# w13_weight has shape (num_experts, 2 * intermediate_size_per_partition, hidden_size)
|
# w13_weight has shape (num_experts, 2 * intermediate_size_per_partition, hidden_size)
|
||||||
# where the first intermediate_size_per_partition rows are w1, the next are w3
|
# where the first intermediate_size_per_partition rows are w1, the next are w3
|
||||||
intermediate_size_per_partition = layer.w13_weight.shape[1] // 2
|
num_shards = 2 if layer.moe_runner_config.is_gated else 1
|
||||||
|
intermediate_size_per_partition = (
|
||||||
|
layer.w13_weight.shape[1] // num_shards
|
||||||
|
)
|
||||||
for expert_id in range(layer.w13_weight.shape[0]):
|
for expert_id in range(layer.w13_weight.shape[0]):
|
||||||
start = 0
|
start = 0
|
||||||
for shard_id in range(2): # w1 and w3
|
for shard_id in range(num_shards): # (w1 and w3) or w13
|
||||||
# Dequantize using the original scale for this shard
|
# Dequantize using the original scale for this shard
|
||||||
dq_weight = per_tensor_dequantize(
|
dq_weight = per_tensor_dequantize(
|
||||||
layer.w13_weight[expert_id][
|
layer.w13_weight[expert_id][
|
||||||
@@ -646,6 +664,34 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
|
|||||||
layer.output2_scales_scalar = Parameter(
|
layer.output2_scales_scalar = Parameter(
|
||||||
output2_scales_scalar, requires_grad=False
|
output2_scales_scalar, requires_grad=False
|
||||||
)
|
)
|
||||||
|
elif get_moe_runner_backend().is_flashinfer_cutlass():
|
||||||
|
assert (
|
||||||
|
hasattr(layer, "w13_input_scale") and layer.w13_input_scale is not None
|
||||||
|
)
|
||||||
|
assert hasattr(layer, "w2_input_scale") and layer.w2_input_scale is not None
|
||||||
|
assert (
|
||||||
|
hasattr(layer, "w13_weight_scale")
|
||||||
|
and layer.w13_weight_scale is not None
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
hasattr(layer, "w2_weight_scale") and layer.w2_weight_scale is not None
|
||||||
|
)
|
||||||
|
|
||||||
|
input_scale = layer.w13_input_scale.to(torch.float32)
|
||||||
|
activation_scale = layer.w2_input_scale.to(torch.float32)
|
||||||
|
w13_weight_scale = layer.w13_weight_scale.to(torch.float32)
|
||||||
|
w2_weight_scale = layer.w2_weight_scale.to(torch.float32)
|
||||||
|
|
||||||
|
layer.fc1_dequant = Parameter(
|
||||||
|
w13_weight_scale * input_scale, requires_grad=False
|
||||||
|
)
|
||||||
|
layer.fc2_quant = Parameter(
|
||||||
|
activation_scale.reciprocal(), requires_grad=False
|
||||||
|
)
|
||||||
|
layer.fc2_dequant = Parameter(
|
||||||
|
activation_scale * w2_weight_scale, requires_grad=False
|
||||||
|
)
|
||||||
|
layer.fc1_input_dequant = Parameter(input_scale, requires_grad=False)
|
||||||
|
|
||||||
def create_moe_runner(
|
def create_moe_runner(
|
||||||
self, layer: torch.nn.Module, moe_runner_config: MoeRunnerConfig
|
self, layer: torch.nn.Module, moe_runner_config: MoeRunnerConfig
|
||||||
@@ -744,6 +790,55 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
|
|||||||
|
|
||||||
return StandardCombineInput(hidden_states=output)
|
return StandardCombineInput(hidden_states=output)
|
||||||
|
|
||||||
|
if get_moe_runner_backend().is_flashinfer_cutlass():
|
||||||
|
activation = ACT_STR_TO_TYPE_MAP[self.moe_runner_config.activation]
|
||||||
|
assert (
|
||||||
|
(
|
||||||
|
activation is ActivationType.Relu2
|
||||||
|
and not self.moe_runner_config.is_gated
|
||||||
|
)
|
||||||
|
or activation is ActivationType.Swiglu
|
||||||
|
and self.moe_runner_config.is_gated
|
||||||
|
), "Only Relu2 non-gated or Swiglu gated are supported for flashinfer cutlass fp8 moe"
|
||||||
|
topk_weights, topk_ids = topk_output.topk_weights, topk_output.topk_ids
|
||||||
|
x_fp8, _ = scaled_fp8_quant(x, layer.w13_input_scale)
|
||||||
|
output_dtype = x.dtype
|
||||||
|
original_col = x.shape[1]
|
||||||
|
x_sf = None
|
||||||
|
|
||||||
|
with use_symmetric_memory(
|
||||||
|
get_tp_group(), disabled=not is_allocation_symmetric()
|
||||||
|
):
|
||||||
|
symm_output = torch.empty(
|
||||||
|
x.shape[0], original_col, dtype=output_dtype, device=x.device
|
||||||
|
)
|
||||||
|
output = flashinfer_cutlass_fused_moe(
|
||||||
|
output=symm_output,
|
||||||
|
input=x_fp8,
|
||||||
|
token_selected_experts=topk_ids.to(torch.int),
|
||||||
|
token_final_scales=topk_weights,
|
||||||
|
fc1_expert_weights=layer.w13_weight,
|
||||||
|
fc2_expert_weights=layer.w2_weight,
|
||||||
|
output_dtype=output_dtype,
|
||||||
|
input_sf=x_sf,
|
||||||
|
quant_scales=[
|
||||||
|
layer.fc1_dequant,
|
||||||
|
layer.fc2_quant,
|
||||||
|
layer.fc2_dequant,
|
||||||
|
layer.fc1_input_dequant,
|
||||||
|
],
|
||||||
|
ep_size=layer.moe_ep_size,
|
||||||
|
ep_rank=layer.moe_ep_rank,
|
||||||
|
tp_size=layer.moe_tp_size,
|
||||||
|
tp_rank=layer.moe_tp_rank,
|
||||||
|
tune_max_num_tokens=next_power_of_2(x.shape[0]),
|
||||||
|
activation_type=activation,
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
from sglang.srt.layers.moe.token_dispatcher import StandardCombineInput
|
||||||
|
|
||||||
|
return StandardCombineInput(hidden_states=output)
|
||||||
|
|
||||||
quant_info = TritonMoeQuantInfo(
|
quant_info = TritonMoeQuantInfo(
|
||||||
w13_weight=layer.w13_weight,
|
w13_weight=layer.w13_weight,
|
||||||
w2_weight=layer.w2_weight,
|
w2_weight=layer.w2_weight,
|
||||||
@@ -1192,10 +1287,12 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
weight_scale_dtype = torch.float8_e4m3fn
|
weight_scale_dtype = torch.float8_e4m3fn
|
||||||
weight_loader = extra_weight_attrs.get("weight_loader")
|
weight_loader = extra_weight_attrs.get("weight_loader")
|
||||||
# GEMM 1
|
# GEMM 1
|
||||||
|
num_shards = 2 if layer.moe_runner_config.is_gated else 1
|
||||||
|
|
||||||
w13_weight = ModelWeightParameter(
|
w13_weight = ModelWeightParameter(
|
||||||
data=torch.empty(
|
data=torch.empty(
|
||||||
layer.num_local_experts,
|
layer.num_local_experts,
|
||||||
2 * intermediate_size_per_partition,
|
num_shards * intermediate_size_per_partition,
|
||||||
# 2 fp4 items are packed in the input dimension
|
# 2 fp4 items are packed in the input dimension
|
||||||
hidden_size // 2,
|
hidden_size // 2,
|
||||||
dtype=weight_dtype,
|
dtype=weight_dtype,
|
||||||
@@ -1224,7 +1321,7 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
w13_weight_scale = ModelWeightParameter(
|
w13_weight_scale = ModelWeightParameter(
|
||||||
data=torch.empty(
|
data=torch.empty(
|
||||||
layer.num_local_experts,
|
layer.num_local_experts,
|
||||||
2 * intermediate_size_per_partition,
|
num_shards * intermediate_size_per_partition,
|
||||||
hidden_size // self.quant_config.group_size,
|
hidden_size // self.quant_config.group_size,
|
||||||
dtype=weight_scale_dtype,
|
dtype=weight_scale_dtype,
|
||||||
),
|
),
|
||||||
@@ -1262,8 +1359,13 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
{"quant_method": FusedMoeWeightScaleSupported.BLOCK.value}
|
{"quant_method": FusedMoeWeightScaleSupported.BLOCK.value}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
w13_weight_scale_shape = (
|
||||||
|
(layer.num_local_experts, 2)
|
||||||
|
if layer.moe_runner_config.is_gated
|
||||||
|
else (layer.num_local_experts,)
|
||||||
|
)
|
||||||
w13_weight_scale_2 = PerTensorScaleParameter(
|
w13_weight_scale_2 = PerTensorScaleParameter(
|
||||||
data=torch.empty(layer.num_local_experts, 2, dtype=torch.float32),
|
data=torch.empty(w13_weight_scale_shape, dtype=torch.float32),
|
||||||
weight_loader=weight_loader,
|
weight_loader=weight_loader,
|
||||||
)
|
)
|
||||||
layer.register_parameter("w13_weight_scale_2", w13_weight_scale_2)
|
layer.register_parameter("w13_weight_scale_2", w13_weight_scale_2)
|
||||||
@@ -1278,8 +1380,9 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
{"quant_method": FusedMoeWeightScaleSupported.TENSOR.value}
|
{"quant_method": FusedMoeWeightScaleSupported.TENSOR.value}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
w13_input_scale_shape = (layer.num_experts, num_shards)
|
||||||
w13_input_scale = PerTensorScaleParameter(
|
w13_input_scale = PerTensorScaleParameter(
|
||||||
data=torch.empty(layer.num_experts, 2, dtype=torch.float32),
|
data=torch.empty(w13_input_scale_shape, dtype=torch.float32),
|
||||||
weight_loader=weight_loader,
|
weight_loader=weight_loader,
|
||||||
)
|
)
|
||||||
w13_input_scale._sglang_require_global_experts = True
|
w13_input_scale._sglang_require_global_experts = True
|
||||||
@@ -1448,15 +1551,18 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# GEMM 1 scale processing
|
# GEMM 1 scale processing
|
||||||
if not torch.allclose(
|
if layer.moe_runner_config.is_gated:
|
||||||
layer.w13_weight_scale_2[:, 0], layer.w13_weight_scale_2[:, 1]
|
if not torch.allclose(
|
||||||
):
|
layer.w13_weight_scale_2[:, 0], layer.w13_weight_scale_2[:, 1]
|
||||||
logger.warning_once(
|
):
|
||||||
"w1_weight_scale_2 must match w3_weight_scale_2. "
|
logger.warning_once(
|
||||||
"Accuracy may be affected."
|
"w1_weight_scale_2 must match w3_weight_scale_2. "
|
||||||
)
|
"Accuracy may be affected."
|
||||||
|
)
|
||||||
|
|
||||||
w13_weight_scale_2 = layer.w13_weight_scale_2[:, 0]
|
w13_weight_scale_2 = layer.w13_weight_scale_2[:, 0]
|
||||||
|
else:
|
||||||
|
w13_weight_scale_2 = layer.w13_weight_scale_2[:]
|
||||||
layer.w13_weight_scale_2 = Parameter(w13_weight_scale_2, requires_grad=False)
|
layer.w13_weight_scale_2 = Parameter(w13_weight_scale_2, requires_grad=False)
|
||||||
|
|
||||||
# Calculate input scales based on strategy
|
# Calculate input scales based on strategy
|
||||||
@@ -1495,7 +1601,7 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
assert torch.all(w13_input_scale == w13_input_scale[0])
|
assert torch.all(w13_input_scale == w13_input_scale[0])
|
||||||
w13_input_scale = w13_input_scale[0]
|
w13_input_scale = w13_input_scale[0]
|
||||||
else:
|
else:
|
||||||
w13_input_scale = layer.w13_input_scale.max(dim=1).values.to(torch.float32)
|
w13_input_scale = layer.w13_input_scale.max(dim=-1).values.to(torch.float32)
|
||||||
w2_input_scale = layer.w2_input_scale
|
w2_input_scale = layer.w2_input_scale
|
||||||
|
|
||||||
# Create shared parameters
|
# Create shared parameters
|
||||||
@@ -1521,15 +1627,15 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Validate weight scales
|
# Validate weight scales
|
||||||
|
assert_dim = 2 if layer.moe_runner_config.is_gated else 1
|
||||||
for name, weight_scale in [
|
for name, weight_scale in [
|
||||||
("w13", layer.w13_weight_scale),
|
("w13", layer.w13_weight_scale),
|
||||||
("w2", layer.w2_weight_scale),
|
("w2", layer.w2_weight_scale),
|
||||||
]:
|
]:
|
||||||
assert (
|
assert (
|
||||||
weight_scale.shape[2] % 16 == 0
|
weight_scale.shape[assert_dim] % 16 == 0
|
||||||
), f"Expected {name}_weight_scale.dim(2) to be divisible by 16"
|
), f"Expected {name}_weight_scale.dim({assert_dim}) to be divisible by 16"
|
||||||
assert (
|
assert (
|
||||||
weight_scale.dtype == torch.float8_e4m3fn
|
weight_scale.dtype == torch.float8_e4m3fn
|
||||||
), f"{name} Weight Blockscale must be represented as FP8-E4M3"
|
), f"{name} Weight Blockscale must be represented as FP8-E4M3"
|
||||||
@@ -1591,13 +1697,45 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
w13_blockscale_swizzled = self.swizzle_blockscale(layer.w13_weight_scale)
|
w13_blockscale_swizzled = self.swizzle_blockscale(layer.w13_weight_scale)
|
||||||
del layer.w13_weight_scale
|
del layer.w13_weight_scale
|
||||||
layer.w13_blockscale_swizzled.data.copy_(w13_blockscale_swizzled)
|
layer.w13_blockscale_swizzled.data.copy_(w13_blockscale_swizzled)
|
||||||
|
|
||||||
|
w13_weight = layer.w13_weight
|
||||||
|
intermediate_size_pad = w13_blockscale_swizzled.size(1) - w13_weight.size(1)
|
||||||
|
if intermediate_size_pad:
|
||||||
|
# padding gated activations will require to split w1 and w3
|
||||||
|
# and pad them individually
|
||||||
|
assert not layer.moe_runner_config.is_gated, (
|
||||||
|
"The intermediate size required padding, "
|
||||||
|
"but padding is also implemented for gated activations"
|
||||||
|
)
|
||||||
|
|
||||||
|
layer.w13_weight = Parameter(
|
||||||
|
torch.nn.functional.pad(
|
||||||
|
w13_weight, (0, 0, 0, intermediate_size_pad)
|
||||||
|
),
|
||||||
|
requires_grad=False,
|
||||||
|
)
|
||||||
|
layer.w2_weight = Parameter(
|
||||||
|
torch.nn.functional.pad(
|
||||||
|
layer.w2_weight, (0, intermediate_size_pad // 2, 0, 0)
|
||||||
|
),
|
||||||
|
requires_grad=False,
|
||||||
|
)
|
||||||
|
layer.w2_weight_scale = Parameter(
|
||||||
|
torch.nn.functional.pad(
|
||||||
|
layer.w2_weight_scale, (0, intermediate_size_pad // 16)
|
||||||
|
),
|
||||||
|
requires_grad=False,
|
||||||
|
)
|
||||||
|
layer.w2_blockscale_swizzled = Parameter(
|
||||||
|
self.swizzle_blockscale(layer.w2_weight_scale), requires_grad=False
|
||||||
|
)
|
||||||
|
|
||||||
layer.w13_weight = Parameter(layer.w13_weight.data, requires_grad=False)
|
layer.w13_weight = Parameter(layer.w13_weight.data, requires_grad=False)
|
||||||
|
|
||||||
# Process w2 weights
|
# Process w2 weights
|
||||||
w2_blockscale_swizzled = self.swizzle_blockscale(layer.w2_weight_scale)
|
w2_blockscale_swizzled = self.swizzle_blockscale(layer.w2_weight_scale)
|
||||||
del layer.w2_weight_scale
|
del layer.w2_weight_scale
|
||||||
layer.w2_blockscale_swizzled.data.copy_(w2_blockscale_swizzled)
|
layer.w2_blockscale_swizzled.data.copy_(w2_blockscale_swizzled)
|
||||||
layer.w2_weight = Parameter(layer.w2_weight.data, requires_grad=False)
|
|
||||||
|
|
||||||
# Both flashinfer cutlass and regular cutlass use same processing for w2
|
# Both flashinfer cutlass and regular cutlass use same processing for w2
|
||||||
|
|
||||||
@@ -1614,7 +1752,7 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
@property
|
@property
|
||||||
def load_up_proj_weight_first(self) -> bool:
|
def load_up_proj_weight_first(self) -> bool:
|
||||||
# FlashInfer CUTLASS kernel assumes [Up, Gate] Proj as W13
|
# FlashInfer CUTLASS kernel assumes [Up, Gate] Proj as W13
|
||||||
return self.enable_flashinfer_cutlass_moe
|
return self.enable_flashinfer_cutlass_moe and self.moe_runner_config.is_gated
|
||||||
|
|
||||||
def create_moe_runner(
|
def create_moe_runner(
|
||||||
self, layer: torch.nn.Module, moe_runner_config: MoeRunnerConfig
|
self, layer: torch.nn.Module, moe_runner_config: MoeRunnerConfig
|
||||||
@@ -1631,10 +1769,11 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
x_sf = dispatch_output.hidden_states_scale
|
x_sf = dispatch_output.hidden_states_scale
|
||||||
topk_output = dispatch_output.topk_output
|
topk_output = dispatch_output.topk_output
|
||||||
|
|
||||||
assert (
|
activation = self.moe_runner_config.activation
|
||||||
self.moe_runner_config.activation == "silu"
|
|
||||||
), "Only SiLU activation is supported."
|
|
||||||
|
|
||||||
|
assert (
|
||||||
|
activation in ACT_STR_TO_TYPE_MAP
|
||||||
|
), f"{activation=} missing from {ACT_STR_TO_TYPE_MAP.keys()=}"
|
||||||
moe_runner_config = self.moe_runner_config
|
moe_runner_config = self.moe_runner_config
|
||||||
|
|
||||||
# Check if this is a FlashInferFP4MoE layer that should handle its own forward
|
# Check if this is a FlashInferFP4MoE layer that should handle its own forward
|
||||||
@@ -1656,13 +1795,17 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
|
|
||||||
# If x_sf is not None, x is FP4 packed (half size), so we need * 2
|
# If x_sf is not None, x is FP4 packed (half size), so we need * 2
|
||||||
# If x_sf is None, x is not packed, so output_col = x.shape[1]
|
# If x_sf is None, x is not packed, so output_col = x.shape[1]
|
||||||
output_col = x.shape[1] * 2 if x_sf is not None else x.shape[1]
|
output_col = x.shape[1]
|
||||||
|
if x_sf is not None and layer.moe_runner_config.is_gated:
|
||||||
|
output_col *= 2
|
||||||
with use_symmetric_memory(
|
with use_symmetric_memory(
|
||||||
get_tp_group(), disabled=not is_allocation_symmetric()
|
get_tp_group(), disabled=not is_allocation_symmetric()
|
||||||
):
|
):
|
||||||
symm_output = torch.empty(
|
symm_output = torch.empty(
|
||||||
x.shape[0], output_col, dtype=output_dtype, device=x.device
|
x.shape[0],
|
||||||
|
output_col,
|
||||||
|
dtype=output_dtype,
|
||||||
|
device=x.device,
|
||||||
)
|
)
|
||||||
|
|
||||||
output = flashinfer_cutlass_fused_moe(
|
output = flashinfer_cutlass_fused_moe(
|
||||||
@@ -1687,6 +1830,7 @@ class ModelOptNvFp4FusedMoEMethod(FusedMoEMethodBase):
|
|||||||
tp_size=layer.moe_tp_size,
|
tp_size=layer.moe_tp_size,
|
||||||
tp_rank=layer.moe_tp_rank,
|
tp_rank=layer.moe_tp_rank,
|
||||||
tune_max_num_tokens=next_power_of_2(x.shape[0]),
|
tune_max_num_tokens=next_power_of_2(x.shape[0]),
|
||||||
|
activation_type=ACT_STR_TO_TYPE_MAP[activation],
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
from sglang.srt.layers.moe.token_dispatcher import StandardCombineInput
|
from sglang.srt.layers.moe.token_dispatcher import StandardCombineInput
|
||||||
|
|||||||
@@ -1194,6 +1194,23 @@ class ServerArgs:
|
|||||||
f"Disabling Radix Cache for {model_arch} as it is not yet supported."
|
f"Disabling Radix Cache for {model_arch} as it is not yet supported."
|
||||||
)
|
)
|
||||||
self.disable_radix_cache = True
|
self.disable_radix_cache = True
|
||||||
|
elif model_arch in ["NemotronHForCausalLM"]:
|
||||||
|
if self.model_config.quantization in [
|
||||||
|
"modelopt",
|
||||||
|
"modelopt_fp8",
|
||||||
|
"modelopt_fp4",
|
||||||
|
]:
|
||||||
|
assert self.model_config.hf_config.mlp_hidden_act == "relu2"
|
||||||
|
if self.model_config.quantization == "modelopt":
|
||||||
|
self.quantization = (
|
||||||
|
"modelopt_fp4"
|
||||||
|
if self.model_config.hf_config.quantization_config["quant_algo"]
|
||||||
|
== "NVFP4"
|
||||||
|
else "modelopt_fp8"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.quantization = self.model_config.quantization
|
||||||
|
self.moe_runner_backend = "flashinfer_cutlass"
|
||||||
elif model_arch in [
|
elif model_arch in [
|
||||||
"Qwen3MoeForCausalLM",
|
"Qwen3MoeForCausalLM",
|
||||||
"Qwen3VLMoeForConditionalGeneration",
|
"Qwen3VLMoeForConditionalGeneration",
|
||||||
@@ -1491,9 +1508,11 @@ class ServerArgs:
|
|||||||
|
|
||||||
def _handle_moe_kernel_config(self):
|
def _handle_moe_kernel_config(self):
|
||||||
if self.moe_runner_backend == "flashinfer_cutlass":
|
if self.moe_runner_backend == "flashinfer_cutlass":
|
||||||
assert (
|
assert self.quantization in [
|
||||||
self.quantization == "modelopt_fp4" or self.quantization is None
|
"modelopt_fp4",
|
||||||
), "modelopt_fp4 quantization or bf16 is required for Flashinfer Cutlass MOE"
|
"modelopt_fp8",
|
||||||
|
None,
|
||||||
|
], f"Invalid quantization '{self.quantization}'. \nFlashInfer Cutlass MOE supports only: 'modelopt_fp4', 'modelopt_fp8', or bfloat16 (None)."
|
||||||
assert self.ep_size in [
|
assert self.ep_size in [
|
||||||
1,
|
1,
|
||||||
self.tp_size,
|
self.tp_size,
|
||||||
|
|||||||
Reference in New Issue
Block a user