Skip to content

Commit f03a5b0

Browse files
committed
feat(qwen3_5_mtp): Qwen3.5 / Qwen3.5-MoE MTP draft models
Self-contained dense (qwen3_5_mtp) and MoE (qwen3_5_moe_mtp) MTP draft packages: each carries its own draft wiring (reuse the main model's req/mem managers + rope caches, is_mtp_draft_model marker) and shares a weight-retarget mixin (mtp.* head, embeddings shared with the main model) plus the MTP pre-layer fuse. No shared model base class.
1 parent 195bed2 commit f03a5b0

13 files changed

Lines changed: 392 additions & 7 deletions

File tree

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
import torch
2-
from typing import List
3-
41
from lightllm.models.qwen2_vl.infer_struct import Qwen2VLInferStateInfo
5-
from lightllm.utils.envs_utils import get_env_start_args
62

73

84
class Qwen35InferStateInfo(Qwen2VLInferStateInfo):
@@ -12,8 +8,7 @@ def __init__(self):
128

139
def init_some_extra_state(self, model):
1410
super().init_some_extra_state(model)
15-
self.b_att_seq_len = self.b_seq_len
16-
mtp_step = get_env_start_args().mtp_step
11+
from lightllm.common.basemodel.mtp_verify_extra_state import init_mtp_verify_extra_state
1712

18-
self.b_buffer_idx = self.b_req_idx * (mtp_step + 1) + self.b_mtp_index
13+
init_mtp_verify_extra_state(self)
1914
return
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from lightllm.models.qwen3_5_moe_mtp.model import Qwen3_5MoeMTPModel
2+
3+
__all__ = ["Qwen3_5MoeMTPModel"]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from lightllm.models.qwen3_5_moe_mtp.layer_weights.transformer_layer_weight import (
2+
Qwen3_5MoeMTPTransformerLayerWeight,
3+
)
4+
5+
__all__ = ["Qwen3_5MoeMTPTransformerLayerWeight"]
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
from lightllm.common.basemodel.layer_weights.meta_weights import (
2+
COLMMWeight,
3+
FusedMoeWeight,
4+
ROWMMWeight,
5+
)
6+
from lightllm.models.qwen3_5_moe.layer_weights.transformer_layer_weight import (
7+
Qwen35MOETransformerLayerWeight,
8+
)
9+
from lightllm.models.qwen3_5_mtp.layer_weights.mtp_retarget_mixin import MTPRetargetMixin
10+
from lightllm.utils.envs_utils import get_env_start_args
11+
12+
13+
class Qwen3_5MoeMTPTransformerLayerWeight(MTPRetargetMixin, Qwen35MOETransformerLayerWeight):
14+
def _init_weight_names(self):
15+
super()._init_weight_names()
16+
self._retarget_attn_norm_names()
17+
18+
def _init_moe(self):
19+
moe_intermediate_size = self.network_config_["moe_intermediate_size"]
20+
self.moe_gate = ROWMMWeight(
21+
in_dim=self.network_config_["hidden_size"],
22+
out_dims=[self.n_routed_experts],
23+
weight_names=f"{self._MTP_PREFIX}{self.layer_num_}.mlp.gate.weight",
24+
data_type=self.data_type_,
25+
quant_method=None,
26+
tp_rank=0,
27+
tp_world_size=1,
28+
)
29+
self.experts = FusedMoeWeight(
30+
gate_proj_name="gate_proj",
31+
down_proj_name="down_proj",
32+
up_proj_name="up_proj",
33+
e_score_correction_bias_name="",
34+
weight_prefix=f"{self._MTP_PREFIX}{self.layer_num_}.mlp.experts",
35+
n_routed_experts=self.n_routed_experts,
36+
hidden_size=self.network_config_["hidden_size"],
37+
moe_intermediate_size=moe_intermediate_size,
38+
data_type=self.data_type_,
39+
quant_method=self.quant_cfg.get_quant_method(self.layer_num_, "fused_moe"),
40+
layer_num=self.layer_num_,
41+
network_config=self.network_config_,
42+
)
43+
self._init_gated_ffn()
44+
45+
def _init_gated_ffn(self):
46+
hidden_size = self.network_config_["hidden_size"]
47+
if "shared_expert_intermediate_size" not in self.network_config_:
48+
return
49+
50+
prefix = f"{self._MTP_PREFIX}{self.layer_num_}.mlp.shared_expert"
51+
inter_size = self.network_config_["shared_expert_intermediate_size"]
52+
if get_env_start_args().enable_ep_moe:
53+
self.gate_up_proj = ROWMMWeight(
54+
in_dim=hidden_size,
55+
out_dims=[inter_size, inter_size],
56+
weight_names=[f"{prefix}.gate_proj.weight", f"{prefix}.up_proj.weight"],
57+
data_type=self.data_type_,
58+
quant_method=self.get_quant_method("gate_up_proj"),
59+
tp_rank=0,
60+
tp_world_size=1,
61+
)
62+
self.down_proj = COLMMWeight(
63+
in_dim=inter_size,
64+
out_dims=[hidden_size],
65+
weight_names=f"{prefix}.down_proj.weight",
66+
data_type=self.data_type_,
67+
quant_method=self.get_quant_method("down_proj"),
68+
tp_rank=0,
69+
tp_world_size=1,
70+
)
71+
else:
72+
self.gate_up_proj = ROWMMWeight(
73+
in_dim=hidden_size,
74+
out_dims=[inter_size, inter_size],
75+
weight_names=[f"{prefix}.gate_proj.weight", f"{prefix}.up_proj.weight"],
76+
data_type=self.data_type_,
77+
quant_method=self.get_quant_method("gate_up_proj"),
78+
)
79+
self.down_proj = COLMMWeight(
80+
in_dim=inter_size,
81+
out_dims=[hidden_size],
82+
weight_names=f"{prefix}.down_proj.weight",
83+
data_type=self.data_type_,
84+
quant_method=self.get_quant_method("down_proj"),
85+
)
86+
87+
self.ffn_gate = ROWMMWeight(
88+
in_dim=hidden_size,
89+
out_dims=[1],
90+
weight_names=f"{self._MTP_PREFIX}{self.layer_num_}.mlp.shared_expert_gate.weight",
91+
data_type=self.data_type_,
92+
bias_names=None,
93+
quant_method=None,
94+
tp_rank=0,
95+
tp_world_size=1,
96+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from lightllm.models.qwen3_5_mtp.model import Qwen3_5MTPModel
2+
from lightllm.models.qwen3_5_moe_mtp.layer_weights.transformer_layer_weight import (
3+
Qwen3_5MoeMTPTransformerLayerWeight,
4+
)
5+
6+
7+
class Qwen3_5MoeMTPModel(Qwen3_5MTPModel):
8+
transformer_weight_class = Qwen3_5MoeMTPTransformerLayerWeight

lightllm/models/qwen3_5_mtp/__init__.py

Whitespace-only changes.

lightllm/models/qwen3_5_mtp/layer_infer/__init__.py

Whitespace-only changes.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import torch
2+
3+
from lightllm.models.qwen3_vl.layer_infer.pre_layer_infer import Qwen3VLMultimodalPreLayerInfer
4+
from lightllm.models.qwen3_5_mtp.layer_weights.pre_and_post_layer_weight import Qwen3_5MTPPreAndPostLayerWeight
5+
from lightllm.models.llama.infer_struct import LlamaInferStateInfo
6+
7+
8+
class Qwen3_5MTPPreLayerInfer(Qwen3VLMultimodalPreLayerInfer):
9+
def __init__(self, network_config):
10+
super().__init__(network_config)
11+
self.eps_ = network_config["rms_norm_eps"]
12+
self.hidden_size = network_config["hidden_size"]
13+
return
14+
15+
def _mtp_fuse(
16+
self,
17+
input_embdings: torch.Tensor,
18+
infer_state: LlamaInferStateInfo,
19+
layer_weight: Qwen3_5MTPPreAndPostLayerWeight,
20+
) -> torch.Tensor:
21+
tgt_embdings = infer_state.mtp_draft_input_hiddens
22+
assert (
23+
input_embdings.shape[0] == tgt_embdings.shape[0]
24+
), f"shape {input_embdings.shape} != shape {tgt_embdings.shape}"
25+
26+
layer_weight.enorm_weight_(input=input_embdings, eps=self.eps_, out=input_embdings)
27+
layer_weight.hnorm_weight_(input=tgt_embdings, eps=self.eps_, out=tgt_embdings)
28+
cat_embdings = torch.cat((input_embdings, tgt_embdings), dim=-1)
29+
30+
return layer_weight.eh_proj_weight_.mm(cat_embdings)
31+
32+
def context_forward(
33+
self, input_ids, infer_state: LlamaInferStateInfo, layer_weight: Qwen3_5MTPPreAndPostLayerWeight
34+
):
35+
input_embdings = super().context_forward(input_ids, infer_state, layer_weight)
36+
return self._mtp_fuse(input_embdings, infer_state, layer_weight)
37+
38+
def token_forward(self, input_ids, infer_state: LlamaInferStateInfo, layer_weight: Qwen3_5MTPPreAndPostLayerWeight):
39+
input_embdings = super().token_forward(input_ids, infer_state, layer_weight)
40+
return self._mtp_fuse(input_embdings, infer_state, layer_weight)

lightllm/models/qwen3_5_mtp/layer_weights/__init__.py

Whitespace-only changes.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from lightllm.common.basemodel.layer_weights.meta_weights import ROWMMWeight, QKVROWNMMWeight
2+
3+
4+
class MTPRetargetMixin:
5+
"""Shared MTP weight-name retargeting (model.layers.* -> mtp.layers.*) and qkv/o_gate wiring,
6+
used by both the dense and MoE Qwen3.5 MTP layer-weight classes (#11). The dense subclass adds
7+
its dense-MLP retargets on top; the MoE subclass must not (it uses fused experts)."""
8+
9+
_MAIN_PREFIX = "model.layers."
10+
_MTP_PREFIX = "mtp.layers."
11+
12+
_ATTN_NORM_NAME_ATTRS = (
13+
"_q_weight_name",
14+
"_q_norm_name",
15+
"_q_bias_name",
16+
"_k_weight_name",
17+
"_k_norm_name",
18+
"_k_bias_name",
19+
"_v_weight_name",
20+
"_v_bias_name",
21+
"_kv_weight_name",
22+
"_kv_bias_name",
23+
"_o_weight_name",
24+
"_o_bias_name",
25+
"_att_norm_weight_name",
26+
"_att_norm_bias_name",
27+
"_ffn_norm_weight_name",
28+
"_ffn_norm_bias_name",
29+
)
30+
31+
def _retarget(self, name):
32+
if name is None:
33+
return None
34+
return name.replace(self._MAIN_PREFIX, self._MTP_PREFIX, 1)
35+
36+
def _retarget_attn_norm_names(self):
37+
for attr in self._ATTN_NORM_NAME_ATTRS:
38+
setattr(self, attr, self._retarget(getattr(self, attr)))
39+
40+
def _init_qkv(self):
41+
in_dim = self.n_embed
42+
q_out_dim = self.q_head_num_ * self.head_dim
43+
self.qkv_proj = QKVROWNMMWeight(
44+
in_dim=in_dim,
45+
q_head_num=self.q_head_num_,
46+
kv_head_num=self.k_head_num_,
47+
head_dim=self.head_dim,
48+
weight_names=[self._q_weight_name, self._k_weight_name, self._v_weight_name],
49+
data_type=self.data_type_,
50+
bias_names=[self._q_bias_name, self._k_bias_name, self._v_bias_name],
51+
quant_method=self.get_quant_method("qkv_proj"),
52+
)
53+
self._o_gate_weight_name = f"{self._MTP_PREFIX}{self.layer_num_}.self_attn.o_gate_proj.weight"
54+
self._o_gate_proj = ROWMMWeight(
55+
in_dim=in_dim,
56+
out_dims=[q_out_dim],
57+
weight_names=[self._o_gate_weight_name],
58+
data_type=self.data_type_,
59+
bias_names=None,
60+
quant_method=self.get_quant_method("o_gate_proj"),
61+
)

0 commit comments

Comments
 (0)