Skip to content

Fix DeepSeek MoE routed experts losing linear_class quantization - #562

Merged
justinchuby merged 1 commit into
mainfrom
fix/deepseek-moe-linear-class-routed-experts
Aug 23, 2026
Merged

Fix DeepSeek MoE routed experts losing linear_class quantization#562
justinchuby merged 1 commit into
mainfrom
fix/deepseek-moe-linear-class-routed-experts

Conversation

@justinchuby

Copy link
Copy Markdown
Member

Root cause

_DeepSeekMoEFFN.__init__ (src/mobius/models/deepseek.py) accepted linear_class
but only forwarded it to _SharedExpertMLP, not to the MoELayer that builds
the routed-expert dense loop-over-experts fallback:

self.moe = MoELayer(config, gate=gate)  # linear_class silently dropped

Consequence: a quantized config correctly quantized attention, dense FFN, and
the shared expert, but every routed MoE expert's gate/up/down projections
stayed plain float MatMul. This both loses quantization for the routed
experts and breaks the fuse_dense_moe_to_qmoe post-hoc rewrite, which only
pattern-matches a quantized MatMulNBits dense-fallback shape — so a
quantized DeepSeek-V2/V3 config would silently fail to fuse into QMoE for its
routed experts.

DeepSeek-V4 is unaffected: its DeepSeekV4MoE uses expert_factory to build
_DeepSeekV4Expert, which independently recomputes its quantized class from
config.quantization per-expert rather than relying on MoELayer's
linear_class forwarding — verified via review, no matching fix needed there.

Fix

One line: thread linear_class through to the routed-expert MoELayer:

self.moe = MoELayer(config, gate=gate, linear_class=linear_class)

Tests

Added test_deepseek_moe_ffn_linear_class_reaches_routed_experts in
deepseek_test.py, which isolates the assertion on module.moe.experts
(routed) separately from module.shared_experts (already worked before this
fix), rather than relying on an aggregate creation count — so it can't pass
for the wrong reason.

  • Verified the new test fails on pre-fix code (assert False at the
    routed-experts isinstance check) and passes post-fix.
  • Full suite: PYTHONPATH=src python3 -m pytest src/3249 passed, 9
    skipped
    , no failures.
  • ruff check / ruff format --check on changed files: clean.
  • Independently reviewed (separate reviewer pass over the diff); verdict:
    Approve, no findings.

Scope

Deliberately minimal and focused: does not touch GLM-5.2 registration,
DeepSeek-V4 QMoE export, YaRN, or any other follow-up — those are tracked/
landed separately (#548, #550, #555, #559, #560).

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

_DeepSeekMoEFFN.__init__ threaded linear_class into _SharedExpertMLP
but not into the MoELayer that builds the routed-expert dense-loop
fallback. A quantized config therefore quantized attention, dense FFN,
and the shared expert, but silently left every routed MoE expert's
gate/up/down projections as plain float MatMul -- losing quantization
and breaking the fuse_dense_moe_to_qmoe post-hoc rewrite, which only
pattern-matches a quantized MatMulNBits dense-fallback shape.

Fix: pass linear_class through to MoELayer's routed-expert
construction (one line). DeepSeek-V4's MoE FFN is unaffected -- it
recomputes its own quantized class per-expert independently of
MoELayer's linear_class and was already correct.

Added test_deepseek_moe_ffn_linear_class_reaches_routed_experts,
which isolates and asserts on module.moe.experts vs
module.shared_experts separately (not just an aggregate creation
count) so the regression is caught precisely. Verified: fails on
pre-fix code, passes post-fix. Full suite (3249 passed, 9 skipped)
and ruff check/format clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@justinchuby
justinchuby requested review from a team and a lite review from Copilot August 23, 2026 07:32
@justinchuby
justinchuby merged commit e71f475 into main Aug 23, 2026
16 of 17 checks passed
@justinchuby
justinchuby deleted the fix/deepseek-moe-linear-class-routed-experts branch August 23, 2026 07:32
@github-actions

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing bed2983087e15f

Model Sub-model Changes Status

No architecture changes detected.


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

@github-actions

Copy link
Copy Markdown

Performance Comparison

Comparing bed2983087e15f

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 68 68 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 66 66 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 105 105 +0.0%
gpt2 model_size_bytes 388 KB 388 KB +0.0%
gpt2 num_nodes 54 54 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 60 60 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 56 56 +0.0%
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0%
mamba (ssm-text-generation) num_nodes 94 94 +0.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 58 58 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 54 54 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 60 60 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 56 56 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 264 264 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 126 126 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 428 428 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 176 176 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a DeepSeek MoE quantization gap by ensuring the linear_class parameter is forwarded into the routed-expert dense fallback (MoELayer) within _DeepSeekMoEFFN, preventing routed experts from silently remaining unquantized and enabling downstream dense→QMoE fusion patterns to match as intended.

Changes:

  • Thread linear_class through _DeepSeekMoEFFN into MoELayer(..., linear_class=...) so routed expert MLPs use the intended linear implementation.
  • Add a focused regression unit test that asserts the routed experts (and shared expert) are constructed using the provided linear_class.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/mobius/models/deepseek.py Forwards linear_class to the routed-expert MoELayer to avoid losing quantization in the dense fallback path.
src/mobius/models/deepseek_test.py Adds a regression test verifying linear_class reaches both routed experts and the shared expert.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants