Mixture-Of-Experts
8 practice sets · 4 coding problems
A Mixture-of-Experts (MoE) is a transformer that carries a huge number of parameters but spends only a small slice of them on any one token. That single sentence is the whole idea, and it answers a question that haunts everyone who builds language models: how do you make a model know more without making it cost more to run? In a plain (“dense”) model those two things are welded together — more knowledge means more weights, and every extra weight is multiplied on every token, so the bill for each word you generate climbs in lockstep with the model's size. MoE pries them apart. This mini-chapter builds the idea from nothing, assuming only that you have met a transformer block and its feed-forward network before (Topic 1). By the end you should be able to follow a token as it enters an MoE layer, gets scored by a router, is sent to just a few experts, and comes back out — and you should understand routing and gating, top- selection, the all-important gap between total and active parameters, load balancing and router collapse, shared and fine-grained experts, and the distributed-systems twist (all-to-all communication) that makes MoE powerful but finicky. Every later question in this topic — DeepSeek-V3's B/B design, FLOP-conservation proofs, collapse diagnoses — is a variation on the parts we assemble here.
The motivation: paying for capacity you don't use
Recall the anatomy of a transformer block (Topic 1): an attention sublayer, where tokens exchange information across positions, and a feed-forward network (FFN, also called the MLP), which transforms each token's vector on its own, with no cross-token interaction. The FFN is where a model does most of its per-token “thinking,” and it is also where most of the parameters live — typically two-thirds or more of the non-embedding weights. So if you want a smarter model, the FFN is the natural place to add capacity.
Here is the problem. A dense model runs every parameter on every token. Widen the FFN to hold twice as many weights and you have done two things at once: doubled its capacity and doubled the arithmetic it performs per token. Capacity and cost rise together, glued. For a B-parameter dense model, generating a single token costs roughly billion floating-point multiply-adds, every token, forever — and that is the floor you accept the moment you choose B parameters.
But do you really need all of that machinery for every token? When the model processes the word def in a Python snippet, the circuitry it has learned for conjugating French verbs sits idle but still gets multiplied through. When it reads a line of poetry, its knowledge of organic-chemistry nomenclature contributes nothing yet still runs. A single token rarely needs more than a sliver of the model's specialized knowledge at once. That observation is the seed of the whole topic.
Conditional computation: the core principle
The general name for “activate only the part of the network that is relevant to this input” is conditional computation. A dense network is unconditional: its computation graph is the same for every input, so every weight is touched every time. A conditionally computed network makes the path through itself depend on the data — different inputs light up different sub-networks.
MoE is the dominant way to realize this in modern LLMs. The recipe is simple: in some or all of the transformer blocks, replace the single FFN with a collection of parallel FFNs — the experts — and add a small learned router that, for each token, picks a few experts to run and skips the rest. Each expert is an ordinary FFN with its own independent weights; nothing about an expert is special except that it is one of many. Crucially, MoE touches only the FFN sublayer. The attention sublayer, the norms, the residual stream — all unchanged. (This is why MoE and the attention-efficiency tricks of Topic 3 stack cleanly: they modify different parts of the block.)
A dense FFN spends every parameter on every token. An MoE layer holds expert FFNs but runs only of them per token, chosen by a router. This decouples total parameters (which set capacity and memory, ) from active parameters and FLOPs (which set per-token cost, ). The slogan is “more parameters, roughly the same compute per token.” MoE replaces the FFN, not the attention.
The router and the gate
The router (also called the gating network) is the brain of the layer — the receptionist from the analogy. In its standard form it is astonishingly small: a single learned matrix that maps a token's hidden vector to one routing logit (or affinity score) per expert,
That is one tiny matrix-vector product per token — negligible next to the experts themselves. From the affinities the router must do two separate things: choose which experts run, and weight how much each chosen expert's output counts.
Top- routing handles the choice: keep the experts with the largest affinities and discard the rest. Typically or , while ranges from up into the hundreds. Write for the chosen set. The router then produces a gate weight for each selected expert, almost always by a softmax taken only over the selected affinities (so the weights of the running experts sum to one):
Finally, the layer's output is the gate-weighted sum of the chosen experts' outputs — experts outside contribute exactly nothing:
This is then added back into the residual stream, exactly as a dense FFN's output would be. The gates earn their keep twice over. They weight the blend, letting the layer lean more on a strongly-matched expert. And because they are differentiable in , they are the only channel through which gradients reach the router: when an expert that was scaled by gate produces a useful output, the gradient flows back through and nudges to score that expert higher for similar tokens next time. The hard top- “pick” itself is non-differentiable — it is a discrete choice — so the soft gate is what makes the router learnable at all.
Total vs. active parameters — the central bookkeeping
The single most important number-sense in MoE is the gap between total and active parameters, so it is worth saying slowly. Total parameters count every weight stored in the model. They set the memory footprint (you must hold them all) and, loosely, the capacity — how much the model can know. Active parameters count only the weights actually touched while processing one token. With the standard rule of thumb that a forward-plus-backward pass costs about FLOPs per active parameter per token (, with the active parameter count and the number of tokens), it is the active count that sets your training and inference compute bill.
A toy case makes it concrete. Suppose each expert is a copy of the dense FFN with parameters, there are experts, and we route top-. The MoE FFN then stores parameters in total but activates only per token: an total-to-active ratio. Capacity grew ; per-token FFN compute is unchanged. With top- you would activate , an ratio. This ratio is so central it has a name — the sparsity, totalactive — and the clear recent trend is toward sparser models (higher ratios) because, holding active compute fixed, adding more idle experts reliably lowers loss.
Now the real designs fall into place:
- Mixtral 8x7B (Mistral): experts per layer, top- routing, SwiGLU experts. About B parameters total but only B active per token — so it generates text at roughly the speed and cost of a B dense model while reaching toward the quality of something much larger. (The total is not B because attention and embeddings are shared, not multiplied.)
- DeepSeek-V3: B parameters total but only B active per token — a sparsity near . It has roughly the knowledge capacity of a B model at roughly the per-token cost of a B one.
This is precisely the True statement the warm-up asks you to confirm: MoE lets you grow capacity without proportionally growing per-token compute.
There is a crucial caveat for serving, and it flips the usual intuition on its head. Even though only experts run per token, all experts must be resident in memory, because the next token might route to any of them. So MoE buys you cheaper compute at the price of a larger memory footprint — the opposite of nearly every other efficiency lever, which trades memory for compute. This is why MoE inference tends to be memory-bound rather than compute-bound, and why expert offloading (parking rarely-used experts on slower CPU memory and fetching them on demand) is a real serving tactic. We return to this at the close.
Capacity, overflow, and token dropping
A subtle problem appears the moment you process tokens in batches rather than one at a time. Hardware loves dense, fixed-shape tensors; it hates ragged ones. But routing is data-dependent, so in any given batch one expert might be chosen by tokens and another by . To keep the math on regular tensors, each expert is given a fixed-size buffer — its expert capacity — and the dispatch fills these buffers. Capacity is set by a capacity factor (commonly to ) relative to the perfectly-balanced load:
where is the number of tokens in the batch, so is the total number of routing slots (each token claims of them) spread over experts. If routing is uneven and more than “capacity” tokens select the same expert, the surplus tokens overflow, and there are two ways to react. Dropping simply discards the overflow: those tokens skip that expert, so their FFN contribution at this layer is zero — but, importantly, the residual connection still carries the token forward unchanged, so a dropped token is not lost from the sequence, it just misses one FFN update. This saves compute but loses information. Padding goes the other way: any under-filled expert's buffer is topped up with dummy slots so all shapes stay fixed; this processes every real token but wastes compute on the padding. A higher capacity factor means fewer drops but more padding, and a lower the reverse — a direct capacity-factor dropping compute trade-off.
Load balancing: keeping experts busy and alive
Why would routing be skewed in the first place? Because nothing in the basic setup forces balance, and imbalance is self-reinforcing. Early in training a few experts happen to get slightly more traffic; more traffic means more gradient updates, so they improve faster; improving faster makes them look better to the router, so they attract still more traffic. It is a rich-get-richer loop. Its pathological endpoint is router collapse (or expert collapse): the router funnels nearly all tokens to a handful of experts, while the rest are starved of gradient and end up undertrained and effectively dead. You paid for experts and got the capacity of a few; the others are dead weight, and meanwhile the popular experts overflow and drop tokens. Collapse defeats the entire point of MoE.
The auxiliary load-balancing loss. The classic fix is to add a small extra term to the training objective that pushes expert usage toward uniform. In the canonical Switch Transformer form, for experts let be the fraction of tokens dispatched to expert and the mean routing probability the router assigned to expert , both averaged over the batch. The loss is
scaled by a small coefficient and added to the language-modeling loss. Two facts make this exactly the right object. First, under perfectly uniform routing , so the sum is (ignoring ): the loss bottoms out at , which doubles as a convenient health gauge — a value near means balanced, larger means skewed. Second, the two factors play different roles. The dispatch fraction comes from a hard top- count, which is non-differentiable, so it carries no gradient; it is just a measured load. The probability is smooth, so the gradient flows entirely through it. The product therefore creates a force that lowers the routing probability of experts that are currently both over-chosen and over-weighted, and relatively raises it for the under-used ones — gently flattening the histogram above.
The catch, and DeepSeek's fix. An auxiliary loss is a second, competing objective. Pushing the router toward uniformity can drag it away from the routing the language-modeling loss actually prefers, costing a little quality — you are paying a small “balance tax.” DeepSeek-V3 popularized an auxiliary-loss-free alternative that sidesteps the tax entirely. The idea: maintain a per-expert bias term that is added to the affinities only for the top- selection, never to the gate value that scales the expert's output. After each step you simply nudge down for over-loaded experts and up for under-loaded ones — a plain feedback controller watching observed load. Because the bias steers who gets selected but never enters the gradient-bearing gate, balancing exerts no force that fights the main loss. You get balance for free. (DeepSeek-V3 also switched the gate from a softmax over all experts to a per-expert sigmoid, which scales more gracefully as grows into the hundreds, since a single softmax over experts squashes most scores toward zero.)
Shared and fine-grained experts
Two refinements from the DeepSeekMoE line are worth knowing because later questions assume them.
A shared expert is an FFN that every token always passes through, in addition to its routed experts — it is never gated off. The motivation is a clean division of labor. Some computation is needed by almost every token: basic grammar, common-word handling, generic “housekeeping.” Without a shared expert, each routed expert wastes part of its capacity re-learning this common knowledge, and the redundancy is sheer waste. A shared expert absorbs the common case once, freeing the routed experts to specialize aggressively on the specific. In DeepSeek-V3 there is shared expert plus routed experts with top- routing, so each token runs experts in all.
Fine-grained experts means slicing each expert into several smaller ones: replace experts of hidden size by experts of hidden size , while activating as many of them. The FLOP budget is unchanged — each expert is cheaper to run, but as many run, so cancels (a fact the harder questions ask you to prove). What you buy with the same compute is sharper specialization. With more, narrower experts the router can assemble a more precise combination per token, and the number of possible top- subsets explodes combinatorially: choosing of experts gives vastly more distinct “recipes” than choosing of . Finer granularity means finer-grained conditional computation.
The systems wrinkle: all-to-all and expert parallelism
With hundreds of experts you cannot fit them all on one GPU, so expert parallelism places different experts on different devices — a natural way to spread the memory of all those weights (link Topic 6's discussion of parallelism). But it creates a communication headache. The router scatters a batch's tokens across all experts wherever they live, so each device must ship its tokens to the devices holding their chosen experts, and then gather the results back. The collective operation for “every device sends a different slice of its data to every other device” is an all-to-all. An MoE layer needs two all-to-alls per forward pass: a dispatch (send each token to its experts) and a combine (send each expert's outputs back to the originating device).
Because an all-to-all moves data proportional to (tokens hidden size) across the interconnect, on a slow network it can dominate the step time — the router and experts may sit idle waiting for tokens to arrive. This is the central reason MoE complicates distributed training, and why MoE efficiency is so sensitive to interconnect bandwidth. It also explains a design choice from earlier: fixed expert capacity and token dropping partly exist to keep these communication tensors a predictable, fixed shape, since variable-sized all-to-alls are far harder to pipeline efficiently.
Training and inference: what changes
MoE shifts several practical realities, and it helps to name them now because the questions probe each.
Training is touchier than dense. The router is a discrete, data-dependent switch sitting in the middle of a differentiable network, so it can oscillate, collapse, or chase a moving target. It is sensitive to router precision (compute routing logits in higher precision to avoid noisy argmax flips), to batch composition (if a micro-batch is too narrow in domain, its routing statistics are biased and balancing suffers — so balance is best computed over a global batch across devices, not per local micro-batch), and to the load-balancing coefficient (too small and experts collapse; too large and uniformity overrides the language objective). A common, cheaper route to an MoE is upcycling: initialize the experts as copies of a pretrained dense model's FFN, then continue training so they diverge — you inherit the dense model's knowledge instead of learning from scratch, though early on the cloned-identical experts must first break symmetry.
Inference is memory-bound, as flagged above: you hold all experts but run few, so the bottleneck is shuttling expert weights, not arithmetic. Batching is also subtler. At high batch sizes, tokens in a batch scatter across many experts, so latency can become spiky — a step waits on whichever expert got the most tokens (or on the all-to-all), and different batches stress different experts. This load-dependent jitter is the routing/batching cause of the inference-latency questions in this topic.
When MoE wins — and when it doesn't
MoE is not free quality; it is a specific trade. It tends to win when:
- You are bottlenecked by training/serving compute (FLOPs) and have memory to spare. MoE gives more quality per training-FLOP than a dense model of equal active size, and serves at the latency of its small active footprint.
- You want a very knowledgeable model but can tolerate a large memory/VRAM footprint to host all the experts.
It tends to be the wrong choice when memory is the binding constraint — e.g. on-device or single-GPU deployments where you cannot hold hundreds of experts, so a dense model of the same memory budget will simply be better. It also adds engineering complexity (all-to-all, balancing, capacity tuning) and can be harder to fine-tune: heavy SFT or RL post-training shifts the input distribution, which can disturb a delicately-balanced router and erode the specialization gains, so the MoE's edge over a comparable dense model sometimes shrinks after alignment. Keeping the router stable through post-training is its own discipline, and several questions in this topic are really about exactly that.
Putting it together / what to watch for
The mental model in one breath: in each MoE transformer block, replace the single FFN with expert FFNs (optionally plus an always-on shared expert); a tiny router scores experts per token, top- selection keeps a few, a restricted softmax (or sigmoid) gives gate weights, the chosen experts run (subject to fixed capacity, with overflow dropped or padded), and their gate-weighted sum is added to the residual stream. Total parameters set capacity and memory; active parameters set compute — decoupling the two is the whole point.
A handful of recurring tensions drive nearly every design choice that follows, and naming them now will make the detailed questions feel familiar:
- Balance vs. quality. Load must be spread (aux loss, or DeepSeek's aux-loss-free bias) or experts collapse — yet over-forcing balance fights the language objective.
- Capacity vs. compute. A higher capacity factor drops fewer tokens but wastes more on padding; a lower one is cheaper but loses information.
- Memory vs. FLOPs. MoE is the rare lever that cuts compute while raising memory, reshaping serving (offloading, batching, memory-bound inference).
- Specialization is fragile. Post-training distribution shift can disturb the router and erode the gains, so a stable router is the prize.
Hold this skeleton — router, top- gate, total/active split, capacity, balancing, all-to-all — and the detailed questions that follow (the aux-loss derivation and its minimum, FLOP-conservation proofs, DeepSeek-V3's bias trick, collapse diagnostics, MoE-vs-dense at fixed budget) will read as variations on parts you have already met.
