Download code/model.py from Akshat-Dwivedi/micro-gpt: direct link, hf CLI and curl.
- Browser
- Download file 5.55 kB
-
https://huggingface.co/Akshat-Dwivedi/micro-gpt/resolve/c92b1119ae5241a558ab27c7790bf4cfc1963da1/code/model.py
- Command line
-
hf download hf://Akshat-Dwivedi/micro-gpt@c92b1119ae5241a558ab27c7790bf4cfc1963da1/code/model.py
-
curl -L -o model.py https://huggingface.co/Akshat-Dwivedi/micro-gpt/resolve/c92b1119ae5241a558ab27c7790bf4cfc1963da1/code/model.py
5.55 kB
| """Dense decoder-only GPT model used by train.py.""" | |
| from __future__ import annotations | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from config import ModelConfig | |
| class RMSNorm(nn.Module): | |
| def __init__(self, width: int, eps: float = 1e-5): | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(width)) | |
| self.eps = eps | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| variance = x.float().square().mean(dim=-1, keepdim=True) | |
| return (x * torch.rsqrt(variance + self.eps)).type_as(x) * self.weight | |
| class Attention(nn.Module): | |
| def __init__(self, cfg: ModelConfig): | |
| super().__init__() | |
| assert cfg.d_model % cfg.n_heads == 0 | |
| self.n_heads = cfg.n_heads | |
| self.head_dim = cfg.d_model // cfg.n_heads | |
| self.qkv = nn.Linear(cfg.d_model, 3 * cfg.d_model, bias=False) | |
| self.output = nn.Linear(cfg.d_model, cfg.d_model, bias=False) | |
| self.dropout = cfg.dropout | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| batch, length, width = x.shape | |
| q, k, v = self.qkv(x).chunk(3, dim=-1) | |
| q = q.view(batch, length, self.n_heads, self.head_dim).transpose(1, 2) | |
| k = k.view(batch, length, self.n_heads, self.head_dim).transpose(1, 2) | |
| v = v.view(batch, length, self.n_heads, self.head_dim).transpose(1, 2) | |
| output = F.scaled_dot_product_attention( | |
| q, k, v, is_causal=True, dropout_p=self.dropout if self.training else 0.0 | |
| ) | |
| return self.output(output.transpose(1, 2).contiguous().view(batch, length, width)) | |
| class SwiGLU(nn.Module): | |
| def __init__(self, cfg: ModelConfig): | |
| super().__init__() | |
| self.gate_up = nn.Linear(cfg.d_model, 2 * cfg.mlp_hidden, bias=False) | |
| self.down = nn.Linear(cfg.mlp_hidden, cfg.d_model, bias=False) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| gate, up = self.gate_up(x).chunk(2, dim=-1) | |
| return self.down(F.silu(gate) * up) | |
| class TransformerBlock(nn.Module): | |
| def __init__(self, cfg: ModelConfig): | |
| super().__init__() | |
| self.attention_norm = RMSNorm(cfg.d_model) | |
| self.attention = Attention(cfg) | |
| self.mlp_norm = RMSNorm(cfg.d_model) | |
| self.mlp = SwiGLU(cfg) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| x = x + self.attention(self.attention_norm(x)) | |
| return x + self.mlp(self.mlp_norm(x)) | |
| class GPT(nn.Module): | |
| """A dense ~50M model. Tied embeddings keep vocabulary capacity efficient.""" | |
| def __init__(self, cfg: ModelConfig): | |
| super().__init__() | |
| self.cfg = cfg | |
| self.token_embedding = nn.Embedding(cfg.vocab_size, cfg.d_model) | |
| self.position_embedding = nn.Embedding(cfg.block_size, cfg.d_model) | |
| self.blocks = nn.ModuleList([TransformerBlock(cfg) for _ in range(cfg.n_layers)]) | |
| self.final_norm = RMSNorm(cfg.d_model) | |
| self.lm_head = nn.Linear(cfg.d_model, cfg.vocab_size, bias=False) | |
| self.lm_head.weight = self.token_embedding.weight | |
| self.gradient_checkpointing = False | |
| self.apply(self._init_weights) | |
| def _init_weights(module: nn.Module) -> None: | |
| if isinstance(module, (nn.Linear, nn.Embedding)): | |
| nn.init.normal_(module.weight, mean=0.0, std=0.02) | |
| def forward(self, input_ids: torch.Tensor, targets: torch.Tensor | None = None): | |
| _, length = input_ids.shape | |
| if length > self.cfg.block_size: | |
| raise ValueError(f"Sequence length {length} exceeds {self.cfg.block_size}.") | |
| positions = torch.arange(length, device=input_ids.device) | |
| x = self.token_embedding(input_ids) + self.position_embedding(positions) | |
| checkpointing = self.training and self.gradient_checkpointing | |
| for block in self.blocks: | |
| x = torch.utils.checkpoint.checkpoint(block, x, use_reentrant=False) if checkpointing else block(x) | |
| x = self.final_norm(x) | |
| if targets is None: | |
| return self.lm_head(x[:, [-1]]), None | |
| # Do not allocate [batch, sequence, 32k] logits at once: this is VRAM-safe on 6GB. | |
| hidden = x[:, :-1].contiguous().view(-1, self.cfg.d_model) | |
| labels = targets[:, 1:].contiguous().view(-1) | |
| loss_sum = hidden.new_zeros((), dtype=torch.float32) | |
| for start in range(0, labels.numel(), 256): | |
| loss_sum = loss_sum + F.cross_entropy( | |
| self.lm_head(hidden[start : start + 256]).float(), labels[start : start + 256], reduction="sum" | |
| ) | |
| return None, loss_sum / labels.numel() | |
| def generate( | |
| self, | |
| input_ids: torch.Tensor, | |
| max_new_tokens: int, | |
| temperature: float = 0.8, | |
| top_k: int = 50, | |
| eos_id: int | None = None, | |
| ): | |
| for _ in range(max_new_tokens): | |
| logits, _ = self(input_ids[:, -self.cfg.block_size :]) | |
| logits = logits[:, -1] / max(temperature, 1e-5) | |
| if top_k > 0: | |
| threshold = torch.topk(logits, min(top_k, logits.size(-1))).values[:, [-1]] | |
| logits = logits.masked_fill(logits < threshold, float("-inf")) | |
| next_token = torch.multinomial(F.softmax(logits, dim=-1), 1) | |
| input_ids = torch.cat((input_ids, next_token), dim=1) | |
| if eos_id is not None and (next_token == eos_id).all(): | |
| break | |
| return input_ids | |
| def parameter_count(model: nn.Module) -> int: | |
| return sum(parameter.numel() for parameter in model.parameters()) | |