215 lines
6.9 KiB
Python
215 lines
6.9 KiB
Python
|
|
# SPDX-License-Identifier: Apache-2.0
|
||
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||
|
|
|
||
|
|
from abc import abstractmethod
|
||
|
|
from typing import TYPE_CHECKING
|
||
|
|
|
||
|
|
import torch
|
||
|
|
|
||
|
|
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
|
||
|
|
from vllm.logger import init_logger
|
||
|
|
from vllm.model_executor.layers.fused_moe.config import (
|
||
|
|
FusedMoEConfig,
|
||
|
|
FusedMoEParallelConfig,
|
||
|
|
FusedMoEQuantConfig,
|
||
|
|
)
|
||
|
|
from vllm.model_executor.layers.fused_moe.modular_kernel import (
|
||
|
|
FusedMoEExpertsModular,
|
||
|
|
FusedMoEPrepareAndFinalizeModular,
|
||
|
|
)
|
||
|
|
from vllm.model_executor.layers.quantization.base_config import (
|
||
|
|
QuantizeMethodBase,
|
||
|
|
)
|
||
|
|
|
||
|
|
if TYPE_CHECKING:
|
||
|
|
from vllm.model_executor.layers.fused_moe.routed_experts import RoutedExperts
|
||
|
|
from vllm.model_executor.layers.fused_moe.runner.shared_experts import SharedExperts
|
||
|
|
|
||
|
|
logger = init_logger(__name__)
|
||
|
|
|
||
|
|
|
||
|
|
class FusedMoEMethodBase(QuantizeMethodBase):
|
||
|
|
def __init__(self, moe: FusedMoEConfig):
|
||
|
|
super().__init__()
|
||
|
|
self.moe: FusedMoEConfig = moe
|
||
|
|
self.moe_quant_config: FusedMoEQuantConfig | None = None
|
||
|
|
self.moe_kernel: mk.FusedMoEKernel | None = None
|
||
|
|
|
||
|
|
@property
|
||
|
|
def supports_internal_mk(self) -> bool:
|
||
|
|
# NOTE(rob): temporary attribute to indicate support for
|
||
|
|
# completed migration to the new internal MK interface.
|
||
|
|
return self.moe_kernel is not None
|
||
|
|
|
||
|
|
@property
|
||
|
|
def mk_can_overlap_shared_experts(self) -> bool:
|
||
|
|
# NOTE(rob): temporary attribute to indicate support for
|
||
|
|
# completed migration to the new internal MK interface.
|
||
|
|
return (
|
||
|
|
self.moe_kernel is not None and self.moe_kernel.can_overlap_shared_experts
|
||
|
|
)
|
||
|
|
|
||
|
|
@abstractmethod
|
||
|
|
def create_weights(
|
||
|
|
self,
|
||
|
|
layer: "RoutedExperts",
|
||
|
|
num_experts: int,
|
||
|
|
hidden_size: int,
|
||
|
|
intermediate_size_per_partition: int,
|
||
|
|
params_dtype: torch.dtype,
|
||
|
|
**extra_weight_attrs,
|
||
|
|
):
|
||
|
|
raise NotImplementedError
|
||
|
|
|
||
|
|
def uses_weight_scale_2_pattern(self) -> bool:
|
||
|
|
"""
|
||
|
|
Returns True if this quantization method uses 'weight_scale_2' pattern
|
||
|
|
for per-tensor weight scales (e.g., FP4 variants), False otherwise.
|
||
|
|
|
||
|
|
This method should be overridden by subclasses that use the
|
||
|
|
'weight_scale_2' pattern instead of the standard 'weight_scale' pattern.
|
||
|
|
"""
|
||
|
|
return False
|
||
|
|
|
||
|
|
def maybe_roundup_sizes(
|
||
|
|
self,
|
||
|
|
hidden_size: int,
|
||
|
|
intermediate_size_per_partition: int,
|
||
|
|
act_dtype: torch.dtype,
|
||
|
|
moe_parallel_config: FusedMoEParallelConfig,
|
||
|
|
) -> tuple[int, int]:
|
||
|
|
"""
|
||
|
|
Given layer hidden size and intermediate size per partition and MoE
|
||
|
|
configurations, round up hidden_size and intermediate_size_per_partition
|
||
|
|
if necessary.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
hidden_size: Layer hidden-size
|
||
|
|
intermediate_size_per_partition: Intermediate size per partition for
|
||
|
|
the layer.
|
||
|
|
act_dtype: Data type of the layer activations.
|
||
|
|
moe_parallel_config: Fused MoE parallelization strategy configuration.
|
||
|
|
|
||
|
|
Return:
|
||
|
|
A tuple of (rounded_hidden_size, rounded_intermediate_size_per_partition),
|
||
|
|
where:
|
||
|
|
- rounded_hidden_size is the possibly rounded up hidden size.
|
||
|
|
- rounded_intermediate_size_per_partition is the possibly rounded
|
||
|
|
up intermediate size per partition.
|
||
|
|
"""
|
||
|
|
from .all2all_utils import maybe_roundup_layer_hidden_size
|
||
|
|
|
||
|
|
return maybe_roundup_layer_hidden_size(
|
||
|
|
hidden_size, act_dtype, moe_parallel_config
|
||
|
|
), intermediate_size_per_partition
|
||
|
|
|
||
|
|
def maybe_make_prepare_finalize(
|
||
|
|
self,
|
||
|
|
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
|
||
|
|
) -> FusedMoEPrepareAndFinalizeModular | None:
|
||
|
|
from .all2all_utils import maybe_make_prepare_finalize
|
||
|
|
|
||
|
|
pf = maybe_make_prepare_finalize(
|
||
|
|
self.moe, self.moe_quant_config, routing_tables
|
||
|
|
)
|
||
|
|
assert pf is None or isinstance(pf, FusedMoEPrepareAndFinalizeModular)
|
||
|
|
return pf
|
||
|
|
|
||
|
|
def select_gemm_impl(
|
||
|
|
self,
|
||
|
|
prepare_finalize: FusedMoEPrepareAndFinalizeModular,
|
||
|
|
layer: "RoutedExperts",
|
||
|
|
) -> FusedMoEExpertsModular:
|
||
|
|
# based on the all2all implementation, select the appropriate
|
||
|
|
# gemm implementation
|
||
|
|
raise ValueError(
|
||
|
|
f"{self.__class__.__name__} uses the new modular kernel initialization "
|
||
|
|
"logic. This function should not be called."
|
||
|
|
)
|
||
|
|
|
||
|
|
@abstractmethod
|
||
|
|
def get_fused_moe_quant_config(
|
||
|
|
self, layer: "RoutedExperts"
|
||
|
|
) -> FusedMoEQuantConfig | None:
|
||
|
|
raise NotImplementedError
|
||
|
|
|
||
|
|
@property
|
||
|
|
def topk_indices_dtype(self) -> torch.dtype | None:
|
||
|
|
if self.moe_kernel is not None:
|
||
|
|
return self.moe_kernel.prepare_finalize.topk_indices_dtype()
|
||
|
|
return None
|
||
|
|
|
||
|
|
@property
|
||
|
|
def skip_forward_padding(self) -> bool:
|
||
|
|
"""Whether to skip the padding in the forward before applying the moe method."""
|
||
|
|
return False
|
||
|
|
|
||
|
|
@property
|
||
|
|
def has_unpadded_output(self) -> bool:
|
||
|
|
"""
|
||
|
|
Indicates that the hidden_states output might be the unpadded
|
||
|
|
hidden_states shape rather than the full padded shape.
|
||
|
|
"""
|
||
|
|
return False
|
||
|
|
|
||
|
|
@property
|
||
|
|
def supports_eplb(self) -> bool:
|
||
|
|
return False
|
||
|
|
|
||
|
|
@property
|
||
|
|
def method_name(self) -> str:
|
||
|
|
return self.__class__.__name__
|
||
|
|
|
||
|
|
@property
|
||
|
|
def is_monolithic(self) -> bool:
|
||
|
|
if self.moe_kernel is None:
|
||
|
|
if hasattr(self, "experts_cls"):
|
||
|
|
return self.experts_cls.is_monolithic()
|
||
|
|
else:
|
||
|
|
return False
|
||
|
|
return self.moe_kernel.is_monolithic
|
||
|
|
|
||
|
|
def apply(
|
||
|
|
self,
|
||
|
|
layer: "RoutedExperts",
|
||
|
|
x: torch.Tensor,
|
||
|
|
topk_weights: torch.Tensor,
|
||
|
|
topk_ids: torch.Tensor,
|
||
|
|
shared_experts: "SharedExperts | None",
|
||
|
|
shared_experts_input: torch.Tensor | None,
|
||
|
|
) -> torch.Tensor:
|
||
|
|
"""
|
||
|
|
Apply the MoE operation using modular kernels.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
layer: RoutedExperts instance containing weight parameters
|
||
|
|
x: Input tensor
|
||
|
|
topk_weights: Expert weights from router
|
||
|
|
topk_ids: Selected expert IDs from router
|
||
|
|
shared_experts_input: Input for shared experts (if any)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Output tensor from routed experts
|
||
|
|
"""
|
||
|
|
raise NotImplementedError
|
||
|
|
|
||
|
|
def apply_monolithic(
|
||
|
|
self,
|
||
|
|
layer: "RoutedExperts",
|
||
|
|
x: torch.Tensor,
|
||
|
|
router_logits: torch.Tensor,
|
||
|
|
input_ids: torch.Tensor | None = None,
|
||
|
|
) -> torch.Tensor:
|
||
|
|
"""
|
||
|
|
Apply the MoE operation using monolithic kernels.
|
||
|
|
|
||
|
|
Args:
|
||
|
|
layer: RoutedExperts instance containing weight parameters
|
||
|
|
x: Input tensor
|
||
|
|
router_logits: Router logits (routing done internally)
|
||
|
|
|
||
|
|
Returns:
|
||
|
|
Output tensor from routed experts
|
||
|
|
"""
|
||
|
|
raise NotImplementedError
|