An educational implementation of a GPT-style language model from scratch using pure NumPy. This project demonstrates how Large Language Models (LLMs) work at every level, from tokenization to training to generation.
This codebase is designed to teach the internals of transformer-based language models through human-readable code with extensive documentation. Every component includes:
- Detailed docstrings explaining the mathematical operations
- References to original research papers
- Clear variable naming that maps to paper notation
This works on a mac/linux machine.
uv syncThis repository includes a dev container configuration for VS Code that provides a fully isolated, interactive development environment. To use it:
- Install the Dev Containers extension
- Open the repository in VS Code
- Click "Reopen in Container" when prompted (or use the command palette: "Dev Containers: Reopen in Container")
Included Tools:
- Python 3.14 with uv package manager
- Claude Code CLI for AI-assisted development (run
claudeto authenticate) - zsh shell with common CLI tools (vim, htop, tmux, fzf, ripgrep, fd, bat, jq, tree)
- Full sudo access for flexibility
Note: The dev container uses pyproject-container.toml which excludes MLX dependencies. MLX acceleration (mac-accel mode) requires Apple Silicon hardware and is not available in containers. For MLX support, run natively on macOS with Apple Silicon.
For a deep understanding of each component, we provide comprehensive documentation files with:
- Mathematical formulas and derivations
- Step-by-step numeric examples
- Visualizations and diagrams (including embedded images from authoritative sources)
- References to research papers
Start with How to learn with this.md for a guided learning path and high level summaries of each section.
| Topic | Documentation | Code |
|---|---|---|
| Tokenization (BPE) | Tokenization.md | src/tokenizer.py |
| Word Embeddings | Embeddings.md | src/layers.py |
| Positional Encoding | PositionalEncoding.md | src/layers.py |
| Attention Mechanism | Attention.md | src/attention.py |
| Feed-Forward Network | FeedForwardNetwork.md | src/transformer.py |
| Transformer Block | TransformerBlock.md | src/transformer.py |
| Full GPT Model | GPTModel.md | src/model.py |
| Training & Optimization | Training.md | src/optimizer.py |
| Text Generation | TextGeneration.md | src/model.py |
| Fine-Tuning & LoRA | FineTuning.md | src/lora.py |
uv run run_demo.py --helpuv run run_demo.py quickuv run run_demo.py fullNOTE this is experimental and does not currently have parity with the NumPy mode.
For significantly faster training on M1/M2/M3/M4 Macs, use the MLX-accelerated mode:
# Run with default settings (small model, 10% data)
uv run run_demo.py mac-accel
# Experiment with larger models and more data
uv run run_demo.py mac-accel --model-size medium --data-size 0.5
uv run run_demo.py mac-accel --model-size large --data-size 1.0
# Use absolute data sizes
uv run run_demo.py mac-accel --model-size large --data-size 500kModel Size Presets:
| Size | Parameters | Embedding | Heads | Layers | Best For |
|---|---|---|---|---|---|
| tiny | ~100K | 64 | 2 | 2 | Quick iteration |
| small | ~500K | 128 | 4 | 4 | Default, balanced |
| medium | ~2M | 256 | 8 | 6 | Better quality |
| large | ~8M | 512 | 8 | 8 | Best quality |
Data Size Options:
- Fraction:
0.1(10% of dataset),0.5(50%),1.0(full) - Absolute:
50000(50K characters) - Suffix:
100k,500k,1m
The purpose of this mode is to demonstrate:
- How larger models improve text generation quality
- How more training data improves model performance
- The speed difference between NumPy (single CPU) and MLX (GPU)
Open Activity Monitor to see GPU utilization during training.
# Pre-train on Shakespeare
uv run train_pretrain.py
# Fine-tune (choose one)
uv run train_finetune_full.py # All parameters
uv run train_finetune_lora.py # LoRA (1% parameters)
# Interactive generation
uv run run_demo.py generateuv run pytest tests/ -v # 139 tests (MLX tests skip on non-Apple systems)Ideally each section would be independent to isolate concepts, but in reality some concepts are dependent on others. Here's a diagram of the main dependencies:
tokenizer.py ─────────────────────────────────────────┐
│
activations.py ──────┬───────────────────────────────┤
│ │
layers.py ───────────┼─── attention.py ───┬──────────┤
│ │ │
└─── transformer.py ─┘ │
│ │
└─────── model.py ─────┤
│ │
lora.py ────┘ │
│
optimizer.py ───────────────────────────────────────┤
│
utils.py ───────────────────────────────────────────┘
│
┌───────────────┼───────────────┐
│ │ │
train_pretrain.py train_finetune_*.py run_demo.py
- Attention Mechanism: Vaswani, A., et al. (2017). Attention Is All You Need
- Pre-LN Transformer: Xiong, R., et al. (2020). On Layer Normalization in the Transformer Architecture
- Residual Connections: He, K., et al. (2015). Deep Residual Learning for Image Recognition
- Layer Normalization: Ba, J., et al. (2016). Layer Normalization
- GPT-1: Radford, A., et al. (2018). Improving Language Understanding by Generative Pre-Training
- GPT-2: Radford, A., et al. (2019). Language Models are Unsupervised Multitask Learners
- GPT-3: Brown, T., et al. (2020). Language Models are Few-Shot Learners
- Weight Tying: Press, O., & Wolf, L. (2017). Using the Output Embedding to Improve Language Models
- BPE Tokenization: Sennrich, R., et al. (2016). Neural Machine Translation of Rare Words with Subword Units
- SentencePiece: Kudo, T., & Richardson, J. (2018). SentencePiece: A simple and language independent subword tokenizer
- Word2Vec: Mikolov, T., et al. (2013). Efficient Estimation of Word Representations in Vector Space
- GloVe: Pennington, J., et al. (2014). GloVe: Global Vectors for Word Representation
- Neural Language Models: Bengio, Y., et al. (2003). A Neural Probabilistic Language Model
- Relative Position: Shaw, P., et al. (2018). Self-Attention with Relative Position Representations
- Rotary Embeddings: Su, J., et al. (2021). RoFormer: Enhanced Transformer with Rotary Position Embedding
- GELU Activation: Hendrycks, D., & Gimpel, K. (2016). Gaussian Error Linear Units
- GLU Variants: Shazeer, N. (2020). GLU Variants Improve Transformer
- Adam Optimizer: Kingma, D.P., & Ba, J. (2014). Adam: A Method for Stochastic Optimization
- AdamW Optimizer: Loshchilov, I., & Hutter, F. (2017). Decoupled Weight Decay Regularization
- Learning Rate Warmup: Goyal, P., et al. (2017). Accurate, Large Minibatch SGD
- Gradient Clipping: Pascanu, R., et al. (2013). On the difficulty of training recurrent neural networks
- Top-K Sampling: Fan, A., et al. (2018). Hierarchical Neural Story Generation
- Nucleus (Top-P) Sampling: Holtzman, A., et al. (2019). The Curious Case of Neural Text Degeneration
- Sampling Strategies: Ippolito, D., et al. (2019). Comparison of Diverse Decoding Methods
- LoRA: Hu, E., et al. (2021). LoRA: Low-Rank Adaptation of Large Language Models
- QLoRA: Dettmers, T., et al. (2023). QLoRA: Efficient Finetuning of Quantized LLMs
- Parameter-Efficient Transfer: He, J., et al. (2021). Towards a Unified View of Parameter-Efficient Transfer Learning
- The Illustrated Transformer: Jay Alammar's Visual Guide
- Attention Mechanism Origins: Bahdanau, D., et al. (2014). Neural Machine Translation by Jointly Learning to Align and Translate