Download model.py from vanshnawander/assignment2-moe-shared: direct link, hf CLI and curl.
- Browser
- Download file 6.14 kB
-
https://huggingface.co/vanshnawander/assignment2-moe-shared/resolve/main/model.py
- Command line
-
hf download hf://vanshnawander/assignment2-moe-shared/model.py
-
curl -L -o model.py https://huggingface.co/vanshnawander/assignment2-moe-shared/resolve/main/model.py
6.14 kB
| """Pre-normalized decoder-only transformer with interchangeable FFNs.""" | |
| import torch | |
| from torch import Tensor, nn | |
| import torch.nn.functional as F | |
| from moe import TransformerConfig, MLP, MoE | |
| class Block(nn.Module): | |
| """Apply pre-normalized causal attention and the selected FFN.""" | |
| def __init__(self, config): | |
| """Create attention projections, normalization layers and the FFN.""" | |
| super().__init__() | |
| self.norm1 = nn.LayerNorm(config.d_model) | |
| self.norm2 = nn.LayerNorm(config.d_model) | |
| self.qkv = nn.Linear(config.d_model, 3 * config.d_model, bias=False) | |
| self.proj = nn.Linear(config.d_model, config.d_model, bias=False) | |
| self.ffn = MLP(config) if config.num_experts == 1 else MoE(config) | |
| self.heads = config.n_heads | |
| self.dropout = config.dropout | |
| def forward(self, x, attention_mask, routing_mask=None): | |
| """Apply causal attention and the FFN to (batch, tokens, hidden) states.""" | |
| b, t, d = x.shape | |
| q, k, v = self.qkv(self.norm1(x)).chunk(3, -1) | |
| q, k, v = [ | |
| z.reshape(b, t, self.heads, d // self.heads).transpose(1, 2) | |
| for z in (q, k, v) | |
| ] | |
| mask = None | |
| if attention_mask is not None and not bool(attention_mask.all()): | |
| mask = ( | |
| torch.ones(t, t, dtype=torch.bool, device=x.device).tril()[None, None] | |
| & attention_mask[:, None, None, :].bool() | |
| ) | |
| attended = F.scaled_dot_product_attention( | |
| q, | |
| k, | |
| v, | |
| attn_mask=mask, | |
| is_causal=mask is None, | |
| dropout_p=self.dropout if self.training else 0.0, | |
| ) | |
| x = x + F.dropout( | |
| self.proj(attended.transpose(1, 2).reshape(b, t, d)), | |
| self.dropout, | |
| self.training, | |
| ) | |
| if isinstance(self.ffn, MoE): | |
| self.ffn.tracking_mask = ( | |
| attention_mask if routing_mask is None else routing_mask | |
| ) | |
| return x + F.dropout(self.ffn(self.norm2(x)), self.dropout, self.training) | |
| class CausalLM(nn.Module): | |
| """Decode tokens using tied embeddings and dense or MoE blocks.""" | |
| def __init__(self, config: TransformerConfig): | |
| """Build the decoder, initialize its weights and tie the output embedding.""" | |
| super().__init__() | |
| if config.d_model % config.n_heads: | |
| raise ValueError("d_model must be divisible by n_heads") | |
| self.config = config | |
| self.context_length = config.context_length | |
| self.usage_mask = None | |
| self.token_embedding = nn.Embedding(config.vocab_size, config.d_model) | |
| self.position_embedding = nn.Embedding(config.context_length, config.d_model) | |
| self.blocks = nn.ModuleList([Block(config) for _ in range(config.n_layers)]) | |
| self.norm = nn.LayerNorm(config.d_model) | |
| self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=False) | |
| self.apply(self._initialize) | |
| self.lm_head.weight = self.token_embedding.weight | |
| def _initialize(module): | |
| """Initialize projection and embedding weights normally, with zero biases.""" | |
| if isinstance(module, (nn.Linear, nn.Embedding)): | |
| nn.init.normal_(module.weight, std=0.02) | |
| if getattr(module, "bias", None) is not None: | |
| nn.init.zeros_(module.bias) | |
| def aux_loss(self): | |
| """Return the mean balancing loss, or zero for a dense model.""" | |
| losses = [ | |
| block.ffn.aux_loss for block in self.blocks if isinstance(block.ffn, MoE) | |
| ] | |
| return ( | |
| torch.stack(losses).mean() | |
| if losses | |
| else self.token_embedding.weight.new_zeros(()) | |
| ) | |
| def forward( | |
| self, input_ids: Tensor, attention_mask: Tensor | None = None | |
| ) -> Tensor: | |
| """Return logits of shape (batch, tokens, vocabulary) for a token sequence. | |
| Use learned absolute positions and causal attention. The optional mask | |
| marks valid input positions; sequences must fit the configured context. | |
| """ | |
| if input_ids.ndim != 2 or input_ids.shape[1] > self.context_length: | |
| raise ValueError("Expected (batch, sequence) within context_length") | |
| positions = torch.arange(input_ids.shape[1], device=input_ids.device) | |
| hidden = ( | |
| self.token_embedding(input_ids) + self.position_embedding(positions)[None] | |
| ) | |
| for block in self.blocks: | |
| hidden = block(hidden, attention_mask, self.usage_mask) | |
| return self.lm_head(self.norm(hidden)) | |
| def parameter_counts(self): | |
| """Count stored and active parameters, counting tied weights once.""" | |
| total = sum(p.numel() for p in self.parameters()) | |
| inactive = sum( | |
| sum(p.numel() for p in b.ffn.parameters()) - b.ffn.active_parameter_count() | |
| for b in self.blocks | |
| if isinstance(b.ffn, MoE) | |
| ) | |
| return {"total": total, "active": total - inactive} | |
| class DenseCausalLM(CausalLM): | |
| """Provide the dense decoder through the earlier constructor interface.""" | |
| def __init__( | |
| self, vocab_size, d_model, n_layers, n_heads, d_ff, context_length, dropout | |
| ): | |
| """Create a decoder configuration with one dense feedforward expert.""" | |
| super().__init__( | |
| TransformerConfig( | |
| d_model, | |
| d_ff, | |
| vocab_size, | |
| context_length, | |
| n_layers=n_layers, | |
| n_heads=n_heads, | |
| dropout=dropout, | |
| ) | |
| ) | |
| def build_model(config): | |
| """Construct the configured dense or MoE causal language model.""" | |
| m = config.model | |
| f = m.ffn | |
| return CausalLM( | |
| TransformerConfig( | |
| m.d_model, | |
| m.d_ff, | |
| m.vocab_size, | |
| m.context_length, | |
| f.num_experts, | |
| f.top_k, | |
| f.shared_expert, | |
| f.match_active_parameters, | |
| m.n_layers, | |
| m.n_heads, | |
| m.dropout, | |
| ) | |
| ) | |