Routing tokens to experts
The router of §13.1 has a problem that sounds fatal. It makes a hard, discrete decision (these two experts, not the other six), and a hard decision has no gradient. Nudge a router weight by a tiny amount and, almost always, the same two experts win, so the derivative of "which experts" with respect to the router is zero almost everywhere. How does the router learn anything?
And a second problem waits on the hardware side. A GPU wants to know the shape of a matrix product before it runs it. Routing decides, token by token, how many rows each expert's product will have, and nothing stops the router from sending half the batch to one expert. This section solves both problems. It derives how gradients reach the router, defines the gating functions real models use, introduces expert capacity and the token dropping that comes with it, describes the alternative where experts choose tokens, and builds the sort, gather, and scatter implementation that every fast MoE kernel is organized around.
How a token chooses experts
For a batch we flatten all sequences into tokens, with row . The router is a matrix for experts, and it produces one logit per token and expert,
Each token keeps the set of its largest logits. Then the logits become gates , the weights in . Three conventions are in use, and they are not interchangeable.
- Top-k, then softmax. for . Shazeer et al. (2017) and Mixtral use this. The gates of each token sum to 1.
- Softmax, then top-k. over all experts and for , without renormalizing. The Switch Transformer (with ) and DeepSeek-V2 use this. The gates of a token sum to less than 1.
- Sigmoid, then top-k, then normalize. , and the chosen values are divided by their sum. DeepSeek-V3 uses this, with a twist for load balancing that §13.3 and §13.5 explain.
The first two are closer than they look. Renormalizing the full softmax over the chosen set gives back the first convention exactly, because the full partition function cancels,
In plain words, "softmax over the chosen logits" and "softmax over everything, then renormalize the chosen ones" are the same function. The only real choice is whether to renormalize.
That choice matters most at . With renormalization, a single chosen expert always gets gate , so the output does not depend on the router's logits at all, and the router receives no gradient from the language-modeling loss. Shazeer et al. conjectured that routing to at least two experts was needed for useful router gradients. Fedus et al. (2022) showed that works if the gate is the unnormalized probability , because then the output is scaled by how confident the router was, and that scale has a gradient.
A top-k router
Implement top_k_router(logits, k) for router logits of shape (T, E). Return (idx, gates), both of shape (T, k). idx[t] holds the indices of token 's largest logits in descending order of logit (break ties by the smaller expert index), and gates[t] is the softmax over just those logits, in the same order. It must be numerically stable for logits in the thousands, and must not loop over tokens.
How the router gets a gradient
The choice of experts has no gradient, but the gates do, and that is enough. Fix one token and drop its index. Let the chosen set be , the gates , the expert outputs , and the upstream gradient , a row vector the size of the output. Since , each gate's gradient is a dot product,
The gates are a softmax over the chosen logits, whose Jacobian (§0.2) is for . A logit outside does not appear in any gate, so as long as a small change does not alter the selection, its derivative is zero. The chain rule gives, for ,
In plain words, among the experts it tried, the router turns up the ones whose outputs pointed more downhill than the gate-weighted average, and turns down the others. It learns nothing at all about the experts it did not try.
That second sentence is the heart of routing. The router is in the position of a gambler who only sees the payoff of the machines it plays. If one expert happens to be chosen often early on, it trains more, gets better, gets chosen even more, and the others never get a chance to show what they could do. This is why routers are given noise, auxiliary losses, or bias corrections to keep exploring, the subject of §13.3. The gradient into follows as usual, , and the gradient into each expert's weights is the expert's ordinary backward pass with its upstream gradient scaled by the gate, .
The two agree, and the four unchosen experts get exactly zero.
The unnormalized convention changes this picture in one instructive way. With taken from the full softmax over all experts, every logit appears in every gate through the partition function, and the Jacobian holds for all , so
An unchosen expert now receives a gradient, but it is , the same signal for every unchosen expert scaled only by its probability. It says how much the chosen experts helped, not whether the skipped ones would have. When the chosen experts helped (), gradient descent lowers every unchosen logit, which sharpens the router toward the choice it already made. In plain words, the full softmax lets the router learn how confident to be, which is what keeps trainable, but it still learns nothing about the quality of experts it did not try.
The gradient of the loss with respect to the router logits
Implement router_logit_grad(r, F_out, G, k) for one token, with logits r of shape (E,), every expert's output F_out of shape (E, m), and upstream gradient G of shape (m,). The layer uses top-k then softmax gating. Return of shape (E,), zero for experts that were not chosen.
Noisy top-k gating
Shazeer et al. (2017) added noise to the logits before choosing, with a learned, input-dependent scale,
with and to keep the scale positive. The top of are chosen and softmaxed. In plain words, the router sometimes tries an expert just outside its current top , so that expert gets a chance to prove itself, and the model can learn how much exploration each token needs.
The noise had a second job. It makes "is expert in the top ?" a random event with a smooth probability, computable with the normal CDF, so Shazeer et al. could write a differentiable estimate of each expert's load and penalize imbalance (§13.3). Later Transformer MoEs kept the exploration but made it lighter. Switch Transformers compared several options in an appendix and settled on input jitter, multiplying the router's input by random factors near 1, which scored slightly better than plain argmax and clearly better than sampling from the softmax. Balancing itself moved to auxiliary losses and bias terms. The cell shows the exploration effect on one token whose third-best expert is just behind the second.
Without noise the token would use experts 0 and 1 every time. With noise, expert 2 is in the top two about half the time and receives gradient on those steps, while experts 3 and 4, far behind, are almost never tried. The noise explores near the boundary, not at random.
Expert capacity
Now the hardware problem. On an accelerator each expert processes its tokens as one matrix product, and the simplest fast implementation gives every expert a buffer of fixed size, its capacity. With tokens, choices each, and experts, a perfectly balanced router would give each expert tokens. Capacity is that fair share times a slack factor, the capacity factor ,
In plain words, each expert agrees to take at most times its fair share of tokens. (Implementations differ in rounding, and GShard computes it per group of tokens rather than per batch.)
A token that arrives at a full expert is dropped for that expert. It is not lost. Its contribution from that expert is zero, and because the MoE layer sits inside a residual connection, the token's vector passes to the next layer unchanged, as if the FFN had output zero. If a token's other choices had room, it still gets their (now smaller) sum. Every drop is computation the layer skipped for that token, so the capacity factor is a real trade-off. Larger drops fewer tokens but pads buffers with empty slots that cost memory, compute, and communication. Switch Transformers trained with or , found the lower factors gave the better speed and quality trade-off, and report that typically fewer than 1% of tokens were dropped. ST-MoE (Zoph et al. 2022) used 1.25 in training and 2.0 at evaluation.
Who gets dropped when an expert overflows? The usual rule is arrival order, the position of the token in the flattened batch, with first choices served before second choices. GShard places every token's first choice, in token order, and only then the second choices into whatever room is left. Riquelme et al. (2021) proposed batch prioritized routing, which serves tokens with larger gates first, so the tokens the router is most confident about are the last to be dropped.
A worked example
Eight tokens, experts, , , so . The router sends tokens 0 to 7 to experts . Expert 0 receives tokens 0 and 2 into its two slots, and tokens 3 and 5 find it full, so they are dropped. Experts 1, 2, and 3 receive two, one, and one tokens, all accepted, leaving two empty slots. Two of eight tokens (25%) are dropped and two of eight slots are wasted padding, even though only one expert was overloaded. The widget below runs the same bookkeeping on 24 tokens, with a router you can skew toward one expert.
Even a perfectly fair router drops tokens, because randomness alone makes some experts busier than others. If each token picks one of experts uniformly and independently, an expert's load is binomial with mean and standard deviation about , and the expected fraction dropped is
The fluctuations shrink relative to the mean as grows, so big routing groups drop less at the same capacity factor.
A uniform random router at still drops 13% of a 64-token group and 1.6% of a 4,096-token group. Real routers are less balanced than uniform, so their drop rates are higher, which is why balancing (§13.3) and capacity go together.
Capacity-limited dispatch with token dropping
Implement dispatch(idx, E, cap) for the router's choices idx of shape (T, k) (column 0 is each token's first choice). Return pos, an integer array of shape (T, k) holding the slot each assignment gets in its expert's buffer (0 to cap - 1), or -1 if it is dropped. Follow GShard's rule. All first choices are placed before any second choice, and so on, and within one choice rank tokens are served in arrival order (by token index). An expert accepts assignments until it holds cap of them.
Interviewers often ask for the loop first and then "now do it without a loop over tokens". The tests accept either, but try the vectorized version with a cumulative sum.
Experts choose tokens
Token-choice routing lets every token pick its favorite experts and then deals with the crowding. Zhou et al. (2022) turned it around. In expert-choice routing every expert picks its favorite tokens. Compute the token-to-expert affinities (a softmax over experts for each token), and let each expert take the tokens with the largest scores in column ,
where plays the role of the capacity factor (the paper's default is , matching the compute of top-2 token choice). In plain words, each expert fills its buffer with the tokens that want it most.
Three consequences follow. Load is perfectly balanced by construction, since every expert processes exactly tokens, so no balancing loss is needed and no buffer slot is wasted. Tokens receive a variable number of experts, from zero to , which spends more compute on tokens many experts find relevant. Zhou et al. report that most tokens in their analysis went to one or two experts, 23% to three or four, and about 3% to more than four, and that expert choice improved training convergence by more than 2x over Switch and GShard routing at the same compute. And tokens can go unprocessed, if no expert ranks them in its top .
The catch is causality. Whether token is chosen depends on how the other tokens in the batch score, including tokens that come later in the same sequence. In training a decoder that leaks information about the future into the present (by counting the possible routing patterns, Wang et al. 2024 show that a 9-layer model with 16 experts and two per token could leak more than 50 bits per token, enough for a token to learn which token comes next), and at inference the future tokens do not exist yet, so the routing cannot be reproduced token by token. Zhou et al. note the method may not apply directly to autoregressive generation. That is why expert choice is used in encoders (Komatsuzaki et al. use it in the T5 encoder and ViT, §13.5), while decoder-only LLMs route by token choice.
A third design enforces balance exactly from the token side. BASE layers (Lewis et al. 2021) treat routing during training as a linear assignment problem, giving every expert exactly tokens of the batch while maximizing the total affinity, and route each token greedily to its best expert at inference. The training assignment has the same dependence on the rest of the batch as expert choice. Clark et al.'s S-BASE router (§13.5) approximates the assignment with a few Sinkhorn iterations, which rescale the rows and columns of the affinity matrix until every expert's column sums to its share.
Expert-choice routing
Implement expert_choice(S, cap) for affinities S of shape (T, E). Each expert takes the cap tokens with the largest S[:, i] (ties go to the smaller token index). Return (tok, gates, n_experts) where tok of shape (E, cap) lists expert 's chosen tokens in descending order of score, gates of shape (E, cap) holds the matching scores, and n_experts of shape (T,) counts how many experts chose each token. No Python loop over tokens.
Sort, gather, compute, scatter
A loop over tokens is hopeless on a GPU, and even the loop over experts in §13.1 hides a cost, because each nonzero call scans the whole batch. Fast MoE layers reorganize the batch so that each expert's tokens sit next to each other in memory, run each expert on its contiguous block, and then put the results back. Here are the steps, with shapes, for tokens and choices each.
- Route. Get
idxandgates, both . Flatten to assignments, withexpert = idx.ravel()andtoken = repeat(arange(T), k). - Sort.
order = argsort(expert, stable)groups the assignments by expert.counts = bincount(expert, minlength=E)gives each expert's number of rows, andoffsets = [0, cumsum(counts)]gives where each expert's block starts. - Gather.
Xs = X[token[order]], shape . This is the permutation of tokens into expert order. Each token appears times. - Compute. Expert processes rows
offsets[i]tooffsets[i+1]ofXs. On a GPU these products of different heights run as one grouped GEMM (§13.4). - Scatter. Multiply each output row by its gate and add it back to its token's row,
out[token[order]] += gates[order] * Ys. Because a token appears times, this must be a scatter-add, not an assignment.
The backward pass mirrors it. The gradient of a gather is a scatter-add, and the gradient of a scatter-add is a gather, so autograd handles both. With capacity, step 2 also discards assignments whose slot is (or pads each block to rows), which is what turns this into the fixed-shape version.
A vectorized MoE layer
This is the MoE coding question in its most common form. Implement moe_forward(X, W_r, experts, k) for tokens X of shape (T, d), router W_r of shape (d, E), and a list of SwiGLU experts, each a tuple (W_g, W_u, W_d) with . Use top-k then softmax gating (ties to the smaller expert index) and no capacity limit. Return the output of shape (T, d). You may loop over experts but not over tokens, and an expert that receives no tokens must not break anything. The tests compare with a token-by-token reference and count the lines your function executes on 2,000 tokens.
In PyTorch
The same algorithm in PyTorch uses argsort, bincount, split for the per-expert blocks, and index_add_ for the scatter. Autograd differentiates through the gather and scatter, and the router gets its gradient through gates, exactly as derived above.
| routing | who chooses | load balance | tokens per expert | experts per token | causal decoding | used by |
|---|---|---|---|---|---|---|
| top-k token choice, softmax over chosen | token | needs help (§13.3) | variable, capped | exactly (fewer if dropped) | yes | Shazeer 2017, Mixtral |
| top-1, unnormalized softmax | token | needs help | variable, capped | 1 or 0 | yes | Switch |
| sigmoid, bias-adjusted top-k | token | bias terms | variable, no dropping | exactly | yes | DeepSeek-V3 |
| expert choice | expert | perfect by construction | exactly | 0 to | no | Zhou 2022, upcycled T5 encoders |
| balanced assignment | a solver over the batch | perfect in training | exactly | 1 | only with greedy routing | BASE, Lewis 2021 |
| hash routing | fixed function of the token id | good if the hash is balanced | variable | yes | Roller 2021 |
Read more
- Shazeer et al. (2017). Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer. Section 2.1 defines noisy top-k gating.
- Lepikhin et al. (2020). GShard: Scaling Giant Models with Conditional Computation and Automatic Sharding. Top-2 gating with capacity, groups, and the first-choices-first dispatch order, in Algorithm 1.
- Fedus, Zoph & Shazeer (2022). Switch Transformers: Scaling to Trillion Parameter Models with Simple and Efficient Sparsity. Top-1 routing, the capacity factor, and its effect on speed and quality.
- Zhou et al. (2022). Mixture-of-Experts with Expert Choice Routing. Experts pick tokens, and what that buys and costs.
- Riquelme et al. (2021). Scaling Vision with Sparse Mixture of Experts. Batch prioritized routing, which drops the least confident tokens first.
- Lewis et al. (2021). BASE Layers: Simplifying Training of Large, Sparse Models. Routing as a balanced assignment problem, with no balancing loss and no dropped tokens.
- Gale et al. (2022). MegaBlocks: Efficient Sparse Training with Mixture-of-Experts. Dropless MoE with block-sparse kernels, the endpoint of the sort and gather approach.