| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| from typing import Optional |
| from .mocae_router import MoCaERouter |
|
|
|
|
| class LoRAExpertFFN(nn.Module): |
|
|
| def __init__(self, base_ffn: nn.Module, lora_weights: dict, lora_scale: float = 2.0): |
| super().__init__() |
| self.base_ffn = base_ffn |
| self.lora_scale = lora_scale |
| for key, tensor in lora_weights.items(): |
| self.register_buffer(key, tensor.contiguous()) |
|
|
| def _lora(self, x: torch.Tensor, A_name: str, B_name: str) -> torch.Tensor: |
| A = getattr(self, A_name) |
| B = getattr(self, B_name) |
| return (x @ A.T.to(x.dtype)) @ B.T.to(x.dtype) * self.lora_scale |
|
|
| def forward(self, h: torch.Tensor) -> torch.Tensor: |
| gate = self.base_ffn.gate_proj(h) + self._lora(h, "gate_A", "gate_B") |
| up = self.base_ffn.up_proj(h) + self._lora(h, "up_A", "up_B") |
| intermediate = F.silu(gate) * up |
| out = self.base_ffn.down_proj(intermediate) + self._lora(intermediate, "down_A", "down_B") |
| return out |
|
|
|
|
| class MoCaELayer(nn.Module): |
|
|
| def __init__( |
| self, |
| original_ffn: nn.Module, |
| expert_ffns: list, |
| router: MoCaERouter, |
| gamma_tilde: list, |
| hidden_size: int, |
| dropout: float = 0.1, |
| ): |
| super().__init__() |
| self.expert_ffns = nn.ModuleList(expert_ffns) |
| self.router = router |
| self.gamma_tilde = gamma_tilde |
| self.layer_norm = nn.LayerNorm(hidden_size) |
| self.dropout = nn.Dropout(p=dropout) |
| self.prev_probs: Optional[torch.Tensor] = None |
|
|
| def forward(self, h: torch.Tensor) -> torch.Tensor: |
| orig_dtype = h.dtype |
| router_device = next(self.router.parameters()).device |
| h = h.to(device=router_device, dtype=torch.bfloat16) |
|
|
| probs, _ = self.router(h) |
|
|
| expert_outputs = torch.stack( |
| [ffn(h) for ffn in self.expert_ffns], dim=-1 |
| ) |
|
|
| gamma = torch.tensor(self.gamma_tilde, device=h.device, dtype=h.dtype) |
| alpha = probs * gamma.unsqueeze(0).unsqueeze(0) |
| alpha = alpha / (alpha.sum(dim=-1, keepdim=True) + 1e-9) |
|
|
| y = (expert_outputs * alpha.unsqueeze(-2)).sum(dim=-1) |
| y_cal = self.dropout(y) |
|
|
| self.prev_probs = probs.detach() |
| return y_cal.to(orig_dtype) |
|
|
| def get_prev_probs(self): |
| return self.prev_probs |
|
|
| def update_gamma(self, new_gamma: list): |
| self.gamma_tilde = new_gamma |
|
|
| def get_expert_activations(self, h: torch.Tensor) -> dict: |
| probs, _ = self.router(h) |
| gamma = torch.tensor(self.gamma_tilde, device=h.device, dtype=h.dtype) |
| alpha = probs * gamma.unsqueeze(0).unsqueeze(0) |
| alpha = alpha / (alpha.sum(dim=-1, keepdim=True) + 1e-9) |
| return { |
| "routing_probs": probs.mean(dim=(0, 1)).cpu().tolist(), |
| "combined_weights": alpha.mean(dim=(0, 1)).cpu().tolist(), |
| } |
|
|