Download code/frozen_stasis/model.py from ShivankXD/stasis-1: direct link, hf CLI and curl.
- Browser
- Download file 5.47 kB
-
https://huggingface.co/ShivankXD/stasis-1/resolve/main/code/frozen_stasis/model.py
- Command line
-
hf download hf://ShivankXD/stasis-1/code/frozen_stasis/model.py
-
curl -L -o model.py https://huggingface.co/ShivankXD/stasis-1/resolve/main/code/frozen_stasis/model.py
5.47 kB
| """Random-initialized decoder: pre-RMSNorm, RoPE, causal SDPA, SwiGLU, tied head.""" | |
| from __future__ import annotations | |
| import math | |
| import torch | |
| from torch import nn | |
| from torch.nn import functional as F | |
| from torch.utils.checkpoint import checkpoint | |
| from .config import ModelConfig | |
| class RMSNorm(nn.Module): | |
| def __init__(self, size, epsilon): | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(size)) | |
| self.epsilon = epsilon | |
| def forward(self, x): | |
| y = x.float() | |
| y = y * torch.rsqrt(y.square().mean(-1, keepdim=True) + self.epsilon) | |
| return y.to(x.dtype) * self.weight | |
| class Attention(nn.Module): | |
| def __init__(self, config): | |
| super().__init__() | |
| self.heads = config.heads | |
| self.head_dim = config.hidden_size // config.heads | |
| self.qkv = nn.Linear(config.hidden_size, 3*config.hidden_size, bias=False) | |
| self.output = nn.Linear(config.hidden_size, config.hidden_size, bias=False) | |
| self.dropout = config.dropout | |
| inv_freq = 1.0 / (config.rope_theta ** (torch.arange(0, self.head_dim, 2).float() / self.head_dim)) | |
| frequencies = torch.outer(torch.arange(config.context_length).float(), inv_freq) | |
| self.register_buffer("cos", frequencies.cos()[None, None], persistent=False) | |
| self.register_buffer("sin", frequencies.sin()[None, None], persistent=False) | |
| def rotate(self, x): | |
| # Interleaved real/imaginary coordinates; RoPE computed in float32. | |
| even, odd = x.float()[..., 0::2], x.float()[..., 1::2] | |
| cos, sin = self.cos[:, :, :x.shape[-2]], self.sin[:, :, :x.shape[-2]] | |
| return torch.stack((even*cos-odd*sin, even*sin+odd*cos), -1).flatten(-2).to(x.dtype) | |
| def forward(self, x): | |
| b, t, h = x.shape | |
| q, k, v = self.qkv(x).view(b,t,3,self.heads,self.head_dim).permute(2,0,3,1,4).unbind(0) | |
| y = F.scaled_dot_product_attention(self.rotate(q), self.rotate(k), v, is_causal=True, | |
| dropout_p=self.dropout if self.training else 0.0) | |
| return self.output(y.transpose(1,2).contiguous().view(b,t,h)) | |
| class Block(nn.Module): | |
| def __init__(self, config): | |
| super().__init__() | |
| h, f = config.hidden_size, config.intermediate_size | |
| self.attention_norm = RMSNorm(h, config.rms_epsilon) | |
| self.attention = Attention(config) | |
| self.ffn_norm = RMSNorm(h, config.rms_epsilon) | |
| self.gate = nn.Linear(h,f,bias=False) | |
| self.up = nn.Linear(h,f,bias=False) | |
| self.down = nn.Linear(f,h,bias=False) | |
| self.dropout = nn.Dropout(config.dropout) | |
| def forward(self, x): | |
| x = x + self.dropout(self.attention(self.attention_norm(x))) | |
| z = self.ffn_norm(x) | |
| return x + self.dropout(self.down(F.silu(self.gate(z))*self.up(z))) | |
| class StasisDecoder(nn.Module): | |
| def __init__(self, config: ModelConfig): | |
| super().__init__() | |
| self.config = config | |
| self.embedding = nn.Embedding(config.vocab_size, config.hidden_size) | |
| self.blocks = nn.ModuleList(Block(config) for _ in range(config.layers)) | |
| self.norm = RMSNorm(config.hidden_size, config.rms_epsilon) | |
| self.apply(self._init) | |
| # Scale residual projections with depth, after common random initialization. | |
| for block in self.blocks: | |
| nn.init.normal_(block.attention.output.weight, std=0.02/math.sqrt(2*config.layers)) | |
| nn.init.normal_(block.down.weight, std=0.02/math.sqrt(2*config.layers)) | |
| def _init(module): | |
| if isinstance(module, (nn.Linear, nn.Embedding)): | |
| nn.init.normal_(module.weight, mean=0.0, std=0.02) | |
| def forward(self, tokens, targets=None, reduction="mean"): | |
| if tokens.ndim != 2 or tokens.shape[1] > self.config.context_length: | |
| raise ValueError("Expected [batch, sequence] within configured context length") | |
| x = self.embedding(tokens) | |
| for block in self.blocks: | |
| x = checkpoint(block, x, use_reentrant=False) if self.training and self.config.gradient_checkpointing else block(x) | |
| logits = F.linear(self.norm(x), self.embedding.weight) | |
| loss = None | |
| if targets is not None: | |
| if targets.shape != tokens.shape: | |
| raise ValueError("Targets must match input shape") | |
| loss = F.cross_entropy(logits.float().reshape(-1, self.config.vocab_size), targets.reshape(-1), reduction=reduction) | |
| return logits, loss | |
| def generate(self, tokens, max_new_tokens=64, temperature=1.0, top_k=50, eos_token_id=None): | |
| if max_new_tokens < 0 or temperature <= 0 or top_k < 0: | |
| raise ValueError("Invalid sampling controls") | |
| was_training = self.training | |
| self.eval() | |
| try: | |
| for _ in range(max_new_tokens): | |
| logits, _ = self(tokens[:, -self.config.context_length:]) | |
| logits = logits[:, -1].float() / temperature | |
| if top_k: | |
| threshold = torch.topk(logits, min(top_k, logits.size(-1))).values[:, -1,None] | |
| logits = logits.masked_fill(logits < threshold, -float("inf")) | |
| nxt = torch.multinomial(F.softmax(logits, dim=-1), 1) | |
| tokens = torch.cat([tokens,nxt], dim=1) | |
| if eos_token_id is not None and bool((nxt == eos_token_id).all()): | |
| break | |
| return tokens | |
| finally: | |
| self.train(was_training) | |