From 8d4fcf2f7bff1cca95ad17cc7520343004a22328 Mon Sep 17 00:00:00 2001 From: jianan-gu Date: Thu, 19 Mar 2026 13:57:55 +0800 Subject: [PATCH] [CPU] Fix MoE layer support for DeepSeek-OCR models (#12555) --- python/sglang/srt/models/deepseek.py | 40 ++++++++++++++++++------ python/sglang/srt/models/deepseek_ocr.py | 35 ++++++++++++++++++++- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/python/sglang/srt/models/deepseek.py b/python/sglang/srt/models/deepseek.py index 675bb1673..39ce8749c 100644 --- a/python/sglang/srt/models/deepseek.py +++ b/python/sglang/srt/models/deepseek.py @@ -48,7 +48,12 @@ from sglang.srt.layers.vocab_parallel_embedding import ( ) from sglang.srt.model_executor.forward_batch_info import ForwardBatch from sglang.srt.model_loader.weight_utils import default_weight_loader -from sglang.srt.utils import add_prefix +from sglang.srt.utils import add_prefix, cpu_has_amx_support, is_cpu + +_is_cpu_amx_available = cpu_has_amx_support() +_is_cpu = is_cpu() +if _is_cpu and _is_cpu_amx_available: + import sgl_kernel # noqa: F401 class DeepseekMLP(nn.Module): @@ -176,14 +181,31 @@ class DeepseekMoE(nn.Module): # router_logits: (num_tokens, n_experts) router_logits, _ = self.gate(hidden_states) topk_output = self.topk(hidden_states, router_logits) - final_hidden_states = fused_moe.fused_moe( - hidden_states, - w1=self.w1, - w2=self.w2, - topk_output=topk_output, - moe_runner_config=MoeRunnerConfig(inplace=True), - ) - + if _is_cpu and _is_cpu_amx_available: + topk_weights, topk_ids, _ = topk_output + final_hidden_states = torch.ops.sgl_kernel.fused_experts_cpu( + hidden_states, + self.w1, + self.w2, + topk_weights, + topk_ids, + False, # inplace # See [Note] inplace should be False in fused_experts. + 0, # CPUQuantMethod.UNQUANT, + None, # w1_scale + None, # w2_scale + None, # w1_zp + None, # w2_zp + None, # block_size + True, # is_vnni + ) + else: + final_hidden_states = fused_moe.fused_moe( + hidden_states, + w1=self.w1, + w2=self.w2, + topk_output=topk_output, + moe_runner_config=MoeRunnerConfig(inplace=True), + ) if self.config.n_shared_experts is not None: final_hidden_states = final_hidden_states + shared_output final_hidden_states = tensor_model_parallel_all_reduce(final_hidden_states) diff --git a/python/sglang/srt/models/deepseek_ocr.py b/python/sglang/srt/models/deepseek_ocr.py index f87c06d32..02ccfb4d6 100644 --- a/python/sglang/srt/models/deepseek_ocr.py +++ b/python/sglang/srt/models/deepseek_ocr.py @@ -41,6 +41,10 @@ from sglang.srt.model_loader.weight_utils import default_weight_loader from sglang.srt.models.deepseek import DeepseekForCausalLM from sglang.srt.models.deepseek_v2 import DeepseekV2ForCausalLM, DeepseekV3ForCausalLM from sglang.srt.models.transformers import maybe_prefix +from sglang.srt.utils import cpu_has_amx_support, is_cpu + +_is_cpu_amx_available = cpu_has_amx_support() +_is_cpu = is_cpu() NestedTensors: TypeAlias = Union[ list["NestedTensors"], @@ -1772,7 +1776,6 @@ class DeepseekOCRForCausalLM(nn.Module): params_dict = dict(self.named_parameters()) loaded_params: Set[str] = set() - for name, loaded_weight in weights: if "rotary_emb.inv_freq" in name: continue @@ -1852,6 +1855,36 @@ class DeepseekOCRForCausalLM(nn.Module): raise RuntimeError( f"Some weights are not initialized from checkpoints: {unloaded_params}" ) + self.post_load_weights() + + def post_load_weights(self): + if _is_cpu and _is_cpu_amx_available: + from sglang.srt.layers.amx_utils import _amx_process_weight_after_loading + + layer_ids = int(self.config.num_hidden_layers) + first_k_dense_replace_id = ( + self.config.first_k_dense_replace + if hasattr(self.config, "first_k_dense_replace") + else -1 + ) + moe_layer_freq_id = ( + self.config.moe_layer_freq + if hasattr(self.config, "moe_layer_freq") + else 1 + ) + for layer_id in range(0, layer_ids): + if ( + layer_id >= first_k_dense_replace_id + and layer_id % moe_layer_freq_id == 0 + ): + if ( + hasattr(self.model, "model") + and hasattr(self.model.model, "layers") + and hasattr(self.model.model.layers[layer_id], "mlp") + ): + self_moe = self.model.model.layers[layer_id].mlp + if hasattr(self_moe, "w1") and hasattr(self_moe, "w2"): + _amx_process_weight_after_loading(self_moe, ["w1", "w2"]) EntryClass = [DeepseekOCRForCausalLM]