Mixture of Experts
Mixture of Experts (MoE), the architectural pattern that decouples parameters from compute. Replace the dense feed-forward block with a sparse mixture of expert sub-networks plus a router that picks which experts process each token. The result is dramatically more parameters at roughly the same FLOPs per token. Used by Mixtral, DeepSeek-V3, GLaM, Switch Transformer, and reportedly GPT-4. This chapter covers the MoE block, top-k routing, load balancing, expert capacity, modern variants, and why MoE is harder to train and serve than dense models.
Chapter 10 closed the training-side arc. We now know how to train a dense transformer at any scale, what hardware to use, what frameworks to reach for, what numerical precision to keep, and roughly how many dollars it costs. Chapters 11 and 12 cover the major architectural alternatives, variants that change the building block itself rather than how it is trained.
Mixture of Experts is the most consequential architectural change since the original transformer. The intuition is short. A dense feed-forward layer uses all its parameters for every token. That couples parameter count to FLOPs per token, doubling parameters doubles per-token compute, which means inference cost scales linearly with model size. MoE breaks the coupling. It replaces the dense FFN with many small “expert” sub-networks plus a router that picks which experts handle each token. Total parameters can grow large while compute per token stays small.
The pattern was first proposed in deep learning by Shazeer et al. in 2017 and gradually became dominant: Switch Transformer (2021), GLaM (2021), Mixtral 8x7B (2024), DeepSeek-V3 (late 2024). GPT-4 is reportedly MoE. Mixtral 8x7B has B total parameters but uses only about B per token, outperforming Llama-2 70B at a fraction of the inference cost. This chapter explains how MoE works, why it is hard to train, and the modern variants that have made it production-viable.
The setup: scaling parameters without scaling FLOPs
The fundamental scaling problem of dense models can be stated in one sentence: parameters and FLOPs per token are coupled. A B-parameter dense model uses B parameters’ worth of compute on every single token it processes. A hypothetical B dense model would use B per token. There is no way to “scale up the brain” without scaling up the per-token bill. Frontier-scale inference costs grow linearly with total parameters, and that linear scaling is the most uncomfortable economic fact about deploying ever-larger models.
MoE’s idea is that this coupling is not actually necessary. Not every parameter has to contribute to every token. The architecture builds sparse activation in directly: a model can have “experts” but only the top of them are computed for any given token. Total parameters scale with times the per-expert size; active parameters per token scale with times the per-expert size. Choosing and already gives a multiplier on capacity per dollar of inference compute. Capacity is decoupled from compute.
The intuition is roughly biological. A human brain does not activate every neuron for every thought: different cortical regions handle different tasks, and only the relevant ones engage. MoE applies this principle to transformers. Different experts learn to specialize through training; the router learns which expert to send each token to. After training, code tokens tend to route to one set of experts, factual tokens to another, syntax tokens to a third. Specialization emerges; it is not built in.
The economic argument is what has made MoE the dominant frontier-architecture choice for open-weights models. At inference time, active parameters drive cost, not total parameters. Mixtral 8x7B with about B active parameters per token routinely beats Llama-2 70B with all B active. Quality-per-inference-dollar is the metric that matters in production, and on that metric MoE wins.
The MoE block: router + experts
What changes
The standard transformer block, from Chapter 5, is: attention, then layer norm, then a dense feed-forward network, then another layer norm. Residual connections wrap each sub-block. The MoE block is: attention, then layer norm, then an MoE layer, then another layer norm. The only difference is the middle component. Attention is unchanged. Residuals are unchanged. Layer norms are unchanged. One sub-block out of the block, one sub-block in.
The MoE layer itself has two components. The first is a small router (sometimes called the gating network), a single linear layer that produces one logit per expert. The second is a collection of separate experts, each itself a full FFN with its own parameter set. The router decides; the experts compute.
The minimalism of the swap matters. Everything that took Chapters 4–6 to build (multi-head attention, causal masking, positional encoding, residual scaffolding) keeps working without modification. MoE is not a redesigned architecture; it is a targeted replacement of one component.
The mathematics
For a standard FFN, the equation is:
where is a nonlinearity (GeLU in most production transformers; the runnable demo below uses ReLU for simplicity). One pass through, all parameters used.
For the MoE layer, the equation generalizes to a weighted sum of expert outputs, where most weights are zero:
The router weights produce logits , one per expert. The operator selects the indices of the largest logits; softmax is then taken over only those values, producing gate values that sum to across the selected experts. Each selected expert runs on ; the outputs are weighted-summed with the gate values to produce the layer’s output.
The critical observation is that only of the FFNs are ever actually computed. The remaining have and contribute nothing. They do not need to be invoked at all. FLOPs per token scale with , independent of .
A runnable forward pass makes the mechanics concrete. The implementation below is a naive numpy version, clear, not fast, but it shows how routing, selection, and weighted combination compose into a single MoE layer.
Top-k routing: the gating function
The routing decision
The router is the smallest piece of the MoE layer and the most consequential. It is a single linear layer , tiny compared to the per-expert FFNs that dominate parameter count. For each token , the router produces logits , one logit per expert, summarizing how well-suited each expert is for this token.
The selection step is discrete. Top- picks the indices of the largest logits. Only the experts at those indices will be invoked. The remaining experts are not run at all: there is no zero output from them; they simply do not appear in the forward pass.
After selection, softmax over only the selected logits produces gate values that sum to . These gates modulate the expert outputs in the weighted sum. Critically, softmax is not applied over the full -logit vector and then masked; it is applied over the selected logits, so gate values for the chosen experts are a proper probability distribution among themselves.
The hybrid nature of this, discrete on top, continuous underneath, is what makes MoE trainable. The top- operation is non-differentiable, but gradients still flow through the soft gate values that modulate expert outputs in the weighted sum. The router learns from those gradients which experts to favor for which kinds of input.
Top-1 vs top-2
Top-1 routing (Switch Transformer) is the simplest possible MoE: one expert per token. The gate value is just the softmax probability of the selected expert (or, in some variants, simply ). No convex combination is needed. Switch Transformer demonstrated that top-1 is sufficient for trillion-parameter-scale training, and its simplicity makes it pedagogically clean.
Top-2 routing (GLaM, Mixtral) sends each token through two experts and combines their outputs with the softmax-normalized gates. It is more expressive: a token that is “mostly code, partly math” can blend a code expert with a math expert at appropriate weights. The cost is roughly the FFN FLOPs per token relative to top-1.
The trade-off has converged on top-2 as the modern standard. GLaM showed that the extra FFN cost is well worth the quality gain; Mixtral confirmed it at open-weights production scale. Higher values (top-4, top-8) have been tried but tend to degrade the parameter-decoupling benefit: past , you are buying more compute without much extra capacity.
Active vs total parameters
The bookkeeping for an MoE model has two distinct numbers that matter for completely different things.
Total parameters = experts per-expert FFN size, plus the shared attention, embedding, output, and router parameters that every layer has anyway. For Mixtral 8x7B: about B parameters total, with experts contributing roughly B each to the FFN portion of every MoE layer.
Active parameters per token = experts per-expert FFN size, plus the shared attention/embedding/output/router parameters. For Mixtral: about B. The other B B of expert parameters are loaded in memory but not invoked for that token.
Sparsity ratio = . Mixtral is , only a quarter of expert parameters are active per token.
The inference economics fall directly out of this split. GPU memory needs the total parameter count, because every expert must be resident in memory to be ready to run on any token. Per-token compute is set by the active count. Memory bandwidth and FFN FLOPs are decoupled: a Mixtral inference deployment must hold B parameters but pays only B parameters’ worth of compute per token, which is cheaper than serving Llama-2 70B even though Mixtral has more total capacity.
Routing decides who does the work. But what happens when the router gets it wrong, when nothing forces all experts to be used and the router collapses onto a favorite few?
Load balancing: the practical hard problem
Router collapse
Nothing in the architecture forces all experts to be used. The router is a learned linear layer; in early training it produces near-random logits, but the moment small differences appear, positive feedback kicks in. Expert happens to get slightly more tokens. With more training signal it improves slightly faster. The router, learning from the loss, learns that expert tends to be useful and routes more tokens there. Other experts under-train, get further ignored, fall further behind.
The failure mode this produces is router collapse. In its extreme form, seven of eight experts are effectively dead: the router sends almost all tokens to one or two favored experts, and the rest of the model’s parameters are wasted. The model uses only about of its capacity. This is catastrophic for MoE: the whole architectural promise is that all experts contribute total capacity, and if only one is used, MoE is just a slow dense FFN with a lot of unused weights.
The reason this matters more for MoE than for dense models is structural. A dense model has no “wasted” parameters: all of them are always active, so they all get gradient signal and all train. MoE’s value depends on all experts being trained and useful. If routing is uneven, training signal is uneven, expert quality is uneven, and the model degenerates.
The auxiliary loss
The standard fix is an auxiliary loss added to the main training objective. Switch Transformer’s formulation defines two per-expert quantities: , the fraction of tokens routed to expert in the batch; and , the average router probability assigned to expert across the batch. The auxiliary loss is:
This expression is minimized when both and are uniform at , exactly the balanced routing pattern we want. Any imbalance, in either how often experts are picked or in the routing probabilities themselves, pushes the loss up and creates a gradient signal that the router uses to rebalance.
The coefficient balances main task vs balance. Too small: collapse risk returns. Too large: the auxiliary signal distorts language modeling because the router is being pushed harder toward uniformity than toward picking actually-useful experts. Modern training tunes carefully and sometimes anneals it through training.
Even with the auxiliary loss, imbalance can persist. Frontier MoE training adds further techniques: router z-loss (ST-MoE, ) to penalize extreme router logit magnitudes, careful initialization to prevent any expert from getting a head start, expert dropout to force the model to use alternatives. DeepSeek-V3 (December ) introduced auxiliary-loss-free load balancing using bias adjustment instead of a separate loss term, with reportedly better results. The auxiliary loss is the foundational technique, not the last word.
The demo below compares the loss value for a balanced routing pattern against a collapsed one. The collapsed case produces a larger loss, which is exactly the gradient signal that pushes the router back toward balance.
Even with balanced routing on average, individual batches can be uneven, raising the question of what happens when an expert is asked to do more work than it can fit in its compute budget.
Expert capacity and dropped tokens
The implementation reality is that experts are not infinitely flexible. Each expert is realized as a fixed-shape matmul that processes a fixed number of tokens per batch, the compute budget allocated to that expert. It cannot dynamically resize. So each expert has a maximum number of tokens it can process in a given step. That maximum is called its capacity.
The capacity formula is
where is the total tokens in the batch, is the number of experts, and is the capacity factor (typically to ). At , each expert is sized for exactly its fair share, the number of tokens it would receive if routing were perfectly balanced. At , each expert has headroom for batches that happen to skew toward it.
When more than tokens get routed to an expert in a batch, the excess is dropped. Dropped does not mean errored. The tokens skip the expert and pass through via the residual connection: their hidden state continues unmodified by the MoE layer. From the model’s perspective, those tokens experienced an identity transformation in that layer. Some semantic content goes through “untransformed,” and that has a real quality cost: too many drops degrade model performance because too much of the model’s representational work is being skipped.
The trade-off across is straightforward. High (say, ) reduces drops to near-zero but allocates extra compute slots that are mostly empty for typical batches, wasted FLOPs. Low (say, ) is efficient with compute but drops aggressively, hurting quality. Typical settings split the difference: at inference (where compute efficiency matters more) and at training (where higher variance in routing means more capacity headroom is needed).
The auxiliary loss interacts with capacity in a useful way. The more balanced the routing, the less expert overflow. Balanced routing minimizes both wasted capacity (no empty slots) and dropped tokens (no overflow). Capacity factor tuning is downstream of routing balance; the cleaner the routing distribution, the smaller the capacity factor needs to be.
With the architectural pieces established (block structure, routing, balance, capacity), the modern variants that have shaped MoE practice follow.
Modern MoE variants
Switch Transformer (2021)
Switch Transformer introduced top-1 routing: one expert per token. This is the simplest MoE, no convex combination, no gate-weighting between two experts, just a hard assignment. Switch demonstrated trillion-parameter MoE training and formalized expert capacity as a concept. It remains pedagogically the cleanest case for explaining the architecture, and several systems still use top-1 in production for its simplicity.
GLaM (2021)
GLaM (Generalist Language Model) used top-2 routing at T total parameters, exceeding the quality of contemporary dense models while using about one-third the training energy and half the per-token inference FLOPs. The headline architectural choice, top-2 instead of top-1, became the de facto standard after GLaM. The extra expressiveness of weighted-combining two experts turned out to be worth the doubled FFN cost.
Mixtral 8x7B (2024)
Mixtral 8x7B was the open-weights breakthrough. Eight experts per MoE layer, top-2 routing, B total parameters and about B active per token. Mistral released the weights (via a magnet-link torrent) in December , with the paper following in January , and the model performed comparably to Llama-2 70B at a fraction of the inference cost. The release made MoE serving infrastructure (vLLM, Mistral.rs, DeepSpeed-MII) a first-class concern in the open-source community and established that production-quality MoE was deployable, not just trainable in research papers.
DeepSeek-MoE (2024)
DeepSeek-MoE pushed the architecture in two directions. First, fine-grained experts: many smaller experts (64 routed experts per layer, versus Mixtral’s 8) rather than fewer large ones. Specialization is finer, and the increased granularity gives the router more freedom to combine expertise. Second, shared experts: a couple of experts always run for every token, capturing features that are common across all inputs, while the routed experts handle specialization. The combination outperforms standard MoE at the same total parameter count and has become the template for modern Chinese-frontier MoE training; DeepSeek-V3 later pushed the fine-grained-expert count further still, to 256 routed experts per layer.
DeepSeek-V3 (December 2024)
DeepSeek-V3 took the DeepSeek-MoE recipe to the frontier: B total parameters, B active per token. It was, as of late , the open-weights state of the art on most benchmarks. Notably, it dispenses with the explicit auxiliary loss in favor of auxiliary-loss-free load balancing, a bias-adjustment scheme that nudges router logits toward balance without adding a separate loss term. Reports indicate it works better than the classic Switch Transformer auxiliary loss, suggesting that the load-balancing problem is genuinely still being refined.
The frontier kept moving on exactly this recipe. DeepSeek itself shipped DeepSeek-V4 in April (open weights, MIT-licensed): a V4-Pro variant at T total / B active parameters and a smaller V4-Flash at B total / B active, both with a M-token context window, continuing the fine-grained-expert, loss-free-balancing template set by V3. By mid-, independent benchmarks (Artificial Analysis’s Intelligence Index) put Z.ai’s GLM-5.2 (B total, about B active, MIT-licensed, released June ) ahead of DeepSeek-V4-Pro, with Moonshot’s Kimi K2.6 (roughly T total, B active) also in the same tier. As of --, “the open-weights frontier” names a cluster of Chinese-lab MoE releases rather than any single model, and whichever one leads will likely change again within months; DeepSeek-V3 remains the right teaching example for the loss-free-balancing technique, but it is a snapshot, not the current state of the art.
The parameter-counting math for an MoE configuration is short enough to write out. The function below computes total and active parameters for a given config and applies it to Mixtral 8x7B and Llama-2 70B for comparison.
The progression from Switch (top-1, ) to GLaM (top-2, ) to Mixtral (top-2 open-weights, ) to DeepSeek-V3 (loss-free balancing, late ) is a clean four-step story. Each step solved a different practical problem: Switch made sparse scaling work at all, GLaM established top-2 as standard, Mixtral made MoE production-ready in open weights, DeepSeek-V3 pushed it to frontier scale with refined balancing. What remains is the honest accounting of why MoE is harder to use than dense models.
Why MoE is hard
Training instability
MoE training is notoriously harder to keep stable than dense training, and the root causes are specific to the architecture. Router gradients are noisy: each token sees only of the experts, the routing decision is discrete, and the gradient that reaches the router has to flow through the soft gate values rather than the hard top- selection. The signal is thin.
Router z-loss (introduced in ST-MoE, ) adds a penalty on the magnitude of router logits, preventing them from drifting to extreme values that destabilize the softmax. Numerical precision matters more than for dense training: router computations must be in higher precision (FP32) even when the rest of the model is in BF16, because the softmax over a small number of values amplifies any precision loss into a routing decision change. Auxiliary loss tuning is delicate: the coefficient must be neither too large (which dominates the language-modeling signal) nor too small (which permits collapse).
Serving complexity
MoE serving is genuinely different from dense serving. All experts must be resident in GPU memory: total parameters drive memory requirements, even though only a fraction of them are computed per token. A Mixtral inference cluster needs B parameters’ worth of HBM whether it is processing one token per second or a million.
Per-batch routing decisions mean that every forward pass has unique expert assignments. Batching efficiency is harder to maintain because the experts a batch needs are not known until the router runs. All-to-all collectives appear in distributed MoE serving: when experts are sharded across GPUs, tokens must be sent to the GPU hosting their assigned expert. Network bandwidth matters for inference, not just for training.
Specialized inference engines (vLLM, Mistral.rs, DeepSpeed-MII) have evolved with MoE-specific optimizations: continuous batching that handles expert load imbalance, KV cache management aware of expert parallelism, fused routing kernels. Running an MoE model with a stock dense inference path is technically possible but leaves substantial throughput on the table.
Fine-tuning is harder
Post-training MoE models is harder than post-training dense models for a structural reason: each expert sees roughly of the data. A fine-tuning corpus of K examples becomes effectively examples per expert in an -expert model with top-2 routing. Gradient signal per expert is thinner, training is noisier, and overfitting risk is higher.
Expert routing can also shift during fine-tuning. A pre-trained router that has learned to specialize on natural-text patterns may re-route in unexpected ways when shown fine-tuning data, breaking the specialization that made the model good in the first place. The modern remedy is to freeze the router during fine-tuning and train only the experts, or to use parameter-efficient methods (Chapter 15) that touch only a small fraction of expert parameters.
What we’ve covered: and what’s next
This chapter introduced the MoE block, router plus experts replacing the dense FFN, and worked through the math of top- routing, the central practical problem of load balancing via auxiliary loss, the bookkeeping of expert capacity and dropped tokens, the modern variants from Switch through DeepSeek-V3, and the honest accounting of why MoE training, serving, and fine-tuning are all harder than their dense counterparts. The pedagogical claim is one sentence: MoE is the architectural pattern that decouples capacity from compute, scaling total parameters with the number of experts while keeping per-token FLOPs proportional only to the top- active subset.
MoE wins where very large total capacity and manageable inference cost both matter. The “sweet spot” for frontier open-weights models in (Mixtral, DeepSeek-V3) was exactly that combination, and the pattern has held as the frontier moved to larger fine-grained MoEs (DeepSeek-V4, GLM-5.2, Kimi K2.6, as of mid-). MoE loses on simplicity. If your use case does not need more than roughly B effective capacity, dense models are easier to train, easier to serve, and very nearly as good.
Exercises
Four exercises that build on the chapter’s machinery. Each is a self-contained problem with a starting template; hints are collapsed by default; try the problem first.
Exercise 1 (easy): MoE forward pass at top-1 vs top-2
Implement the MoE forward pass. Verify that top-1 routing uses exactly one expert per token; top-2 uses two with normalized gates summing to .
Hint
For each token:
- Compute router logits: .
- Find the top- expert indices (by argsort of logits).
- Softmax over only the selected logits to get gate values summing to .
- Run only the top- FFNs and combine: .
For top-1, the gate is the softmax of a single logit, which is just (a softmax over one value always returns ).
Solution
Route by the top- logits, softmax only over those (not the full vector), and combine each selected expert’s FFN output weighted by its gate:
import numpy as np
def softmax(x, axis=-1):
x = x - x.max(axis=axis, keepdims=True)
return np.exp(x) / np.exp(x).sum(axis=axis, keepdims=True)
def moe_forward(x, router_W, expert_W1s, expert_W2s, k=2):
logits = x @ router_W.T
idx = np.argsort(-logits)[:k] # top-k indices, descending
gates = softmax(logits[idx]) # softmax over selected logits only
out = np.zeros_like(x)
for i, g in zip(idx, gates):
h = np.maximum(x @ expert_W1s[i].T, 0) # ReLU
out += g * (h @ expert_W2s[i].T)
return out, idx, gatesWith the seeded setup: top-1 selects expert [3] with gate [1.]; top-2 selects experts [3, 2] with gates [0.548, 0.452], which sum to 1.
Exercise 2 (medium): Parameter counting (Mixtral 8x7B verification)
Compute total and active parameters for an MoE transformer with a given config. Verify against Mixtral 8x7B’s published numbers (B total, B active).
Hint
Per MoE layer:
- Attention: (Q, K, V, O projections)
- LayerNorms:
- Per-expert FFN (SwiGLU: gate, up, down projections):
- Router:
Total layer params (all experts) = attn LN router. Active layer params (top- only) = attn LN router.
Plus embeddings: (input embedding + output projection, often tied).
For Mixtral 8x7B: , , , , , .
Solution
Total counts every expert’s FFN; active counts only the top- FFNs actually run per token. Everything else (attention, layernorms, router, embeddings) is dense and counted once either way:
def count_moe_params(num_layers, d_model, d_ffn, num_experts, top_k, vocab_size=32000):
attn = 4 * d_model * d_model
ln = 4 * d_model
ffn_per_expert = 3 * d_model * d_ffn
router = num_experts * d_model
layer_total = attn + ln + num_experts * ffn_per_expert + router
layer_active = attn + ln + top_k * ffn_per_expert + router
total = num_layers * layer_total + 2 * vocab_size * d_model
active = num_layers * layer_active + 2 * vocab_size * d_model
return total, activeMixtral 8x7B (computed): total = 47.5B (published: 46.7B), active = 13.7B (published: 12.9B), sparsity = 29%. Llama-2 70B dense: total = active = 78.4B. So Mixtral has ~61% of Llama-2 70B’s total params and ~17% of its active params, for quality closer to the 70B model: the payoff of sparsity.
Exercise 3 (medium): Auxiliary load balance loss
Implement the Switch Transformer auxiliary load balancing loss. Verify that it is lower for balanced routing than for collapsed routing, providing the gradient signal that prevents collapse.
Hint
The auxiliary loss is:
where:
- = fraction of tokens routed to expert (from argmax assignments).
- = average router probability for expert (from softmax outputs).
- = balance coefficient, typically .
The loss is minimized when both and are uniform at . Imbalance pushes the loss up.
Solution
is a hard count (from argmax assignments), and is a soft average (from the full softmax); mixing a hard and soft signal is what makes this loss differentiable despite argmax being involved:
import numpy as np
def aux_load_balance_loss(router_probs, expert_assignments, num_experts, alpha=0.01):
f = np.bincount(expert_assignments, minlength=num_experts) / len(expert_assignments)
P = router_probs.mean(axis=0)
return alpha * num_experts * np.sum(f * P)With the seeded setup: balanced routing loss = 0.01001 (close to the theoretical minimum ), collapsed routing loss = 0.03841, a 3.84x ratio: collapse is heavily penalized.
Exercise 4 (hard): Expert capacity and dropped tokens
Simulate expert capacity. Given a batch of routing decisions, count how many tokens are dropped at each capacity factor. Sweep from to and report the drop rate at each value.
Hint
Expert capacity formula: where is total tokens, is number of experts, is the capacity factor.
For each expert:
- Count how many tokens were assigned to it (from top-1 argmax).
- If count , drop tokens.
Total drops . Drop rate total drops total tokens.
At : perfectly balanced routing has zero drops; imperfect routing has some. At : very generous; most batches have zero drops but compute is wasted.
Solution
Count tokens per expert with np.bincount, then any excess over capacity is dropped:
import numpy as np
def simulate_drops(expert_assignments, num_experts, capacity_factor):
num_tokens = len(expert_assignments)
capacity = int(capacity_factor * num_tokens / num_experts)
counts = np.bincount(expert_assignments, minlength=num_experts)
drops = sum(max(0, c - capacity) for c in counts)
return drops, drops / num_tokensWith the seeded Zipf-like weights ([3.0, 2.0, 1.5, 1.0, 1.0, 0.8, 0.5, 0.3], normalized), tokens per expert come out to [318, 179, 167, 85, 92, 80, 57, 22] against a perfect balance of 125:
| rho | drops | rate |
|---|---|---|
| 0.5 | 549 | 54.9% |
| 0.8 | 364 | 36.4% |
| 1.0 | 289 | 28.9% |
| 1.25 | 196 | 19.6% |
| 1.5 | 131 | 13.1% |
| 2.0 | 68 | 6.8% |
Even at drops don’t hit zero, because this synthetic skew is severe (the busiest expert gets 2.5x the fair share); real trained routers, kept balanced by the auxiliary loss, don’t skew this hard.
Chapter 12 takes the other architectural path. MoE keeps the attention mechanism intact and changes only what happens after it; state-space models like Mamba replace attention itself, trading attention’s memory and compute for throughout. A different bet on what is actually holding transformers back. Together, Chapters 11 and 12 cover the two major architectural alternatives to standard dense transformers that have emerged in the last few years.
After Chapter 12 closes Part IV, the tutorial enters post-training (Chapters 13–16), taking a pre-trained model (dense or MoE) and turning it into something useful through SFT, RLHF, DPO, and the rest of the post-training stack.