[ROCm][Quantization][MOE] Enable fused shared experts for block-quantized FP8 - #53097
[ROCm][Quantization][MOE] Enable fused shared experts for block-quantized FP8#53097xuebwang-amd wants to merge 2 commits into
Conversation
Signed-off-by: xuebwang-amd <xuebwang@amd.com>
fxmarty-amd
left a comment
There was a problem hiding this comment.
What model is it for?
DeepSeek-R1-0528 as one example model, see PR description. |
Makes sense, looking at https://huggingface.co/deepseek-ai/DeepSeek-R1-0528 it indeed uses the same dtype for routed experts and shared expert. |
fxmarty-amd
left a comment
There was a problem hiding this comment.
LGTM great!
Just one comment
| if quant_config.store_dtype == "mxfp4": | ||
| return ( | ||
| False, | ||
| "FP8 stores routed experts as MXFP4 while shared experts at " | ||
| f"{shared_expert_prefix} remain FP8", | ||
| ) |
There was a problem hiding this comment.
store_dtype comes from #45200
specifically https://huggingface.co/XiaomiMiMo/MiMo-V2.5-Pro-FP4-DFlash
I think the message here is misleading see https://huggingface.co/XiaomiMiMo/MiMo-V2.5-Pro-FP4-DFlash/blob/main/config.json#L130 - I would just use if quant_config.store_dtype is not None => not supported
There was a problem hiding this comment.
Good catch, agreed.
On MiMo-V2.5-Pro-FP4-DFlash, n_shared_experts: null means it never reaches this helper, so the gate stays defensive.
Updated, thanks.
Signed-off-by: xuebwang-amd <xuebwang@amd.com>
Purpose
To enable fp8 models such as DeepSeek-R1-0528 ("n_shared_experts": 1, "quant_method": "fp8")
On top of PR #51695.
PR #51695 (
88b2bff2c63) centralised the "may this model fuse its shared experts into the routed grouped GEMM?" decision intois_shared_expert_quant_fse_compatible. That helper implementsNone,DeepseekV4FP8ConfigandQuarkConfig, and closes with a TODO plus a generic fallback for everything else.Fp8Configtherefore lands in the fallback. On ROCm withVLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS=1, a block-quantized FP8 checkpoint (DeepSeek-V3/R1 and friends) now reportsis logged as
VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS is enabled but cannot be enabled: ...(vllm/model_executor/layers/fused_moe/utils.py:85-90, duplicated atvllm/models/deepseek_v4/amd/model.py:239-245andvllm/models/minimax_m3/amd/model.py:386-392) and falls back to running the shared expert as separate linears. This is not a silent failure -- it warns -- but before #51695deepseek_v2.pygated fused shared experts on the env var alone, with no quantization check, so this configuration did take the fused path.This PR fills that TODO for
Fp8Config, restoring the fused path for the case that is actually safe, and rejecting the cases that are not.What changes
Three gates, then compatible.
Gate 1 --
store_dtype == "mxfp4"(config_utils.py:152-157)This gate is defensive. Folding an FP8 shared expert into MXFP4 expert slots is exactly the storage-format mismatch this helper exists to catch.
Gate 2 --
weight_block_size is None(config_utils.py:161-166)Fused shared experts are enabled only for block-quantized FP8. Two distinct populations are rejected here:
IndexError; on a size-1 tensor withn_shared_experts > 1the following divisibility assert (deepseek_v2.py:1689-1692) fails instead. Reporting these compatible would turn a model that loads today into a load-time crash.Gate 3 -- asymmetric
ignored_layers(config_utils.py:169-185)ignored_layers+ignored_layers_match_modeis the only per-layer discriminator onFp8Config; the whole class body (fp8.py:92-236) has nolayer_quant_config/targets/overrides, and every other field (activation_scheme,weight_block_size,is_checkpoint_fp8_serialized,use_deep_gemm) is model-global and so cannot differ between routed and shared experts by construction. Once ignore-status agrees, both sides are driven by the same config object and reach the same FP8 scheme.Unit Test Plan & Result
Plan
7 new test functions (9 parametrized cases) in
tests/model_executor/layers/test_fused_shared_expert.py, modelled on the existing Quark compatibility tests in the same file:test_block_fp8_shared_expert_fse_is_compatible(True, None)test_per_tensor_fp8_shared_expert_fse_is_incompatibletest_mxfp4_store_dtype_fp8_shared_expert_fse_is_incompatibletest_fp8_shared_expert_fse_rejects_asymmetric_ignored_layerstest_fp8_shared_expert_fse_allows_symmetric_ignored_layerstest_fp8_shared_expert_fse_expands_packed_projectionspacked_modules_mappingfused-shard expansiontest_fp8_shared_expert_fse_propagates_partial_shard_exclusionResults
Passed
End-to-end Test Plan & Result (negative -> positive)
Plan
Two arms:
upstream/mainDeepSeek-R1-0528 is used to run the end-to-end validation:
DeepSeek-R1-0528, unmodified, all 61 layers. Itsquantization_configis{"quant_method": "fp8", "activation_scheme": "dynamic", "fmt": "e4m3", "weight_block_size": [128, 128]}withn_shared_experts: 1,n_routed_experts: 256andfirst_k_dense_replace: 3, so layers 3..60 -- 58 layers -- are MoE layers with a shared expert. It resolves to plainFp8Config: notQuarkConfig, notCompressedTensorsConfig, and notDeepseekV4FP8Config. That is exactly the config class that had no branch, and it is the class every stock DeepSeek-V3/R1 FP8 release lands on.VLLM_ROCM_USE_AITER=1 VLLM_ROCM_USE_AITER_MOE=1 VLLM_ROCM_USE_AITER_FUSION_SHARED_EXPERTS=1.Results
GSM8K
upstream/main)"cannot be enabled"warningsThroughput (non-eager)
5 timed repeats per batch size, median reported:
upstream/main)Essential Elements of an Effective PR Description Checklist
supported_models.mdandexamplesfor a new model.