Skip to content

Commit 04a1910

Browse files
Fix LFM2 MoE routing to match the HF reference (sigmoid gating) (ml-explore#1354)
Lfm2Moe is a sigmoid-gated MoE (DeepSeek-V3 style): the router scores are sigmoid(gate(x)); expert_bias is an aux-loss-free load-balancing bias used only to select the top-k experts; the combination weights are gathered from the unbiased sigmoid scores and then scaled by routed_scaling_factor. The current implementation used softmax, folded expert_bias into the weights, and omitted routed_scaling_factor, so MLX routing diverges from the HF Transformers reference (and llama.cpp). This aligns it with the reference; routed_scaling_factor is added to ModelArgs and defaults to 1.0. Co-authored-by: Yi Zhong <207368749+vincentzed@users.noreply.github.com>
1 parent 8239c72 commit 04a1910

1 file changed

Lines changed: 15 additions & 8 deletions

File tree

mlx_lm/models/lfm2_moe.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class ModelArgs(BaseModelArgs):
3636
conv_bias: bool
3737
conv_L_cache: int
3838
rope_theta: float = 1000000.0
39+
routed_scaling_factor: float = 1.0
3940
rope_parameters: Optional[dict] = None
4041
full_attn_idxs: Optional[List[int]] = None
4142
layer_types: Optional[List[str]] = None
@@ -196,6 +197,7 @@ def __init__(self, args: ModelArgs):
196197
self.top_k = args.num_experts_per_tok
197198
self.norm_topk_prob = args.norm_topk_prob
198199
self.use_expert_bias = args.use_expert_bias
200+
self.routed_scaling_factor = args.routed_scaling_factor
199201

200202
self.gate = nn.Linear(dim, num_experts, bias=False)
201203
self.switch_mlp = SwitchGLU(dim, intermediate_size, num_experts)
@@ -206,18 +208,23 @@ def __call__(
206208
self,
207209
x: mx.array,
208210
):
209-
gates = self.gate(x).astype(mx.float32)
210-
gates = mx.softmax(gates, axis=-1)
211-
212-
if self.use_expert_bias:
213-
gates += self.expert_bias
211+
# Sigmoid-gated routing (matches the HF Transformers Lfm2Moe reference):
212+
# the expert_bias is used only to select the top-k experts; the routing
213+
# weights are gathered from the unbiased sigmoid scores and then scaled by
214+
# routed_scaling_factor.
215+
routing_weights = mx.sigmoid(self.gate(x))
214216

215217
k = self.top_k
216-
inds = mx.argpartition(gates, kth=-k, axis=-1)[..., -k:]
218+
if self.use_expert_bias:
219+
scores_for_routing = routing_weights.astype(mx.float32) + self.expert_bias
220+
inds = mx.argpartition(scores_for_routing, kth=-k, axis=-1)[..., -k:]
221+
else:
222+
inds = mx.argpartition(routing_weights, kth=-k, axis=-1)[..., -k:]
217223

218-
scores = mx.take_along_axis(gates, inds, axis=-1)
224+
scores = mx.take_along_axis(routing_weights, inds, axis=-1)
219225
if self.norm_topk_prob:
220-
scores /= mx.sum(scores, axis=-1, keepdims=True) + 1e-20
226+
scores = scores / (mx.sum(scores, axis=-1, keepdims=True) + 1e-6)
227+
scores = scores * self.routed_scaling_factor
221228
scores = scores.astype(x.dtype)
222229

223230
y = self.switch_mlp(x, inds)

0 commit comments

Comments
 (0)