Skip to content

Commit 8d06796

Browse files
AirRunnerThump604
andcommitted
fix(mtp): stack per-expert MTP weights for MoE models in sanitize()
MTP layers in MoE models (35B-A3B, 122B-A10B) ship unfused per-expert weights (mtp.layers.{l}.mlp.experts.{i}.gate_proj.weight) whereas the backbone uses pre-fused switch_mlp format. Conversion was failing with ~768 parameters not in model. Add a stacking loop in qwen3_5_moe.py sanitize() after the backbone expert loop, mirroring the same pattern for MTP prefixes. Co-authored-by: Thump604 <thump604@users.noreply.github.com>
1 parent a66d242 commit 8d06796

1 file changed

Lines changed: 18 additions & 0 deletions

File tree

mlx_lm/models/qwen3_5_moe.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from dataclasses import dataclass
44

5+
import mlx.core as mx
6+
57
from .base import BaseModelArgs
68
from .qwen3_5 import Model as Qwen3_5Model
79

@@ -49,4 +51,20 @@ def sanitize(self, weights):
4951
f"{prefix}.experts.down_proj"
5052
)
5153

54+
# Stack per-expert MTP weights into switch_mlp format.
55+
# MTP layers use unfused per-expert weights (experts.{i}.gate_proj etc)
56+
# unlike backbone layers which use fused gate_up_proj.
57+
mtp_num = getattr(self.language_model.args, "mtp_num_hidden_layers", 0)
58+
num_experts = self.language_model.args.num_experts
59+
for l in range(mtp_num):
60+
prefix = f"language_model.mtp.layers.{l}.mlp"
61+
test_key = f"{prefix}.experts.0.gate_proj.weight"
62+
if test_key in new_weights:
63+
for n in ["gate_proj", "up_proj", "down_proj"]:
64+
to_join = [
65+
new_weights.pop(f"{prefix}.experts.{e}.{n}.weight")
66+
for e in range(num_experts)
67+
]
68+
new_weights[f"{prefix}.switch_mlp.{n}.weight"] = mx.stack(to_join)
69+
5270
return self.language_model.sanitize(new_weights)

0 commit comments

Comments
 (0)