Built to understand GPT-style Transformers by implementing one from scratch β not a production model or a state-of-the-art system. Trained on modest data with modest compute; its purpose is to demonstrate a correct, working understanding of the architecture, training loop, and generation process end to end.
miniGPT is a compact, decoder-only Transformer implemented from scratch in PyTorch β no transformers library, no pretrained weights. Every component (attention, embeddings, normalization, the training loop, and inference-time sampling) is hand-written, so the mechanics of a GPT-style model are fully transparent β the same core ideas that power GPT-2 and GPT-3, scaled down.
flowchart TD
A["Token IDs (B, T)"] --> B["Token Embedding"]
A --> C["Positional Embedding"]
B --> D["Sum"]
C --> D
D --> E["Transformer Block Γ N"]
subgraph Block["Pre-Norm Transformer Block"]
direction TB
E1["LayerNorm β Self-Attention β Residual Add"] --> E4["LayerNorm β FeedForward (GELU) β Residual Add"]
end
E --> F["Final LayerNorm"]
F --> G["Linear Head β Logits (vocab_size)"]
Self-attention, briefly: input is projected into Q/K/V, split across n_head heads, scored as (QΒ·Kα΅)/βhead_size, masked so no token can attend to future positions (causal masking β what makes the model autoregressive), softmaxed, and used to weight V. Heads are concatenated and projected back to n_embd.
| File | Responsibility |
|---|---|
config.py |
GPTConfig β all hyperparameters |
tokenizer.py |
GPT-2 BPE setup + dataset loading |
model.py |
Attention, FeedForward, Block, GPTLanguageModel |
utils.py |
Batching, loss estimation, LR schedule, checkpointing |
train.py |
Training loop |
generate.py |
Generation, decoding comparison, attention plots |
Component notes (Token/Positional Embedding, LayerNorm, Residuals, Decoder Block)
- Token Embedding β maps each token ID to a learnable dense vector; the model's first representation of meaning.
- Positional Embedding β a learned vector per position, added to the token embedding, since attention has no inherent notion of order.
- LayerNorm β normalizes each token's activations; used pre-sublayer (GPT-2 convention) for training stability.
- Residual Connections β
x = x + sublayer(x), giving gradients a direct path through deep stacks. - Decoder Block β
x = x + Attn(LN(x)), thenx = x + FFN(LN(x)), stackedn_layertimes.
Self-supervised next-token prediction: the model predicts token t+1 from tokens 0..t at every position simultaneously.
flowchart LR
A[Raw Text] --> B[BPE Tokenize] --> C["Input/Target Pairs (shift by 1)"]
C --> D[Forward Pass] --> E[Cross-Entropy Loss] --> F[Backprop] --> G[AdamW Step]
G --> H{Eval Interval?}
H -- Yes --> I[Eval Loss + Sample + Checkpoint]
H -- No --> C
I --> C
- Gradient accumulation simulates a larger batch than fits in memory.
- Mixed precision (
torch.amp) speeds up CUDA training. - LR schedule: linear warmup, then cosine decay to
min_lr. - Every
eval_intervalsteps: estimate train/val loss, generate a sample, savelatest.ptand (if improved)best.pt.
Autoregressive: one token generated at a time, fed back in as input for the next step.
flowchart LR
A[Prompt] --> B[Tokenize] --> C[Forward Pass] --> D["Logits Γ· Temperature"]
D --> E["Top-k / Top-p Filter"] --> F[Softmax] --> G[Sample Token] --> H[Append] --> C
| Aspect | V2 β Character-Level | V3 β GPT-2 BPE |
|---|---|---|
| Vocabulary size | ~65 | 50,257 |
| Final train / val loss | 1.05 / 1.61 | 4.42 / 4.40 |
| Overfitting? | Yes (tiny dataset) | No (103M tokens) |
| Tokens per word | ~5 chars/token | ~1.3 tokens/word |
| Hyperparameter | Value |
|---|---|
n_embd / n_layer / n_head |
384 / 6 / 6 |
Context length (block_size) |
256 |
| Vocabulary size | 50,257 (GPT-2 BPE) |
| Dropout | 0.1 |
| Batch size (effective) | 16 Γ 4 grad-accum = 64 |
| Learning rate (peak β min) | 3e-4 β 3e-5 |
| Warmup / max iterations | 200 / 5,000 |
| Optimizer | AdamW |
| Total parameters | 49.38M (~49.4M) |
| Training hardware / time | 1Γ NVIDIA T4 GPU (Google Colab) / ~15 mins |
git clone https://github.com/MohamedAbdelaiem/miniGPT.git
cd miniGPT
pip install -r requirements.txt
python train.py # train (auto-resumes from checkpoints/latest.pt)
python generate.py # generate text / compare decoding strategiesfrom config import GPTConfig
from model import GPTLanguageModel
from tokenizer import setup_tokenizer
from generate import generate_text
from utils import load_checkpoint
import torch
config = GPTConfig()
encode, decode, config.vocab_size = setup_tokenizer()
model = GPTLanguageModel(config).to(config.device)
optimizer = torch.optim.AdamW(model.parameters(), lr=config.learning_rate)
scaler = torch.amp.GradScaler('cuda', enabled=(config.device == 'cuda'))
load_checkpoint('checkpoints/best.pt', model, optimizer, scaler, config.device)
print(generate_text(model, encode, decode, config,
prompt="Once upon a time", max_new_tokens=200,
temperature=0.8, top_p=0.9))miniGPT/
βββ config.py # Hyperparameters
βββ tokenizer.py # GPT-2 BPE + dataset loading
βββ model.py # Model definition
βββ utils.py # Training helpers
βββ train.py # Training loop
βββ generate.py # Inference & sampling
βββ requirements.txt
βββ checkpoints/ # latest.pt, best.pt
βββ outputs/run_*/ # checkpoints, samples, losses.png
Built as a hands-on exploration of GPT-style Transformers β from tokenization to text generation, one component at a time.