Mixture of Experts

What a mixture of experts really is

A mixture of experts holds many parallel sub-networks and sends each token to only a few of them, so the model can carry huge knowledge while doing small work per word.

On this page 9
  1. The short answer
  2. The hospital you have queued in
  3. What problem this solves
  4. Who does the sending
  5. The catch that surprises everybody
  6. Which part gets replaced
  7. Where you have already seen this
  8. Remember this
  9. What to learn next

One lesson, three depths. Pick the one that fits you today — you can switch any time.

Beginner — No maths. Plain English.

The short answer

A mixture of experts is a model of many small sub-models, and each word visits only a couple.

The hospital you have queued in

A big government hospital has thirty departments. Orthopaedics, cardiology, eye, skin, and so on.

You arrive with a swollen ankle. At the front desk someone glances at you and sends you to orthopaedics. You do not walk through all thirty departments.

The hospital is enormous. Your visit is small. That is exactly the trade a mixture of experts makes.

What problem this solves

More knowledge in a model means more stored numbers. More stored numbers means more work for every word, and more cost for every user, forever.

The two used to be locked together. A bigger model was always a slower model.

A mixture of experts breaks the lock. Add a hundred departments and the hospital knows more. Each patient still visits one department, so each visit costs the same.

   ordinary layer                mixture of experts
   --------------                ------------------
   one block of numbers          32 blocks of numbers
   every word uses all of it     each word uses 4 of them

   knowledge:  fixed             knowledge: 32 blocks worth
   work/word:  all of it         work/word: 4 blocks worth

Who does the sending

A small piece called the router looks at each word and picks which departments it should visit. It is tiny — a fraction of the size of one department.

Nobody tells the router what the departments should specialise in. It works that out during training, together with the departments themselves.

And what it learns is often not what people expect. Departments rarely split by topic like "medicine" or "law". They split by patterns in the text that are harder to name.

The catch that surprises everybody

You still need every department standing there, staffed, with the lights on. A hospital cannot conjure up cardiology when a heart patient walks in.

So the memory cost is the whole model. Every expert must be loaded, even the ones this word will not use.

That is the real trade. Less work per word, the same memory. These models are cheap to run and expensive to hold. It is why they suit big shared servers and are awkward on a laptop.

Which part gets replaced

Only one part of each layer becomes a set of experts: the thinking block that sits after attention.

The attention part is untouched. Everything you learned about attention still applies. A mixture-of-experts model is an ordinary transformer with one component swapped.

Where you have already seen this

  • Mixtral, DeepSeek, Qwen and gpt-oss, all open models you can download.
  • Almost every large hosted assistant. The design is close to universal at the top end.
  • Any model described as "671 billion parameters, 37 billion active".

Remember this

  • Many parallel sub-networks called experts, and a small router choosing a few per word.
  • Knowledge grows with the number of experts. Work per word does not.
  • Memory does not shrink. Every expert has to be loaded.

What to learn next

Developer — Code and libraries.

Setup

bash
pip install torch

Written against PyTorch 2.5.1, Python 3.10. CPU, instant.

A mixture-of-experts layer, complete

moe_intro.py
import torch, torch.nn as nn, torch.nn.functional as F

torch.manual_seed(0)
d_model, d_ff, n_exp, top_k, n_tok = 16, 32, 8, 2, 6

class MoE(nn.Module):
    def __init__(self):
        super().__init__()
        self.router = nn.Linear(d_model, n_exp, bias=False)
        self.experts = nn.ModuleList(
            nn.Sequential(nn.Linear(d_model, d_ff), nn.GELU(), nn.Linear(d_ff, d_model))
            for _ in range(n_exp))

    def forward(self, x):
        logits = self.router(x)                             # one score per expert per token
        probs = F.softmax(logits, dim=-1)
        weight, idx = probs.topk(top_k, dim=-1)             # keep the best few only
        weight = weight / weight.sum(-1, keepdim=True)      # renormalise the survivors
        out = torch.zeros_like(x)
        for slot in range(top_k):
            for e in range(n_exp):
                hit = idx[:, slot] == e
                if hit.any():                               # this expert runs only if chosen
                    out[hit] += weight[hit, slot, None] * self.experts[e](x[hit])
        return out, idx, weight

moe = MoE()
x = torch.randn(n_tok, d_model)
out, idx, weight = moe(x)

print("token -> experts chosen (and their weights)")
for t in range(n_tok):
    picks = ", ".join(f"E{idx[t,s].item()} {weight[t,s]:.2f}" for s in range(top_k))
    print(f"  token {t}: {picks}")

one_expert = sum(p.numel() for p in moe.experts[0].parameters())
total = sum(p.numel() for p in moe.parameters())
active = one_expert * top_k + moe.router.weight.numel()
print(f"\nparameters in one expert     : {one_expert:,}")
print(f"total parameters in the layer: {total:,}")
print(f"used for any single token    : {active:,}   ({active/total:.1%})")

print("\nsame layer, more experts, same work per token")
print(f"{'experts':>8}{'total params':>15}{'active params':>15}{'fraction':>10}")
for n in (8, 64, 256, 512):
    tot = n * one_expert + d_model * n
    act = top_k * one_expert + d_model * n
    print(f"{n:>8}{tot:>15,}{act:>15,}{act/tot:>10.2%}")

print("\nreal models (from each config.json on the Hub)")
print(f"{'model':<24}{'experts':>8}{'top-k':>7}{'shared':>8}")
for nm, e, k, s in [("Mixtral-8x7B-v0.1", 8, 2, 0), ("DeepSeek-V3", 256, 8, 1),
                    ("Qwen3-30B-A3B", 128, 8, 0), ("Qwen3-Next-80B-A3B", 512, 10, 1),
                    ("gpt-oss-20b", 32, 4, 0)]:
    print(f"{nm:<24}{e:>8}{k:>7}{s:>8}")
Output
token -> experts chosen (and their weights)
  token 0: E5 0.53, E7 0.47
  token 1: E7 0.60, E0 0.40
  token 2: E3 0.66, E5 0.34
  token 3: E7 0.58, E0 0.42
  token 4: E6 0.57, E7 0.43
  token 5: E6 0.60, E4 0.40

parameters in one expert     : 1,072
total parameters in the layer: 8,704
used for any single token    : 2,272   (26.1%)

same layer, more experts, same work per token
 experts   total params  active params  fraction
       8          8,704          2,272    26.10%
      64         69,632          3,168     4.55%
     256        278,528          6,240     2.24%
     512        557,056         10,336     1.86%

real models (from each config.json on the Hub)
model                    experts  top-k  shared
Mixtral-8x7B-v0.1              8      2       0
DeepSeek-V3                  256      8       1
Qwen3-30B-A3B                128      8       0
Qwen3-Next-80B-A3B           512     10       1
gpt-oss-20b                   32      4       0

Reading the output

Different tokens went to different experts. Token 2 chose E3 and E5; token 5 chose E6 and E4. That divergence, on an untrained router with random weights, is only routing noise. In a trained model it is the mechanism the whole design rests on.

Expert 7 appears in four of six tokens. Experts 1 and 2 appear in none. With a random router this is chance. In training, this exact pattern becomes self-reinforcing and is the failure mode of the entire technique — see load balancing and expert collapse.

The scaling table is the whole argument. From 8 experts to 512, total parameters grow 64x while active parameters grow 4.5x — and that growth is entirely the router, which must produce one score per expert. The expert compute per token is fixed at top_k experts, forever.

Real models sit far along that curve. Qwen3-Next-80B-A3B activates 10 of 512 experts. Naming a model "80B-A3B" is the industry's shorthand: 80 billion total, 3 billion active.

The loop is teaching code, not production code

for slot ... for e ... runs top_k * n_exp iterations with a boolean mask each time. It is readable and it is slow. Production kernels sort tokens by expert assignment once and then run one grouped matrix multiply per expert.

transformers uses a MixtralTopKRouter that computes softmax then topk then renormalises, exactly as above, and hands indices to a fused MixtralExperts module. The routing logic is what you see here; the dispatch is not.

What is and is not replaced

An MoE transformer layer is:

x -> attention (unchanged, still GQA or MLA) -> MoE feed-forward -> out

Attention stays dense. Everything in Fast Attention and Long Context applies unchanged to an MoE model. Some models keep the first few layers dense: DeepSeek-V3's first_k_dense_replace: 3 means layers 0 to 2 use an ordinary feed-forward block, because routing is unstable in the earliest layers.

Common mistakes

Expecting a memory saving. Every expert is resident. A 671B-parameter model needs 671B parameters' worth of memory whatever its active count. MoE trades memory for compute, in that direction only.

Expecting interpretable specialisation. Published analyses find experts specialising by token identity, syntax and position far more than by human topic. Do not promise a "legal expert".

Forgetting to renormalise the top-k weights. Without weight / weight.sum(), output magnitude varies with how confident the router was, and training becomes unstable.

Comparing an MoE and a dense model by parameter count. Mixtral-8x7B has around 47B parameters and about 13B active. It behaves like neither a 47B nor a 13B dense model. Compare on measured quality and measured cost, never on the headline number.

Assuming any training framework handles it. MoE needs balancing losses, capacity handling and expert-parallel communication. Naive data-parallel training of the code above will run, and will collapse onto a few experts.

Try it yourself

Set top_k = 8 with n_exp = 8. Every expert now runs for every token, which is a dense layer with extra steps. Compare the active-parameter fraction. Then set top_k = 1 and see how much the output changes when one router decision flips.

What to learn next

Researcher — Mathematics and papers.

Definition

Replace the feed-forward sublayer with $N$ experts ${E_i}$ and a gating function $G$:

$$ y = \sum_{i=1}^{N} G(x)_i \, E_i(x) $$

$G(x) \in \mathbb{R}^{N}$ is sparse: at most $k \ll N$ entries are non-zero, so only $k$ experts are evaluated. Shazeer et al. (2017), Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer (arXiv:1701.06538) introduced the sparse form for LSTM language models, reaching 137B parameters at a time when dense models were three orders of magnitude smaller.

The dense ancestor is Jacobs, Jordan, Nowlan and Hinton (1991), Adaptive Mixtures of Local Experts, where all experts are evaluated and the gate produces a convex combination. Sparsity is the modern contribution; the decomposition is thirty-five years old.

Cost model

Let $P_e$ be parameters per expert, $P_{\text{other}}$ the attention and embedding parameters, and $L_{\text{moe}}$ the number of MoE layers.

$$ P_{\text{total}} = P_{\text{other}} + L_{\text{moe}} N P_e, \qquad P_{\text{active}} = P_{\text{other}} + L_{\text{moe}} (k P_e + d N) $$

The $dN$ term is the router. FLOPs per token track $P_{\text{active}}$; memory tracks $P_{\text{total}}$. The design freedom is that $N$ enters memory linearly and compute only through the negligible router term.

Clark et al. (2022), Unified Scaling Laws for Routed Language Models fit scaling laws in which routed models follow a dense-equivalent parameter count that grows sublinearly in $N$, with returns diminishing beyond roughly 64 experts at the scales they studied. Later work with fine-grained experts pushed that ceiling substantially — see shared and fine-grained experts.

Lineage

YearWorkContribution
1991Jacobs et al.Dense mixture of local experts
2017Shazeer et al.Sparse top-$k$ gating, noisy gating, load-balancing loss
2020GShard (Lepikhin et al., arXiv:2006.16668)Top-2 routing, expert capacity, automatic sharding at 600B
2021Switch Transformer (Fedus, Zoph, Shazeer, arXiv:2101.03961)Top-1 routing, simplified balancing loss, 1.6T parameters
2022ST-MoE (Zoph et al., arXiv:2202.08906)Router z-loss, stability and fine-tuning recipes
2024Mixtral 8x7B (Jiang et al., arXiv:2401.04088)First widely used open sparse model
2024DeepSeekMoE (arXiv:2401.06066)Fine-grained experts plus shared experts
2024DeepSeek-V3 (arXiv:2412.19437)671B total, 37B active; auxiliary-loss-free balancing

What experts learn

The evidence contradicts the marketing. Mixtral's own paper reports no clear topic-level specialisation, and finds strong consecutive-token routing locality instead: adjacent tokens are frequently sent to the same expert. Switch Transformer and ST-MoE report specialisation by shallow features — punctuation, numbers, conjunctions, proper nouns.

The mechanistic reading is that experts partition the representation space, not the subject space, and that the router is closer to a learned hash than to a triage nurse. Anyone designing evaluation around expected expert semantics should read those sections first.

Where the difficulty actually is

The forward pass above is fifteen lines. Everything hard about MoE is elsewhere:

  • Balance. Nothing in the objective prevents one expert taking everything. Load balancing.
  • Discreteness. topk has zero gradient with respect to unselected experts, so the router learns only from experts it already chose. The router.
  • Capacity. Fixed-size expert buffers mean tokens can be dropped. Expert capacity.
  • Communication. Experts live on different devices, and every token must travel. Expert parallelism.
  • Fine-tuning. MoE models overfit more readily than dense models of equal active size; ST-MoE devotes a section to it.

Each of those is a lesson in this section, and together they are the reason MoE took seven years from Shazeer's paper to become the default at the frontier.

What to learn next

What to learn next

These follow on from what you just read.

  • Mixture of Experts

    The router

    The router is a tiny layer that picks which experts each token visits, and it has to learn that job through a hard selection step that gives it almost no feedback.

  • Mixture of Experts

    Load balancing and expert collapse

    Nothing in a model's objective wants the experts used evenly, so a few take almost everything and the rest go dead unless you add a rule that forces the traffic apart.

  • Mixture of Experts

    Expert capacity and dropped tokens

    Each expert gets a fixed number of seats per batch, so an oversubscribed expert silently skips the extra tokens, and the size of that buffer is a direct trade between waste and dropping.