| name | mobe-mixture-basis-experts |
| title | MoBE - Mixture-of-Basis-Experts for MoE Compression |
| version | 0.0.2 |
| engine | skillxiv-v0.0.2-claude-opus-4.6 |
| license | MIT |
| url | https://arxiv.org/abs/2508.05257 |
| keywords | ["model-compression","mixture-of-experts","weight-factorization","efficient-inference"] |
| description | Compresses MoE language models through shared basis factorization of expert weight matrices, achieving 24-30% parameter reduction with minimal accuracy loss. |
MoBE: Mixture-of-Basis-Experts for MoE Compression
Core Concept
MoBE compresses large Mixture-of-Experts language models by factorizing expert weight matrices into shared basis representations. Instead of maintaining unique full-rank matrices for each expert, MoBE decomposes them into unique low-rank components combined with shared basis matrices that are reused across all experts within a layer.
Architecture Overview
- Expert-Specific Factorization: Each expert's up/gate matrix decomposed as W = AB with expert-unique matrix A
- Shared Basis Representation: Larger matrix B expressed as linear combination of shared basis matrices {Bi}
- Cross-Expert Sharing: Basis matrices used across all experts within a given MoE layer
- Lightweight Reconstruction: Simple matrix multiplication recovers original weight dimensions
Implementation Steps
Step 1: Analyze Expert Weight Structures
Examine original expert weights to determine compression targets:
class ExpertWeightAnalyzer:
def __init__(self, model):
super().__init__()
self.model = model
def analyze_weight_distribution(self, layer_idx):
"""
Analyze weight matrices in MoE layers for compression potential.
"""
moe_layer = self.model.layers[layer_idx].moe
expert_weights = []
for expert in moe_layer.experts:
up_weight = expert.up_proj.weight.data
gate_weight = expert.gate_proj.weight.data
expert_weights.append({
'up': up_weight,
'gate': gate_weight,
'shape_up': up_weight.shape,
'shape_gate': gate_weight.shape
})
stacked_up = torch.stack([w['up'] for w in expert_weights])
redundancy_score = compute_redundancy(stacked_up)
return {
'experts': expert_weights,
'redundancy_score': redundancy_score,
'compression_potential': 1 - (redundancy_score / len(expert_weights))
}
def compute_redundancy(self, weights):
"""
Measure how similar expert weights are.
"""
U, S, V = torch.svd(weights.view(weights.shape[0], -1))
torch.(S[:]) / torch.(S)
Step 2: Decompose Expert Weight Matrices
Factorize each expert's weights:
class ExpertDecomposer(nn.Module):
def __init__(self, num_experts, input_dim, output_dim, rank=64, num_basis=8):
super().__init__()
self.num_experts = num_experts
self.rank = rank
self.num_basis = num_basis
self.expert_factors = nn.ParameterList([
nn.Parameter(torch.randn(input_dim, rank))
for _ in range(num_experts)
])
self.basis_matrices = nn.ParameterList([
nn.Parameter(torch.randn(rank, output_dim))
for _ in range(num_basis)
])
self.basis_coefficients = nn.Parameter(
torch.randn(num_experts, num_basis)
)
def decompose_original_weights(self, original_weights):
"""
Factorize original weights to learned factors.
Args:
original_weights: (num_experts, input_dim, output_dim)
Returns:
factors_A: (num_experts, input_dim, rank)
basis_B: (num_basis, rank, output_dim)
coefficients: (num_experts, num_basis)
"""
num_experts, input_dim, output_dim = original_weights.shape
factors_A = []
for exp_idx in range(num_experts):
U, S, V = torch.svd(original_weights[exp_idx])
A = U[:, :.rank] * torch.sqrt(S[:.rank]).unsqueeze()
factors_A.append(A)
factors_A = torch.stack(factors_A)
residuals = []
exp_idx (num_experts):
residual = original_weights[exp_idx] - factors_A[exp_idx] @ torch.randn(
.rank, output_dim
)
residuals.append(residual)
residuals_flat = torch.cat([r.view(-) r residuals])
basis_matrices = initialize_basis_via_kmeans(
residuals_flat, .num_basis, output_dim
)
factors_A, basis_matrices
():
factor_A = .expert_factors[expert_idx]
coeff = .basis_coefficients[expert_idx]
basis_combo = torch.zeros(
.rank, x.shape[-],
device=x.device, dtype=x.dtype
)
b_idx, basis (.basis_matrices):
basis_combo += coeff[b_idx] * basis
output = x @ factor_A @ basis_combo
output
Step 3: Train Decomposed Model
Fine-tune the factorized weights to minimize reconstruction error:
def train_decomposed_model(original_model, decomposed_model, training_data, num_epochs=5):
"""
Train the decomposed model to match original model outputs.
"""
optimizer = AdamW(decomposed_model.parameters(), lr=1e-4)
scheduler = CosineAnnealingLR(optimizer, T_max=num_epochs)
for epoch in range(num_epochs):
total_loss = 0
for batch in training_data:
input_ids = batch['input_ids']
target_labels = batch['labels']
decomposed_output = decomposed_model(input_ids)
original_output = original_model(input_ids)
reconstruction_loss = F.mse_loss(
decomposed_output.logits,
original_output.logits
)
task_loss = F.cross_entropy(
decomposed_output.logits.view(-1, decomposed_output.logits.size(-1)),
target_labels.view(-1)
)
total_loss = 0.7 * reconstruction_loss + 0.3 * task_loss
optimizer.zero_grad()
total_loss.backward()
torch.nn.utils.clip_grad_norm_(decomposed_model.parameters(), 1.0)
optimizer.step()
scheduler.step()
print(f"Epoch {epoch+1}: Loss = {total_loss:.4f}")
return decomposed_model
Step 4: Optimize Basis Sharing
Refine which basis matrices are shared across expert layers:
class BasisOptimizer:
def __init__(self, num_layers, num_basis):
super().__init__()
self.num_layers = num_layers
self.num_basis = num_basis
def optimize_basis_sharing(self, decomposed_model):
"""
Determine optimal basis sharing patterns across layers.
"""
basis_similarity_matrix = torch.zeros(
self.num_layers, self.num_layers
)
for l1 in range(self.num_layers):
for l2 in range(self.num_layers):
bases_l1 = decomposed_model.layers[l1].basis_matrices
bases_l2 = decomposed_model.layers[l2].basis_matrices
similarity = compute_set_similarity(bases_l1, bases_l2)
basis_similarity_matrix[l1, l2] = similarity
clusters = cluster_layers(basis_similarity_matrix)
shared_bases = {}
for cluster_id, layer_indices in enumerate(clusters):
shared_bases[cluster_id] = merge_bases_from_layers(
decomposed_model, layer_indices
)
return shared_bases
def apply_shared_bases(self, decomposed_model, shared_bases):
"""
Update model to use shared bases across similar layers.
"""
cluster_id, (layer_indices, shared_basis) (shared_bases.items()):
layer_idx layer_indices:
decomposed_model.layers[layer_idx].basis_matrices = shared_basis
decomposed_model
Practical Guidance
Hyperparameters and Configuration:
- Rank of expert-specific factors: 32-64 (depends on matrix dimensions)
- Number of shared basis matrices: 4-8 per layer
- Compression ratio: Target 24-30% parameter reduction
- Fine-tuning learning rate: 1e-4 to 5e-5
- Optimization epochs: 3-10 depending on dataset size
When to Use MoBE:
- Compressing very large MoE models (100B+)
- Scenarios requiring significant parameter reduction with minimal accuracy loss
- Deployment environments with storage or memory constraints
- Models where expert weight matrices show moderate to high redundancy
When NOT to Use:
- Small MoE models where compression provides minimal benefit
- Tasks extremely sensitive to model quality degradation
- Real-time systems where decomposition/reconstruction adds latency
- When training data is insufficient for fine-tuning
Implementation Notes:
- Analyze redundancy before committing to compression (not all MoE models compress equally)
- Fine-tuning is critical for maintaining accuracy
- Consider per-layer vs global basis sharing based on model structure
- Monitor expert utilization distribution (may shift with compression)
- Store basis matrices separately for potential reuse across models
Reference
Paper: MoBE: Mixture-of-Basis-Experts for Compressing MoE LLMs
ArXiv: 2508.05257
Performance: 24-30% parameter reduction on DeepSeek-V3 and Kimi-K2-Instruct with 1-2% accuracy loss (vs 7-14% for other methods)