Type at least 2 letters. Enter opens the first result, Esc closes.

    Deep Learning · Lesson 15 · Modern architectures and theory

    Mixture of experts: routing, load balancing and capacity

    Advanced 75 min 4 interactive figures 3 videos Colab notebook

    Before this lesson: lesson 04 (the SwiGLU feed-forward network), lesson 05 (the residual path, which carries dropped tokens), lesson 09 (the softmax and its Jacobian), lesson 11 (the feed-forward sublayer and parameter counting) and lesson 12 (bf16 and the z-loss). The all-to-all collective is covered in the ML-systems track. Time: about 75 minutes, plus the notebook.


    Why this matters #

    A dense transformer uses every parameter for every token: a forward pass costs about 2N2N FLOPs per token for NN parameters (lesson 11). An MoE layer breaks this link. Parameters grow with the number of experts EE; compute per token grows only with the number kk of experts each token uses.

    The gains are large. With 64 experts, Switch-Base reached the quality of the FLOP-matched dense T5-Base in one seventh of the training time (Fedus et al. 2021). Mixtral 8×7B has about 47B parameters but uses about 13B per token, and matches or beats Llama 2 70B on most benchmarks. DeepSeek-V3 has 671B parameters and uses about 37B per token. Interviews ask about each part: "count the active parameters", "why does the router collapse?", "what is the capacity factor?".

    1. Predict first #

    2. The picture #

    One token, two experts. Take a token vector x∈RDx \in \mathbb{R}^D and two experts E1,E2E_1, E_2, each a small feed-forward network. The router is a linear map: logits z=xWr∈R2z = xW_r \in \mathbb{R}^2 and probabilities p=softmax⁡(z)p = \operatorname{softmax}(z). Top-1 routing picks i∗=arg⁡max⁡ipii^* = \arg\max_i p_i and outputs y=pi∗ Ei∗(x)y = p_{i^*}\, E_{i^*}(x). Only one expert runs. The layer has twice the parameters of one network but the same compute, plus a tiny router.

    From soft to sparse. The classic mixture of experts (Jacobs et al. 1991) runs every expert and mixes the outputs: y=∑ipi(x) Ei(x)y = \sum_i p_i(x)\, E_i(x). A sparse MoE keeps only the kk largest terms of this sum, so the other E−kE - k experts never run for this token.

    Why multiply by pi∗p_{i^*}? The choice i∗i^* is a step function of zz: it has zero gradient almost everywhere. The factor pi∗p_{i^*} is smooth in zz. Through it, the loss can tell the router "this expert helped, give it more weight". Without it, the router would get no gradient from the main loss.

    Where it sits. In a transformer the feed-forward sublayer acts on each token separately (lesson 11). So an MoE layer can replace it with no other change: each token in the batch picks its own experts. Attention stays dense. Mixtral replaces every feed-forward sublayer; GShard and most Switch models replace every other one; DeepSeek-V3 replaces all but the first three.

    Interactive figure: loading…

    Drag the router vectors. Here the colors name the four experts: blue, orange, aqua and violet. Each dot is a token, colored by its first choice; the shaded regions show where each expert wins. The bars are the loads, with the capacity CC as a dashed line. Assignments above the capacity are dropped and shown in magenta. Try one expert wins: one expert gets most tokens and many are dropped. Then set k=2k = 2 (rings show the second choice) and raise the capacity factor.

    Four new problems, one per part of section 3:

    • balance: the router can send most tokens to a few experts (3.3);
    • fixed buffers: an expert can receive more tokens than it has room for (3.4);
    • stability: the router's exponentials amplify bf16 rounding (3.5);
    • communication: experts live on different GPUs, so tokens must travel (3.6).
    Quick check

    A top-1 MoE layer outputs y=pi∗Ei∗(x)y = p_{i^*}E_{i^*}(x), with i∗=arg⁡max⁡ipii^* = \arg\max_i p_i. Why is the expert output multiplied by pi∗p_{i^*} instead of being used alone?

    3. Definitions and main results #

    3.1 The top-k MoE layer #

    Notation, used in the rest of the lesson:

    • TT tokens in the batch (batch × sequence, flattened), model width DD, EE experts, kk experts per token;
    • expert ii is a function Ei:RD→RDE_i: \mathbb{R}^D \to \mathbb{R}^D (with an index; without one, EE is the number of experts);
    • xt=LN⁡(ht)x_t = \operatorname{LN}(h_t) is the normalized residual stream hth_t of token tt (pre-LN, lesson 05);
    • the router has a weight Wr∈RD×EW_r \in \mathbb{R}^{D \times E} and no bias; logits zt=xtWr∈REz_t = x_t W_r \in \mathbb{R}^E; probabilities pt=softmax⁡(zt)p_t = \operatorname{softmax}(z_t) with entries pt,ip_{t,i};
    • StS_t is the set of the kk largest pt,ip_{t,i};
    • gate weights gt,ig_{t,i} for i∈Sti \in S_t; the output is yt=∑i∈Stgt,i Ei(xt)y_t = \sum_{i \in S_t} g_{t,i}\, E_i(x_t), and the block outputs ht+yth_t + y_t.

    Two choices of gate weights are common:

    • Switch (k=1k = 1): gt,i=pt,ig_{t,i} = p_{t,i}, the raw probability.
    • Renormalized (GShard top-2, Mixtral): gt,i=pt,i/∑j∈Stpt,jg_{t,i} = p_{t,i} \big/ \sum_{j \in S_t} p_{t,j}, so the kk weights sum to 1.

    Renormalized top-kk = softmax over the top-kk logits. This is Mixtral's G(x)=Softmax⁡(TopK⁡(xWg))G(x) = \operatorname{Softmax}(\operatorname{TopK}(x W_g)), with the logits outside the top kk set to −∞-\infty. Proof: pt,i/∑j∈Stpt,j=ezt,i/∑j∈Stezt,jp_{t,i} / \sum_{j \in S_t} p_{t,j} = e^{z_{t,i}} / \sum_{j \in S_t} e^{z_{t,j}}, because the full softmax denominator ∑all jezt,j\sum_{\text{all } j} e^{z_{t,j}} cancels. The softmax is monotone, so ranking the logits or the probabilities selects the same StS_t.

    Parameter count. Let each expert be a SwiGLU network with hidden width ded_e: three matrices, 3Dde3Dd_e weights (lesson 04). Per MoE layer:

    • total expert parameters: E⋅3DdeE \cdot 3Dd_e;
    • active expert parameters per token: k⋅3Ddek \cdot 3Dd_e;
    • router: DEDE;
    • forward FLOPs per token: about 2k⋅3Dde2k \cdot 3Dd_e for the experts and 2DE2DE for the router.

    Everything else (attention, norms, embeddings) is shared and always active. So total parameters = shared + all experts, and active parameters = shared + kk experts per MoE layer.

    Interactive figure: loading…

    Pick a model, then move EE and kk. The top bar is all parameters, the bottom bar the parameters one token uses. Blue is shared (always active), violet the experts that work on the token, faint violet the experts that are stored but idle. Notice that EE changes only the top bar: memory grows, compute per token does not.

    Quick check

    Mixtral 8×7B has 32 layers. Each layer has 8 SwiGLU experts of about 176M parameters each, and top-2 routing. The shared parameters (attention, norms, embeddings, routers) are about 1.6B. About how many parameters are active per token?

    3.2 How the router learns from the main loss #

    Let L\mathcal{L} be the main loss and at,i=∂L/∂gt,i=(∂L/∂yt) Ei(xt)⊤a_{t,i} = \partial \mathcal{L} / \partial g_{t,i} = (\partial \mathcal{L} / \partial y_t)\, E_i(x_t)^\top, a number: how fast the loss changes when the weight of expert ii grows. The softmax Jacobian is ∂pt,i/∂zt,j=pt,i(1[i=j]−pt,j)\partial p_{t,i} / \partial z_{t,j} = p_{t,i}(\mathbb{1}[i = j] - p_{t,j}), where 1[⋅]\mathbb{1}[\cdot] is 1 if the condition holds and 0 otherwise (lesson 09).

    • Switch (k=1k = 1, raw gate), with i∗i^* the chosen expert: ∂L/∂zt,j=at,i∗ pt,i∗(1[j=i∗]−pt,j)\partial \mathcal{L} / \partial z_{t,j} = a_{t,i^*}\, p_{t,i^*}(\mathbb{1}[j = i^*] - p_{t,j}). If the chosen expert helped (at,i∗<0a_{t,i^*} < 0), gradient descent raises zt,i∗z_{t,i^*} and lowers the other logits. Shazeer et al. (2017) thought that k≥2k \ge 2 was needed for useful router gradients; Switch showed that k=1k = 1 works.
    • Top-1 with renormalization. Then gt,i∗=pt,i∗/pt,i∗=1g_{t,i^*} = p_{t,i^*}/p_{t,i^*} = 1 for every zz. The main loss sends no gradient to the router at all.
    • Top-2, renormalized. gt,1=s(zt,1−zt,2)g_{t,1} = s(z_{t,1} - z_{t,2}) (with ss the logistic sigmoid) and gt,2=1−gt,1g_{t,2} = 1 - g_{t,1}. The gradient with respect to the logit difference is (at,1−at,2) gt,1gt,2(a_{t,1} - a_{t,2})\, g_{t,1} g_{t,2}: the router learns which of the two chosen experts is better. It learns nothing about the experts it did not choose.

    3.3 Collapse and the Switch load-balancing loss #

    Collapse. Shazeer et al. (2017) observed that the router "tends to converge to a state where it always produces large weights for the same few experts. This imbalance is self-reinforcing": the favored experts get more tokens, so more gradient updates, so they improve and are chosen even more. Unused experts are wasted parameters. On many GPUs the busiest expert also sets the step time.

    Definition (Switch load-balancing loss). For a batch of TT tokens and top-1 routing,

    Laux=α E∑i=1EfiPi,fi=1T∑t1[arg⁡max⁡jpt,j=i],Pi=1T∑tpt,i.\mathcal{L}_{\text{aux}} = \alpha\, E \sum_{i=1}^{E} f_i P_i, \qquad f_i = \frac{1}{T}\sum_t \mathbb{1}\big[\arg\max_j p_{t,j} = i\big], \qquad P_i = \frac{1}{T}\sum_t p_{t,i}.

    fif_i is the share of tokens dispatched to expert ii: a count. PiP_i is the mean router probability of expert ii. Switch uses α=10−2\alpha = 10^{-2}, chosen from a sweep over 10−110^{-1} to 10−510^{-5}: large enough to balance quickly, small enough not to disturb the main loss.

    Top-kk version. fif_i is the share of the TkTk assignments that go to expert ii, so ∑ifi=1\sum_i f_i = 1 again. Conventions differ by a factor kk: the Hugging Face transformers Mixtral code counts all kk choices and divides by TT, so its uniform value is kk.

    Facts.

    1. Uniform routing gives α\alpha. With fi=Pi=1/Ef_i = P_i = 1/E: αE⋅E⋅(1/E2)=α\alpha E \cdot E \cdot (1/E^2) = \alpha. The factor EE makes this value independent of the number of experts.
    2. A collapsed router gives αE\alpha E. If every token has pt=(1,0,…,0)p_t = (1, 0, \dots, 0), then f1=P1=1f_1 = P_1 = 1 and the loss is αE\alpha E.
    3. Why both ff and PP. ∑iPi=1\sum_i P_i = 1 always, so a loss linear in PP alone would be constant. And ff alone has no gradient: it counts argmax decisions. The product weights each expert's mean probability by its real load, and the gradient flows through PP only.

    Theorem (gradient of the load-balancing loss). Treat ff as a constant (it has zero gradient almost everywhere). Then

    ∂Laux∂zt,j=αET  pt,j (fj−fˉt),fˉt=∑ifi pt,i.\frac{\partial \mathcal{L}_{\text{aux}}}{\partial z_{t,j}} = \frac{\alpha E}{T}\; p_{t,j}\,\big(f_j - \bar f_t\big), \qquad \bar f_t = \sum_i f_i\, p_{t,i}.

    Proof.

    1. Only PiP_i depends on zt,jz_{t,j}, and only through token tt: ∂Pi/∂zt,j=1T ∂pt,i/∂zt,j\partial P_i / \partial z_{t,j} = \frac{1}{T}\, \partial p_{t,i} / \partial z_{t,j}.
    2. Softmax Jacobian: ∂pt,i/∂zt,j=pt,i(1[i=j]−pt,j)\partial p_{t,i} / \partial z_{t,j} = p_{t,i}(\mathbb{1}[i = j] - p_{t,j}).
    3. Sum over ii: ∂Laux∂zt,j=αET∑ifi pt,i(1[i=j]−pt,j)=αET(fjpt,j−pt,j∑ifipt,i)\frac{\partial \mathcal{L}_{\text{aux}}}{\partial z_{t,j}} = \frac{\alpha E}{T}\sum_i f_i\, p_{t,i}(\mathbb{1}[i = j] - p_{t,j}) = \frac{\alpha E}{T}\big(f_j p_{t,j} - p_{t,j}\sum_i f_i p_{t,i}\big).
    4. Factor out pt,jp_{t,j}. ■\blacksquare

    Reading the formula.

    • fˉt\bar f_t is the load that token tt "sees", averaged with its own probabilities. A descent step lowers zt,jz_{t,j} when expert jj is busier than that (fj>fˉtf_j > \bar f_t) and raises it when expert jj is less busy. Probability moves from overloaded to underloaded experts.
    • The factor pt,jp_{t,j} means that a token mainly changes the logits of experts it already likes.
    • ∑j∂Laux/∂zt,j=αET(fˉt−fˉt)=0\sum_j \partial \mathcal{L}_{\text{aux}} / \partial z_{t,j} = \frac{\alpha E}{T}(\bar f_t - \bar f_t) = 0, as for any loss of a softmax. For the router weights (row convention, Z=XWrZ = XW_r): ∂Laux/∂Wr=X⊤G\partial \mathcal{L}_{\text{aux}} / \partial W_r = X^\top G, where row tt of GG is ∂Laux/∂zt\partial \mathcal{L}_{\text{aux}} / \partial z_t.
    Interactive figure: loading…

    Press "1 descent step" a few times. The left chart shows the dispatch shares fif_i and the mean probabilities PiP_i; the aqua line is the uniform share 1/E1/E. The right chart shows one token's probabilities and the orange gradient ETpt,j(fj−fˉt)\frac{E}{T}p_{t,j}(f_j - \bar f_t) on its logits: positive (pushed down) for the busy expert 1, negative for the idle ones. Watch the loss fall to about 1 as the loads become equal.

    It is a surrogate. Switch says the loss "is minimized under a uniform distribution", but this is not exactly true: a few very confident tokens on an underused expert can push it below α\alpha. In practice the gradient above still drives the routing toward balance. (Shazeer et al. 2017 used noisy top-kk gating and two separate balancing losses; Switch merged them into this one.)

    Quick check

    A router with E=8E = 8 experts sends every token to expert 1 with probability 1. What is E∑ifiPiE\sum_i f_i P_i (with α=1\alpha = 1), and what is its value for perfectly uniform routing?

    3.4 Expert capacity and token dropping #

    Why a fixed capacity. Switch was built for TPUs, which need static tensor shapes. Each expert gets a buffer of CC token slots, fixed before the batch is routed. The expert capacity is

    C=⌈k T⋅cfE⌉,C = \Big\lceil \frac{k\, T \cdot \mathrm{cf}}{E} \Big\rceil,

    where cf is the capacity factor: cf=1\mathrm{cf} = 1 gives exactly enough slots for perfectly balanced routing.

    Dropping. When an expert's buffer is full, further assignments to it are dropped: that expert's computation is skipped for the token. If all its assignments are dropped, the token reaches the next layer unchanged, through the residual connection (ht+0h_t + 0). For top-kk, a common priority order (GShard-style) is: all first choices in token order, then all second choices.

    The trade-off. The layer computes E⋅C≈kT cfE \cdot C \approx kT\,\mathrm{cf} slots, filled or not, so compute, activation memory and communication all grow with cf. A larger cf drops fewer tokens but pays for padding. Switch found the best speed–quality trade-off at cf 1.0 and 1.25, and with its auxiliary loss it typically dropped less than 1% of the tokens.

    Balanced on average is not enough. The load of one expert is random. For T=4096T = 4096 and E=8E = 8 it has mean 512 and standard deviation 4096⋅18⋅78≈21\sqrt{4096 \cdot \tfrac18 \cdot \tfrac78} \approx 21, and the busiest of 8 experts is about 1.5 standard deviations above the mean. So cf =1= 1 drops tokens even then.

    Dropless MoE. MegaBlocks (Gale et al. 2022) writes the expert computation as block-sparse matrix products that accept any number of tokens per expert, so no token is dropped. DeepSeek-V3 reports no dropped tokens in training or inference, thanks to its balancing (section 3.7).

    Quick check

    Top-1 routing, T=1024T = 1024 tokens, E=8E = 8 experts, capacity factor 1.25. Expert 3 receives 200 tokens. What happens?

    3.5 The router z-loss: stability in low precision #

    The problem. bf16 has 7 stored fraction bits, so neighboring bf16 numbers in [2m,2m+1)[2^m, 2^{m+1}) are 2m−72^{m-7} apart: 0.0078 in [1,2)[1, 2), but 0.5 in [64,128)[64, 128) (lesson 12). A rounding error ε\varepsilon in a logit multiplies the probability ratio of two experts by eεe^{\varepsilon}. In exact arithmetic the softmax ignores a common shift of the logits, but not after rounding: large logits carry large errors. ST-MoE (Zoph et al. 2022) links the instabilities of large sparse models to these extra exponentials in the router.

    Definition (router z-loss, ST-MoE). For TT tokens with router logits ztz_t,

    Lz=1T∑t=1T(log⁡∑j=1Eezt,j)2=1T∑tlse⁡t2.\mathcal{L}_z = \frac{1}{T}\sum_{t=1}^{T}\Big(\log\sum_{j=1}^{E} e^{z_{t,j}}\Big)^2 = \frac{1}{T}\sum_t \operatorname{lse}_t^2 .

    The total loss is LCE+cBLaux+czLz\mathcal{L}_{\text{CE}} + c_B \mathcal{L}_{\text{aux}} + c_z \mathcal{L}_z, where LCE\mathcal{L}_{\text{CE}} is the cross-entropy and cBc_B plays the role of α\alpha; ST-MoE uses cz=0.001c_z = 0.001.

    What it does.

    • Gradient: ∂Lz/∂zt,j=2Tlse⁡t pt,j\partial \mathcal{L}_z / \partial z_{t,j} = \frac{2}{T}\operatorname{lse}_t\, p_{t,j}, because ∂lse⁡t/∂zt,j=pt,j\partial \operatorname{lse}_t / \partial z_{t,j} = p_{t,j}.
    • It fixes the free shift of the softmax. Since max⁡jzt,j≤lse⁡t≤max⁡jzt,j+log⁡E\max_j z_{t,j} \le \operatorname{lse}_t \le \max_j z_{t,j} + \log E, keeping lse⁡t\operatorname{lse}_t near 0 keeps the largest logit in [−log⁡E,0][-\log E, 0], where bf16 is precise.
    • When lse⁡t>0\operatorname{lse}_t > 0 a descent step lowers all logits, most for the most likely expert. It also makes the router a little less confident.

    In ST-MoE the z-loss made all runs stable and slightly improved quality; tighter update clipping also stabilized training, but hurt quality. Earlier, Switch used selective precision (the router in float32, cast back to bf16 before the dispatch) and a 10 times smaller initialization scale. ST-MoE found selective precision alone not enough at its largest scale.

    Interactive figure: loading…

    Move the shift cc. All three logits get the same cc, so the aqua exact softmax never changes. The blue bars use the logits rounded to bf16. Try c=99c = 99: 100.3100.3 rounds to 100.5100.5 (the bf16 spacing there is 0.5) and the probabilities change by about 0.05. The z-loss lse⁡2\operatorname{lse}^2 is the only quantity that sees cc; it is 0 at the preset lse = 0.

    Quick check

    Which quantity does the router z-loss push toward 0, and why does that help?

    3.6 Expert parallelism #

    Layout. GG GPUs form an expert-parallel group. Each GPU holds E/GE/G experts and its own TgT_g tokens. The other layers (attention, norms, router) are copies on every GPU (data parallelism).

    One MoE layer, forward pass:

    1. Route. Each GPU runs the router and the top-kk on its own tokens.
    2. Dispatch (all-to-all 1). Each GPU sends every token copy to the GPU that holds its expert. An all-to-all is a collective in which every GPU sends a different piece of data to every other GPU. With a capacity, the buffers have the static shape (E,C,D)(E, C, D).
    3. Expert compute. Each GPU runs its experts, each on the batch of tokens it received.
    4. Combine (all-to-all 2). The outputs go back to the tokens' home GPUs, where they are multiplied by the gates, summed over the kk copies and added to the residual.

    The backward pass runs the same two all-to-alls in reverse (output gradients to the experts, input gradients back). So a training step has four all-to-alls per MoE layer.

    Volume. In one all-to-all a GPU sends about k Tg D bk\,T_g\,D\,b bytes (bb bytes per value). With uniform routing, the fraction (G−1)/G(G-1)/G leaves the GPU. Example: G=8G = 8, Tg=4096T_g = 4096, k=2k = 2, D=4096D = 4096, bf16 (b=2b = 2): 2⋅4096⋅4096⋅2=67.12 \cdot 4096 \cdot 4096 \cdot 2 = 67.1 MB per GPU, of which 78⋅67.1=58.7\tfrac78 \cdot 67.1 = 58.7 MB go to other GPUs, per all-to-all. This traffic scales with tokens and kk (activations), not with parameters, unlike the gradient all-reduce of data parallelism.

    What makes it slow. Stragglers: all GPUs wait for the busiest one, so imbalance costs time even without dropping. Slow links: the all-to-all crosses nodes (InfiniBand instead of NVLink). DeepSeek-V3 limits each token to at most 4 nodes ("node-limited routing") and overlaps communication with computation.

    3.7 Balancing without an auxiliary loss, and other routers #

    DeepSeek-V3. Each MoE layer has Ns=1N_s = 1 shared expert, used by every token, and Nr=256N_r = 256 routed experts, of which Kr=8K_r = 8 are active. The router computes affinities si,t=sigmoid⁡(ut⊤ei)s_{i,t} = \operatorname{sigmoid}(u_t^\top e_i), with utu_t the input of token tt and eie_i a learned vector per expert. Then:

    • selection uses biased scores: the top KrK_r of si,t+bis_{i,t} + b_i;
    • gate values use the unbiased scores: gi,t=si,t/∑j selectedsj,tg_{i,t} = s_{i,t} / \sum_{j \text{ selected}} s_{j,t};
    • after each step, bi←bi−γb_i \leftarrow b_i - \gamma if expert ii was overloaded in the batch, and bi←bi+γb_i \leftarrow b_i + \gamma if it was underloaded (γ=0.001\gamma = 0.001 for most of training, then 0).

    Why: an auxiliary loss sends gradients into the router that compete with the language-model loss (a large α\alpha hurts quality, a small one leaves imbalance). The bias has no gradient. It acts like a feedback controller on the load: it changes which experts are picked, and the mixture weights stay undistorted. DeepSeek-V3 keeps only a sequence-wise balance loss with a tiny α=0.0001\alpha = 0.0001, against extreme imbalance inside one sequence.

    Fine-grained and shared experts (DeepSeekMoE). Split each expert into mm smaller ones and activate mkmk of them: the same parameters and FLOPs, but many more combinations. Top-2 of 16 experts gives (162)=120\binom{16}{2} = 120 combinations; after a 4-way split, top-8 of 64 gives (648)≈4.4×109\binom{64}{8} \approx 4.4 \times 10^9. Shared experts hold common knowledge, so the routed experts can specialize.

    Expert-choice routing (Zhou et al. 2022). Each expert picks its top-CC tokens from the batch: perfect balance, and a token can get zero or several experts. But the choice uses later tokens of the batch, so it does not directly apply to autoregressive generation.

    3.8 MoE against dense #

    Dense FFN MoE FFN (EE experts of the same size, top-kk)
    Parameters PP E⋅PE \cdot P (+ router DEDE)
    Forward FLOPs per token 2P2P ≈2kP\approx 2kP
    Weights to store PP E⋅PE \cdot P: all experts, always
    Extra communication none 4 all-to-alls per layer per training step
    Weights read per decoding step, batch BB PP about E(1−(1−k/E)B)⋅PE\big(1 - (1 - k/E)^B\big) \cdot P
    New failure modes none imbalance, drops, router instability, overfitting when fine-tuning
    • Quality: better per FLOP (the Switch and Mixtral numbers above), worse per parameter: a dense model with the same total parameters is an upper bound for an MoE (DeepSeekMoE).
    • Fine-tuning: sparse models overfit small tasks more; Switch used dropout 0.4 inside the experts and 0.1 elsewhere ("expert dropout").
    • Decoding: E(1−(1−k/E)B)E(1 - (1 - k/E)^B) is the expected number of distinct experts that BB tokens touch under uniform routing: kk at batch 1, nearly all EE at a large batch. Decoding is limited by reading weights from memory, so a small batch reads far fewer bytes per step than a dense model of the same total size.
    • Rule of thumb (Hugging Face blog): MoE for high throughput on many accelerators; dense when memory is the limit.

    Answers to section 1. (1) The router collapses onto a few experts, and the effect feeds itself (3.3); every MoE needs a balancing mechanism (3.3, 3.7). (2) About 2%: a simulation (section 5) gives 1.65%, because the busiest expert gets about 544 tokens on average. With capacity factor 1.25 (640 slots) the drops almost vanish; with E=64E = 64 at cf 1.0 they are about 5%.

    4. Worked example #

    Setup. E=3E = 3 experts, numbered 0, 1, 2, and T=4T = 4 tokens. To make the arithmetic simple, the logits are zt=log⁡ptz_t = \log p_t, so the router probabilities are:

    token pt,0p_{t,0} pt,1p_{t,1} pt,2p_{t,2} top-1
    1 0.7 0.2 0.1 0
    2 0.6 0.3 0.1 0
    3 0.5 0.1 0.4 0
    4 0.2 0.5 0.3 1

    (a) Gates. Switch (top-1, raw): token 1 uses 0.7 E0(x1)0.7\,E_0(x_1), and token 4 uses 0.5 E1(x4)0.5\,E_1(x_4). Top-2 renormalized, token 3: experts 0 and 2, with weights 0.5/0.9=0.5560.5/0.9 = 0.556 and 0.4/0.9=0.4440.4/0.9 = 0.444. These equal ez0/(ez0+ez2)e^{z_0}/(e^{z_0} + e^{z_2}) and ez2/(ez0+ez2)e^{z_2}/(e^{z_0} + e^{z_2}): the softmax over the two chosen logits.

    (b) Load-balancing loss (α=1\alpha = 1).

    1. Top-1 counts: expert 0 gets 3 tokens, expert 1 gets 1, expert 2 gets 0. So f=(0.75,0.25,0)f = (0.75, 0.25, 0).
    2. Column means: P=(0.7+0.6+0.5+0.24,0.2+0.3+0.1+0.54,0.1+0.1+0.4+0.34)=(0.5,0.275,0.225)P = \big(\tfrac{0.7+0.6+0.5+0.2}{4}, \tfrac{0.2+0.3+0.1+0.5}{4}, \tfrac{0.1+0.1+0.4+0.3}{4}\big) = (0.5, 0.275, 0.225).
    3. ∑ifiPi=0.75⋅0.5+0.25⋅0.275+0=0.375+0.06875=0.44375\sum_i f_i P_i = 0.75 \cdot 0.5 + 0.25 \cdot 0.275 + 0 = 0.375 + 0.06875 = 0.44375.
    4. Laux=3×0.44375=1.331\mathcal{L}_{\text{aux}} = 3 \times 0.44375 = 1.331. Compare: 1 for uniform routing, 3 for a fully collapsed router.

    (c) Gradient for token 1.

    1. fˉ1=0.75⋅0.7+0.25⋅0.2+0⋅0.1=0.525+0.05=0.575\bar f_1 = 0.75 \cdot 0.7 + 0.25 \cdot 0.2 + 0 \cdot 0.1 = 0.525 + 0.05 = 0.575.
    2. f−fˉ1=(0.175,−0.325,−0.575)f - \bar f_1 = (0.175, -0.325, -0.575), and αE/T=3/4\alpha E/T = 3/4.
    3. ∂Laux∂z1=34(0.7⋅0.175,  0.2⋅(−0.325),  0.1⋅(−0.575))=(0.0919,  −0.0488,  −0.0431).\frac{\partial \mathcal{L}_{\text{aux}}}{\partial z_1} = \tfrac34\big(0.7 \cdot 0.175,\; 0.2 \cdot (-0.325),\; 0.1 \cdot (-0.575)\big) = (0.0919,\; -0.0488,\; -0.0431).

    The entries sum to 0. A descent step lowers token 1's logit for the overloaded expert 0 and raises the other two. Token 4 (fˉ4=0.275\bar f_4 = 0.275) gets (0.0713,−0.0094,−0.0619)(0.0713, -0.0094, -0.0619): it is pushed away from expert 0 as well, and mostly toward the unused expert 2.

    (d) Capacity (top-1).

    • cf =1.0= 1.0: C=⌈4/3⌉=2C = \lceil 4/3 \rceil = 2. Expert 0 receives tokens 1, 2, 3 in order, so token 3 is dropped: its MoE output is 0 and only its residual h3h_3 goes on. Slots: 3×2=63 \times 2 = 6, of which 3 are used and 3 are padding (50%).
    • cf =2.0= 2.0: C=⌈8/3⌉=3C = \lceil 8/3 \rceil = 3. Nothing is dropped, but 5 of the 9 slots (56%) are padding. A larger capacity factor is an expensive fix for imbalance.

    (e) Router z-loss. Here lse⁡t=log⁡∑jpt,j=log⁡1=0\operatorname{lse}_t = \log\sum_j p_{t,j} = \log 1 = 0 for every token, so Lz=0\mathcal{L}_z = 0. Now add 10 to every logit. The probabilities, the main loss and Laux\mathcal{L}_{\text{aux}} do not change, but lse⁡t=10\operatorname{lse}_t = 10 and Lz=100\mathcal{L}_z = 100. Its gradient for token 1 is 24⋅10⋅(0.7,0.2,0.1)=(3.5,1.0,0.5)\frac{2}{4} \cdot 10 \cdot (0.7, 0.2, 0.1) = (3.5, 1.0, 0.5): it pulls the logits back down, most for expert 0.

    (f) Counting DeepSeek-V3. From its configuration: D=7168D = 7168, expert width 2048 (SwiGLU), 256 routed experts and 1 shared expert, top-8, MoE in 58 of 61 layers.

    1. One expert: $3 \cdot 7168 \cdot 2048 = 44{,}040{,}192 \approx 44.0$M.
    2. All routed experts: $58 \cdot 256 \cdot 44.04\text{M} \approx 653.9$B.
    3. Active expert parameters per token: $58 \cdot (8 + 1) \cdot 44.04\text{M} \approx 23.0$B.
    4. The rest (attention, the 3 dense layers, embeddings, routers) is about 14.6B, used by every token. Total $\approx 671$B, and active about 37B, as reported. Only 9/257=3.5%9/257 = 3.5\% of the expert parameters work on any one token.

    5. In code #

    PyTorch's autograd checks the two gradient formulas on the worked example, and NumPy reproduces the drop rate and the counts:

    import numpy as np
    import torch
    
    p = torch.tensor([[0.7, 0.2, 0.1], [0.6, 0.3, 0.1],
                      [0.5, 0.1, 0.4], [0.2, 0.5, 0.3]], dtype=torch.float64)
    T, E = p.shape
    
    # (b), (c) the Switch loss and its gradient; f is a count, so it carries no gradient
    z = p.log().requires_grad_()
    probs = z.softmax(-1)
    f = torch.bincount(probs.argmax(-1), minlength=E).double() / T
    aux = E * (f * probs.mean(0)).sum()
    aux.backward()
    fbar = (p * f).sum(1, keepdim=True)
    print(round(aux.item(), 4), z.grad[0].numpy().round(4))
    print(torch.allclose(z.grad, (E / T) * p * (f - fbar)))
    # 1.3312 [ 0.0919 -0.0488 -0.0431]
    # True
    
    # (e) the router z-loss after a shift of +10
    z = (p.log() + 10).requires_grad_()
    z_loss = (torch.logsumexp(z, dim=1) ** 2).mean()
    z_loss.backward()
    print(round(z_loss.item(), 6), z.grad[0].numpy().round(6))
    # 100.0 [3.5 1.  0.5]
    
    # large router logits in bf16: same softmax in exact arithmetic, different after rounding
    big = torch.tensor([100.3, 100.0, 99.0])
    for zz in (big, big - 99.0):
        zb = zz.to(torch.bfloat16).float()
        print(zb.tolist(), torch.softmax(zb, 0).numpy().round(3))
    # [100.5, 100.0, 99.0] [0.547 0.331 0.122]
    # [1.296875, 1.0, 0.0] [0.496 0.369 0.136]      (exact: [0.497 0.368 0.135])
    
    # token drops of a router that is balanced on average (T = 4096, E = 8, top-1)
    rng = np.random.default_rng(0)
    for C in [512, 640]:                                 # capacity factor 1.0 and 1.25
        loads = np.stack([np.bincount(rng.integers(0, 8, 4096), minlength=8) for _ in range(2000)])
        dropped = np.maximum(loads - C, 0).sum(axis=1) / 4096
        print(f"C={C}: mean busiest load {loads.max(axis=1).mean():.0f}, dropped {100 * dropped.mean():.2f}%")
    # C=512: mean busiest load 544, dropped 1.65%
    # C=640: mean busiest load 544, dropped 0.00%
    
    # (f) DeepSeek-V3 expert parameters
    D, d_e, n_moe = 7168, 2048, 58
    expert = 3 * D * d_e                                 # SwiGLU expert: three matrices
    print(expert, round(n_moe * 256 * expert / 1e9, 1), round(n_moe * (8 + 1) * expert / 1e9, 1))
    # 44040192 653.9 23.0
    

    The notebook builds the full layer and checks these formulas with exercises.

    6. Where it shows up #

    • Language models. Sparse MoE layers in Switch (up to 1.6T parameters and 2,048 experts), ST-MoE, Mixtral, DeepSeek-V2 and V3, and many other recent models. Vision MoE (V-MoE, Riquelme et al. 2021) applies the same layer to the tokens of a vision transformer. Hybrid models such as Jamba combine MoE layers with state-space layers (lesson 16).
    • ML for science. Physical fields mix regimes (ocean and land, tropics and poles). Spatial Mixture-of-Experts (Dryden and Hoefler, NeurIPS 2022) routes each grid location to experts and was applied to medium-range weather prediction. In an Earth-system foundation model, MoE adds capacity for many variables and regimes at a fixed FLOP cost. But a per-batch balancing loss pushes all experts to the same load, even when the regimes are not equally frequent.
    • Practice. Log per layer: the busiest expert's load divided by the mean load, the fraction of dropped tokens, the auxiliary loss and the z-loss (or the mean lse⁡t\operatorname{lse}_t). In a bf16 MoE run with loss spikes, check the router logits, the router precision and the z-loss first.

    7. Common mistakes #

    • "Mixtral 8×7B has 56B parameters." Attention, norms and embeddings are shared. It has about 46.7B parameters, and 12.9B are active per token.
    • Renormalizing a top-1 gate. The gate becomes exactly 1, and the main loss no longer trains the router.
    • Mixing conventions for top-kk. ff normalized by TkTk gives 1 for uniform routing; normalized by TT it gives kk. Check the uniform value of your implementation.
    • "cf = 1 is enough when routing is balanced on average." Random fluctuations alone drop about 2% of the tokens at T=4096T = 4096, E=8E = 8, and more with more experts. Raising cf to fix imbalance pays for padding; fix the balance instead.
    • "MoE saves memory." It saves FLOPs. All experts must be stored.
    • A bf16 router with no protection. Use a float32 router, a z-loss, or both.
    • A balancing coefficient that is too large. It competes with the main loss and hurts quality.

    8. Check yourself #

    1. A Switch-style layer with top-1 routing renormalizes its gate. What gradient does the main loss send to the router, and what does the router learn?
      AnswerNone. The gate is pi∗/pi∗=1p_{i^*}/p_{i^*} = 1 for all logits, and the argmax is piecewise constant. Only the auxiliary loss trains the router, so it learns balance but not which expert is good for which token. Switch uses the raw pi∗p_{i^*} for this reason.
    2. With E=64E = 64 experts, what is E∑ifiPiE\sum_i f_i P_i for uniform routing, and for a router that gives every token the probability vector (1,0,…,0)(1, 0, \dots, 0)?
      AnswerUniform: 64⋅64⋅(1/64)2=164 \cdot 64 \cdot (1/64)^2 = 1. Collapsed: f1=P1=1f_1 = P_1 = 1, so 64⋅1⋅1=6464 \cdot 1 \cdot 1 = 64.
    3. T=2048T = 2048 tokens, E=64E = 64 experts, top-2, capacity factor 1.25. Give the capacity, the total number of slots, and the smallest possible fraction of empty slots.
      AnswerC=⌈2⋅2048⋅1.25/64⌉=80C = \lceil 2 \cdot 2048 \cdot 1.25 / 64 \rceil = 80. Slots: 64⋅80=512064 \cdot 80 = 5120 for 2⋅2048=40962 \cdot 2048 = 4096 assignments. Even with perfect balance, 1024 slots (20%) are empty.
    4. Derive ∂Lz/∂zt,j\partial \mathcal{L}_z / \partial z_{t,j} for Lz=1T∑tlse⁡t2\mathcal{L}_z = \frac{1}{T}\sum_t \operatorname{lse}_t^2. Why does the z-loss fix the free shift of the softmax?
      Answer∂lse⁡t/∂zt,j=pt,j\partial \operatorname{lse}_t/\partial z_{t,j} = p_{t,j}, so the gradient is 2Tlse⁡t pt,j\frac{2}{T}\operatorname{lse}_t\, p_{t,j}. Adding cc to all logits of token tt leaves ptp_t and the other losses unchanged but turns lse⁡t2\operatorname{lse}_t^2 into (lse⁡t+c)2(\operatorname{lse}_t + c)^2. The z-loss prefers lse⁡t≈0\operatorname{lse}_t \approx 0, which keeps the largest logit in [−log⁡E,0][-\log E, 0], where bf16 rounding is small.
    5. DeepSeek-V3-style balancing with 4 experts. In one step the loads are 40%, 30%, 20% and 10% of the assignments, and γ=0.001\gamma = 0.001. How do the biases change, and why is the bias not used in the gate value?
      AnswerThe mean load is 25%. Experts 1 and 2 are overloaded: bi←bi−0.001b_i \leftarrow b_i - 0.001. Experts 3 and 4 are underloaded: bi←bi+0.001b_i \leftarrow b_i + 0.001. The bias only changes which experts are selected; the gate uses the original affinity, so the mixture weights are not distorted and no balancing gradient competes with the main loss.
    6. A Mixtral-style layer (E=8E = 8, top-2) decodes with batch 1 and with batch 16. How many experts per layer are read on average (uniform routing)? What does that mean for memory bandwidth?
      AnswerBatch 1: 2 experts, a quarter of the expert weights. Batch 16: 8(1−0.7516)≈7.928(1 - 0.75^{16}) \approx 7.92, nearly all of them. Decoding is limited by reading weights, so at batch 1 the MoE reads far fewer bytes per step than a dense model of the same total size; at batch 16 it reads almost all of its weights, but this cost is shared by 16 tokens.
    Recall cardsAnswer in your head first, then reveal. Add the cards to your review deck to see them again just before you would forget.
    1. Write the output of a top-kk MoE layer for token tt.

    2. Why is an expert's output multiplied by its gate value?

    3. Mixtral 8×7B: total and active parameters?

    4. Write the Switch load-balancing loss.

    5. What is the difference between fif_i and PiP_i in the balance loss?

    6. Gradient of the Switch loss with respect to the router logit zt,jz_{t,j}?

    7. Formula for the expert capacity?

    8. What happens to a token whose expert is full?

    9. What does the router z-loss penalize, and what is its gradient?

    10. How many all-to-alls does one MoE layer need per training step, and why?

    Open the review →

    9. Interview angle #

    • "Mixtral is 8×7B: how many parameters, how many active?" — About 47B and 13B; only the feed-forward sublayers are replicated.
    • "Why does a router collapse, and how do you prevent it?" — Rich-get-richer feedback; the Switch loss, noisy top-kk, DeepSeek-V3's biases, expert-choice routing.
    • "Derive the gradient of the Switch loss." — αETpt,j(fj−fˉt)\frac{\alpha E}{T} p_{t,j}(f_j - \bar f_t); ff is a constant count.
    • "What limits MoE training on many GPUs?" — Four all-to-alls per layer per step, stragglers, bandwidth between nodes.
    • "Your bf16 MoE run has loss spikes. What do you check?" — Router logit size (lse⁡\operatorname{lse}), the z-loss, a float32 router, the initialization scale, per-expert load and drop rate.

    10. Practice #

    The notebook for this lesson runs in Colab. It has five exercises, each with a check cell and a folded solution, and two demos:

    1. Write top_k_gating (raw and renormalized gates) and check it on the worked example.
    2. Write a sparse MoE forward pass with SwiGLU experts and check it against a dense reference.
    3. Write the Switch load-balancing loss and check its gradient against the formula of section 3.3.
    4. Write capacity-limited dispatch with the GShard priority order (top-1 and top-2).
    5. Write the router z-loss and check its value and gradient.

    Demos: a tiny MoE trained with and without the balancing loss (watch one expert die without it), and the parameter counts of Mixtral 8×7B and DeepSeek-V3.

    11. Watch and read #

    Top-1 routing, the capacity factor and the load-balancing loss of sections 3.1–3.4.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The Switch authors on routing, capacity, selective precision and fine-tuning sparse models.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The top-2 renormalized router and the parameter count of section 3.1.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play