Expert Networks: MoE Architecture & FFN Implementation

Michael BrenndoerferNovember 13, 202557 min read

Explains how expert networks power Mixture of Experts models. Examines FFN-based experts, capacity factors, expert counts, and transformer placement strategies.

Language AI HandbookAll chapters
Part 33 of 67Mixture of Experts
Chapter 240Expert Networks
Reading the full chapter · 12 sections · 57 min12 sections · 57 min

All 12 sections · 57 min read

Expert NetworksFull chapter · Sections in this chapter1 / 12 ↓
Track your reading progress

Sign in to mark sections as read and track your learning journey

Sign in →
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Expert Networks

In the previous chapter, we explored how sparse models achieve computational efficiency by activating only a subset of parameters for each input. The Mixture of Experts (MoE) architecture realizes this principle through a collection of specialized subnetworks, each designed to handle different aspects of the input distribution. These subnetworks, called experts, form the computational backbone of MoE models. Understanding their structure, their relationship to the classical feed-forward network, and the engineering constraints that govern how they handle batches of tokens is the focus of this chapter.

Think of the overall MoE layer as a library staffed by specialists. When a patron arrives with a question, a receptionist (the router) glances at their request and directs them to the most relevant expert in the building. Each expert in that library has spent years developing deep knowledge in a specific domain: one handles legal questions, another handles medical topics, another handles creative writing, and so on. The patron walks directly to the right desk, gets a highly informed answer, and leaves. No single patron talks to every specialist; each consultation draws on exactly the expertise relevant to that person's needs. The MoE layer works the same way: each token is a patron, each expert is a domain specialist, and the router ensures that every token reaches the expert most likely to transform it usefully.

This analogy captures the aspirational design of MoE, but the implementation details matter enormously. What exactly is the internal structure of each expert? Why is the feed-forward network the natural choice rather than, say, a small attention module? How does the model manage the computational challenge that different tokens take different paths through the network during batch processing? And how many experts should a model have, and where in the transformer stack should they be placed? These questions have real consequences for model quality, memory requirements, and training stability. Working through them carefully gives you the conceptual toolkit to understand every major MoE model in the literature, from Switch Transformer to Mixtral to DeepSeek.

A useful frame throughout this chapter is the tension between capacity and computation. Every parameter we add to a neural network increases its capacity to model complex patterns in data. But every parameter we invoke during a forward pass increases the computational cost of that pass. In dense transformers, these two quantities are locked together: more capacity means more compute, always. MoE breaks this lock. By maintaining a large pool of expert weights and activating only a small fraction per token, the architecture decouples the model's total parameter count from its per-token floating-point operations. The experts are the mechanism by which this decoupling is achieved, so understanding their design is central to understanding why MoE works at all.

This chapter examines what experts are, how they are structured, and the architectural decisions that determine their effectiveness. We will see why the feed-forward network became the dominant choice for expert implementation, how to manage the flow of tokens through experts during batch processing, and where to place expert layers within the transformer architecture. We will also examine the parameter-counting arithmetic that makes MoE scaling attractive, work through a numerical example of capacity-constrained routing, and discuss the practical limitations that arise when deploying expert-based models. By the end, you will have a thorough understanding of the building blocks that make sparse expert models function.

Historical Context

The idea of using multiple specialized subnetworks dates to the Mixture of Experts model introduced by Jacobs, Jordan, Nowlan, and Hinton in 1991, which combined small networks using a learned gating function. That early work operated on shallow models with modest expert counts. The concept lay relatively dormant for decades until the era of large-scale transformers made the compute-capacity tradeoff acutely relevant. Shazeer et al. (2017) revived MoE at scale with their "Outrageously Large Neural Networks" paper, integrating sparsely-gated expert layers into LSTM sequence models and showing dramatic efficiency gains. The leap to transformers came with Switch Transformer (Fedus et al., 2021), which replaced every FFN layer with a single-expert routing system and scaled to over a trillion parameters. Since then, GShard and GLaM have refined these ideas, as have Mixtral and DeepSeek-MoE. Their work improved expert design and routing while addressing load balancing, and MoE has become a foundational pattern in frontier-scale language modeling.

What Is an Expert?

Before the implementation details, define what an expert is and its role in the architecture. The term "expert" suggests specialization, and MoE experts often develop distinct functional roles during training. Some experts might specialize in processing mathematical expressions, others in handling conversational text, and still others in domain-specific terminology. This specialization emerges from the routing mechanism rather than being explicitly programmed: gradient descent makes certain experts more effective for particular input types, and the routing mechanism learns to use that structure. No engineer hand-labels which expert should handle which kind of input; the division of labor arises from optimizing the training objective.

An expert is a small neural-network component, typically the size of a standard feed-forward block, rather than a separate model or reasoning system. It receives a token representation, applies a learned non-linear transformation, and returns a representation of the same dimensionality. The expert's output is weighted by the router's confidence score and added to the residual stream, as a standard FFN output would be. To the surrounding transformer, an MoE layer with sparse expert routing resembles a dense FFN layer: a vector goes in and a transformed vector comes out. The difference is that a dense layer uses one shared FFN, whereas an MoE layer uses one of NN specialized FFNs.

Experts address a representational as well as a computational problem. A single dense FFN must learn one set of weight matrices that works reasonably well for all possible tokens. That generalist weight set may be adequate for common patterns but suboptimal for rare or specialized ones. An MoE layer can maintain weight sets tuned to different subsets of the token distribution. Each expert can overfit, in a productive sense, to transformations useful for its assigned token types.

Expert Network

An expert is an independent neural network component within an MoE layer. Each expert processes tokens routed to it, creating output representations that are combined based on routing weights. The expert applies a learned non-linear transformation that maps a token's hidden representation to a new representation of the same dimensionality, ready to be added back into the residual stream.

An expert is a transformation function that maps an input vector to an output vector within the same space. A token representation therefore emerges transformed but compatible with the rest of the model's architecture:

Ei:Rd→RdE_i: \mathbb{R}^d \to \mathbb{R}^d

where:

  • EiE_i: the function representing the ii-th expert
  • Rd\mathbb{R}^d: the dd-dimensional real vector space in which token representations live
  • dd: the model's hidden dimension, which determines both input and output size

This dimensionality constraint is not incidental; it is architecturally needed. The expert's output must integrate smoothly with residual connections and subsequent layers in the transformer. If the expert produced vectors of a different size, the residual addition would be impossible. The input and output spaces are identical by design, so the expert slot can substitute for a standard FFN without any other changes to the surrounding block.

For a set of NN experts {E1,E2,…,EN}\{E_1, E_2, \ldots, E_N\}, each processes its assigned tokens independently. The key insight is that different tokens can be routed to different experts, letting the model to apply specialized processing based on input characteristics. Consider a sentence containing both technical terminology and conversational phrases: the technical terms might be routed to one expert that has learned relevant transformations for such vocabulary, while the conversational phrases flow to a different expert optimized for casual language patterns. This routing happens dynamically for each forward pass, adapting the computation to the specific characteristics of each input.

Experts as Feed-Forward Networks

The most common implementation uses standard feed-forward networks as experts. This choice is not arbitrary; it stems from a basic property of transformer FFN layers. Understanding this property reveals why FFN layers are uniquely suited for expert-based sparsification, and why attention layers, by contrast, are almost never sparsified through expert routing.

Think of the FFN layer in a transformer as a per-token function applier. It takes each token's vector representation, runs it through a two-layer nonlinear network, and returns a transformed vector, completely ignoring all other token positions. This is sometimes called "position-wise" processing, because the same function is applied independently at each position. The attention mechanism, which comes before the FFN in a standard transformer block, handles the cross-position communication: it allows token representations to incorporate information from neighboring positions. Once that communication has occurred, the FFN's job is to transform each resulting representation locally, without any further inter-token interaction.

This position-independence property is exactly what makes FFN layers good candidates for sparsification. If token at position 3 is processed entirely independently of token at position 7 during the FFN phase, then there is no reason these two tokens must use the same FFN weights. We can send token 3 to expert E2E_2 and token 7 to expert E5E_5, and neither computation interferes with the other. The same logic does not apply to attention: because attention computes a weighted sum over all positions simultaneously, you cannot route different positions to different "attention experts" without fundamentally changing the information flow. FFN layers are structurally decoupled from one another across positions, and that decoupling is the key technical reason they become the natural home for MoE routing.

The Position-Independence Property

Recall that the feed-forward network in a transformer block operates identically on each position in the sequence, processing each token in isolation. This property, sometimes called "position-wise" or "token-wise" processing, means that the FFN applies the exact same transformation to every token without any awareness of neighboring tokens. The computation for any single token xx is self-contained. Mathematically, the FFN applies a point-wise transformation:

We can write this transformation for a single token's vector xx as:

FFN(x)=W2⋅σ(W1⋅x+b1)+b2\text{FFN}(x) = W_2 \cdot \sigma(W_1 \cdot x + b_1) + b_2

where:

  • xx: the input vector corresponding to a specific token position, with dimension dmodeld_{\text{model}}
  • W1W_1: the weight matrix for the first linear transformation, mapping from Rdmodel\mathbb{R}^{d_{\text{model}}} to Rdff\mathbb{R}^{d_{\text{ff}}}
  • b1b_1: the bias vector for the first linear layer, with dimension dffd_{\text{ff}}
  • W1⋅x+b1W_1 \cdot x + b_1: the projection into the intermediate expansion dimension, before any nonlinearity
  • σ\sigma: the non-linear activation function, typically ReLU or GELU, applied element-wise
  • σ(W1⋅x+b1)\sigma(W_1 \cdot x + b_1): the activated intermediate representation, with each dimension independently passed through the nonlinearity
  • W2W_2: the weight matrix for the second linear transformation, mapping from Rdff\mathbb{R}^{d_{\text{ff}}} back to Rdmodel\mathbb{R}^{d_{\text{model}}}
  • b2b_2: the bias vector for the output layer, with dimension dmodeld_{\text{model}}

Let's trace through this computation to build intuition. The input vector xx first undergoes a linear transformation through W1W_1, which projects it from the model dimension dmodeld_{\text{model}} into a higher-dimensional intermediate space (typically four times larger). This expansion gives the network room to represent more complex combinations of the input features: in a dmodel=1024d_{\text{model}} = 1024 model with a standard four-times expansion, the intermediate representation has 4096 dimensions. The bias term b1b_1 shifts this projection, and the activation function σ\sigma introduces nonlinearity by selectively suppressing or passing through different components of the intermediate representation. The choice of activation matters here: ReLU zeroes out negative values entirely, creating hard sparsity in the intermediate activations, while GELU provides a smoother transition that most modern models prefer. Finally, W2W_2 projects the result back to the original dimensionality, with b2b_2 giving a final bias adjustment.

Why does this formula make sense? Notice that the two-layer structure is the minimal architecture that can represent arbitrary nonlinear functions given enough hidden units, a result dating back to the universal approximation theorem. The expansion-then-contraction shape (wide in the middle, narrow at the edges) is a practical implementation of this principle: the wide middle layer provides the capacity for complex representations, and the contraction back to the model dimension ensures compatibility with the residual stream. The position-independence falls directly out of the fact that W1W_1, b1b_1, W2W_2, and b2b_2 are shared parameters applied identically to each position: nowhere in this formula does the position index appear.

This position-independence is important for MoE. Because each token passes through the FFN independently, we can route different tokens to different FFN-style experts without disrupting the computational flow. If the FFN for token at position 3 has no interaction with the FFN for token at position 7, then there is no reason they must use the same FFN weights. Attention layers, by contrast, require all positions to interact simultaneously through the query-key-value mechanism. This makes them poor candidates for expert-based sparsification.

Expert FFN Architecture

Each expert in an MoE layer typically mirrors the standard transformer FFN structure, but with its own independent set of parameters. Building on the gated architectures covered in Part XV, most modern expert implementations use the SwiGLU variant. This architecture splits the input into two parallel paths: one for gating and one for features. The gate acts as a dynamic filter that modulates which information from the feature path is allowed to flow through to the output projection. This gating mechanism is inspired by long short-term memory (LSTM) cells, where gates selectively retain or forget information, but applied to the feed-forward context rather than recurrence.

To understand the motivation for gated FFNs over the simpler two-layer variant, consider what happens with ReLU activation. The ReLU activation sets negative intermediate values to zero, effectively performing hard binary gating: a neuron either fully activates or is entirely silent. SwiGLU generalizes this: instead of a fixed zero threshold, the network learns a soft, continuous gate for each dimension based on the current input. This learned gating makes the expert's behavior more adaptive and context-sensitive, which is particularly valuable in the sparse MoE setting where each expert sees only a subset of the data distribution and must be maximally expressive within its assigned domain.

The SwiGLU expert architecture computes its output through the following sequence of operations. The input token representation is projected into a higher-dimensional space along two parallel paths, and their element-wise product forms the pre-output representation:

Gate(x)=SiLU(Wgate(i)⋅x)Features(x)=Wup(i)⋅xEi(x)=Wdown(i)⋅(Gate(x)⊙Features(x))\begin{aligned} \text{Gate}(x) &= \text{SiLU}(W_{\text{gate}}^{(i)} \cdot x) \\ \text{Features}(x) &= W_{\text{up}}^{(i)} \cdot x \\ E_i(x) &= W_{\text{down}}^{(i)} \cdot (\text{Gate}(x) \odot \text{Features}(x)) \end{aligned}

where:

  • xx: the input token representation vector, with dimension dmodeld_{\text{model}}
  • Wgate(i)W_{\text{gate}}^{(i)}: the gating projection weights for expert ii, with shape (dff,dmodel)(d_{\text{ff}}, d_{\text{model}})
  • Wup(i)W_{\text{up}}^{(i)}: the feature up-projection weights for expert ii, with the same shape (dff,dmodel)(d_{\text{ff}}, d_{\text{model}})
  • SiLU\text{SiLU}: the Sigmoid Linear Unit activation function, defined as SiLU(z)=z⋅σ(z)\text{SiLU}(z) = z \cdot \sigma(z) where σ\sigma is the sigmoid function
  • Gate(x)\text{Gate}(x): the gating mask values computed by the first path, with dimension dffd_{\text{ff}}
  • Features(x)\text{Features}(x): the candidate features computed by the second path, also with dimension dffd_{\text{ff}}
  • ⊙\odot: the element-wise (Hadamard) multiplication operator, which filters features using the gate values
  • Wdown(i)W_{\text{down}}^{(i)}: the down-projection weights for expert ii, with shape (dmodel,dff)(d_{\text{model}}, d_{\text{ff}}), transforming back to the model dimension

To understand why this gated structure is valuable, consider what each component contributes. The up-projection Wup(i)xW_{\text{up}}^{(i)} x generates candidate features by expanding the input into a higher-dimensional space where the model can represent more complex patterns. Meanwhile, the gating path SiLU(Wgate(i)x)\text{SiLU}(W_{\text{gate}}^{(i)} x) computes a vector of values that can range smoothly above and below zero (since SiLU is not bounded like sigmoid), acting as a soft, content-dependent filter. Each element of this gating vector indicates how much the corresponding candidate feature should be emphasized or suppressed.

The element-wise product (⊙\odot) then filters the candidates with this gate, letting the network suppress or preserve specific features before the final projection Wdown(i)W_{\text{down}}^{(i)}. This gating mechanism gives the expert fine-grained, input-dependent control over which information flows through. Some dimensions might be nearly silenced (gate value close to zero) while others pass through strongly (large positive gate value); the decision is dynamic for each input rather than fixed by the weight matrices alone. This selective filtering allows experts to develop specialized behaviors for different input types during training.

Why does this formula make sense? Notice that the two separate projections Wgate(i)W_{\text{gate}}^{(i)} and Wup(i)W_{\text{up}}^{(i)} serve entirely different purposes despite having the same shape. The gate path learns to recognize which features in the input are relevant for the current transformation, while the up path learns what the actual feature values should be. The product of these two paths means that only features deemed relevant by the gate path are allowed to influence the output. This is fundamentally more expressive than a simple two-layer FFN where the activation function applies the same threshold rule to every input uniformly.

The key parameters defining an expert's architecture include:

  • Input/output dimension (dmodeld_{\text{model}}): Matches the transformer's hidden dimension, typically 2048 to 8192 in large models. This must be identical for all experts since they all receive the same token representations from the attention layer.
  • Intermediate dimension (dffd_{\text{ff}}): The expansion dimension within the expert. For standard FFNs this is often 4dmodel4d_{\text{model}}. For SwiGLU variants, a common choice is 83dmodel\frac{8}{3}d_{\text{model}}, which preserves the parameter count of the original FFN despite having three weight matrices instead of two (since each of the three matrices is proportionally smaller).
  • Activation function: SiLU (also called Swish) for gated variants, or GELU or ReLU for standard two-layer FFNs.

Each expert maintains its own weight matrices, completely independent of other experts. This independence is what enables specialization: expert E1E_1 can learn entirely different transformations than expert E2E_2. During training, the gradient updates for each expert depend only on the tokens routed to that expert, letting different experts to develop distinct computational roles. If expert E3E_3 only ever sees tokens from formal academic writing, its weights will be pushed by gradients shaped entirely by that sub-distribution, naturally developing transformations that work well for that register.

Parameter Counting

To evaluate the memory footprint of an MoE layer, we sum the parameters across all experts. Understanding this calculation helps you anticipate memory requirements and make informed architectural decisions. The math is straightforward, but tracing through it carefully reveals the core efficiency tradeoff of MoE.

Consider an MoE layer with NN experts, where each expert is a standard two-layer FFN with intermediate dimension dffd_{\text{ff}}. For a single expert, the parameter count breaks down into the two weight matrices and their associated bias vectors. We can compute this systematically:

Nweights=2⋅dmodel⋅dffNbiases=dff+dmodelNexpert=Nweights+Nbiases=2⋅dmodel⋅dff+dff+dmodel\begin{aligned} N_{\text{weights}} &= 2 \cdot d_{\text{model}} \cdot d_{\text{ff}} \\ N_{\text{biases}} &= d_{\text{ff}} + d_{\text{model}} \\ N_{\text{expert}} &= N_{\text{weights}} + N_{\text{biases}} \\ &= 2 \cdot d_{\text{model}} \cdot d_{\text{ff}} + d_{\text{ff}} + d_{\text{model}} \end{aligned}

where:

  • NexpertN_{\text{expert}}: the total parameter count for a single expert
  • NweightsN_{\text{weights}}: the parameter count for the weight matrices alone
  • NbiasesN_{\text{biases}}: the parameter count for the bias vectors
  • dmodeld_{\text{model}}: the hidden dimension of the model
  • dffd_{\text{ff}}: the intermediate dimension inside the expert FFN
  • 2⋅dmodel⋅dff2 \cdot d_{\text{model}} \cdot d_{\text{ff}}: parameters for the two weight matrices (W1W_1 and W2W_2), each of size dmodel×dffd_{\text{model}} \times d_{\text{ff}}
  • dff+dmodeld_{\text{ff}} + d_{\text{model}}: parameters for the bias vectors (b1b_1 of size dffd_{\text{ff}} and b2b_2 of size dmodeld_{\text{model}})

Let's trace through why the weight matrix formula has the factor of 2. The first weight matrix W1W_1 has shape (dff,dmodel)(d_{\text{ff}}, d_{\text{model}}), mapping from the model dimension to the intermediate dimension, contributing dmodel×dffd_{\text{model}} \times d_{\text{ff}} parameters. The second weight matrix W2W_2 has shape (dmodel,dff)(d_{\text{model}}, d_{\text{ff}}), mapping back from the intermediate dimension to the model dimension, contributing another dff×dmodeld_{\text{ff}} \times d_{\text{model}} parameters. Together, these two matrices account for the 2⋅dmodel⋅dff2 \cdot d_{\text{model}} \cdot d_{\text{ff}} term. The bias vectors add a relatively small number of additional parameters: dffd_{\text{ff}} for b1b_1 and dmodeld_{\text{model}} for b2b_2.

For NN experts, the total MoE layer parameters are approximately N×2×dmodel×dffN \times 2 \times d_{\text{model}} \times d_{\text{ff}}. A model with 8 experts in each MoE layer has roughly 8 times the FFN parameters of an equivalent dense model. However, if only 2 experts activate per token (the "top-2" routing scheme used by Mixtral), the compute per forward pass remains comparable to a dense model with 2 times the FFN size, which is still much less than 8 times. This is the basic tradeoff of MoE: we store many more parameters than we use for any single input, gaining model capacity without proportional computational cost. The price we pay is in memory, not arithmetic.

VisualizationOut [3]
CollapseExpand
Bar chart comparing dense FFN baseline (1x), MoE total parameters (8x, red), and MoE active parameters per token (2x, green), illustrating that active compute stays nearly constant as total parameter count grows.
Comparison of total versus active parameters as expert counts increase. While total parameters (red) scale linearly with the number of experts, active parameters per token (green) remain nearly constant, letting significant capacity scaling without proportional increases in computational cost.

Expert Capacity

When processing batches of sequences, MoE models face a unique challenge that dense transformers never encounter: different tokens route to different experts, but hardware prefers uniform workloads. GPUs and TPUs are most efficient when performing the same operation on identically-sized blocks of data. Batch matrix multiplications, in particular, assume that every element of the batch has the same shape and that no element requires extra synchronization. MoE routing violates this assumption by design: in a perfectly-functioning model, popular experts attract many tokens while niche experts process only a few. Without some mechanism to manage this variability, the hardware efficiency that motivated MoE in the first place can be partially or entirely lost to irregular computation patterns.

Think of expert capacity as a physical queue in front of each expert's desk in our library analogy. Each queue can hold at most KK patrons at a time. When a popular expert's queue is full and a new patron arrives, that patron cannot wait and instead must either go to their second-best option or handle their query on their own using only the general information they already have. This queue limit prevents the popular expert from becoming a bottleneck that makes everyone else wait, at the cost of occasional suboptimal service for tokens that get redirected or dropped. The capacity factor CC determines how long each queue can be relative to the perfectly-balanced case.

Expert capacity mechanisms manage this tension by imposing limits on how many tokens each expert can process per batch. The limits are not arbitrary restrictions: they are engineering constraints that make it possible to implement MoE layers as regular tensor operations on hardware that demands predictable memory layouts and computation graphs. Without these limits, implementing an MoE layer would require dynamic tensor shapes that change based on the routing decisions, making the computation far harder to optimize and parallelize.

The key insight is that capacity constraints create a tradeoff: tighter constraints improve hardware utilization and reduce memory overhead, but they also mean some tokens receive inferior processing (or none at all) when routing is imbalanced. Looser constraints preserve routing quality but waste memory and compute on empty buffer slots. Finding the right capacity factor is therefore a hyperparameter tuning problem with direct implications for both model quality and training efficiency.

The Batching Problem

Consider a batch of 1024 tokens being processed by an 8-expert MoE layer. If routing were perfectly balanced, each expert would receive exactly 128 tokens. In practice, some tokens cluster around certain experts while others receive few tokens. Without constraints, popular experts become computational bottlenecks while unpopular experts sit idle.

This load imbalance creates practical problems for efficient computation. Modern hardware, particularly GPUs, achieves peak throughput when performing regular, predictable operations on uniformly-sized data. When one expert must process 300 tokens while another processes only 50, we cannot fully utilize parallel processing capabilities. The expert with 300 tokens becomes a serialization point, and the expert with 50 tokens wastes potential compute cycles.

Load imbalance also creates memory management difficulties. In a static-graph compilation framework (such as XLA on TPU), every tensor must have a known shape at compile time. If expert queue lengths vary dynamically based on routing decisions, the compiler cannot determine tensor shapes in advance. The capacity mechanism solves this by pre-allocating a fixed-size buffer for each expert: we know at compile time that each expert will process at most KK tokens, so we can pre-allocate tensors of exactly that size. This static shape guarantee is what makes MoE layers compilable to efficient hardware kernels. The capacity constraint therefore is a load-balancing heuristic and a basic requirement for static-graph compilation.

Capacity Factor

To prevent bottlenecks where popular experts become overloaded, we calculate the expert capacity KK (the maximum tokens per expert) based on the total batch size and a buffer multiplier. This approach provides a principled way to balance routing flexibility against computational efficiency. We want each expert's buffer to be large enough to absorb typical routing variance without being so large that most of the allocated space goes unused.

The capacity formula computes how many tokens each expert is allowed to hold:

K=C⋅TEK = C \cdot \frac{T}{E}

where:

  • KK: the expert capacity (maximum tokens per expert for this batch)
  • CC: the capacity factor (≥1.0\geq 1.0), a buffer multiplier that determines headroom above the perfectly-balanced allocation
  • TT: the total number of tokens in the batch
  • EE: the total count of available experts
  • T/ET/E: the baseline load per expert under perfectly balanced routing, serving as the reference point

The intuition behind this formula is straightforward. The ratio T/ET/E represents the average number of tokens each expert would receive under perfectly balanced routing. In our example with 1024 tokens and 8 experts, this baseline is 128 tokens per expert. The capacity factor CC then scales this baseline to determine how much deviation from perfect balance we allow. Setting C=1.25C = 1.25 means each expert can accept up to 160 tokens (128 times 1.25). This provides a 25% buffer above the perfectly balanced load.

A capacity factor of 1.0 means each expert can handle exactly its "fair share" of tokens. Values greater than 1.0 provide headroom for imbalanced routing. The choice of CC affects model behavior in several ways:

  • C=1.0C = 1.0: Minimal buffer. Routing must be nearly perfectly balanced to avoid token dropping. Useful when load balancing losses are strong and routing is well-behaved.
  • C=1.25C = 1.25: Common choice in practice. This provides a 25% buffer for routing variance. This value was used in the original Switch Transformer paper and has become something of a default.
  • C=2.0C = 2.0: Large buffer. Rarely causes overflow but doubles the per-expert memory allocation. Useful for smaller models where routing is more difficult to balance.

Choosing the right capacity factor involves balancing memory efficiency against routing flexibility. A higher capacity factor allocates more buffer space for each expert's token queue, consuming more memory. However, it also reduces the probability that tokens are dropped due to capacity overflow, improving model quality. Empirically, the sweet spot for most models is between 1.0 and 1.5, with 1.25 being a reasonable starting point.

VisualizationOut [4]
CollapseExpand
Stacked bar chart showing tokens per expert for capacity factors from 1.0 to 2.0. Blue bars represent the base allocation of 128 tokens; orange bars show increasing buffer headroom as the capacity factor grows.
Expert capacity allocation for different capacity factors (C) with 1024 tokens and 8 experts. The base allocation (blue) represents perfect load balancing, while the buffer headroom (orange) scales with C to accommodate routing variance and prevent token dropping.

Overflow and Dropped Tokens

When an expert reaches capacity, additional tokens routed to it are typically "dropped": they bypass the MoE layer entirely and pass through via the residual connection. This ensures forward passes complete in a predictable amount of time, but it means some tokens receive less processing than intended. The model still produces an output for every dropped token, but that output is simply the unchanged input representation, as if the MoE layer were not there at all for those positions.

Token Dropping

Dropped tokens receive no expert processing, relying solely on the residual connection to carry their representation to the next layer. High drop rates indicate routing imbalance and can degrade model quality, because the model effectively has fewer effective parameters for those tokens. Drop rates above 10% are generally considered a sign that routing is not well-calibrated and that auxiliary load-balancing losses may need to be strengthened.

The drop rate provides a diagnostic signal during training. If 10% of tokens are dropped during training, the routing mechanism is not distributing load effectively, motivating the auxiliary losses we will explore in upcoming chapters on load balancing. You monitor drop rates as a health metric for your MoE training runs, as sustained high drop rates often correlate with degraded downstream performance. A good rule of thumb is that drop rates above 1 to 2% are worth investigating, and drop rates above 5% indicate a routing problem that should be addressed rather than tolerated.

Small amounts of token dropping are not necessarily harmful. Because the routing mechanism is differentiable (the router learns from gradients flowing back through the routing weights), moderate overflow can guide the router toward more balanced distributions. The prospect of dropped tokens creates an implicit incentive for the router to spread load more evenly, complementing explicit load-balancing loss terms. Consistently high drop rates indicate that the model is failing to use its expert capacity, and performance suffers.

Capacity During Training vs. Inference

Training typically uses fixed capacity factors to enable efficient batched operations. The predictable memory allocation allows for optimized kernel implementations and stable training dynamics. Fixed capacity also makes gradient computation more predictable, since the set of tokens that receive expert processing is determined before the forward pass rather than dynamically during it.

During inference, especially for single sequences, capacity constraints may be relaxed. Some implementations process all routed tokens regardless of capacity during inference, accepting variable compute time for better quality. This flexibility makes sense because inference often involves smaller batches where strict capacity constraints provide less benefit, and you typically prefer accuracy over strict latency guarantees. Single-request inference, for example, processes only one sequence at a time, so there is no competing workload to balance against and no risk of one expert starving another. For these cases, you can simply route every token to its top-k experts and process them all, recovering the full quality of the model.

Some production deployment strategies use a hybrid approach: during inference, maintain capacity constraints but set the capacity factor higher than during training. This preserves the memory and latency predictability of capacity-constrained routing while letting more routing flexibility than the training configuration. The optimal inference capacity factor is something you tune based on your latency budget and quality requirements.

Expert Count Selection

Choosing the number of experts involves balancing specialization potential, memory requirements, and computational efficiency. Different models have explored configurations ranging from 8 to thousands of experts, and there is no universally optimal number. The right choice depends on the training data distribution, available hardware, and target use case. Understanding the tradeoffs qualitatively helps you reason about architectural decisions in the models you read about and build.

Think of expert count selection as deciding how many specialized workshops to maintain in a factory. A factory with 4 specialized workshops can route different types of work to appropriate stations, but the specialization is relatively coarse. A factory with 64 workshops can be much more finely specialized, with each workshop handling a narrow type of task extremely well. But maintaining 64 workshops requires more floor space, more staff, and more coordination overhead. At some point, adding more workshops provides diminishing returns if there is not enough work of each specialized type to keep every workshop productive. The optimal number of workshops depends on how diverse the incoming work is and how much floor space (memory) you have available.

The choice of expert count is also intimately connected to the routing topology. A model with 8 experts and top-2 routing activates 25% of its experts per token. A model with 64 experts and top-2 routing activates only about 3% of its experts per token. The second model has much greater sparsity, which means each expert sees a much smaller fraction of the training distribution. This can lead to richer specialization but also to undertraining risks if some experts see too few tokens to learn effective transformations. The relationship between expert count, routing sparsity, and training dynamics is an active area of research, and the empirical findings from major MoE papers provide useful reference points.

Trade-offs in Expert Count

Increasing the number of experts has several competing effects that you need to weigh against each other. More experts generally improves the model's capacity to specialize, which can improve quality on diverse tasks. But it also increases the engineering and systems complexity of training and deployment.

Benefits of more experts:

  • Greater total parameter count without proportional compute increase, giving the model more capacity to represent complex functions
  • Finer-grained specialization potential, letting experts to develop highly targeted transformations for narrow sub-distributions
  • Better scaling on distributed systems with expert parallelism, since more experts can be spread across more devices

Costs of more experts:

  • More memory required to store all expert weights, even those not activated for a given token
  • Communication overhead when experts span multiple devices, requiring tokens to be transferred to the device holding the relevant expert
  • Risk of some experts being chronically underutilized, leading to undertrained weights that hurt quality when they are eventually selected

The communication overhead point deserves special attention because it is often the binding constraint in practice. In a model where each expert lives on a separate device (the "expert parallelism" strategy), every routing decision that sends a token to an expert on a different device requires network communication. With 8 experts on 8 devices, routing is relatively simple: most tokens stay on their local device or transfer to one of a small number of remote devices. With 1024 experts on 64 devices, routing becomes a complex all-to-all communication problem. The time spent on this communication can easily dominate the time spent on actual computation, eliminating the efficiency gains that motivated MoE in the first place.

Empirical Findings

Major MoE models have converged on different expert counts based on their design goals and available hardware:

Expert count configurations across major MoE architectures.
ModelExpertsActive ExpertsRationale
Switch Transformer128-20481Maximum sparsity, extreme scaling
Mixtral 8x7B82Balance simplicity with capacity
GShard20482Very large scale translation
DeepSeek-MoE646Finer-grained routing

Switch Transformer demonstrated that even single-expert routing (top-1, meaning only one active expert per token) works well when the expert count is very large, letting massive parameter counts while keeping per-token compute low. The original Switch Transformer scaled to over a trillion parameters by using thousands of experts per layer. Mixtral showed that a much simpler configuration (8 experts, top-2 routing) achieves strong quality while remaining practical to deploy on commodity hardware. DeepSeek-MoE explored a middle ground, using many fine-grained experts (64) with a higher active count (6) to capture more routing granularity.

VisualizationsOut [5]
CollapseExpand
Grouped bar chart showing total expert counts (gray) and active experts per token (green) for Mixtral, DeepSeek, Switch Transformer, and GShard. Active experts remain in the single digits across all models despite total expert counts reaching into the hundreds or thousands.
Comparison of total and active expert counts across Mixtral, DeepSeek, Switch Transformer, and GShard. While total expert counts (gray) range from 8 to 2048, the number of active experts per token (green) remains consistently low, illustrating the basic sparsity of different MoE architectures.
Bar chart of capacity-to-compute ratios for four MoE architectures. GShard shows the highest ratio at 1024x, followed by Switch Transformer at 512x, DeepSeek at about 11x, and Mixtral at 4x.
Ratios of total capacity to active computation for different MoE architectures. GShard has the highest ratio at 1024x, followed by Switch Transformer at 512x, showing how sparse routing allows for massive parameter counts with minimal active compute per token.

The Capacity-Compute Relationship

More experts with fewer active per token increases the capacity-to-compute ratio. A model with 64 experts but only 2 active has 32 times more parameters than its per-token compute would suggest. This ratio affects:

  • Memory requirements: all expert weights must be stored in memory (or on the same device in a distributed setting), even those that will not be used for any given batch
  • Potential for overfitting: more capacity than the training data can support leads to experts that memorize training patterns rather than learning generalizable transformations
  • Inference efficiency: expert weights may need to be loaded from slower memory (system RAM or disk) if the GPU memory cannot hold all of them simultaneously

The key insight here is that the right capacity-to-compute ratio depends on the training data volume as much as the hardware configuration. A model trained on 1 trillion tokens can productively use much higher capacity than a model trained on 100 billion tokens, because there is sufficient diversity in the data to train every expert to do something useful. Scaling laws for MoE are an active research area, and the relationship between expert count, training tokens, and final quality is not yet as well-characterized as for dense models.

Shared vs. Independent Experts

Some architectures include "shared" experts that process every token alongside the sparse routing to specialized experts. DeepSeek-MoE uses this approach, with 2 shared experts plus routing among 64 specialized experts. Shared experts capture common transformations that benefit every token, such as basic syntactic processing or common lexical patterns, while specialized experts handle token-specific or domain-specific processing.

The intuition for shared experts comes from the observation that not all processing is specialized. Every sentence, regardless of its domain, involves basic grammatical relationships, function-word handling, and common semantic operations that benefit from consistent treatment. If specialized experts learn these common operations through repeated routing of common tokens, they may not have sufficient capacity left for specialization. Dedicated shared experts can handle these universal computations, leaving specialized experts to focus on differences between domains and token types.

Expert Placement in Transformer

Not every layer in a transformer needs to be an MoE layer. The placement of expert layers significantly impacts model behavior and efficiency. This section examines the design patterns that have proven effective in practice and explains the reasoning behind the architectural choices that major models have converged on.

The transformer architecture has a regular repeating structure: each layer consists of a self-attention module followed by a feed-forward network, with layer normalization and residual connections wrapping each component. When introducing MoE, the key question is which components to replace with expert versions and in which layers. The answer is not purely a matter of taste: it reflects deep considerations about what each component does and whether sparse routing is compatible with that function.

Think of the choice of where to place MoE layers as deciding which departments in a company should have specialists and which should have generalists. In most companies, the legal and finance departments are highly specialized (only certain people can do that work), while the IT helpdesk and facilities management are shared across the whole company. Similarly, certain layers of a transformer may benefit from specialization while others are better off shared. The empirical pattern that has emerged is that the FFN component of the transformer block is the right place for expert routing, and that alternating between dense and MoE layers strikes a useful balance.

The placement decision also has consequences for the routing mechanism. If MoE layers appear at every depth, the router at each layer must learn to distribute tokens appropriately for that layer's function. Early layers tend to handle lower-level linguistic structure (morphology, basic syntax), while deeper layers handle higher-level semantics and task-specific reasoning. A model where every layer is MoE must learn routing policies that account for this depth-dependent specialization. Alternating patterns allow dense layers to create shared representations that subsequently MoE layers can then specialize from, potentially making the routing problem simpler.

FFN Replacement Pattern

The standard approach replaces some or all FFN layers with MoE layers while keeping attention layers unchanged. This follows naturally from the position-independence property discussed earlier.

In a transformer block, the computation flows through a sequence of operations. The input first passes through layer normalization, then through the attention mechanism where tokens exchange information. After a residual connection adds the input back to the attention output, another layer normalization prepares the representation for the FFN (or MoE) layer. Finally, another residual connection completes the block. The FFN position, coming after attention has already enabled cross-position communication, becomes the natural location for the MoE layer. At this point in the computation, each token has already had the opportunity to incorporate information from its context, and the remaining job is to transform that enriched representation in a token-specific way.

The FFN position becomes the MoE position. Attention layers remain dense. This keeps all positions can still interact before expert-specialized processing. This design principle reflects the distinct roles of attention and FFN layers in transformers: attention handles information routing between positions, while FFN layers turn the representation at each position independently. MoE exploits this independence by running different transformations for different tokens, all without interfering with the cross-position communication that attention handles.

Interleaving Strategies

Several patterns have been explored for alternating dense and MoE layers, each which reflects different assumptions about how much expert processing is beneficial relative to the overhead it introduces:

  • Every layer MoE: All FFN layers become MoE layers. This maximizes expert utilization but increases routing overhead and may not improve quality proportionally. Training is also more complex, since every layer introduces the routing instability and load-balancing challenges associated with MoE.

  • Alternating (every 2nd layer): A common pattern in practice. Every other FFN layer is replaced with MoE. Mixtral uses this approach. Dense layers provide regular checkpoints of shared processing between expert layers, which may help stabilize the representation and prevent expert drift across layers.

  • Periodic (every nth layer): More aggressive interleaving, such as MoE at every 4th layer. This reduces router overhead significantly but limits expert specialization opportunities, since tokens pass through fewer expert layers overall.

VisualizationOut [6]
CollapseExpand
Diagram of a 12-layer transformer showing three MoE placement strategies side by side: all MoE layers (every layer red), alternating MoE and dense layers (every 2nd layer), and periodic MoE layers (every 4th layer), with dense FFN layers shown in gray.
MoE layer placement strategies in a 12-layer transformer. The alternating pattern (every 2nd layer) is a common configuration that balances the routing overhead of experts (red) with the shared processing of dense FFN layers (gray).

Layer-Specific Expert Counts

Some architectures vary the number of experts by layer depth. Early layers might use fewer experts because input patterns are more uniform and syntax-driven, while later layers use more experts where task-specific and domain-specific specialization becomes most valuable. This pattern appears in certain custom architectures, though major publicly released models typically use uniform expert counts for simplicity. Token features become more diverse in deeper layers, so later layers may gain more from fine-grained specialization than early layers.

Cross-Layer Expert Sharing

An emerging technique shares expert weights across multiple layers while maintaining layer-specific routers. This reduces total parameters while preserving adaptive routing. Each layer can route differently even if the underlying expert computations are shared. The key insight is that the specialization in MoE comes partly from routing decisions (which expert gets which token) and partly from the expert's weight matrices (what transformation that expert applies). Cross-layer sharing retains the routing flexibility while reducing the parameter count by reusing expert weights. This is an active area of research, and we may see more production models adopt it as the field develops better understanding of how routing and weight specialization interact.

Worked Example: Expert Specialization

Let's work through a concrete numerical trace of how an MoE layer processes a small batch of tokens. This example makes the abstract architecture tangible and helps you see exactly where the parameter count, capacity limits, and routing decisions come together.

Suppose we have a small MoE layer with the following configuration: dmodel=4d_{\text{model}} = 4, dff=8d_{\text{ff}} = 8, 4 experts, and top-1 routing. We have a batch of 8 tokens, and the routing scores (which we assume have already been computed by the router) assign each token to one expert:

  • Tokens 0, 2, 4: assigned to Expert 0 (3 tokens)
  • Tokens 1, 5: assigned to Expert 1 (2 tokens)
  • Token 3: assigned to Expert 2 (1 token)
  • Tokens 6, 7: assigned to Expert 3 (2 tokens)

With a capacity factor of C=1.25C = 1.25 and 4 experts over 8 tokens, the capacity per expert is K=1.25×(8/4)=2.5K = 1.25 \times (8/4) = 2.5, which rounds down to 2. Expert 0 is assigned 3 tokens but has capacity for only 2. Token 4, which has the lowest router confidence score among Expert 0's three assigned tokens, is dropped. It will pass through the MoE layer unchanged via the residual connection.

Now let's trace the computation for token 0, which is routed to Expert 0. Token 0's hidden representation is a 4-dimensional vector, let's call it x0=[0.5,−0.3,0.8,0.2]x_0 = [0.5, -0.3, 0.8, 0.2]. Expert 0 applies its weight matrices:

First, the up-projection: h=W1x0+b1h = W_1 x_0 + b_1, creating an 8-dimensional intermediate vector.

Then, the activation: a=σ(h)a = \sigma(h), where σ\sigma applies SiLU element-wise.

Finally, the down-projection: y0=W2a+b2y_0 = W_2 a + b_2, creating the 4-dimensional output.

This output y0y_0 is then scaled by the router's confidence weight for Expert 0 (let's say 0.87) and added to the residual: the final output for token 0 is x0+0.87⋅y0x_0 + 0.87 \cdot y_0.

Token 4, the dropped token, skips the expert computation entirely. Its output is simply x4+0=x4x_4 + 0 = x_4: the residual connection carries the unchanged representation to the next layer. This means the MoE layer contributes nothing to token 4's representation, which is the cost of token dropping.

The key observation from this trace is that each expert processes its assigned tokens independently and in parallel. Expert 0 processes tokens 0 and 2 simultaneously (as a batch of size 2). Expert 1 processes tokens 1 and 5. Expert 2 processes token 3. Expert 3 processes tokens 6 and 7. All four experts run at the same time on different hardware units. The total wall-clock time for this MoE layer is determined by the slowest expert, which in this case is Expert 0 (processing 2 tokens) rather than Expert 2 (processing 1 token). This illustrates why load balancing matters: uneven load means some hardware is busy while other hardware waits, reducing efficiency.

Code Implementation

Let's implement a simple expert network structure and explore capacity-constrained processing.

PythonIn [7]
CollapseExpand
import torch.nn as nn
import torch.nn.functional as F


# Expert as a standard FFN with SiLU activation
class Expert(nn.Module):
    def __init__(self, d_model, d_ff):
        super().__init__()
        self.w1 = nn.Linear(d_model, d_ff, bias=True)
        self.w2 = nn.Linear(d_ff, d_model, bias=True)

    def forward(self, x):
        return self.w2(F.silu(self.w1(x)))


# Create experts with shared configuration
d_model = 256
d_ff = 512
num_experts = 8

experts = nn.ModuleList([Expert(d_model, d_ff) for _ in range(num_experts)])

Each expert is an independent FFN with its own weight matrices W1W_1 and W2W_2. The nn.ModuleList container ensures that PyTorch registers all expert parameters for gradient tracking and optimizer updates. Note that creating experts inside a ModuleList is needed: if you used a plain Python list, the expert parameters would not appear in model.parameters() and would not receive gradient updates during training.

Let's examine the parameter counts to verify our earlier analysis:

PythonIn [8]
CollapseExpand
def count_parameters(module):
    return sum(p.numel() for p in module.parameters())


single_expert_params = count_parameters(experts[0])
total_expert_params = count_parameters(experts)
ConsoleOut [9]
CollapseExpand
Parameters per expert: 262,912
Total expert parameters: 2,103,296
Expansion factor: 8x over single FFN

The total parameter count (approx. 2.1 million) reflects the sum of all 8 experts, but the active parameters per token remain small (approx. 260,000, since only 2 experts activate per token in a typical top-2 routing scheme). This is the MoE tradeoff made concrete: 8 times the stored parameters, but only 2 times the per-token compute.

Now let's implement capacity-constrained expert processing. This simulates how tokens are assigned to experts with overflow handling:

PythonIn [10]
CollapseExpand
def process_with_capacity(
    tokens,  # (batch_size * seq_len, d_model)
    expert_assignments,  # (num_tokens,) - which expert each token goes to
    experts,  # list of expert modules
    capacity_factor=1.25,
):
    """
    Process tokens through experts with capacity constraints.
    Returns processed tokens and drop statistics.
    """
    num_tokens = tokens.shape[0]
    num_experts = len(experts)
    d_model = tokens.shape[1]

    # Calculate capacity per expert
    base_capacity = num_tokens // num_experts
    capacity = int(capacity_factor * base_capacity)

    # Track assignments and outputs
    output = torch.zeros_like(tokens)
    expert_counts = torch.zeros(num_experts, dtype=torch.long)
    dropped_counts = torch.zeros(num_experts, dtype=torch.long)

    # Process each token
    for i in range(num_tokens):
        expert_idx = expert_assignments[i].item()

        if expert_counts[expert_idx] < capacity:
            # Expert has capacity: process the token
            with torch.no_grad():  # For demonstration only
                output[i] = experts[expert_idx](tokens[i : i + 1]).squeeze(0)
            expert_counts[expert_idx] += 1
        else:
            # Expert at capacity: token is dropped (uses residual)
            output[i] = tokens[i]  # Pass through unchanged
            dropped_counts[expert_idx] += 1

    return output, expert_counts, dropped_counts

This implementation processes tokens sequentially to clearly show the capacity-checking logic. In a production implementation, you would batch all tokens for each expert together and process them simultaneously, but the sequential version makes the capacity constraint logic transparent. Notice that dropped tokens receive tokens[i] as their output: this simulates the residual connection carrying the token representation unchanged past the MoE layer.

Let's simulate imbalanced routing to see capacity constraints in action:

PythonIn [11]
CollapseExpand
import torch
import torch.nn as nn
import torch.nn.functional as F

# Recreate experts for this cell (needed since earlier cell has output: false)
d_model = 256
d_ff = 512
num_experts = 8


class Expert(nn.Module):
    def __init__(self, d_model, d_ff):
        super().__init__()
        self.w1 = nn.Linear(d_model, d_ff, bias=True)
        self.w2 = nn.Linear(d_ff, d_model, bias=True)

    def forward(self, x):
        return self.w2(F.silu(self.w1(x)))


experts = nn.ModuleList([Expert(d_model, d_ff) for _ in range(num_experts)])

# Generate random tokens
torch.manual_seed(42)
batch_tokens = 256
tokens = torch.randn(batch_tokens, d_model)

# Create imbalanced routing: some experts more "popular"
# Expert 0 and 1 get most tokens
probs = torch.tensor([0.3, 0.25, 0.1, 0.1, 0.08, 0.07, 0.05, 0.05])
expert_assignments = torch.multinomial(probs, batch_tokens, replacement=True)

# Define capacity factor
capacity_factor = 1.25

# Process with capacity constraint
output, counts, dropped = process_with_capacity(
    tokens, expert_assignments, experts, capacity_factor=capacity_factor
)

total_dropped = dropped.sum().item()
drop_rate = 100 * total_dropped / batch_tokens

# Calculate per-expert statistics
expert_stats = []
for i in range(num_experts):
    assigned = (expert_assignments == i).sum().item()
    processed = counts[i].item()
    dropped_count = dropped[i].item()
    expert_stats.append((i, assigned, processed, dropped_count))

The routing probabilities here are intentionally skewed: experts 0 and 1 receive over half of all tokens, while experts 6 and 7 receive only 5% each. This kind of imbalanced routing is what the capacity mechanism must handle without letting the popular experts to become bottlenecks.

ConsoleOut [12]
CollapseExpand
Expert utilization with capacity_factor=1.25:
Base capacity: 32 tokens per expert
Actual capacity: 40 tokens per expert

Expert 0: 82 assigned, 40 processed, 42 dropped
Expert 1: 65 assigned, 40 processed, 25 dropped
Expert 2: 28 assigned, 28 processed, 0 dropped
Expert 3: 22 assigned, 22 processed, 0 dropped
Expert 4: 22 assigned, 22 processed, 0 dropped
Expert 5: 15 assigned, 15 processed, 0 dropped
Expert 6: 10 assigned, 10 processed, 0 dropped
Expert 7: 12 assigned, 12 processed, 0 dropped

Total drop rate: 26.2%
VisualizationOut [13]
CollapseExpand
Bar chart of token counts routed to experts E0 through E7. Overloaded experts (E0, E1) have bars split into accepted tokens (blue) and dropped tokens (red) above the capacity threshold dashed line; underutilized experts (E5-E7) show only partial blue bars.
Token load distribution across 8 experts under imbalanced routing conditions. Popular experts (e.g., E0, E1) exceed the capacity threshold (dashed line) and must drop excess tokens (red), while underutilized experts leave allocated capacity unused.

The imbalanced routing causes popular experts to drop tokens. Let's visualize how different capacity factors affect drop rates:

PythonIn [14]
CollapseExpand
capacity_factors = [1.0, 1.1, 1.25, 1.5, 1.75, 2.0]
drop_rates = []

for cf in capacity_factors:
    _, _, dropped = process_with_capacity(
        tokens, expert_assignments, experts, cf
    )
    drop_rate = 100 * dropped.sum().item() / batch_tokens
    drop_rates.append(drop_rate)
VisualizationOut [15]
CollapseExpand
Line chart showing token drop rate decreasing from about 32% at capacity factor 1.0 to about 7% at capacity factor 2.0.
Relationship between capacity factor and token drop rate under skewed routing. Increasing the capacity factor reduces drops substantially, but because experts 0 and 1 receive a highly disproportionate share of tokens, even C=2.0 does not eliminate dropping entirely, illustrating that capacity alone cannot compensate for severe routing imbalance.

The plot demonstrates that increasing the capacity factor reduces the drop rate. In this simulation, the heavy skew in routing (30% of tokens going to a single expert) means some dropping persists even at C=2.0C = 2.0. This is an important lesson: capacity factors are a management tool, not a cure. If the routing mechanism produces severely imbalanced assignments, capacity alone cannot compensate. You need both appropriate capacity and good routing, which is exactly why load-balancing auxiliary losses (covered in upcoming chapters) are so important for making MoE training work.

The diversity across experts also becomes apparent when we process the same token through all 8 experts. In an untrained model, the outputs differ simply because the weight matrices are randomly initialized. After training, this diversity becomes meaningful: each expert has learned to produce outputs optimized for its assigned token distribution.

PythonIn [16]
CollapseExpand
import torch

# Simulated hidden representations for different token types
# (In practice, these come from earlier transformer layers)
code_token = torch.randn(1, d_model)  # Represents "def"
english_token = torch.randn(1, d_model)  # Represents "the"
math_token = torch.randn(1, d_model)  # Represents a math symbol

# Each expert produces different outputs
# due to their independent weights
with torch.no_grad():
    code_outputs = [experts[i](code_token) for i in range(num_experts)]
    english_outputs = [experts[i](english_token) for i in range(num_experts)]

# Measure output diversity across experts
code_var = torch.stack(code_outputs).var(dim=0).mean().item()
english_var = torch.stack(english_outputs).var(dim=0).mean().item()
ConsoleOut [17]
CollapseExpand
Code token output variance across experts: 0.0396
English token output variance across experts: 0.0416

Each expert transforms the same input differently. After training, a well-functioning MoE learns to route tokens to experts whose transformations are most appropriate for that token type. The router, which we will explore in the next chapter, learns to make these assignments based on the input representation.

VisualizationOut [18]
CollapseExpand
Bar chart of L2 distance from the mean output for each of 8 experts processing the same code token. Bars vary in height across experts, showing that each expert produces a distinct transformation.
L2 distance between individual expert outputs and the mean output for the same code token. The substantial deviation of each expert from the mean demonstrates that experts learn distinct transformations, giving specialized processing for the same input.

Key Parameters

Understanding what each hyperparameter controls helps you reason about MoE configurations you encounter in the literature or need to tune yourself. The key parameters for the Expert implementation are:

  • d_model: The dimensionality of input and output tokens. This must match the transformer's hidden size since experts sit inside the transformer block and their outputs flow into residual connections. Changing this value requires changing the entire model, including the expert.
  • d_ff: The intermediate expansion dimension within each expert FFN. This controls the representational capacity of each expert. Larger values mean more expressive experts but more memory and compute per activated expert. The standard is 4 times d_model for two-layer FFNs and approximately 2.67 times d_model for SwiGLU variants.
  • num_experts: The total number of experts available in the layer. This is the primary lever for trading memory against specialization. More experts means more total parameters and potentially finer-grained specialization, but also more memory and communication overhead.
  • capacity_factor: A multiplier determining the maximum number of tokens an expert can process relative to a perfectly balanced load. This is the primary lever for trading routing quality against hardware efficiency. Higher values reduce token dropping but increase memory allocation per expert.

Limitations and Practical Considerations

Expert networks introduce several practical challenges. The most immediate is memory overhead: storing NN experts requires NN times the memory of a single FFN layer, even though most experts remain idle for any given token. A Mixtral 8x7B model stores 8 separate FFN copies per MoE layer, consuming significant GPU memory despite only activating 2 at inference time. This memory-compute asymmetry means MoE models often require more hardware to deploy than their per-token FLOP count suggests. A model computationally equivalent to a 7B dense model at inference time may require the memory bandwidth and storage of a 47B parameter model because all expert weights must be resident in memory.

This memory-compute split creates practical deployment challenges that do not arise with dense models. For dense models, memory and compute scale together: a model that needs more memory also needs more compute, and hardware allocation is relatively straightforward. For MoE models, you must separately optimize for peak memory (which is determined by all expert weights) and throughput (which is determined by active expert weights). Quantizing expert weights to lower precision (int4 or int8) is one common approach to managing this tradeoff, since quantization reduces memory footprint with relatively modest quality degradation for the inactive experts.

Expert underutilization presents another concern. Without careful load balancing, some experts may receive far fewer tokens during training, leading to undertrained weights that hurt model quality when they are eventually selected. This creates a feedback loop: underutilized experts produce worse outputs, causing the router to select them even less frequently. The auxiliary losses we will discuss in upcoming chapters directly address this problem by penalizing routing distributions that are too concentrated. The severity of this collapse behavior depends on the routing mechanism and the training hyperparameters, and it remains one of the more delicate aspects of MoE training.

The capacity mechanism itself introduces discontinuities in the training signal. Tokens dropped due to capacity overflow still contribute to the loss but receive no expert processing, creating a mismatch between forward computation and gradient flow. The router does not receive a gradient signal telling it that certain tokens were dropped: from the perspective of the loss function, those tokens simply passed through the residual connection, which is a valid (if suboptimal) outcome. Various techniques attempt to make this process more differentiable, but some information loss is inherent to hard capacity constraints. This is one reason why some recent MoE variants have explored soft capacity mechanisms and token merging approaches that avoid hard dropping entirely.

Load balancing at inference time is also a non-trivial concern. During training, the model sees a diverse mixture of inputs, and the routing distribution averages out over many batches. At inference time, however, you may process a stream of requests that are all from the same domain: a coding assistant receiving only code inputs, for example, may route almost all tokens to a small subset of experts. This request-time imbalance can cause expert parallelism bottlenecks that did not appear during training, degrading inference throughput. Some deployment systems address this by routing similar requests together to avoid within-batch imbalance, but this adds infrastructure complexity.

Finally, expert placement decisions remain somewhat empirical. While alternating dense and MoE layers has become standard, the optimal interleaving pattern likely varies by task and scale. Some recent work suggests that certain layer positions benefit more from expert diversity than others, but clear design principles are still emerging from the research community. The field does not yet have the equivalent of scaling laws for MoE placement: we know that some configurations work well empirically, but a principled theory of why certain placements outperform others is still an open question.

Summary

Expert networks form the computational substrate of Mixture of Experts models. The key insights from this chapter include:

  • Experts are independent FFNs that mirror standard transformer feed-forward layers. The position-independence of FFN operations makes them natural candidates for sparse expert-based routing, while attention layers are poor candidates because they require cross-position communication that cannot be easily distributed across independent subnetworks.

  • The SwiGLU architecture used in modern expert implementations adds a learned, input-dependent gating mechanism that allows experts to selectively filter their intermediate representations. This makes experts more expressive than simple two-layer FFNs and better suited to developing specialized transformations for narrow input distributions.

  • Expert capacity manages the tension between flexible routing and efficient batched computation. The capacity factor determines how much routing imbalance the system tolerates before dropping tokens. The right capacity factor balances memory efficiency against routing quality, and monitoring drop rates during training is needed for diagnosing routing health.

  • Expert count involves tradeoffs between specialization potential, memory requirements, and utilization efficiency. Successful models range from 8 experts (Mixtral) to thousands (Switch Transformer). More experts means more capacity but also more memory, more communication overhead in distributed settings, and greater risk of expert underutilization.

  • Expert placement typically replaces FFN layers while keeping attention layers dense. Alternating dense and MoE layers is a common pattern that balances routing overhead with expert specialization, and this approach has been validated empirically across multiple major MoE architectures.

  • Practical limitations of expert networks include memory-compute asymmetry, expert collapse (underutilization feedback loops), non-differentiable capacity constraints, and inference-time load imbalance. These challenges motivate the auxiliary losses and deployment engineering techniques covered in subsequent chapters.

With the expert architecture established, the natural question becomes: how does the model decide which expert should process each token? The next chapter on gating networks addresses exactly this question, introducing the routing mechanisms that make sparse expert selection possible and the loss functions that keep routing balanced during training.

Quiz

Ready to test your understanding? Take this quick quiz to reinforce what you've learned about expert networks in Mixture of Experts architectures.

Expert Networks

Question 1 of 80 of 8 completed
What property of feed-forward networks makes them ideal candidates for expert-based sparsification in MoE models?
Track your reading progress

Sign in to mark sections as read and track your learning journey

Sign in →
Join the discussionAsk a question or share what you learned.

Comments

Add to the conversation

A question, a correction, or an example from your own work.

No comments yet. Be the first to share your thoughts!

A different angle
All writing
Language AI Handbook · Vol. 10

Switch Transformer: Top-1 Routing at Scale

Explains how Switch Transformer simplifies MoE with top-1 routing, capacity factors, and training stability for trillion-parameter language models.

64 min read
Language AI Handbook · Vol. 10

Expert Parallelism: Distributed Computing for MoE Models

Explains how expert parallelism distributes MoE experts across devices using all-to-all communication, enabling efficient training of trillion-parameter models.

58 min read
Language AI Handbook · Vol. 10

Router Z-Loss: Numerical Stability for MoE Training

Explains how z-loss stabilizes Mixture of Experts training by penalizing large router logits. Topics include formulation, coefficient tuning.

61 min read

About the author

Reference

Citation details

Cite or share this article.

BIBTEXAcademic
@misc{brenndoerfer2025expertnetworks, author = {Michael Brenndoerfer}, title = {Expert Networks: MoE Architecture & FFN Implementation}, year = {2025}, url = {https://mbrenndoerfer.com/writing/expert-networks-moe-architecture-ffn-implementation}, organization = {mbrenndoerfer.com}, note = {Accessed: 2026-10-24} }
APAAcademic
Michael Brenndoerfer (2025). Expert Networks: MoE Architecture & FFN Implementation. Retrieved from https://mbrenndoerfer.com/writing/expert-networks-moe-architecture-ffn-implementation
MLAAcademic
Michael Brenndoerfer. "Expert Networks: MoE Architecture & FFN Implementation." 2026. Web. October 24, 2026. <https://mbrenndoerfer.com/writing/expert-networks-moe-architecture-ffn-implementation>.
CHICAGOAcademic
Michael Brenndoerfer. "Expert Networks: MoE Architecture & FFN Implementation." Accessed October 24, 2026. https://mbrenndoerfer.com/writing/expert-networks-moe-architecture-ffn-implementation.
HARVARDAcademic
Michael Brenndoerfer (2025) 'Expert Networks: MoE Architecture & FFN Implementation'. Available at: https://mbrenndoerfer.com/writing/expert-networks-moe-architecture-ffn-implementation (Accessed: October 24, 2026).
SimpleBasic
Michael Brenndoerfer (2025). Expert Networks: MoE Architecture & FFN Implementation. https://mbrenndoerfer.com/writing/expert-networks-moe-architecture-ffn-implementation
Newsletter

Stay up to date

Get new articles, book updates, and notes on AI and technology.

Unsubscribe at any time. Privacy policy