class MoELayer(nn.Module):
"""Mixture-of-experts FFN: a token-choice top-k router over E experts."""
def __init__(self, num_hiddens, num_experts, num_active):
super().__init__()
self.num_experts, self.num_active = num_experts, num_active
self.router = nn.Linear(num_hiddens, num_experts, bias=False)
self.experts = nn.ModuleList([d2l.FeedForward(num_hiddens)
for _ in range(num_experts)])
self.register_buffer('expert_bias', torch.zeros(num_experts))
self.register_buffer('usage', torch.zeros(num_experts))
def forward(self, X):
probs = F.softmax(self.router(X), -1) # (B, T, E)
scores = probs + self.expert_bias # selection only
idx = scores.topk(self.num_active, -1).indices # (B, T, k)
mask = torch.zeros_like(probs).scatter(-1, idx, 1.0)
gates = probs * mask # weight = p_i
Y = torch.stack([e(X) for e in self.experts], -1) # (B, T, d, E)
out = (Y * gates.unsqueeze(-2)).sum(-1)
frac = mask.sum((0, 1)) / mask.sum() # realized load
self.usage += mask.sum((0, 1)).detach()
self.aux_loss = self.num_experts * (frac * probs.mean((0, 1))).sum()
return out