Understanding, extending and improving quantile balancing

· 69 min · 14584 words · nor


TL;DR

Token-choice MoEs require load balancing for their experts, and quantile balancing (QB) is one such algorithm that does so, and is used in the Kimi K3 paper, for instance.

We first discuss MoEs and figure out why they need load balancing, why optimal load distributions might not be uniform, and what granularity load balancing should be done on.

We then turn to QB. We first explain it, as done in other places, both intuitively as well as by deriving it using a single delayed block-coordinate descent step on the dual of a constrained token-expert assignment problem, and how it connects to aux-loss-free load balancing.

We then generalize it to expert-specific load ranges (i.e., a new way to control balancing strength), exact non-uniform targets and softer convex load penalties, building some theory along the way, and also talk about convergence-related issues with the usual QB update.

Considering this view, some desiderata of a good optimization algorithm, as well as other important aspects of the QB algorithm such as the score function and the histogram implementation, we come up with a generalization of the QB algorithm, which we then test across four different MoE sparsity levels.

We show two improvements - one for improving load balancing/steering of the load distribution, and another for improving loss by making load balancing strength configurable.

We find that using logits as scores for QB instead of sigmoid values (with different ways to compress these scores) makes quantile balancing much more accurate and steerable (as far as the requested loads are concerned), and that allowing for slack around the uniform load (flexibility around strength of balancing) improves loss, showing that perfect load balancing is not necessarily loss-optimal. The question of using it with a fixed exact non-uniform load distribution is still open. We also think about other possible algorithms under this view of load balancing, for the sake of completeness.

We also release code for reproducibility purposes.

Reading map

The following map/outline of the post is due to GPT 5.6 Sol.

  • If you are new to MoE load balancing, it is probably best to read the post in order. The first few sections explain why load balancing is needed, where the balance should hold, and why uniform expert loads are not necessarily optimal.

  • Readers already familiar with MoEs can skip the two collapsed sections Choosing the balancing granularity and A brief history of MoE load balancing (though they may connect with the rest of the post). The new material begins at Understanding and extending quantile balancing, and the empirical conclusions are in Improving quantile balancing: design and results.

  • If you mainly want implementation and training guidance, read Choosing the balancing granularity and then the score, experimental setup and results subsections under Improving quantile balancing: design and results. The mathematical section explains the theory, but is not required to follow the experiments.

  • If you mainly want the optimization argument, begin with Understanding and extending quantile balancing. It derives the assignment problem and its dual, connects some bias-update methods, generalizes the load constraints, and examines where the quantile update can fail.

Outline of the post

  • Introduction explains the two main reasons for balancing expert loads: preventing experts from being starved of training signal, and avoiding stragglers in expert-parallel systems. It also talks about logical imbalance across experts and physical imbalance across devices, the latter of which systems based on dynamic expert replication can partly address.

  • Choosing the balancing granularity talks about sequence-level, microbatch and global-batch balancing, and pruning for inference. It explains why finer-grained balance is more robust to distribution shift, gives a simple bound for mixtures of balanced subgroups explaining why, and discusses the corresponding tradeoff with expert specialization.

  • What should expert loads look like? asks whether uniform loading is actually the right objective. It discusses high- and low-frequency experts, shared experts and dense layers as ways to factor out common computation, routed experts as content-addressable capacity, and possible designs with several expert tiers, routers, sparsities or target ranges.

  • A brief history of MoE load balancing introduces auxiliary balancing losses, hash routing, reinforcement-learning approaches, global versus microbatch balancing, aux-loss-free bias controllers, SMEBU and quantile balancing. It places aux-loss-free and quantile updates within a common optimization problem that is discussed later.

  • Understanding and extending quantile balancing first gives an elementary two-quantile explanation: one quantile determines each token's routing threshold, and another adjusts each expert's price to obtain its desired load. It explains why the method uses the previous batch rather than repeatedly solving the current assignment.

    The section then formulates top-KK routing as a degree-constrained bipartite assignment problem, establishes feasibility and integrality, and derives its dual. Per-token dual variables become routing thresholds, while per-expert variables become prices that discourage overloaded experts and encourage underloaded ones.

    This dual view connects proportional and sign-based aux-loss-free updates to gradient descent, interprets quantile balancing as a delayed block-coordinate method, and generalizes it from exact uniform loads to per-expert load intervals and arbitrary exact targets. It also considers softer convex load penalties, explains why the quantile update has no general convergence guarantee, and gives an example of a suboptimal fixed point.

  • Improving quantile balancing: design and results looks at three choices that can be changed independently: the score used to value assignments, the desired load constraints, and the algorithm used to update expert prices.

    • Choosing the assignment score: sigmoid vs. logit argues that sigmoid saturation can make important score differences and quantiles difficult to resolve. It proposes using pre-sigmoid logits and compacting score margins relative to the previous expert price, which concentrates histogram resolution near the expected next update. It also discusses exact-quantile simulations, affine transformations, Moving Quantile Balancing and existing public implementations.

    • Experimental setup describes the four 285--293M-parameter MoE setups with varied active parameter counts, baseline and routing-parameter tuning, five-seed matched comparisons, and the metrics used to distinguish absolute imbalance from failure to follow the requested load profile.

    • Do logit scores improve balance and loss? finds that logits follow exact uniform targets more accurately in every matched comparison and generally follow slack or non-uniform targets more accurately as well. Exact-balance loss estimates all favor logits, although some of them are still inconclusive in a strict sense.

    • Relaxing exact load constraints motivates two alternatives to perfect uniform loading: allowing each expert a load interval, or prescribing an exact non-uniform load distribution. It argues that preventing expert starvation may matter more than tightly limiting heavily used experts, especially in very sparse MoEs.

    • Does load slack improve loss? reports lower mean validation loss for slack in all eight matched architecture/router comparisons. The selected bounds also suggest that keeping lower bounds relatively tight is more important than imposing equally tight upper bounds.

    • Do inverse-rank load targets help? finds no clear loss difference between the tested inverse-rank targets and exact uniform balancing. This suggests that freedom within a load range may be more useful than committing in advance to one particular unequal distribution, but could also improve with further tuning.

    • Alternatives to the quantile update asks whether more stable optimization methods could replace the delayed quantile step, and considers the computation, memory and communication costs that make otherwise attractive methods difficult to use during large-scale training.

We release code and configurations for this post at this link.

Introduction

When training MoEs, we want some amount of load balancing across experts, i.e., routed experts should activate at similar rates. There are at least two reasons for this - a training-related reason and a systems reason.

The training-related reason is, as discussed in e.g. OLMoE, Outrageously Large Neural Networks: The Sparsely-Gated Mixture-of-Experts Layer, and Expert Choice Routing, that not having load balancing tends to concentrate tokens on a small set of experts, and the starved experts remain under-trained, potentially leading to a feedback loop where better optimized experts get chosen more often.

The systems reason is that we want the model to be easy to serve (and train) - in an expert-parallel setup, where experts are sharded across GPUs/nodes, if there is a single replica of each expert, then the expert having to serve 1+x1 + x times the mean number of tokens will roughly take 1+x1 + x times more time than the average expert (as a first approximation). Having such stragglers is not optimal and from a systems perspective, having tokens spread out evenly across experts is desirable. One caveat here is that now there are already libraries for expert parallelism that dynamically replicate experts to have perfect physical load balancing across ranks (not logical load balancing across experts), such as MoonEP, and this straggler mechanism used to be more of an issue with DeepEP and such. Expert replication is still kind of messy, so some may still prefer load balancing for systems reasons too.

So we'll talk about how load balancing is done, what our desiderata might be, and two improvements on top of quantile balancing - one for improved balance/steerability, and one for flexibility (quantile balancing in itself does not have a way to control the amount of balancing).

Three-panel
Figure 1: Overview of steerable and flexible quantile balancing. QB uses router logits for price-adjusted top-K assignment (owing to robustness to noise) while sigmoid gates continue to weight the selected expert outputs. Centering each expert's logit margins at its previous price before compacting them concentrates histogram resolution heuristically near the expected next update, leading to better quantile estimation. We relax perfect balance requirements to merely requiring that loads be in prescribed intervals that have a feasible assignment. For an arbitrary feasible load interval, the load at zero price determines whether the new price is negative, zero or positive, placing the resulting load at the lower bound, inside the interval or at the upper bound. The experiments find that logit QB follows requested loads more accurately than sigmoid QB; and allowing slack instead of requiring perfect balance improves loss.

Choosing the balancing granularity

Background: balancing granularity and distribution shift

Now that we understand that we need to balance token allocation across experts, let's think about the granularity we want the balance to hold on.

There are multiple possible granularities. From a serving perspective, one would want perfect load balance across the inference token distribution, and the ideal case is having such balance in every time interval, across all user queries.

In training, load balancing is done either within the microbatch (or sequence-wise) or the whole global batch.

Note that a learned router would have at least some kind of specialization (not necessarily interpretable) as far as token/context distribution goes (otherwise we could allocate experts uniformly at random without quality loss). Intuitively, a router could learn to route to certain experts for certain kinds of documents/prompts (dictated by activation geometry). So under a distribution shift, we would see an increase in imbalance in a globally load balanced MoE. Typically, the finer the granularity, the lower one would expect the variance in the load to be, and a sequence or microbatch balanced MoE would be more robust to such distribution shifts - more specifically, let's say for each "subgroup" ss, we have a normalized load vector psp_s satisfying psp*ε\|p_s - p^*\| \le \varepsilon, then for any reweighting of these subgroups, the final load imbalance is swspsp*swspsp*ε\|\sum_{s} w_s p_s - p^*\| \le \sum_s w_s \|p_s - p^*\| \le \varepsilon. But this also trades off expert specialization and quality, so there may be an optimal non-zero extent of such balancing that can help the model in general.

A
Figure 2: Balancing at a finer scope preserves information that a single global count discards. The constructed batch is globally balanced even though its sequences and microbatches are not. After two subgroups are reweighted, global-only balance shifts to 68%/32%, whereas keeping each subgroup within five percentage points of uniform bounds the deviation of every mixture and gives 53%/47% here.

As an aside, strong expert specialization can sometimes imply the existence of experts that are always active or are important in some other sense, so if a small set of experts covers the majority of experts used for a certain type of task, then things such as pruning or even loading these experts into faster memory (in e.g. heterogeneous memory setups such as local inference of large models with CPU offloading) can be considered. However, it does not seem likely that optimizing for this kind of human-interpretable expert specialization is optimal as far as loss/benchmark performance is concerned. One example is REAP, an expert-pruning method that seems to preserve performance on certain domains after pruning experts.

What should expert loads look like?

For now, we ignore the systems concerns (naively delegating all of them to something like MoonEP), as well as sequence-wise and microbatch load balancing, and think about how things should be, in an intuitive manner.

Let's fix a certain data distribution (say, the training distribution). If there is expert specialization, the load distributions over experts that we see in loss-wise optimal setups are not necessarily uniform. There might also be more than one of these optimum distributions, and load distribution is just a low dimensional observation of something much more complex. It would generally depend on the full training dynamics, through things such as the job of the rest of the architecture (e.g. whether or not we have shared experts or other similar mechanisms), what kinds of feedback loops exist/what instabilities we face during training (and which optima are actually reachable through training), how expert specialization happens (and in general, how the router is learned and its geometry).

One way to think about the optimal load distribution intuitively is by coarsely splitting experts into high and low frequency experts. For high-frequency experts, the routing for them can be approximated by them being always on (e.g., shared experts in a single layer or alternating dense/MoE FFN layers if we consider the whole network), and the rest of the experts are routed experts. Other aspects can also be important for routing - one can imagine a bias on top of a linear router or even a nonlinear router to improve routing quality as well as balance (and the ZAYA1-8B technical report shows that increasing the expressivity of routers empirically seems to help both quality as well as balance).

One might want to "factorize" out as much of this kind of behavior as possible in different ways (such as the shared experts in DeepSeekMoE or the alternating dense/MoE FFN layers in the MAI-Thinking-1 report), and keep the routed part of the MoE fairly balanced, because we want to both avoid routing collapse (because it leads to reduced expressivity) as well as not lose out on expressivity due to reduced expert specialization (as far as more obvious experts that can be shared/factored out in some other way are concerned). The former is the much bigger concern, because training stability in general governs many of the decisions made for large scale training runs.

Jianlin Su's blog series on MoE has yet another view of MoE as some kind of a "low-rank" approximation to the full dense MLP. In that sense, one can think of some frequent experts corresponding to certain "important" or "common" features (or something analogous to SVD modes), and the less frequent ones (or the ones to be balanced) as the more specific features that don't get activated all the time (but when they do, they end up being in the top few experts that matter as opposed to always having a tiny contribution).

Note that when we have shared experts, we can think of the model as a dense backbone plus some extra experts that need to be routed to - these are the extra parameters that potentially behave like a content-addressable memory (where the addressing is done by the router) and help reduce loss by storing "knowledge" without necessarily being active all of the time. The factorization might be important because if we do not do so, and have perfect load balancing, then if there is some shared work that is necessary to be done for every token, every chosen subset of experts has to do at least part of the work that this shared expert would have done, reducing the available capacity for expert specialization. Tuning the number of shared experts can help.

It is still unclear whether this 2-way factorization is optimal or not - it might very well be possible that the optimal way would have more tiers to such routing while still controlling how the router works (we can have multiple routers, each balanced separately, with different expert subsets with different sparsities, a special case being shared experts corresponding to the trivial router plus the rest of the experts with another router, or a load balancing setup that has different load targets/target ranges for different experts, all balanced at the same time, or even learned-routing-free methods such as hash routing for some subsets).

A brief history of MoE load balancing

Background: representative load-balancing methods

The traditional way to achieve load balancing has been adding an auxiliary loss term, called a load balancing loss, which is a loss that is minimized when we have perfect load balancing, as in the original sparsely-gated MoE and the Switch Transformer. The tradeoff between expert specialization/model quality and perfect load balancing is decided by the coefficient of this aux loss term as compared to the usual modeling loss term.

An alternative is hash routing - instead of a learned router, it hashes some kind of token (or n-gram) information and maps these hashes to the experts in some way (optionally in a Zipf-aware manner for balance). This works surprisingly well, though learned routers are generally supposed to be better.

Another historical method was to treat routing as an RL problem (a natural choice considering routing is discontinuous), as in Conditional Computation in Neural Networks for Faster Models, but it turned out that this was overkill.

In practice, people now use a global load balancing loss and it tends to work quite well (people typically used microbatch load balancing earlier) - Demons in the Detail compares global-batch and microbatch load balancing and also discusses the effect on specialization.

DeepSeek introduced aux-loss-free load balancing, which does not require an auxiliary global load balancing loss (but DeepSeek still used one for sequence-level load balancing to avoid extreme load imbalance within sequences). The idea is to decouple expert selection from expert weighting to a certain extent. Traditionally, the same gate value is used to weight experts (alongside normalization) and to choose experts for a given token. Let's say SijS_{ij} is token ii's score for expert jj. Then instead of SijS_{ij}, selection in this method uses Sij+bjS_{ij} + b_j for top-K choices, and bjb_j is not optimized using typical gradient descent. Rather, it is treated as a per-expert controller, and the updates to it are done in a direction that restores the balance for a given expert (e.g., if the load is higher than desired, bjb_j is reduced by a constant, and vice versa). Other variations also exist, such as SMEBU which improves upon the brittle sign-like updates and smooths them using momentum as well as a smoother replacement of sign(x)\mathrm{sign}(x). In general, routing gradients do not seem very well understood as far as load balancing is concerned, so having this optimization be a separate problem makes things somewhat cleaner.

Quantile balancing uses a similar controller idea, and, intuitively speaking, tries to solve for the correct biases directly (approximately). It formulates it as an optimization problem, and while aux-loss-free optimization can be thought of as some kind of dual descent for that problem, quantile balancing is a way to do something like a block coordinate descent to solve the same problem, but in an amortized (and stochastic) manner over multiple training steps. We will explain this in the next section.

Also note that there are many other algorithms that we will not cover, in order to avoid overwhelming the reader. The algorithms mentioned above are only representative of the ones that have been prominently used in large training runs.

Understanding and extending quantile balancing

There are two ways to understand quantile balancing. The first is an ad hoc way and is a bit easier to think about in an elementary manner, and the second one is a more theoretically grounded way and also helps us generalize quantile balancing (which we will do in this section).

To gain some intuition, let's first look at expert-choice routing (what we had been talking about so far was token-choice routing). In this type of routing, experts choose tokens based on their allocated capacity. In this setting, we can have perfect load balancing, but at the cost of causal leakage - a routing decision made for an earlier token based on a later token can act as a side channel for information, and for large models it can let the model use this to predict the next token much better, while not being compatible with (and worsening the performance at) inference. It also causes information leakage across different sequences in a batch. Note that in the case of token-choice routing, dropping tokens itself (when experts have hard capacity constraints) can lead to causal leakage too, depending on implementation (in general, strictly causal decisions on dropping tokens can lead to a strong positional bias in which tokens are dropped or lead to wastage of capacity if some kind of budgeting is done, so load balancing is important here too).

Still, we would like something that has as good of a balance as possible. Assuming that the token distribution doesn't change much from batch to batch, it might be fine if we accept a delay of a single step when optimizing for perfect load balancing for a top-KK token-choice MoE.

Let's say we have the scores SijS_{ij} for an expert jj for a token ii (these can be completely unrelated to the gate values, but in the standard quantile balancing setup, they are taken to be the same). Let's interpret the biases as "costs" (and use SijbjS_{ij} - b_j this time, so that demand for an expert increases its cost).

We now want to update the biases from bjb_j to bjb'_j so that after the update, we have reasonable balance.

Now, consider a single token. To be in the top KK experts (out of NN) for a token, a given biased score needs to beat the K+1K+1-th largest value of all the expert biased scores SijbjS_{ij} - b_j (taken over jj). This gives us a threshold aia_i for each token as a quantile of the biased-score distribution of experts for that token. Now, suppose bjb_j is fixed. In the spirit of alternating optimization algorithms such as EM, let's try to "solve" for bb' assuming bb is constant (that is, the thresholds are frozen). For a given expert jj, we want MK/NMK/N tokens (out of a total of MM) to have their cutoff be satisfied by this expert. As earlier, this happens if Sijbj>aiS_{ij} - b'_j > a_i, or equivalently bj<Sijaib'_j < S_{ij} - a_i, for MK/NMK/N tokens out of a total of MM tokens. Thus, the new bias is again a quantile but this time of a distribution of biased scores over tokens for that expert.

In most cases, intuitively, this iteration, if carried out on the old batch, should get sufficiently close to the bias that solves the problem optimally. However, this needs us to compute statistics over a batch repeatedly and has non-trivial communication overhead, so the quantile balancing method does only one step (instead of iterating to convergence) and tries to amortize across steps in a similar vein to minibatch stochastic optimization methods.

A
Figure 3: One QB update in a synthetic six-token, four-expert, top-2 example. The largest unselected score gives each token's threshold; an expert-wise quantile of the resulting token margins gives its new price. For an exactly uniform target the prices may be mean-centered without changing the routes, and the stored result is first used on the next batch.

Let's now look at it from a slightly different perspective. The following approach, for the special case of perfect balancing, is mostly covered in the Kimi K3 report, but we will generalize it below.

Matching tokens to experts is a bipartite matching problem but with more general degree constraints instead of the usual unit degree constraints. To see why, let's draw a bipartite graph, with tokens on the left and experts on the right. Each expert has a specific number of tokens it is assigned, which will be its degree in the matching, and each token has KK experts that it will be assigned to (i.e., their degrees must be KK). The Gale-Ryser theorem gives us a condition for when a balanced matching exists and we'd need to verify it to check feasibility. It states that non-increasing non-negative integer sequences (r1,,rM)(r_1,\ldots,r_M) and (d1,,dN)(d_1,\ldots,d_N) are the two degree sequences of a simple bipartite graph if and only if their sums agree and, for every m{1,,M}m \in \{1,\ldots,M\},

i=1mrij=1Nmin(dj,m) \sum_{i = 1}^{m} r_i \leq \sum_{j = 1}^{N} \min(d_j, m)

When we require perfect balance (i.e., all experts have the same degree and all tokens have the same degree), the condition in the theorem is trivially true, and a balanced matching is guaranteed. Even when we have arbitrary degree constraints on experts while all tokens must have the same degree KK, it is fairly mechanical to verify that the Gale-Ryser condition is satisfied as long as the expert degrees sum to KMKM and the degrees are bounded by their trivial bounds.

In fact, we can make this a bit more general. Let's say that token ii can be assigned to between lil_i and uiu_i experts (this specific generalization is not useful for usual top-KK MoEs, but we keep this regardless for generality), and expert jj can be assigned to between ljl'_j and uju'_j tokens (this specific generalization is important if we want to have slack in our expert loads or arbitrary exact loads and so on), and assume that these thresholds are such that there exists a solution to this assignment problem. (Also, we assume that these bounds are integers and are bounded by the natural bounds one would have for degrees). The case where li=ui=Kl_i = u_i = K and lj=ujl'_j = u'_j corresponds to the situation above. The analysis for the special case of perfect balancing (plus lj=uj=ql'_j = u'_j = q) is a bit easier, but we will deliberately use the more general case to be able to generalize quantile balancing further.

Now we need to choose one among these balanced matchings. To do so, we need to define the quality of a matching. We define it as

ijSijXij \sum_{ij} S_{ij} X_{ij}

where SijS_{ij} is the score given by token ii to expert jj, and XijX_{ij} is 11 if token ii is routed to expert jj and 00 otherwise. Note that SijS_{ij} is not necessarily related to the gating score in this formulation, but it is taken to be equal to the gating score in quantile balancing, intuitively because a better expert should have a higher score. Later, we will see how taking SijS_{ij} to be the post-sigmoid gate and the pre-sigmoid gate logit makes things different, so it is worth thinking about SijS_{ij} as a value we use for defining an objective function, separately from its utility as a gating function (which is also helpful for gradient flow through the router).

Note that XijX_{ij} is a discrete binary variable here. Let's for the moment relax its domain to [0,1][0, 1]. For the moment, for perfect balancing, assume that q=MK/Nq = MK/N is an integer.

We want to show that all the vertices of the polyhedron corresponding to the equivalent linear programming problem are integral, so that for each linear "quality" function, the optimum value of the function is achieved at an integral point (as opposed to only at points where XijX_{ij} is fractional for some i,ji, j). This follows because the optimum of any linear function is achieved at an extreme point of the polyhedron.

There are two ways to approach this.

Proof using total unimodularity. The first, a bit mechanical, way is to try and show total unimodularity of the matrix that defines the polyhedron, i.e., any square submatrix has determinant 00 or ±1\pm 1. Let Ave=1[v is an endpoint of e]A_{ve} = 1[v \text{ is an endpoint of } e]. Then if uu is the vector of upper bounds and ll is the vector of lower bounds, then AxbA'x \le b' defines the polyhedron, where AA' is formed by stacking A,A,I,IA, -A, I, -I and bb' is formed by stacking u,l,1|E|,0|E|u, -l, 1_{|E|}, 0_{|E|} respectively. To show that AA' is totally unimodular, we only need to show that AA is totally unimodular (easy to reduce). For this we consider expanding a submatrix CC along a column - any column has at most two 11-s. If there is a column with at most one of those, expanding along the column lets us induct downwards. Otherwise every column has exactly two 11-s - in which case, it corresponds to an edge, and the two vertices correspond to an expert and a token. Now partition the rows (corresponding to a vertex) into experts and tokens, and sum them - both sets of them sum to the all-ones vector, so the rows of CC are dependent, in which case its determinant is 00, and we are done.
More intuitive proof using flows. The second way is a bit more intuitive, and that is to treat this as a flow problem. For a bipartite graph (L,R,E)(L, R, E) (with vertices on the left being LL, on the right being RR, and edges being EE), add a source vertex ss and a sink vertex tt, and construct the following edges with the mentioned flow constraints: tst \to s with [0,iui+juj][0, \sum_i u_i + \sum_j u'_j], sis \to i with [li,ui][l_i, u_i], iji \to j with [0,1][0, 1] iff (i,j)(i, j) is an edge, jtj \to t with [lj,uj][l'_j, u'_j]. A feasible assignment xx gives rise to a circulation on this flow graph uniquely. Now consider a fractional xx. First note that there can't be exactly one fractional directed edge incident to any vertex (due to flow balance conditions) in this flow graph, so if we consider the vertices that are incident to any fractional edge, the minimum undirected degree in that graph is 22, so it must contain an undirected cycle. Since all the edges in this undirected cycle are fractional, we can choose an appropriate ε\varepsilon such that we can "send" a circulation of ±ε\pm \varepsilon along this undirected cycle (with appropriate edge signs) while the flow constraints are still satisfied. But in that case, xx is a midpoint of two other feasible points in the polyhedron, which means it is not an extreme point.

So, we have shown that it does not matter if we relax our integer programming problem (corresponding to assigning experts to tokens) to a linear programming problem. This means that we can use standard ideas from optimization.

Let's write the original linear programming problem for a top-KK MoE again: maxijSijXij\max \sum_{ij} S_{ij} X_{ij} subject to 0Xij10 \le X_{ij} \le 1, kXik=K\sum_k X_{ik} = K, ljkXkjujl'_j \le \sum_k X_{kj} \le u'_j. Here we enforced each token to use exactly KK experts to reflect that we use top-KK MoEs as well as to simplify the analysis.

We now think about the Lagrangian. To do this, introduce a variable αi\alpha_i for each constraint of the form kXik=K\sum_k X_{ik} = K, λj0\lambda_j \ge 0 for kXkjuj\sum_k X_{kj} \le u'_j, and μj0\mu_j \ge 0 for kXkjlj\sum_k X_{kj} \ge l'_j.

Consider the sum

(X,α,λ,μ)=ijSijXij+iαi(KkXik)+jλj(ujkXkj)+jμj(kXkjlj) \mathcal{L}(X, \alpha, \lambda, \mu) = \sum_{ij} S_{ij} X_{ij} + \sum_i \alpha_i (K - \sum_k X_{ik}) + \sum_{j} \lambda_j (u'_j - \sum_{k} X_{kj}) + \sum_{j} \mu_j (\sum_{k} X_{kj} - l'_j)

Define g(α,λ,μ)=maxX[0,1]M×N(X,α,λ,μ)g(\alpha, \lambda, \mu) = \max_{X \in [0, 1]^{M \times N}} \mathcal{L}(X, \alpha, \lambda, \mu). Note that under the constraints on α,λ\alpha, \lambda and μ\mu, we have, for any feasible XX, that (X,α,λ,μ)ijSijXij\mathcal{L}(X, \alpha, \lambda, \mu) \ge \sum_{ij} S_{ij} X_{ij}, so g(α,λ,μ)g(\alpha, \lambda, \mu) is an upper bound on the value of any feasible matching. So we can minimize this over the multipliers α,λ\alpha, \lambda and μ\mu to get the best possible upper bound on the value of any feasible matching. Strong duality says that, since this is a feasible linear program, this bound is actually tight. That is,

maxfeasible XijSijXij=minλ0,μ0,αmaxX[0,1](X,α,λ,μ) \max_{\text{feasible } X} \sum_{ij} S_{ij} X_{ij} = \min_{\lambda \ge 0, \mu \ge 0, \alpha} \max_{X \in [0,1]} \mathcal{L}(X, \alpha, \lambda, \mu)

Now, let's rearrange and perform the inner maximization to get the maximum value as:

minλ0,μ0,αmaxX[0,1]ij(Sijαi+μjλj)Xij+Kiαi+j(λjujμjlj) \min_{\lambda \ge 0, \mu \ge 0, \alpha} \max_{X \in [0, 1]} \sum_{ij} (S_{ij} - \alpha_i + \mu_j - \lambda_j) X_{ij} + K \sum_{i} \alpha_i + \sum_{j} (\lambda_j u'_j - \mu_j l'_j)

which equals (by choosing XijX_{ij} appropriately)

minλ0,μ0,αij(Sijαi+μjλj)++Kiαi+j(λjujμjlj) \min_{\lambda \ge 0, \mu \ge 0, \alpha} \sum_{ij} (S_{ij} - \alpha_i + \mu_j - \lambda_j)_{+} + K \sum_{i} \alpha_i + \sum_{j} (\lambda_j u'_j - \mu_j l'_j)

Let's define βj=λjμj\beta_j = \lambda_j - \mu_j as the signed "price" (we explain this terminology later) of the expert jj (for now, this is just how λ\lambda and μ\mu appear in the non-linear part of the objective) - then the summand on the rightmost end is minimized when λj=(βj)+\lambda_j = (\beta_j)_{+} and μj=(βj)+\mu_j = (-\beta_j)_{+}. Substituting and defining hj(β)=ujβh_j(\beta) = u'_j \beta for β0\beta \ge 0 and ljβl'_j \beta otherwise, we get

minα,βij(Sijαiβj)++Kiαi+jhj(βj) \min_{\alpha, \beta} \sum_{ij} (S_{ij} - \alpha_i - \beta_j)_{+} + K \sum_{i} \alpha_i + \sum_{j} h_j(\beta_j)

As an aside, let's see what hjh_j is really about. In the case where lj=ujl'_j = u'_j, hjh_j becomes a linear function. Otherwise, it has an interesting interpretation - if we instead parameterize the slack interval as being centered qj=lj+uj2q_j = \frac{l'_j + u'_j}{2} and within δj=ujlj2\delta_j = \frac{u'_j - l'_j}{2} of it, then hj(β)h_j(\beta) is qjβ+δj|β|q_j \beta + \delta_j |\beta|. This is equivalent to adding an L1 penalty on β\beta on top of the original problem, leading to some bias towards β\beta sparsity.

Let's first try to eliminate α\alpha. For fixed β\beta, it is a convex function in each αi\alpha_i separately, with the (global and local) optimum being when the slope (subgradient) is 00, i.e., SijαiβjS_{ij} - \alpha_i - \beta_j is non-negative (define slope at 00 arbitrarily) at KK values of jj, i.e., αi\alpha_i is anywhere between the KK-th and K+1K+1-th highest values in SiβS_{i \cdot} - \beta (so it corresponds to setting the XX corresponding to the top KK to 11 and the rest to 00, modulo ties). So the minimization objective becomes iTopKSumK(Siβ)+jhj(βj)\sum_{i} \mathrm{TopKSum}_K(S_{i \cdot} - \beta) + \sum_j h_j(\beta_j).

Before deriving quantile balancing here, we pause to see what happens if we throw gradient descent at this problem, as noted in the Kimi K3 paper for exact balance without slack. In the case of exact balance without slack (i.e., hjh_j is linear and equals qjβq_j \beta), away from ties in routing, the gradient becomes exactly the negative of the imbalance, and the gradient descent step increases β\beta if the expert is overloaded, and reduces β\beta if it is starved. SignSGD instead of gradient descent gives DeepSeek's aux-loss-free load balancing, and e.g., SMEBU from Arcee Trinity Large is a soft version of it, somewhat reminiscent of an Adam-atan2 + LaProp style version of the algorithm.

However, if we had some slack, the gradient step would look like this: if β>0\beta > 0 (i.e., the cost of choosing an expert is positive), then we look at the upper bound - if it is violated, then we increase β\beta by a value proportional to the violation, and if it is not, we reduce it by the same amount (note that we are being deliberately conservative in updating β\beta so that only the bare minimum for keeping the load within bounds is done). Something analogous happens for the lower bound. However, if β=0\beta = 0, then we update β\beta only if there is any violation, and update it in the correct direction; if there is no violation then β\beta remains 00. Doing a SignSGD step here like in the aux-loss-free load balancing family of algorithms would work too. Note how we get an analogue for a with-slack version of load balancing almost immediately.

One may ask, why not solve the optimization problem optimally in the first place, instead of using gradient descent? After all, gradient descent or SignSGD have the issue that the step sizes for β\beta are not sensitive to the score density near the margins (and an update size that does not take this into account can lead to either a drastic change or a negligible change in load) - standard Newton-ish methods could be worth trying too, though second-order estimation could be an issue (and besides, this is a pretty discrete problem, so it would need some kind of continuous approximation).

Two
Figure 4: Two experts begin with the same overload, but the same fixed price increase changes their loads from 8 to 7 and from 8 to 1 because their token margins have different density near the cutoff. Choosing the appropriate quantile of each margin distribution instead leaves the target load of 5 in both cases.

The naive way is to try and solve minβiTopKSumK(Siβ)+jhj(βj)\min_\beta \sum_i \mathrm{TopKSum}_K(S_{i \cdot} - \beta) + \sum_j h_j(\beta_j) by noting that this is a convex optimization problem. However, this does not have a closed form, and typical convex optimization algorithms are iterative in nature. In particular, if they require computing the objective function or a subgradient many times, it could get expensive quite quickly. Also, if the batch is small enough, fitting to the current batch perfectly is likely to be suboptimal for the next batch (and would necessitate some kind of proximal penalty for things to be stable across steps). So we would like to do a single iteration (or a few iterations tops) of any such algorithm, taking inspiration from stochastic minibatch optimization algorithms. Note that approaches such as bundle/ADMM/primal-dual algorithms/LP solvers/Sinkhorn-style approaches can be quite expensive (computationally or memory/communications-wise), so we don't pursue them here.

Since this form of the problem doesn't look as tractable, let's go back to

minα,βij(Sijαiβj)++Kiαi+jhj(βj) \min_{\alpha, \beta} \sum_{ij} (S_{ij} - \alpha_i - \beta_j)_{+} + K \sum_{i} \alpha_i + \sum_{j} h_j(\beta_j)

We can try and solve this problem using blockwise coordinate descent (i.e., alternating between optimizing over α\alpha and β\beta while keeping the other fixed), and it has a simple and fast closed form iteration. For the special case of perfect balance, we see that this recovers our old balancing - optimizing over α\alpha while keeping β\beta fixed gives the top-K sum as earlier, and the α\alpha is the threshold we need to cross to get into the top KK of the biased scores SijβjS_{ij} - \beta_j (consider what happens when we pair up each copy of αi\alpha_i with each active expert). The same argument when we keep α\alpha fixed gives us the equivalence for the β\beta update in our old analysis and this setup.

Let's reason about it again in the most general case, to generalize quantile balancing. When optimizing for α\alpha, nothing changes because hjh_j is separate, and it leads to the same definition that leads to them being the top-KK cutoffs for a given token. When optimizing for β\beta, things change a bit.

Note that β=0\beta = 0 is a privileged value in the general case (unlike the standard quantile balancing setup, where we can shift all the βj\beta_j by the same constant and it would not affect the result) - this is because β=λμ\beta = \lambda - \mu, and at most one of the λ\lambda and μ\mu is non-zero as we saw earlier - positive λ\lambda corresponds to a positive β\beta, which is used to penalize an expert that would be over-selected if no penalty was applied, and a negative β\beta encourages an otherwise under-selected expert to be selected. If both are 00, it means the expert is already fine and needs no help. In some way, since α\alpha is the threshold vector for each token, and it is decided by the value of SβS - \beta, β\beta can be thought of as a price that one has to pay for an expert (while gaining SS from being allotted that expert) and this β\beta is decided by the demand/supply situation due to the forces wanting to have expert balance.

Keeping α\alpha fixed, the problem for a given expert becomes, defining Rij=SijαiR_{ij} = S_{ij} - \alpha_i as the margins for token ii for expert jj, minβji(Rijβj)++hj(βj)\min_{\beta_j} \sum_i (R_{ij} - \beta_j)_{+} + h_j(\beta_j). This is a convex problem, and its local minimizers are also its global minimizers, and have zero in the subgradient. In what follows, we will ignore ties to make our life easier. Let Nj(x)N_j(x) be the number of tokens whose current margins RijR_{ij} for this expert exceed xx - this is a non-increasing function of xx. Then for x<0x < 0, the derivative of the objective is ljNj(x)l'_j - N_j(x), and for x>0x > 0, the derivative is ujNj(x)u'_j - N_j(x).

Now we derive the update - if we have ljNj(0)ujl'_j \le N_j(0) \le u'_j, we can just set βj=0\beta_j = 0. If lj>Nj(0)l'_j > N_j(0), since NjN_j is non-increasing, the minimizer satisfies βj<0\beta_j < 0 - in which case we find xx such that Nj(x)=ljN_j(x) = l'_j. Similarly, if uj<Nj(0)u'_j < N_j(0), we find xx such that Nj(x)=ujN_j(x) = u'_j. This is the same as finding the (usually non-unique) quantiles corresponding to either ljl'_j or uju'_j, and is a natural generalization of the standard quantile balancing update from Kimi K3 (modulo how the exact inverse-quantile value is chosen - this is non-unique due to gaps between token margins).

Three
Figure 5: The generalized block-coordinate slack update for permitted loads from 5 to 7, with token thresholds held fixed. The load at zero price selects one of three branches: an underloaded expert receives a negative price that reaches the lower bound, an expert already inside the interval uses price zero, and an overloaded expert receives a positive price that reaches the upper bound. Each blue vertical band contains the prices attaining the active bound; the diamond marks the displayed midpoint.

An important detail to be kept in mind is that since this is a non-smooth problem that is also not "separable" into the blocks of α\alpha and β\beta that we perform block-coordinate descent on, this algorithm does not necessarily converge, either for standard quantile balancing or our version. There may be non-benign oscillations that don't converge to the optimum (there do not seem to be any convergence guarantees from theory), but also, the convention we use can also get stuck at a non-optimal point (for the latter, consider M=N=3M = N = 3, K=q=1K = q = 1, S=[[3,1,0],[1,3,3],[1.9,2,2]]S = [[-3, 1, 0], [1, 3, -3], [-1.9, -2, 2]] - starting at β=(0,0,0)\beta = (0, 0, 0) gives α=(0,1,1.9)\alpha = (0, 1, -1.9) and β=(0,1,0)\beta = (0, 1, 0) as a fixed point, with the dual objective 55, whereas the optimal assignment is 121 \to 2, 212 \to 1, 333 \to 3 whose score is 44, so the solution found was suboptimal).

One way to reduce noisy β\beta assignments could be to check at the old βj\beta_j first, to avoid jitter from our quantile/0 assignments (or even overfitting to the new batch), but we don't pursue this. Another way is adding a penalty such as γ|βjβjold|p\gamma |\beta_j - \beta_j_{\mathrm{old}}|^p for p=1p = 1 or 22 or something similar - this still keeps the problem convex as well as solvable in the same step (potentially needing a binary search), but again has the issue of γ\gamma not being equivalent across experts - ideally such a penalty has a geometry that respects the relevant score density, which we will not pursue further due to its complexity.

Another aspect to think about is that hard constraints might not necessarily be good, and that the effective hjh_j might be better off being smoother, and it is possible that in the current formulation for infinitely large batches, expert load gets stuck at the boundaries for many experts. One way of thinking about this could be the following: as we already noted, hjh_j is a sum of a linear term and an L1 penalty - this L1 penalty controls how much load balance matters for us (and the shape of hjh_j is a plausible way of controlling what load profiles matter for us). To control this "profile importance", we might want some kind of principled way of thinking about hjh_j's shape - it should presumably be convex (to keep the problem convex). As it turns out, there indeed is a way of doing so, and that way derives hjh_j as a convex conjugate of a penalty being subtracted. Instead of hard constraints, we add a penalty term on the per-expert load in the primal objective that we want to maximize, say jϕj(dj(X))-\sum_j \phi_j(d_j(X)), where dj(X)=iXijd_j(X) = \sum_i X_{ij} is the load on expert jj, and we also keep the primal top-KK constraint (i.e., X[0,1]M×NX \in [0, 1]^{M \times N} becomes X[0,1]M×NX \in [0, 1]^{M \times N} with jXij=K\sum_j X_{ij} = K, call this constrained set 𝒳\mathcal{X}) instead of dualizing it. Note that integrality of the optimum doesn't carry over for general ϕ\phi, but we will still think about this nevertheless. If ϕj*\phi_j^* is the convex conjugate of ϕj\phi_j, then ϕj(d)=infβ(ϕj*(β)βd)-\phi_j(d) = \inf_\beta (\phi_j^* (\beta) - \beta d). Plugging this in, we get supX𝒳infβS,Xjβjdj(X)+jϕj*(βj)\sup_{X \in \mathcal{X}} \inf_{\beta} \langle S, X \rangle - \sum_j \beta_j d_j(X) + \sum_j \phi_j^*(\beta_j). If this satisfies the finite dimensional Fenchel-Rockafellar conditions, then we can swap the sup\sup and inf\inf, to get infβsupX𝒳ij(Sijβj)Xij+jϕj*(βj)\inf_\beta \sup_{X \in \mathcal{X}} \sum_{ij} (S_{ij} - \beta_j) X_{ij} + \sum_j \phi_j^*(\beta_j), which shows that hjh_j corresponds to the role of a convex conjugate ϕj*\phi_j^* of the negative of the penalty, and indeed, the convex conjugate of hjh_j is the indicator function of the interval (\infty outside the interval, 00 inside the interval). Of course, if hjh_j is not piecewise linear, one would in general need a binary search (or similar) to find the zero subgradient points.

It is also possible that noise softens the boundaries more than the hard constraints tell us, because we have to satisfy the "oracle" balance plus the noise, instead of just the oracle balance. Solving the problem exactly is also likely not ideal, because as we mentioned earlier, the optimal ββ-s can be very different between batches (and also be overfitted) unless we have some kind of regularization/damping towards the previous β\beta.

We tried two special cases of this generalization and compared it to the usual perfect-balance QB setup. In the slack setup, we use [lj,uj]=[round((1l)q),round((1+u)q)][l'_j, u'_j] = [\mathrm{round}((1 - l)q), \mathrm{round}((1 + u)q)]. The other setup has no slack but a different exact load assignment for each expert, proportional to 1/(c+j)1/(c + j) for the jj-th expert, for a specified value of cc. The results are discussed later.

Improving quantile balancing: design and results

Let's first list down some considerations one may have for a method of solving the load balancing problem, and think about them in relation to quantile balancing.

For an online/stochastic optimization algorithm, there are some things we care about, such as robustness to noise, convergence speed, proximity to the previous iterate/staying within a trust region, and flexibility/generality in a meaningful way. We also care about the implementation being reasonably fast (either exactly or while being a reasonable approximation).

Let's come back to quantile balancing. Specific to quantile balancing, there are a few things: the objective function (linear sum of scores), the constraints (exact balancing), and the actual optimization algorithm (histogram-based block coordinate descent like stochastic algorithm amortized over the run).

Choosing the assignment score: sigmoid vs. logit

As far as the objective function goes, it is unclear what the scores SijS_{ij} for a given token-expert assignment should be. Standard quantile balancing (as done in Kimi K3 and the Jianlin Su blog posts) uses SijS_{ij} as the gate value. A motivation for the score being monotonic in the value of the gate is that the gate affects the expert's magnitude of contribution, so it is in some sense an importance score. However, this does not tell us that it must be the gate value itself - why not a higher power of the gate (such as its square), or the gate's pre-sigmoid logit? One way to interpret the objective function is how it scores things - for a sigmoid gate, it may not differentiate much between a gate value of 0.990.99 and 0.9990.999, but the pre-sigmoid logit score would (which could be either beneficial or not). In the end, the ideal case is that we have good (or at least steerable) load balance while also having better loss.

Note that different score functions will generally lead to different optimal assignments. In the case where the optimal choice of score function is not clear from a loss or training dynamics perspective, we may want to turn to other desiderata, as long as it does not adversely affect the loss and other things we care about. So in some sense, we have some freedom of choosing SijS_{ij} - for our experiments we choose the pre-sigmoid logit as one choice. On a skim, it might seem like a natural choice since it is more likely to behave like a typical pre-activation of a neural network (which in literature is typically assumed to be Gaussian-like or just constant std with both positive and negative values and doesn't necessarily saturate) than the sigmoid. But there are other more important reasons behind this choice, which are: compatibility with implementation, robustness to noise, and equivariance under linear transforms, and we discuss these below.

Consider the implementation for quantile balancing - it requires us to compute quantiles of a quantity over all tokens, for each expert separately (the other quantile computation is fairly simple to do locally). However, doing this exactly has a non-trivial communication overhead, especially in large scale training. The Kimi K3 paper's solution to this is to use a histogram - since the scores are all sigmoids (and hence bounded), one can just compress things into a βminΘ(1)\beta_{\min} - \Theta(1) to βmax+Θ(1)\beta_{\max} + \Theta(1) range. The paper claims that the error in β\beta is governed by the bin sizing. However, this is not the full story - consider a router with a low logit temperature (or low entropy in some sense, for a single token). Scores then concentrate around 0 or 1. Similarly (but a bit less crucially), when the router has a high logit temperature, the bounds on sigmoid are not tight (the actual part of the support being used is small), and many bins are wasted. If you recall the discussion on Newton-ish methods from the previous section, for good load balancing, it is not only these bin sizes that matter, and we need to also care about how the biased scores are packed within bins, for good quantile estimation as far as the resulting load error is concerned (e.g., in an extreme case, a bin can have the entire distribution and not provide good enough resolution even after interpolation, which assumes a uniform distribution inside the bin). So for a loose analogy, this is like using Newton's method or some other second order algorithm for an ill-conditioned problem under low precision.

A
Figure 6: Equal histogram widths need not imply equal load error. In this constructed example the same price error of 0.10 crosses one token margin in a low-density bin but eight margins in a high-density bin, changing the implied load by one route and eight routes respectively. This isolates within-bin sensitivity.

For a given expert jj, we want to compute a quantile of SijαiS_{ij} - \alpha_i over all tokens ii. When SijS_{ij} is a pre-sigmoid logit, one issue we face while trying to apply compression is that it is not bounded naturally like sigmoid is. So we need to compress the quantiles from \mathbb{R} to some bounded interval (say [1,1][-1, 1]), while also preserving the resolution at the tails more than e.g. sigmoid, for extreme sparsity. One way of doing so is to use the map xx1+|x|x \mapsto \frac{x}{1 + |x|} (we name this "compact"), and indeed we use this for most of our experiments. However, there is a better way. For a sensible optimization algorithm, one would expect that βj\beta_j and the distributions of SijαiS_{ij} - \alpha_i do not change too much between consecutive batches. In that case, the relevant quantile of Sijαiβold,jS_{ij} - \alpha_i - \beta_{\mathrm{old}, j} would roughly be zero. So if we compress these values instead of SijαiS_{ij} - \alpha_i, with an [1,1]\mathbb{R} \to [-1, 1] compression function which has a good resolution in [δ,δ][-\delta, \delta] where δ\delta is the typical change in β\beta between consecutive steps, we have already solved this problem to some extent (it is likely possible to improve it by estimating δ\delta, but we just use the same compression function x/(1+|x|)x / (1 + |x|) after this shift; we call this method "relative-compact"). We notice the most stark benefits of this in settings where the desired expert loads are highly non-uniform.

A
Figure 7: A fixed histogram spends its resolution differently in sigmoid and logit score spaces. The three histograms use the same simulated router batch at temperature 0.125 and show one expert with 64 bins, chosen for visibility rather than the 1,000 bins used in the training runs. The black and dashed red lines are the exact quantile and histogram estimate, and the shaded interval is the selected bin; centering logit margins at the previous price restores fine resolution near the expected update.

So the above deals with histogram resolution as a potential issue, which can also be verified using numerical simulations. But what happens if instead of using a histogram, we just used the entire distribution, with the caveat that we still have minibatch noise (so perhaps it does not matter as much at large pretraining batch sizes)? Does sigmoid still fare poorly? The answer seems to be yes, at least a bit worse, if one checks numerical simulations for instance. A somewhat nice theoretical aside is that when we transform the logits by a positive scaling and a translation, SijS_{ij} and αi\alpha_i both translate by the same amount, so the things we need to take the quantile of scale by the same amount. Under the exact setting (no histogram), this changes the "conditioning" everywhere uniformly, unlike in the sigmoid case, where things can start to concentrate in different places.

Expert-load
Figure 8: A controller-only simulation of next-batch expert-load imbalance as router temperature decreases. Each batch routes using the price estimated from the preceding batch; points are means over six runs and bands show one standard deviation. Exact logit QB is scale-equivariant in this construction, while finite-bin sigmoid QB (and exact sigmoid, to a lesser degree) degrades at low temperatures; the exact curves are no-histogram references.

As an aside, note that, as mentioned earlier, we don't necessarily have to limit ourselves to global load balancing, and that it might be good to have some "local" load balancing such as across sequences or microbatches. Moving Quantile Balancing (MQB) by Jianlin Su is one way to do so for sequence-level load balancing. It maintains a histogram (using an EMA) that is causal and its strength is controllable by a multiplier 1\le 1. We don't focus on it here, however.

When I looked at the public implementations of quantile balancing after running the experiments below, the situation was a bit varied. The initial Megatron implementation uses QB with raw router logits, as we propose, but it also does not use a histogram (and instead uses a crude approximation by averaging per-microbatch quantiles). It cites Marin which implemented quantile balancing, and one of their agent-generated reports says that using the sigmoid gate instead of pre-sigmoid logits was catastrophically worse (in their setup), and compression also made things much worse for them. Separately, NVIDIA's Kimi K3 support tracker links to a global-batch histogram implementation PR which tries to match K3 by applying QB to raw sigmoid scores and using histograms too.

Now let's look at how sigmoid vs logit fare, at four different sparsity scales. The hypothesis here is that logit would be better than sigmoid as far as balance is concerned, with potential benefits in loss, but not guaranteed.

Experimental setup

Architecture, tuning, and metrics

We use a fairly standard transformer backbone which can be seen, alongside optimization details, in code. For the MoE layer, we use sigmoid routing, one shared expert, Kimi K3 style RMSNorm after combining routed experts. Model sizes are (total/active, including embeddings/lm head): 293.3M/15.5M, 293.3M/26.1M, 285.5M/29.6M and 285.5M/38.0M respectively. So experiments are quite small scale, but we may expect many of our proposed improvements to hold at scale, and at least motivate larger-scale testing.

The four sparsity setups are D384-R2 (1+2/1601+2/160 (shared + routed / total)), D384-R8 (1+8/1601+8/160), D512-R2 (1+2/641+2/64) and D512-R4 (1+4/641+4/64). We first tuned strong standard QB baselines (for optimal loss) for all eight sparsity/router combinations with thousands of HEBO trials (HEBO was run on each setup independently) as well as sharing top configurations across stages (to reduce issues related to one router family or the other being harder to tune), and different seeds across search trials to avoid bias. After selecting those baselines, we froze all other hyperparameters and tuned only the routing-specific slack bounds or inverse-rank offset (this makes the results for our new changes pessimistic, and retuning can likely help increase the gains we see, but we didn't pursue this further). In the end, we ran the best candidates with the same 5 (held-out) seeds, and we report the means over those seeds. Paired comparisons are done using the same seeds, and we also compute the 95% paired tt intervals. For potential improvements (loss-wise or balance-wise) to quantile balancing, we only test the hypotheses we mention below instead of iterating.

For balance, we look at two values, one for absolute balance and the other for "steerability". Let U-MAD be the mean absolute deviation from uniform perfect load, divided by that perfect load. Let B-MAD be the mean absolute distance outside the desired per-expert bounds, divided by the perfect load again. For exact uniform balance, they coincide, but for slack and inverse profiles, U-MAD tells us about imperfect load balance and B-MAD tells us how accurately QB follows the desired interval/target. We also report the corresponding signed extreme violations. MaxVio is the largest overload and MinVio the largest underload, and there are two variants: for the uniform-reference variants, they are the maximum and minimum of (niq)/q(n_i - q)/q; for the bound-reference variants, loads inside their assigned interval have zero violation and loads outside it retain the sign of their distance from the nearest bound, again divided by qq. We report both the mean across MoE layers and the worst layer. Each table entry is a five-seed mean computed after the layer aggregation within each run. The routing statistics are for the final training batch under the previous bias, while val loss is computed using the bias updated from that batch. Noise in val loss was typically around 0.0020.002.

Six
Figure 9: The same six expert loads measured against two references. U-MAD and its signed extrema measure deviation from the uniform load qq; B-MAD and its extrema measure only distance outside the assigned interval. Loads inside that interval therefore contribute to U-MAD but contribute zero to B-MAD. Every displayed deviation is divided by qq.

Do logit scores improve balance and loss?

We compare the sigmoid and logit versions of QB below. The loss difference is logit minus sigmoid (so negative loss difference means logit is better). "Perfect compact" refers to perfect balance using x/(1+|x|)x / (1 + |x|) for logit and the standard setup for sigmoid, "perfect relative" refers to centering using the old bias before using x/(1+|x|)x / (1 + |x|) for logit and the standard setup for sigmoid, "slack" refers to relaxed load bounds, "inverse" refers to the setting with the 1/(c+j)\propto 1/(c + j) expert loads, and ErE_r is the number of routed experts.

Full sigmoid/logit comparison table

In the last four columns, U means the uniform perfect-load reference, B means the assigned-bound reference, and S/L means sigmoid/logit. Each entry is a signed (MaxVio,MinVio)(\mathrm{MaxVio},\mathrm{MinVio}) pair. The U and B columns coincide for exact-uniform settings by definition.

Architecture Pair Parameters (sigmoid / logit) Val loss (sigmoid / logit) Paired loss difference [95% interval] U-MAD (sigmoid / logit) B-MAD (sigmoid / logit) Mean U Max/Min (S/L) Worst U Max/Min (S/L) Mean B Max/Min (S/L) Worst B Max/Min (S/L)
D384-R2 perfect compact -- 2.901449 / 2.900888 -0.000560 [-0.003219, +0.002099] 0.0782 / 0.0532 0.0782 / 0.0532 S (+0.4281, -0.2369) / L (+0.1937, -0.2301) S (+0.6380, -0.4342) / L (+0.3201, -0.4462) S (+0.4281, -0.2369) / L (+0.1937, -0.2301) S (+0.6380, -0.4342) / L (+0.3201, -0.4462)
D384-R2 perfect relative -- 2.901449 / 2.900683 -0.000766 [-0.004327, +0.002796] 0.0782 / 0.0537 0.0782 / 0.0537 S (+0.4281, -0.2369) / L (+0.1953, -0.2291) S (+0.6380, -0.4342) / L (+0.2867, -0.4472) S (+0.4281, -0.2369) / L (+0.1953, -0.2291) S (+0.6380, -0.4342) / L (+0.2867, -0.4472)
D384-R2 slack l=0.1,u=0.5l=0.1,u=0.5 / l=0.1,u=0.5l=0.1,u=0.5 2.898040 / 2.898441 +0.000401 [-0.002754, +0.003557] 0.2784 / 0.2483 0.0985 / 0.0789 S (+0.5968, -0.7538) / L (+0.5575, -0.5195) S (+0.7627, -0.8260) / L (+0.6556, -0.6748) S (+0.0979, -0.6538) / L (+0.0652, -0.4194) S (+0.2628, -0.7259) / L (+0.1556, -0.5748)
D384-R2 inverse c=8Erc=8E_r / c=8Erc=8E_r 2.902396 / 2.900841 -0.001555 [-0.004741, +0.001631] 0.0830 / 0.0589 0.0787 / 0.0528 S (+0.4829, -0.2467) / L (+0.2191, -0.2447) S (+0.8263, -0.4272) / L (+0.3298, -0.4820) S (+0.4743, -0.2377) / L (+0.1877, -0.2293) S (+0.8323, -0.4249) / L (+0.2981, -0.4734)
D384-R8 perfect compact -- 2.844130 / 2.842805 -0.001325 [-0.004043, +0.001393] 0.0488 / 0.0435 0.0488 / 0.0435 S (+0.3870, -0.2541) / L (+0.3457, -0.2531) S (+0.6189, -0.3819) / L (+0.5946, -0.3799) S (+0.3870, -0.2541) / L (+0.3457, -0.2531) S (+0.6189, -0.3819) / L (+0.5946, -0.3799)
D384-R8 perfect relative -- 2.844130 / 2.843154 -0.000976 [-0.003565, +0.001613] 0.0488 / 0.0435 0.0488 / 0.0435 S (+0.3870, -0.2541) / L (+0.3503, -0.2441) S (+0.6189, -0.3819) / L (+0.6872, -0.3980) S (+0.3870, -0.2541) / L (+0.3503, -0.2441) S (+0.6189, -0.3819) / L (+0.6872, -0.3980)
D384-R8 slack l=0.1,u=1l=0.1,u=1 / l=0.4,u=0.1l=0.4,u=0.1 2.841212 / 2.842229 +0.001017 [-0.002485, +0.004519] 0.2093 / 0.0781 0.0381 / 0.0079 S (+0.9616, -0.3694) / L (+0.4470, -0.4627) S (+1.0347, -0.4823) / L (+0.7575, -0.5107) S (+0.0083, -0.2694) / L (+0.3470, -0.0635) S (+0.0374, -0.3823) / L (+0.6575, -0.1107)
D384-R8 inverse c=0.125Erc=0.125E_r / c=0.25Erc=0.25E_r 2.843212 / 2.841549 -0.001663 [-0.004448, +0.001121] 0.5117 / 0.3879 0.0475 / 0.0421 S (+2.5337, -0.6420) / L (+1.4764, -0.5846) S (+2.7900, -0.7285) / L (+1.6135, -0.7007) S (+0.3898, -0.2724) / L (+0.3375, -0.2690) S (+0.5968, -0.4187) / L (+0.5685, -0.4030)
D512-R2 perfect compact -- 2.834531 / 2.829211 -0.005319 [-0.009065, -0.001574] 0.0596 / 0.0440 0.0596 / 0.0440 S (+0.2577, -0.1659) / L (+0.1511, -0.1673) S (+0.5016, -0.3464) / L (+0.3244, -0.3420) S (+0.2577, -0.1659) / L (+0.1511, -0.1673) S (+0.5016, -0.3464) / L (+0.3244, -0.3420)
D512-R2 perfect relative -- 2.834531 / 2.830004 -0.004527 [-0.007968, -0.001086] 0.0596 / 0.0424 0.0596 / 0.0424 S (+0.2577, -0.1659) / L (+0.1352, -0.1362) S (+0.5016, -0.3464) / L (+0.2117, -0.3004) S (+0.2577, -0.1659) / L (+0.1352, -0.1362) S (+0.5016, -0.3464) / L (+0.2117, -0.3004)
D512-R2 slack l=0,u=2l=0,u=2 / l=0,u=0.5l=0,u=0.5 2.833618 / 2.827550 -0.006068 [-0.010053, -0.002083] 0.3200 / 0.1936 0.1600 / 0.0969 S (+1.5599, -0.4643) / L (+0.4685, -0.3350) S (+1.7797, -0.6137) / L (+0.5343, -0.4402) S (+0.0000, -0.4642) / L (+0.0064, -0.3350) S (+0.0000, -0.6137) / L (+0.0349, -0.4402)
D512-R2 inverse c=Erc=E_r / c=Erc=E_r 2.835464 / 2.829649 -0.005815 [-0.008933, -0.002697] 0.1780 / 0.1752 0.0609 / 0.0435 S (+0.5387, -0.3491) / L (+0.4791, -0.3343) S (+0.6932, -0.5208) / L (+0.5660, -0.4573) S (+0.2876, -0.1983) / L (+0.1468, -0.1633) S (+0.6442, -0.3816) / L (+0.2531, -0.3021)
D512-R4 perfect compact -- 2.807199 / 2.804300 -0.002899 [-0.004877, -0.000922] 0.0637 / 0.0410 0.0637 / 0.0410 S (+0.5813, -0.1997) / L (+0.1610, -0.1994) S (+1.3182, -0.3533) / L (+0.3455, -0.3818) S (+0.5813, -0.1997) / L (+0.1610, -0.1994) S (+1.3182, -0.3533) / L (+0.3455, -0.3818)
D512-R4 perfect relative -- 2.807199 / 2.804427 -0.002772 [-0.005549, +0.000005] 0.0637 / 0.0392 0.0637 / 0.0392 S (+0.5813, -0.1997) / L (+0.1506, -0.1808) S (+1.3182, -0.3533) / L (+0.3044, -0.3375) S (+0.5813, -0.1997) / L (+0.1506, -0.1808) S (+1.3182, -0.3533) / L (+0.3044, -0.3375)
D512-R4 slack l=0.1,u=1l=0.1,u=1 / l=0.4,u=1l=0.4,u=1 2.804404 / 2.801389 -0.003015 [-0.006773, +0.000744] 0.2745 / 0.3976 0.0739 / 0.0191 S (+0.8959, -0.4180) / L (+0.8917, -0.5328) S (+0.9817, -0.5244) / L (+0.9953, -0.6079) S (+0.0022, -0.3179) / L (+0.0016, -0.1328) S (+0.0160, -0.4244) / L (+0.0126, -0.2079)
D512-R4 inverse c=0.5Erc=0.5E_r / c=Erc=E_r 2.807132 / 2.803575 -0.003556 [-0.006556, -0.000557] 0.2721 / 0.1754 0.0624 / 0.0407 S (+0.8834, -0.4579) / L (+0.4867, -0.3672) S (+1.3415, -0.5962) / L (+0.5910, -0.5316) S (+0.5686, -0.1891) / L (+0.1745, -0.2093) S (+1.2025, -0.3103) / L (+0.3420, -0.3419)

For exact uniform matching (perfect balance), we notice that logit has lower U-MAD/B-MAD throughout compared to sigmoid, showing better balance than sigmoid in general. When slack/inverse settings are comparable, it has lower B-MAD too. U-MAD is proportional to a total-variation distance to the uniform distribution over experts, and MaxVio is more of an LL_{\infty} version of that - we see much larger relative gains in MaxVio in our experiments than in U-MAD.

Layer-mean
Figure 10: Final-batch signed routing extremes after averaging across MoE layers within each run and then across five seeds. Each horizontal interval runs from MinVio to MaxVio for sigmoid or logit QB; the left panel uses uniform load as the reference and the right uses each expert's assigned bounds. Underlined rows use the same released load constraints for sigmoid and logit.
Worst-layer
Figure 11: The corresponding worst-layer view. For each run, each endpoint is taken from its most extreme MoE layer before averaging across the five seeds, so MinVio and MaxVio may come from different layers. The two reference-specific horizontal scales are the same as in the layer-mean figure above.

For loss, logit seems either comparable or better across all settings with perfect balance, but the story is a bit mixed across slack settings. So the primary benefit of logit seems to be better/more steerable balance (less violation of desired bounds) compared to sigmoid.

Relaxing exact load constraints

Now that we've considered the objective function, let's come to the constraints.

The constraints in quantile balancing are quite exact, and there is no way to control how strong the load balancing is. As discussed in the previous section, perfect balance is not really needed if things such as MoonEP can reduce the serving/training penalty of not having perfect balance. There are at least two orthogonal-looking ways of deviating from uniformity - either we give experts some slack, or assign exact loads that are not uniform. The former might be more unpredictable, but for the latter, under perfect balance, it is possible to statically allocate experts and have load balancing across different ranks, if the exact load assignment is chosen appropriately (there is a wide range of choices that lets this work).

One intuition is that a model learns "greedily" at each step, which may lead to feedback loops leading to expert starvation. Load balancing forces it to have a good balance, because in the longer term it may be better to share capacity across experts. One of the predictions we can make is that for extremely sparse MoEs, a lower bound on the expert load is much more important than upper bounding. Controlling the slack we give to these bounds (or controlling the deviation from uniformity) can be a good way to balance the model's greedy choices with the model's long term health/training performance (and also load-balance-wise and loss-wise optimality). It could even be possible to schedule this rather than have it be static, but this would be quite complicated so we don't pursue this idea further.

So we try the following two things: first, instead of load being exactly qq, we assign bounds (1l)q(1 - l) q and (1+u)q(1 + u) q where l,u0l, u \ge 0 and l1l \le 1 (to see if we benefit from slack), and second, we assign non-uniform loads proportional to 1/(c+j)1/(c + j) for expert rank jj, with c=kErc = k E_r for a tuned multiplier kk on top of the total number of routed experts ErE_r and appropriate rounding (to see if we benefit from a certain non-uniform load distribution). The goal is to check whether this translates to loss improvements.

Does load slack improve loss?

We see that slack tends to help compared to enforcing perfect balance via quantile balancing, as far as loss goes.

Full slack comparison table
Architecture Router Selected (l,u)(l,u) Perfect loss Slack loss Slack minus perfect [95% interval] Mean U Max/Min Worst U Max/Min Mean B Max/Min Worst B Max/Min
D384-R2 sigmoid (0.1,0.5)(0.1,0.5) 2.901449 2.898040 -0.003409 [-0.006757, -0.000061] (+0.5968, -0.7538) (+0.7627, -0.8260) (+0.0979, -0.6538) (+0.2628, -0.7259)
D384-R2 logit (0.1,0.5)(0.1,0.5) 2.900888 2.898441 -0.002448 [-0.004894, -0.000001] (+0.5575, -0.5195) (+0.6556, -0.6748) (+0.0652, -0.4194) (+0.1556, -0.5748)
D384-R8 sigmoid (0.1,1)(0.1,1) 2.844130 2.841212 -0.002918 [-0.004402, -0.001434] (+0.9616, -0.3694) (+1.0347, -0.4823) (+0.0083, -0.2694) (+0.0374, -0.3823)
D384-R8 logit (0.4,0.1)(0.4,0.1) 2.842805 2.842229 -0.000576 [-0.001389, +0.000236] (+0.4470, -0.4627) (+0.7575, -0.5107) (+0.3470, -0.0635) (+0.6575, -0.1107)
D512-R2 sigmoid (0,2)(0,2) 2.834531 2.833618 -0.000913 [-0.003746, +0.001920] (+1.5599, -0.4643) (+1.7797, -0.6137) (+0.0000, -0.4642) (+0.0000, -0.6137)
D512-R2 logit (0,0.5)(0,0.5) 2.829211 2.827550 -0.001661 [-0.003429, +0.000107] (+0.4685, -0.3350) (+0.5343, -0.4402) (+0.0064, -0.3350) (+0.0349, -0.4402)
D512-R4 sigmoid (0.1,1)(0.1,1) 2.807199 2.804404 -0.002795 [-0.006396, +0.000806] (+0.8959, -0.4180) (+0.9817, -0.5244) (+0.0022, -0.3179) (+0.0160, -0.4244)
D512-R4 logit (0.4,1)(0.4,1) 2.804300 2.801389 -0.002911 [-0.004233, -0.001588] (+0.8917, -0.5328) (+0.9953, -0.6079) (+0.0016, -0.1328) (+0.0126, -0.2079)

The selected values of (l,u)(l, u) (and relative performance of other runs) show that it seems generally more important to keep the lower bounds somewhat tight while the upper bounds might be relaxed, especially for very sparse MoEs. The idea is that for very sparse MoEs, it is much more important to avoid underloaded experts (perhaps more learning signal is the bottleneck for these experts) than to tightly constrain the upper bounds on the load (which also gives evidence for the idea that a single shared expert might not be the optimal way to model high frequency experts).

Do inverse-rank load targets help?

We don't see a clear general benefit (and it doesn't harm loss either), so it does not seem that enforcing a distribution improves (or worsens) the loss as compared to perfect balancing. So it is possible that having slack matters more than an exact distribution, but I would also expect this setup to be at least slightly more sensitive to hyperparameters than the slack setup. Figuring out/learning the optimal exact distribution and/or its schedule (or in general, the exact lower and upper bounds), or further tuning, is out of scope, but could be worth trying.

Full inverse-rank comparison table
Architecture Router Selected cc Matching histogram Perfect loss Inverse loss Inverse minus perfect [95% interval] Mean U Max/Min Worst U Max/Min Mean B Max/Min Worst B Max/Min
D384-R2 sigmoid 8Er8E_r compact 2.901449 2.902396 +0.000948 [-0.000759, +0.002655] (+0.4829, -0.2467) (+0.8263, -0.4272) (+0.4743, -0.2377) (+0.8323, -0.4249)
D384-R2 logit 8Er8E_r relative-compact 2.900683 2.900841 +0.000158 [-0.001597, +0.001913] (+0.2191, -0.2447) (+0.3298, -0.4820) (+0.1877, -0.2293) (+0.2981, -0.4734)
D384-R8 sigmoid 0.125Er0.125E_r compact 2.844130 2.843212 -0.000918 [-0.002940, +0.001105] (+2.5337, -0.6420) (+2.7900, -0.7285) (+0.3898, -0.2724) (+0.5968, -0.4187)
D384-R8 logit 0.25Er0.25E_r relative-compact 2.843154 2.841549 -0.001605 [-0.004023, +0.000814] (+1.4764, -0.5846) (+1.6135, -0.7007) (+0.3375, -0.2690) (+0.5685, -0.4030)
D512-R2 sigmoid ErE_r compact 2.834531 2.835464 +0.000933 [-0.000640, +0.002507] (+0.5387, -0.3491) (+0.6932, -0.5208) (+0.2876, -0.1983) (+0.6442, -0.3816)
D512-R2 logit ErE_r relative-compact 2.830004 2.829649 -0.000355 [-0.002148, +0.001439] (+0.4791, -0.3343) (+0.5660, -0.4573) (+0.1468, -0.1633) (+0.2531, -0.3021)
D512-R4 sigmoid 0.5Er0.5E_r compact 2.807199 2.807132 -0.000067 [-0.003003, +0.002868] (+0.8834, -0.4579) (+1.3415, -0.5962) (+0.5686, -0.1891) (+1.2025, -0.3103)
D512-R4 logit ErE_r relative-compact 2.804427 2.803575 -0.000852 [-0.005657, +0.003953] (+0.4867, -0.3672) (+0.5910, -0.5316) (+0.1745, -0.2093) (+0.3420, -0.3419)
Three
Figure 12: Paired validation-loss differences across the five matched evaluation seeds. Diamonds, circles, or squares show paired means, horizontal bars show paired 95% tt intervals, and grey points show individual seed differences. Negative values favor the tested change: logit over sigmoid in the first panel, and slack or inverse-rank targets over their matching perfect-balance controls in the other panels.

Alternatives to the quantile update

The remaining part is the actual algorithm being used to update expert prices. We've seen most of the relevant tradeoffs, so we will summarize those and talk about some in detail. We saw earlier how, under the same linear programming setup, aux-loss-free load balancing is a way to do gradient descent like algorithms on the dual and is quite cheap, but their step size doesn't handle the density of scores near the boundary, for which a Newton-like update could be plausibly thought of (though this problem is non-smooth, and needs approximation) - a diagonal Hessian-like approximation could be plausible. QB instead approximately solves each β\beta coordinate problem so it automatically adapts to such conditioning in some way, though it does not necessarily converge to an optimal solution. Optimizing using only a single batch's worth of data could also overfit on the batch and leave the estimate useless for the next update, so damping the update or checking whether the previous value gives a reasonable load or adding a proximal term that penalizes distance from previous parameter could help. These are relatively cheap and can be done using a histogram like standard QB. As we mentioned earlier, there seems to not be a good way of using stuff like bundle methods, ADMM, primal dual methods, LP/MCF solvers, Sinkhorn-style methods and so on, so currently there does not seem to be an obviously theoretically superior solver once training cost is taken into account, among the general purpose convex optimization solvers. Figuring this out could be interesting future work.

Acknowledgements

I would like to acknowledge various people in the Eleuther AI discord for discussing mixture of experts recently, GPT 5.6 Sol for writing up the outline section, creating images and writing most of the code.

References

Cite this post
@online{quantile-balancing,
  author    = {nor},
  title     = {Understanding, extending and improving quantile balancing},
  year      = {2026},
  month     = {09},
  day       = {02},
  url       = {https://nor-blog.pages.dev/posts/2026-09-02-quantile-balancing/},
}