| """TinyLM — from-scratch transformer used for the VIVEK series. |
| Character-level language model: embedding + positional encoding + |
| causal Transformer encoder blocks + output head.""" |
| import torch |
| import torch.nn as nn |
|
|
|
|
| class TinyLM(nn.Module): |
| def __init__(self, vocab_size, d_model=128, n_layers=2, n_heads=4, |
| ff=256, max_len=256, dropout=0.1): |
| super().__init__() |
| self.config = dict(vocab_size=vocab_size, d_model=d_model, |
| n_layers=n_layers, n_heads=n_heads, ff=ff, |
| max_len=max_len, dropout=dropout) |
| self.tok = nn.Embedding(vocab_size, d_model) |
| self.pos = nn.Embedding(max_len, d_model) |
| layer = nn.TransformerEncoderLayer( |
| d_model=d_model, nhead=n_heads, dim_feedforward=ff, |
| dropout=dropout, activation="gelu", batch_first=True, |
| norm_first=True) |
| self.enc = nn.TransformerEncoder(layer, num_layers=n_layers) |
| self.head = nn.Linear(d_model, vocab_size) |
|
|
| def forward(self, x): |
| B, L = x.shape |
| h = self.tok(x) + self.pos(torch.arange(L, device=x.device)) |
| mask = torch.triu(torch.ones(L, L, dtype=torch.bool), diagonal=1) |
| h = self.enc(h, mask=mask) |
| return self.head(h) |
|
|
| @staticmethod |
| def from_config(path="config.json"): |
| import json |
| cfg = json.load(open(path)) |
| return TinyLM(**{k: cfg[k] for k in |
| ("vocab_size", "d_model", "n_layers", "n_heads", |
| "ff", "max_len", "dropout") if k in cfg}) |
|
|