| """Specialized experts: force each MoE expert to own a specific skill domain. |
| |
| THE INNOVATION. Standard MoE experts are interchangeable β routing is based |
| on phase similarity, not content. This module adds: |
| |
| 1. Domain labels: each expert is assigned a domain (code, math, text...). |
| 2. A diversity loss: penalizes two experts that produce similar outputs |
| for the same input β forces specialization. |
| 3. A domain-matching bonus: when the input matches an expert's domain, |
| the routing gate is boosted. |
| |
| This makes the MoE a true SKILL DISPATCHER: the Kuramoto phases detect the |
| cognitive mode, and the specialized experts provide domain-specific processing. |
| |
| Usage: |
| spec = ExpertSpecialization( |
| n_experts=4, |
| domains=["code", "math", "language", "reasoning"], |
| ) |
| div_loss = spec.diversity_loss(all_expert_outputs) # add to training loss |
| """ |
|
|
| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
|
|
|
|
| class ExpertSpecialization(nn.Module): |
| """Forces MoE experts to specialize on distinct domains. |
| |
| Args: |
| n_experts: number of experts. |
| domains: list of domain names (one per expert). |
| d_model: model dimension (for the domain embedding). |
| """ |
|
|
| def __init__( |
| self, |
| n_experts: int = 4, |
| domains: list = None, |
| d_model: int = 128, |
| ): |
| super().__init__() |
| self.n_experts = n_experts |
| if domains is None: |
| domains = [f"domain_{i}" for i in range(n_experts)] |
| self.domains = domains[:n_experts] |
|
|
| |
| |
| self.domain_vectors = nn.Parameter( |
| torch.randn(n_experts, d_model) * 0.02 |
| ) |
|
|
| def diversity_loss(self, expert_outputs: torch.Tensor) -> torch.Tensor: |
| """Penalize experts that produce similar outputs. |
| |
| Args: |
| expert_outputs: (E, D) β the output of each expert on the same input. |
| Returns: |
| loss: scalar. Lower = more diverse (better specialization). |
| """ |
| if expert_outputs.shape[0] < 2: |
| return torch.tensor(0.0, device=expert_outputs.device) |
|
|
| |
| |
| sims = F.cosine_similarity( |
| expert_outputs.unsqueeze(1), |
| expert_outputs.unsqueeze(0), |
| dim=-1, |
| ) |
|
|
| |
| eye = torch.eye(self.n_experts, device=expert_outputs.device) |
| sims = sims * (1 - eye) |
|
|
| |
| return sims.abs().sum() / (self.n_experts * (self.n_experts - 1)) |
|
|
| def domain_embedding_loss(self) -> torch.Tensor: |
| """Keep domain vectors well-separated (orthogonal). |
| |
| Returns: |
| loss: scalar. Lower = domains more distinct. |
| """ |
| if self.n_experts < 2: |
| return torch.tensor(0.0) |
| |
| gram = self.domain_vectors @ self.domain_vectors.T |
| |
| norms = self.domain_vectors.norm(dim=-1, keepdim=True) |
| normed_gram = gram / (norms @ norms.T + 1e-10) |
| |
| eye = torch.eye(self.n_experts, device=gram.device) |
| off_diag = normed_gram * (1 - eye) |
| return off_diag.abs().sum() / (self.n_experts * (self.n_experts - 1)) |
|
|
| def get_domain(self, expert_idx: int) -> str: |
| """Get the domain label for an expert.""" |
| if 0 <= expert_idx < len(self.domains): |
| return self.domains[expert_idx] |
| return "unknown" |
|
|
| def info(self) -> dict: |
| return { |
| "n_experts": self.n_experts, |
| "domains": self.domains, |
| "method": "diversity + orthogonality loss", |
| } |
|
|