Download task1/model.py from ZappY-AI/anlp-a2: direct link, hf CLI and curl.
- Browser
- Download file 7.54 kB
-
https://huggingface.co/ZappY-AI/anlp-a2/resolve/main/task1/model.py
- Command line
-
hf download hf://ZappY-AI/anlp-a2/task1/model.py
-
curl -L -o model.py https://huggingface.co/ZappY-AI/anlp-a2/resolve/main/task1/model.py
7.54 kB
| from dataclasses import dataclass | |
| import torch | |
| import torch.nn as nn | |
| class TransformerConfig: | |
| model_dim: int # Dimension of the model | |
| num_heads: int | |
| N_ctx: int # Number of context tokens | |
| use_moe: bool # Whether to use MoE or MLP | |
| num_experts : int | |
| active_experts : int | |
| shared_experts : int | |
| expert_width : int | |
| vocab_size : int | |
| layers : int | |
| class MHA(nn.Module): | |
| def __init__(self, num_heads, model_dim): | |
| super().__init__() | |
| self.num_heads = num_heads | |
| self.model_dim = model_dim | |
| self.head_dim = model_dim // num_heads | |
| self.q = nn.Linear(in_features=model_dim, out_features=model_dim) | |
| self.k = nn.Linear(in_features=model_dim, out_features=model_dim) | |
| self.v = nn.Linear(in_features=model_dim, out_features=model_dim) | |
| self.out_proj = nn.Linear(in_features=model_dim, out_features=model_dim) | |
| def forward(self, x, mask=None): | |
| q = self.q(x) | |
| k = self.k(x) | |
| v = self.v(x) | |
| q_heads = q.reshape(x.shape[0], x.shape[1], self.num_heads, self.head_dim).permute(0, 2, 1, 3) | |
| k_heads = k.reshape(x.shape[0], x.shape[1], self.num_heads, self.head_dim).permute(0, 2, 1, 3) | |
| v_heads = v.reshape(x.shape[0], x.shape[1], self.num_heads, self.head_dim).permute(0, 2, 1, 3) | |
| attention_scores = (q_heads @ torch.transpose(k_heads, 3, 2)) / self.head_dim ** (1/2) | |
| if mask is not None: | |
| attention_scores = attention_scores.masked_fill(mask == 0, float('-inf')) | |
| attention_scores = torch.softmax(attention_scores, dim=-1) | |
| self_attention = attention_scores @ v_heads | |
| mha_output = self_attention.permute(0, 2, 1, 3).reshape(x.shape[0], x.shape[1], self.model_dim) | |
| output = self.out_proj(mha_output) | |
| return output | |
| class RMSNorm(nn.Module): | |
| def __init__(self, model_dim): | |
| super().__init__() | |
| self.model_dim = model_dim | |
| self.gamma = nn.Parameter(torch.ones(model_dim)) | |
| def forward(self, x): | |
| eps = 1e-5 | |
| rms = (((torch.sum(torch.square(x), dim=-1, keepdim=True)) / self.model_dim) + eps) ** (1/2) | |
| norm = self.gamma * (x / rms) | |
| return norm | |
| class SinusoidalPositionalEmbeddings(nn.Module): | |
| def __init__(self, model_dim, N=10000): | |
| super().__init__() | |
| self.d = model_dim | |
| self.N = N | |
| self.register_buffer('k', torch.arange(0, model_dim, step=2)) | |
| def forward(self, x): | |
| t = torch.arange(x.shape[1], device=x.device).unsqueeze(1) | |
| sin = torch.sin(t/self.N ** (self.k/self.d)) | |
| cos = torch.cos(t/self.N ** (self.k/self.d)) | |
| total_angles = torch.cat([sin, cos], dim=-1).unsqueeze(0) | |
| return x + total_angles | |
| class Block(nn.Module): | |
| def __init__(self, config): | |
| super().__init__() | |
| self.config = config | |
| self.self_attn = MHA(config.num_heads, config.model_dim) | |
| self.norms = nn.ModuleList([RMSNorm(config.model_dim) for _ in range(2)]) | |
| if config.use_moe: | |
| self.ffn = MoE(self.config) | |
| else: | |
| self.ffn = MLP(self.config) | |
| def forward(self, x, mask=None, pad_mask=None): | |
| x = x + self.self_attn(self.norms[0](x), mask) | |
| h, aux, counts = self.ffn(self.norms[1](x), pad_mask) | |
| return x + h, aux, counts | |
| class Expert(nn.Module): | |
| def __init__(self, config): | |
| super().__init__() | |
| self.expert = nn.Sequential( | |
| nn.Linear(config.model_dim, config.expert_width * config.model_dim), | |
| nn.GELU(), | |
| nn.Linear(config.expert_width * config.model_dim, config.model_dim) | |
| ) | |
| def forward(self, x): | |
| x = self.expert(x) | |
| return x | |
| class MoE(nn.Module): | |
| def __init__(self, config: TransformerConfig): | |
| super().__init__() | |
| self.config = config | |
| self.router = nn.Sequential( | |
| nn.Linear(config.model_dim, config.num_experts - config.shared_experts), | |
| nn.Softmax(-1) | |
| ) | |
| self.experts = nn.ModuleList([Expert(config) for _ in range(config.num_experts)]) | |
| def forward(self, x, pad_mask=None): | |
| B, N, D = x.shape | |
| x_flat = x.reshape(-1, D) # (T, D) where T = B*N tokens | |
| probs = self.router(x_flat) # (T, num_routed) | |
| top_w, top_idx = torch.topk(probs, self.config.active_experts) # both (T, k) | |
| out = torch.zeros_like(x_flat) # (T, D) | |
| # shared routers | |
| if self.config.shared_experts: | |
| for i in range(self.config.shared_experts): | |
| out += self.experts[i](x_flat) | |
| real = pad_mask.reshape(-1).bool() | |
| r_probs = probs[real] | |
| r_idx = top_idx[real] | |
| num_routed = self.config.num_experts - self.config.shared_experts | |
| counts = torch.bincount(r_idx.reshape(-1), minlength=num_routed) | |
| for e in range(self.config.num_experts - self.config.shared_experts): | |
| e_mask = (top_idx == e).any(dim=-1) | |
| if not e_mask.any(): | |
| continue | |
| e_wt = (top_w * (top_idx == e)).sum(dim=-1) | |
| out[e_mask] += e_wt[e_mask, None] * self.experts[self.config.shared_experts + e](x_flat[e_mask]) | |
| f = counts / r_idx.numel() | |
| P = r_probs.mean(dim=0) | |
| aux = num_routed * (f * P).sum() | |
| return out.reshape(B, N, D), aux, counts | |
| class MLP(nn.Module): | |
| def __init__(self, config: TransformerConfig): | |
| super().__init__() | |
| self.mlp = nn.Sequential( | |
| nn.Linear(config.model_dim, 4 * config.model_dim), | |
| nn.GELU(), | |
| nn.Linear(4 * config.model_dim, config.model_dim) | |
| ) | |
| def forward(self, x, pad_mask=None): | |
| x = self.mlp(x) | |
| return x, 0, None | |
| class Transformer(nn.Module): | |
| def __init__(self, config: TransformerConfig): | |
| super().__init__() | |
| self.config = config | |
| self.token_embedding = nn.Embedding(config.vocab_size, config.model_dim) | |
| self.pos_embedding = SinusoidalPositionalEmbeddings(config.model_dim) | |
| self.blocks = nn.ModuleList([Block(config) for _ in range(config.layers)]) | |
| self.norm = RMSNorm(config.model_dim) | |
| self.lm_head = nn.Linear(config.model_dim, config.vocab_size, bias=False) | |
| self.lm_head.weight = self.token_embedding.weight | |
| nn.init.normal_(self.token_embedding.weight, std=0.02) | |
| def forward(self, input_ids, attention_mask, device, last_positions=None): | |
| N = input_ids.shape[-1] | |
| causal_mask = torch.tril(torch.ones(N, N, device=device, dtype=torch.bool)) | |
| pad_mask = attention_mask | |
| attn_mask = causal_mask & pad_mask[:, None, None, :].bool() | |
| x = self.token_embedding(input_ids) | |
| x = self.pos_embedding(x) | |
| aux_total = 0 | |
| all_counts = [] | |
| for block in self.blocks: | |
| x, aux, counts = block(x, attn_mask, pad_mask) | |
| aux_total = aux_total + aux | |
| all_counts.append(counts) | |
| if last_positions is not None: # generation: only each row's next-token logits are needed -> (B, V) | |
| x = x[torch.arange(x.shape[0], device=x.device), last_positions] | |
| logits = self.lm_head(self.norm(x)) | |
| return logits, aux_total, all_counts | |