Advanced Mixture-of-Experts (MoE) Guide
ForeBlocks implements a production-grade MoE stack with flexible routers, load-balancing strategies, expert types, and auxiliary mechanisms for stable training.
Architecture Overview
Input: [B, T, D]
↓
Router (learns per-token scores)
↓
Top-k selector (selects top-k experts per token)
↓
Dispatcher (routes tokens to experts)
↓
Experts (parallel FFNs, only active experts compute)
↓
Combiner (weighted sum of expert outputs)
↓
Output: [B, T, D]Auxiliary losses computed alongside:
- Load balancing loss (prevent expert collapse)
- Auxiliary expert loss (sparse routing)
- Auxiliary token loss (encourage specialization)
Enabling MoE in Transformers
Basic Enable
encoder = TransformerEncoder(
input_size=8,
d_model=256,
nhead=8,
num_layers=6,
use_moe=True,
num_experts=8, # Total number of experts
top_k=2, # Route to top-k experts per token
)Full Control (via FeedForwardBlock)
from foreblocks.modules.moe.ff import FeedForwardBlock
ffn = FeedForwardBlock(
d_model=256,
dim_ff=1024,
use_moe=True,
# ── Expert config ──
num_experts=16,
num_shared=1, # Additional shared expert (always active)
top_k=2,
use_swiglu=True, # SwiGLU experts (recommended)
# ── Router config ──
router_type="noisy_topk",
router_temperature=1.0,
router_perturb_noise=0.01,
# ── Routing mode ──
routing_mode="token_choice", # or "expert_choice"
# ── Load balancing ──
load_balance_weight=0.01,
z_loss_weight=0.001,
moe_aux_lambda=1.0,
# ── Latent projection (optional) ──
moe_use_latent=True,
moe_latent_dim=128,
moe_latent_d_ff=512,
# ── Capacity control ──
moe_capacity_factor=1.25,
)Router Types
Noisy Top-K (Recommended Default)
router_type="noisy_topk"
router_temperature=1.0 # Gumbel noise temperature
router_perturb_noise=0.01 # Gaussian noise stdBehavior:
- Compute router logits:
logits = linear(x) - Add Gumbel noise:
logits += -log(-log(uniform())) - Scale by temperature:
logits /= temperature - Select top-k (differentiable via Gumbel-max trick)
Why: Encourages exploration early, recovers to deterministic routing when trained.
Straight-Through Top-K
router_type="straight_topk"Deterministic top-k with straight-through gradient (no noise).
Pros: Fast, deterministic, reproducible. Cons: No exploration—can converge to poor local optima.
Soft Dense
router_type="soft_dense"Soft assignment to all experts (no sparsity).
Use case: Debugging, baseline comparison. Not recommended for production (no compute savings).
Hash Router
router_type="hash"
router_hash_num_hashes=2
router_hash_num_buckets=64
router_hash_bucket_size=8
router_hash_seed=17Hash-based expert assignment: O(1) routing, no learned router.
Pros: Fast, fixed compute, no training instability. Cons: Fixed routing pattern, less adaptive.
How it works:
- Hash input ID (or token position) → bucket
- Map bucket → expert group
- Route to experts in group
Use case: Inference-only, federated learning, extremely large scale.
Adaptive Top-K
router_type="adaptive_noisy_topk"
adaptive_k_head_dim=32
adaptive_k_tau=1.0
adaptive_k_baseline_momentum=0.99
adaptive_k_sparsity_lambda=0.0Learn per-token k (number of experts to route to).
Behavior: Router outputs both logits (expert assignment) and k (number of experts).
Auxiliary loss: Encourages sparse k via sparsity_lambda.
Use case: Highly variable compute budgets.
Auxiliary-Token Router
router_type="auxiliary_token"Expert routing via auxiliary token embeddings (learned per expert).
Routing Modes
Token Choice (Default)
routing_mode="token_choice"Each token independently selects its top-k experts. O(T*k) capacity.
Pros: Natural load balancing, each token gets its choice. Cons: Can overload high-capacity experts.
Expert Choice
routing_mode="expert_choice"
expert_choice_tokens_per_expert=32 # Max tokens per expertEach expert independently selects its top-m tokens. O(E*m) capacity.
Pros: Strict capacity control, predictable compute. Cons: Tokens may not get their top-k experts.
Load Balancing Mechanisms
MoE naturally suffers from expert collapse: most tokens route to the same expert, wasting capacity. ForeBlocks provides multiple balancing strategies.
Auxiliary Losses (Standard)
Load Balance Loss
Encourage uniform expert usage:
load_balance_weight=0.01Loss: sum((expert_capacity_ratios - ideal_ratio)^2)
Where:
expert_capacity_ratios: fraction of tokens routed to each expertideal_ratio: 1 / num_experts
Z Loss (Gating Entropy)
Encourage uniform (soft) routing probabilities:
z_loss_weight=0.001Loss: sum(log(total_router_probability)) (from GShard paper)
Auxiliary Expert Loss
Multi-head auxiliary loss (one head per expert):
router_aux_num_aux=1Encourages each expert to specialize.
Router Bias Balance (Optional)
Learned per-expert router biases, annealed during training:
moe_router_bias_balance=True
moe_router_bias_warmup_steps=1000
moe_router_bias_min_usage=0.01
moe_router_bias_update_rate=0.01
moe_router_bias_clip=2.0Behavior: Dynamically adjust per-expert router bias to keep minimum usage above threshold.
Soft Capacity (Optional)
Soft (differentiable) capacity constraint instead of hard dropping:
moe_soft_capacity=True
moe_capacity_factor=1.25 # 125% of ideal capacity
moe_capacity_min=0.5 # Min capacity floor
moe_capacity_max=2.0 # Max capacity ceilingEffect: Penalizes exceeding capacity but allows it (smooth, differentiable).
Entropy Regularization (Optional)
Encourage diversity in token-to-expert assignments:
moe_entropy_reg_weight=0.01Expert Types
SwiGLU (Recommended)
use_swiglu=TrueGated linear unit with learnable gate:
u = w1(x)
v = w2(x)
output = silu(u) * vvs. standard FFN:
output = w2(gelu(w1(x)))Why SwiGLU: More expressive, used in modern models (PaLM, LLaMA).
Standard FFN
use_swiglu=False
activation="gelu" # or "relu", "elu", etc.Shared Experts
Purpose
Some tokens/features are best handled by a shared expert (always active, not routed). Reduces variance in per-expert gradients.
Enable
num_shared=1 # Number of shared experts (0 = none)Effect: 1 shared expert + 8 routed experts = 9 total expert computations.
Shared Expert Combination
shared_combine="add" # or "concat"
shared_scale_init=1.0How routed output combines with shared output:
"add":output_routed + output_shared"concat":[output_routed, output_shared] → linear
Latent Projection (Optional)
Project tokens through a latent space before routing. Encourages abstraction.
Enable
moe_use_latent=True
moe_latent_dim=128 # Latent dimension
moe_latent_d_ff=512 # Hidden dim in latent FFNArchitecture:
Input: [B, T, D]
↓
Project to latent: linear(D → L)
↓
Latent FFN: [L → latent_d_ff → L]
↓
Project back: linear(L → D)
↓
Router consumes latent features
↓
Experts still operate on original inputWhy: Separate routing logic from expert computation, reduces overfitting of router.
Capacity & Computation Control
Capacity Factor
moe_capacity_factor=1.25 # Default 125% of idealIdeal capacity per expert: (B * T) / num_experts Actual capacity: ideal * moe_capacity_factor
Tokens exceeding capacity are dropped (or soft-penalized if moe_soft_capacity=True).
Mixture-of-Tokens-Per-Expert (MTP)
Track per-expert token counts, predict optimal task difficulty:
mtp_num_heads=2 # Number of MTP heads
mtp_loss_weight=0.01 # Auxiliary loss weight
mtp_init_scale=0.02 # Initialization scaleGradient Checkpointing
Reduce memory footprint for large MoE layers:
use_gradient_checkpointing=TrueRecomputes expert activations during backward. Trade: 20–30% more compute, 50–70% less memory.
Production Tuning Guide
For Stable Training
config = {
"num_experts": 8,
"num_shared": 1,
"top_k": 2,
"router_type": "noisy_topk",
"router_temperature": 1.0,
"router_perturb_noise": 0.01,
"load_balance_weight": 0.01,
"z_loss_weight": 0.001,
"moe_aux_lambda": 1.0,
"use_swiglu": True,
"use_gradient_checkpointing": True,
}Baseline setting: works well across most datasets.
For Compute Efficiency
config = {
"num_experts": 16,
"top_k": 1, # Single expert (most sparse)
"router_type": "hash", # O(1) routing
"routing_mode": "expert_choice",
"expert_choice_tokens_per_expert": 32,
"use_gradient_checkpointing": True,
}Minimize compute: hash routing + expert choice + k=1.
For High Accuracy (Research)
config = {
"num_experts": 32,
"num_shared": 2,
"top_k": 4, # More experts per token
"router_type": "adaptive_noisy_topk",
"adaptive_k_sparsity_lambda": 0.01,
"load_balance_weight": 0.05,
"z_loss_weight": 0.005,
"moe_use_latent": True,
"moe_latent_dim": 256,
"moe_entropy_reg_weight": 0.01,
"moe_soft_capacity": True,
}Maximize expressivity: adaptive k + latent + high load balancing weight.
Complete Example: MoE-Heavy Encoder
from foreblocks.config import TrainingConfig
from foreblocks.core.training.trainer import Trainer
from foreblocks.models.transformer.tf_encoder import TransformerEncoder
# ── High-capacity MoE encoder ──
encoder = TransformerEncoder(
input_size=8,
d_model=384,
nhead=8,
num_layers=6,
dim_feedforward=2048,
# ── MoE ──
use_moe=True,
num_experts=16,
num_shared=2,
top_k=2,
router_type="noisy_topk",
routing_mode="token_choice",
load_balance_weight=0.02,
z_loss_weight=0.001,
moe_aux_lambda=1.0,
# ── Latent routing ──
moe_use_latent=True,
moe_latent_dim=192,
moe_latent_d_ff=768,
# ── Capacity ──
moe_capacity_factor=1.5,
# ── Efficiency ──
use_gradient_checkpointing=True,
# ── Per-layer dropout (see transformer-advanced.md) ──
layer_dropout_schedule=LayerDropoutSchedule(
num_layers=6,
base_dropout=0.05,
max_dropout=0.15,
),
)
config = TrainingConfig(
num_epochs=100,
learning_rate=1e-3,
weight_decay=0.01,
use_llrd=True,
llrd_decay=0.9,
scheduler_type="warmup_cosine",
warmup_ratio=0.1,
batch_size=64,
)
trainer = Trainer(model=encoder, config=config)
history = trainer.train(train_loader, val_loader)Monitoring MoE Health
Expert Utilization
Enable logging:
from foreblocks.modules.moe.experts.moe_logging import MoELogger
moe_logger = MoELogger()
encoder = TransformerEncoder(
...,
moe_logger=moe_logger,
step_getter=lambda: trainer.global_step,
)Access per-step metrics:
if moe_logger:
expert_counts = moe_logger.expert_token_counts # [num_experts]
load_balance = moe_logger.load_balance_loss
z_loss = moe_logger.z_lossDebugging Collapse
If all tokens route to the same expert:
- Increase
load_balance_weight(0.05–0.1) - Enable
moe_router_bias_balance=True - Reduce
router_temperature(increase sharpness) - Increase
num_experts(more targets)
Avoiding Overflow
If loss explodes:
- Reduce
router_perturb_noise - Enable
moe_soft_capacity=True - Reduce
top_k(fewer experts per token) - Increase gradient clipping threshold
Inference Optimization
Fused Kernels
use_grouped_kernel=True # Grouped GEMM for dispatching
use_fused_router_topk=True # Fused top-k operationReduces kernels launches, improves GPU utilization.
Torchcompile
compile_router=True
compile_experts=TrueJIT-compile router and experts for faster execution (PyTorch 2.0+).
Paged KV Cache
MoE output can be cached in paged KV cache (see Attention docs):
# In transformer decoder with MoE
decoder = TransformerDecoder(
...,
use_moe=True,
# KV caching transparent to MoE layer
)References
- GShard (Lepikhin et al., 2020): Load balancing in large-scale MoE
- Switch Transformers (Lewis et al., 2021): Simplified MoE with k=1
- Base Layers (Lewis et al., 2023): Shared expert patterns
- Expert Choice Routing (Zhou et al., 2022): Expert-side token selection