MoE Load Balancing from Scratch
Building Mixture-of-Experts routing from the ground up — sigmoid scores, top-K selection, expert biases, aux-loss-free balancing, and SMEBU — all derived step by step with a 4-expert toy model.
The Arcee Trinity Large technical report introduces a 400-billion-parameter sparse Mixture-of-Experts language model that activates only 13 billion parameters per token. The model has 256 routed experts per layer, but only 4 fire for any given token. That means 252 experts sit idle on every forward pass. How does the model decide which 4 to activate? And how does it prevent all tokens from piling onto the same few “popular” experts while the rest gather dust?
We will derive the entire MoE routing and load balancing mechanism from scratch, culminating in the paper’s novel contribution: SMEBU (Soft-clamped Momentum Expert Bias Updates), a new method for keeping experts balanced during training. We will use a single tiny example — 4 experts, 8 tokens — and trace every computation end to end.
The Setup: Our Running Example
We will work with the simplest possible Mixture-of-Experts layer:
- 4 routed experts: (labeled experts 1, 2, 3, 4)
- 1 shared expert: (always active for every token)
- Top-2 routing: (each token activates exactly 2 of the 4 routed experts)
- Model dimension: (so every vector has 3 components)
Each expert is a small feedforward network (FFN). The shared expert processes every token. The routed experts compete for tokens — each token picks its top-2 favorites.
We have one token vector:
and four router vectors, one per routed expert:
That is the entire setup. Every derivation and numerical check in this post uses these exact vectors.
What is a Mixture-of-Experts Layer?
A Mixture-of-Experts (MoE) layer replaces the single feedforward network in a standard transformer block with a collection of smaller feedforward networks (the “experts”), plus a routing mechanism that decides which experts process each token. The idea is simple: the model can have enormous total capacity (many experts = many parameters) while keeping per-token computation cheap (only a few experts fire per token).
The output of the MoE layer for token is:
where is the input to the MoE layer, are the shared experts (always active), are the routed experts, and is the gating score for routed expert on token . Most of the are zero — only the top- selected experts have nonzero gates.
Concrete example
In our setup (, , ), this becomes:
Exactly 2 of the 4 gating scores will be nonzero (the top-2 selected experts), and the other 2 will be zero. The shared expert always contributes.
The entire challenge is computing those gating scores . That requires three steps: (1) compute routing scores, (2) select the top- experts, and (3) normalize the scores into gates. We derive each step now.
Step 1: Sigmoid Routing Scores
The routing score measures how much a given token “prefers” a given expert. We compute it by taking the dot product of the token vector with the expert’s router vector , then passing the result through the sigmoid function.
The sigmoid function maps any real number to the interval :
The routing score for routed expert on token is:
Why sigmoid instead of softmax?
Many earlier MoE models use softmax over all expert scores, which forces all scores to sum to 1. This means pushing one expert’s score up necessarily pushes others down — the scores are coupled. Trinity uses sigmoid routing, where each expert gets an independent score in . This decoupling leads to more stable router logits during training, which matters especially when using the Muon optimizer.
Numerical check
Let us compute the four routing scores for our token .
Dot products:
Sigmoid scores:
Ranking from highest to lowest: expert 4 () > expert 1 () > expert 2 () > expert 3 ().
Notice that the scores are independent — they do not sum to 1 (). Each expert gets its own “affinity” for this token.
Quick sanity check using the identity : we have and , and indeed . ✓
Step 2: Top-K Selection with Expert Bias
Now we select which experts to activate. The selection uses the routing score plus an expert bias :
The expert bias is a scalar associated with each expert that gets updated during training (but outside the gradient computation — it is “decoupled” from backpropagation). Its purpose is load balancing: by increasing for underutilized experts and decreasing it for overutilized ones, we can steer tokens toward less popular experts.
We select the top- experts by their selection scores:
This is the part that confuses almost everyone
Read the equation above carefully. The top- selection uses to decide WHICH experts get selected. But the gating value for the selected experts is — the routing score WITHOUT the bias. The bias influences the selection, but not the weight.
Why? Because the expert bias is updated by a heuristic rule (not gradient descent). If the bias affected the gating weights, those heuristic updates would corrupt the gradient signal. By keeping the bias out of the gating computation, we ensure the gradient flows cleanly through the routing scores while still allowing the bias to redirect token-to-expert assignments.
A natural follow-up question: if the bias does not affect the gating weights, how can it change the model’s behavior at all? The answer: it changes the SET of active experts. Selecting expert 2 instead of expert 4 means running a completely different FFN — even if the gating weights stay the same. The bias is a selection mechanism, not a weighting mechanism.
Numerical check with zero bias
Starting with (no load balancing intervention yet):
| Expert | Selected? | |||
|---|---|---|---|---|
| 1 | 0.731 | 0 | 0.731 | ✓ (2nd) |
| 2 | 0.622 | 0 | 0.622 | ✗ |
| 3 | 0.269 | 0 | 0.269 | ✗ |
| 4 | 0.818 | 0 | 0.818 | ✓ (1st) |
Top-2 by selection score: experts 4 and 1.
So , , , .
Numerical check with nonzero bias
Now suppose training has been running for a while and the load balancer has set , , , . Expert 4 was overloaded, so its bias was decreased. Expert 2 was underloaded, so its bias was increased.
| Expert | Selected? | |||
|---|---|---|---|---|
| 1 | 0.731 | 0 | 0.731 | ✓ (2nd) |
| 2 | 0.622 | 0.2 | 0.822 | ✓ (1st) |
| 3 | 0.269 | 0 | 0.269 | ✗ |
| 4 | 0.818 | -0.2 | 0.618 | ✗ |
Top-2 by selection score: experts 2 and 1.
The bias flipped the selection. Expert 4, which had the highest routing score (), got demoted because its negative bias () dragged its selection score below expert 2’s boosted score ().
The gating values: , , , .
Notice that , not . The bias affected selection but not the gate value. This is the decoupled design in action.
Step 3: The Gating Mechanism
The gating values need to be normalized so they sum to 1. We divide each nonzero gate by the sum of all nonzero gates:
Numerical check (zero bias case)
The nonzero gates are and . Their sum is:
Normalized:
Check: . ✓
Numerical check (nonzero bias case)
The nonzero gates are and . Their sum is:
Normalized:
Check: . ✓
The MoE Output
We now have everything we need. The MoE layer output for our token is (zero-bias case):
The shared expert always contributes. Of the 4 routed experts, only experts 1 and 4 fire. Expert 4 gets slightly more weight () than expert 1 () because its routing score was higher ( vs ).
Experts 2 and 3 do nothing for this token. Their parameters are not accessed, their computation is skipped entirely. This is the source of MoE’s efficiency: 400B total parameters, but only 13B worth of computation per token.
Interpretation
Let us now step back and look at the full pipeline:
The expert bias enters at exactly one point: the top- selection. It affects who gets chosen, not how much weight they carry. Everything else flows through the learned routing scores .
The question that remains: how do we set the expert biases? That is the load balancing problem.
The Load Balancing Problem
Imagine training our 4-expert model on many tokens. If the router learns to always prefer experts 1 and 4 (because early in training they happen to give slightly better representations), then experts 2 and 3 never get selected. Experts that never get selected never receive gradient updates. Experts that never update never improve. Experts that never improve never get selected. This is a death spiral.
The result is called expert collapse: a few experts handle all the work while the rest are wasted. In Trinity Large, with 256 routed experts per layer, a collapse would mean the model effectively has far fewer experts than designed — a massive waste of parameters and compute.
We need a mechanism that gently steers tokens toward underutilized experts and away from overutilized ones. This is load balancing.
Our batch example
For the rest of this post, we track what happens across a batch of tokens, all processed in a single training step. Suppose the router (with current biases) makes the following top-2 selections:
| Token | Expert selected 1 | Expert selected 2 |
|---|---|---|
| 1 | 4 | 1 |
| 2 | 4 | 2 |
| 3 | 4 | 1 |
| 4 | 4 | 3 |
| 5 | 4 | 1 |
| 6 | 4 | 2 |
| 7 | 4 | 2 |
| 8 | 4 | 1 |
Every single token chose expert 4 as its first pick. Expert 4 is the “popular” expert. The load counts (number of times each expert was selected) are:
Total selections: . ✓
The mean load is:
In a perfectly balanced world, every expert would handle exactly 4 tokens. Instead, expert 4 handles 8 (twice the mean) and expert 3 handles just 1 (a quarter of the mean). Expert 4 is severely overloaded. Expert 3 is starving.
Aux-Loss-Free Load Balancing (The Sign-Based Method)
The standard aux-loss-free approach maintains a bias vector that is updated after each training step using a simple rule: increase the bias for underloaded experts, decrease it for overloaded ones.
Step 1. Compute the mean load:
Step 2. Update each bias using the sign of the deviation from the mean:
where is a small step size (the “bias update speed”), and the sign function returns if the argument is positive, if negative, and if zero.
Step 3. Apply the update:
Step 4. Center the biases (subtract the mean so they sum to zero):
The centering step prevents the biases from drifting collectively upward or downward, which would shift the overall selection threshold without improving balance.
Numerical check
Using our batch loads and , starting from , with :
Deviations :
Sign of deviations:
Updates:
After applying updates (Step 3):
Centering (Step 4):
Check that the centered biases sum to zero: . ✓
Interpretation
Expert 3 was the most underloaded (1 token vs mean 4) and expert 4 was the most overloaded (8 tokens vs mean 4). After the update, expert 3 has the second-highest bias () and expert 4 has the lowest (). On the next training step, the biases will push tokens toward experts 2 and 3 and away from expert 4. This is exactly the rebalancing behavior we want.
But there is a problem hiding in the sign function.
Why Sign-Based Updates Oscillate
Look again at the updates: expert 2 had a deviation of (slightly underloaded) and expert 3 had a deviation of (severely underloaded). Both received the exact same update , because .
The sign function is blind to magnitude. It treats a tiny imbalance and a massive imbalance identically.
This becomes a serious problem near convergence. Suppose after many training steps the loads become nearly balanced: . The mean is still .
Sign-based updates for the nearly balanced case:
Expert 3 is only 1 token below average, yet it gets the full boost — the same magnitude as when it was 3 tokens below average. Expert 4 is only 1 token above average, yet it gets the full penalty.
These large updates overshoot. On the next step, expert 3 might become slightly overloaded, triggering a swing in the other direction. Then it undershoots again. The biases oscillate around the equilibrium, never settling.
The paper puts it precisely: “Under the assumption that the ideal expert bias value is a fixed value, we note that the standard aux-loss-free load balancing cannot precisely converge on that value, as each local update under the operator is always .”
As the total number of experts increases (Trinity Large has 256), the per-layer bias norm grows, making the oscillations larger and contributing to training instability.
We need an update rule that is aggressive when the imbalance is large and gentle when the imbalance is small. We need SMEBU.
SMEBU: Soft-Clamped Momentum Expert Bias Updates
SMEBU replaces the sign-based update with three modifications: (1) a normalized, magnitude-aware update via , (2) centering, and (3) momentum smoothing. We derive each step.
Step 1: Normalize the Violation
First, we compute how far each expert’s load deviates from the mean, as a fraction of the mean:
We call the normalized violation for expert . A positive means the expert is underloaded (fewer tokens than average). A negative means overloaded.
Dividing by makes the violation scale-independent. Whether the batch has 8 tokens or 8 million, lives on the same scale. An expert handling twice the mean load always has , regardless of the absolute numbers.
Numerical check (heavily imbalanced)
Using our loads with :
Expert 3 has : it handled only 25% of its fair share. Expert 4 has : it handled twice its fair share.
Numerical check (nearly balanced)
Using loads with :
The violations are much smaller now. Under the sign-based method, these would all produce the same updates. Under SMEBU, they produce proportionally smaller updates, as we will see next.
Step 2: Soft-Clamp with tanh
We apply the hyperbolic tangent function, scaled by a parameter :
The hyperbolic tangent function is:
It maps any real number to the interval . Three properties make it perfect for this job:
Property 1: Near zero, tanh is approximately the identity. For small :
We can see why. When is small, and . Substituting:
So near balance (small ), the update is proportional to the violation itself. A tiny imbalance produces a tiny update.
Property 2: Far from zero, tanh saturates at . As , . So for large imbalances, the update is bounded — we never apply an update larger than (the learning rate from Step 3).
Property 3: tanh is a smooth approximation of sign. In fact:
The parameter controls how quickly tanh transitions from the linear regime to the saturated regime. Large makes it behave more like sign. Small makes it more linear. Trinity Large uses .
Here is a comparison table with :
| (violation) | Ratio | |||
|---|---|---|---|---|
| — | ||||
The “Ratio” column shows how much of the full sign-step SMEBU applies. At large violations (), SMEBU applies 96.4% of the sign step — nearly identical. At moderate violations (), it applies 46.2%. At tiny violations (), it applies only 19.7%.
This is exactly the behavior we wanted: aggressive for large imbalances, gentle near equilibrium.
Numerical check (heavily imbalanced)
Violations: . With :
Let us verify explicitly:
And :
Numerical check (nearly balanced)
Violations: . With :
Compare with sign: , .
SMEBU gives where sign gives . The update is less than half the sign-based step, because the imbalance is moderate. Near perfect balance, the ratio would shrink further — for , SMEBU gives only , which is 10% of the sign step.
Step 3: Scale, Center, and Apply Momentum
The remaining three operations turn the soft-clamped violations into actual bias updates.
Scale by the load-balance learning rate :
Center the updates so they sum to zero:
Apply momentum, maintaining a momentum buffer (initialized to 0):
Update the bias:
The momentum here works exactly like momentum in SGD. Instead of applying the raw update directly, we maintain a running average that blends the current update with past updates. When the updates are noisy (pointing in different directions on different steps), the momentum averages out the noise. When the updates are consistent (pointing in the same direction), the momentum accumulates and accelerates convergence.
The parameter controls the memory: means no momentum (use the raw update), means infinite memory (ignore new updates). Trinity Large uses , giving equal weight to the current update and the accumulated history.
Why does momentum help here? Near convergence, expert loads fluctuate randomly around the mean — sometimes expert gets one extra token, sometimes one fewer. These fluctuations produce small, noisy, rapidly-alternating bias updates. Without momentum, the biases jitter. With momentum, consecutive opposing updates () cancel in the running average, and the bias stays steady.
Full Numerical Walkthrough: SMEBU vs Sign-Based
Let us trace both methods through our heavily imbalanced batch (, ), starting from and .
Hyperparameters: for sign-based, , , for SMEBU.
Sign-based method
| Step | Formula | Expert 1 | Expert 2 | Expert 3 | Expert 4 |
|---|---|---|---|---|---|
| Loads | — | 4 | 3 | 1 | 8 |
| — | 0 | 1 | 3 | ||
| — | 0 | 1 | 1 | ||
| 0 | 0.1 | 0.1 | |||
| After add | 0 | 0.1 | 0.1 | ||
| Mean of | — | ||||
| After center |
Experts 2 and 3 got the same update (), despite expert 3 being far more underloaded. The sign function erased the magnitude information.
SMEBU method
| Step | Formula | Expert 1 | Expert 2 | Expert 3 | Expert 4 |
|---|---|---|---|---|---|
| Loads | — | 4 | 3 | 1 | 8 |
| 0 | 0.25 | 0.75 | |||
| 0 | 0.50 | 1.50 | |||
| 0 | 0.462 | 0.905 | |||
| 0 | 0.0462 | 0.0905 | |||
| Mean of | — | ||||
| Centered | |||||
Let us verify the mean of before centering:
And verify the centered updates sum to zero:
(The tiny residual is from rounding to 4 decimal places.)
Comparing the results
| Expert | Sign-based | SMEBU |
|---|---|---|
| 1 (balanced) | ||
| 2 (slightly under) | ||
| 3 (severely under) | ||
| 4 (severely over) |
Two critical differences:
1. SMEBU differentiates by severity. Under sign-based, experts 2 and 3 both got — the same bias despite very different loads (3 vs 1). Under SMEBU, expert 3 got and expert 2 got . SMEBU gave more help to the expert that needed it more.
2. SMEBU gives smaller updates overall. The momentum halves the first-step update (since and starts at 0). On subsequent steps, the momentum accumulates consistent signals and dampens noise. The sign-based method applies the full every step regardless.
Near-Balance Behavior: Where SMEBU Truly Shines
The comparison above shows SMEBU’s advantages for a heavily imbalanced batch. But the difference becomes even more dramatic near convergence.
Suppose after many training steps the loads are nearly balanced: , . This is only a tiny deviation from perfect balance.
Sign-based
The full step. The same magnitude as when expert 4 was carrying double the load. The update does not know that balance is almost achieved.
SMEBU
After centering and momentum, the effective step is even smaller. SMEBU recognizes that the imbalance is mild and responds gently.
If the imbalance were even tinier — say with one token fluctuation — the violation would be , giving , which is only 11.9% of the sign step. The biases would barely budge, because there is barely anything to fix. This is convergence.
The fundamental problem with the sign function, stated precisely: it maps the continuous violation signal to the discrete set , destroying all magnitude information. The tanh function preserves magnitude while still bounding the updates to , preventing any single step from being catastrophically large. It is a “continuous relaxation of the discrete update,” exactly as the paper describes.
Connecting It All: The Unified View
Let us step back and see the sign-based and SMEBU methods as special cases of a single framework. Both methods compute a bias update of the form:
where is a function that maps the normalized violation to an update magnitude.
For the sign-based method:
For SMEBU:
Both functions are odd (), both are bounded (), and both have . The difference is entirely in how they treat intermediate values:
- is a step function: it jumps from 0 to at , with no values in between.
- is a smooth S-curve: it transitions gradually, with near zero and far from zero.
As , the tanh curve becomes steeper and approaches the sign function. As , the tanh curve becomes shallower and approaches a pure linear update . The parameter controls where on this spectrum we sit.
Trinity Large uses , which is in the moderate range: the update is noticeably different from sign for violations smaller than about 0.5, but behaves almost identically to sign for violations larger than 1.
Adding momentum is the second key difference. The momentum buffer acts as a low-pass filter on the update sequence. High-frequency noise (random fluctuations in expert loads) gets attenuated, while low-frequency signals (persistent imbalances) pass through and accumulate. This is the same principle behind why momentum SGD converges faster than vanilla SGD in noisy settings.
The Full SMEBU Algorithm
For reference, here is the complete SMEBU update, combining all the pieces we derived:
Given: Expert loads from the current training step. Maintained state: bias vector , momentum buffer (both initialized to zero). Hyperparameters: (learning rate), (tanh scale), (momentum).
Trinity Large uses , , .
Summary
We built the Mixture-of-Experts routing mechanism from the ground up: a token vector hits each expert’s router vector, the dot products pass through sigmoid to produce independent routing scores in , the expert bias shifts the selection threshold without affecting the gating weights (the decoupled design), the top- experts fire, and their outputs are weighted by normalized routing scores. The expert bias is the lever for load balancing — increasing it for underused experts, decreasing it for overused ones — and SMEBU is the mechanism that adjusts that lever intelligently. By replacing the sign function with , SMEBU produces updates proportional to the severity of the imbalance: aggressive corrections for large deviations, gentle nudges near equilibrium, and convergence to zero updates at perfect balance. Momentum smooths out the noise from stochastic load fluctuations, preventing the oscillation that plagues sign-based methods. Together, these changes enabled Trinity Large to train stably with 256 experts per layer across 17 trillion tokens with zero loss spikes.
Previous: Foundation Prior: How LLM Outputs Reshape Bayesian Beliefs
Next: Mathematical Prerequisites for Mixture of Experts
Enjoyed this post?
Subscribe to get notified when I publish new posts. No spam, unsubscribe anytime.