The Project: tinyformer-lab at Aurora Station
Every lesson in this course adds to one project: your own configurable transformer lab, tinyformer-lab. Every design choice in it is a switch you can flip and measure: the position method, the normalisation, the activation, the number of key/value heads, tied embeddings, sliding windows, experts. You finish with one repository that builds, trains, serves, adapts and takes apart a transformer, with a test for every claim it makes.
The lab has a setting, so that its data, its people and its deadlines stay the same from Module 1 to Module 15. Aurora Station and everyone on it are made up. All names, logs, numbers and events in this setting are fictional and simulated. The real cases that open each lesson are separate, cited, and marked as real.
The setting
Aurora Station is a fictional research station on Mars with a crew of about forty. It keeps a written record of everything: maintenance logs, assignment sheets, crew conversations, greenhouse reports and procedures. That record, up to sol 399, is the course corpus (a sol is a Martian day).
The station has one problem that shapes the whole course: its link to Earth is slow, narrow and sometimes down. A message takes several minutes each way and bandwidth is rationed. Large hosted models on Earth can be asked a question only now and then, through a relay, and not at all during an outage. So the station runs its own small models, on its own small CPU servers. That is why every lesson in this course runs on a CPU, why every model is small enough to train in minutes, and why efficiency, quantisation and honest measurement matter here.
You are the station's machine-learning engineer. You own tinyformer-lab.
The people
| Name | Role | In the course |
|---|---|---|
| You | Machine-learning engineer | Build, train and measure everything in the lab |
| Maya Lindqvist | Station Systems Lead | Owns the maintenance logs. Wants each log turned into a four-field card (Module 12) and makes the ship decision |
| Kofi Mensah | Reliability Engineer | Reviews every claim the lab makes. His rule: no comparison without a seed noise floor and a test (Modules 5, 6, 15) |
| Hana Aoki | Compute Officer | Owns the station's compute and the budget for training time hired on Earth. Asks what every run costs (Modules 9, 11, 15) |
| Tariq Haddad | Comms Officer | Owns the link to Earth. Sets the limits on latency, bandwidth and what may be sent (Modules 10, 11, 12) |
| Isla Duarte | Robotics Engineer | Owns the robots' cameras and their test cards: the shapes dataset (Module 13) |
All five names come from the corpus's own name lists, so they can turn up in generated documents too. That is a coincidence of the generator, not a story event. Minor characters may appear in one lesson; the main cast above does not change.
The data
Everything is generated by code in 06-shared-data.md, from one seed. Nothing is downloaded except the two small published models below.
| Data | What it is | Size |
|---|---|---|
| The Aurora corpus | Five kinds of station document: maintenance logs, assignment sheets with questions, dialogues, greenhouse reports, procedures | 5,700 training and 300 validation documents, about 1.8 million characters |
| The character tokeniser | One token per character, plus <pad>, <doc> and <unk> | 69 tokens |
| Synthetic tasks | copy, reverse and induction, each with a known 100% ceiling. The shared induction task always copies from 16 back, so a fixed-offset shortcut solves it; Module 14 adds a variable-gap version (make_varied) | 4,096 sequences each |
| Lookup questions | Held-out assignment sheets ending in a question, with the expected answer. At about 442 characters they are longer than base's 128-character context (see 06, blocks 10 and 19) | 200 |
| Log cards | A maintenance log in, a four-field card out (person, system, place, outcome), written as field: value lines, never JSON (the character vocabulary has no {, " or }). A log and its card together run 305 to 440 characters, so Module 12 uses a 448-character window (CARD_WINDOW), not base's 128 | 1,000 training and 200 evaluation pairs |
| Camera test cards | 32 by 32 pictures of one to three squares, circles or triangles in one colour, labelled shape, colour, count and caption | 2,000 images |
The base model | The lab's reference configuration, trained from the corpus in every lesson that needs it: 4 layers, 4 heads, width 128, context 128, rotary positions, RMSNorm, SwiGLU, tied embeddings | 800,512 parameters; validation loss 0.256 after 600 steps |
Two real, published models are used where a lesson needs to look inside a model someone else trained: GPT-2 small (openai-community/gpt2, MIT licence), the course's reference model, and SmolLM2-135M (HuggingFaceTB/SmolLM2-135M, Apache 2.0), the example of the modern default stack. Details and how to load them: 02-setup-colab-and-llm-providers.md, section 6.
Fixed facts
These are set once here so that every module states them the same way.
- Course date: Friday 9 October 2026. Versions, model names, prices and documentation were checked on this day. Lessons say "(checked October 2026)".
- Currency: US dollars, for every cost in the course, because the hardware prices the course cites are published in dollars.
- The data snapshot is sol 400. Every generated document is dated sol 1 to 399. The story starts on sol 400.
- The seed is 11 (
SEEDin 06). Datasets that must not overlap useSEED + 1,SEED + 2and so on, as 06 shows. - Validation loss always means the average over
VALID_BATCHES(eight fixed batches of 32 windows of 128 tokens) in nats per token, unless a lesson says otherwise. - Every number is labelled as one of three kinds: measured here (from running the lesson's code, with the machine named), cited (from a named source, with a date) or arithmetic (worked out from a formula on the page). Timings are always "measured here" and always say which machine.
- Hosted model prices are kept in one place: the table in course-guide/02, section 4. GPU hire prices and hardware datasheet figures are kept in one place: the table in Module 9, Lesson 4, section 8 ("Costing a training run"), dated there (Lambda prices, October 2026; the H100 dense figure from NVIDIA's datasheet). Module 10's memory-bandwidth figures should refer to it. Other lessons refer to those two places and do not restate the numbers.
- Hana's 1,000-times model is
big: width 2,048, 16 layers, 16 heads, context 2,048, 805,777,408 parameters (about 1,007 timesbase). Module 9, Lesson 4 defines it and the Module 9 lab saves it asscaling.BIG. Earlier Module 9 lessons round it to "about 800 million" or 1,000 timesbase(800,512,000);bigis the one to quote. - The station's model servers are small CPU machines, the same shape as a free Colab CPU runtime. A lesson's training run should finish in a few minutes there (a Module 12 LoRA run at the 448-character card window takes 3 to 4 minutes on the author's Mac, one thread).
Key events
Lessons refer to these events. They are fixed, so every module tells the same story. They happen in sols after the snapshot.
| Sol | Module | Event |
|---|---|---|
| 400 | 1 | You start tinyformer-lab. The station record up to sol 399 becomes the corpus. You race a GRU against a transformer on the copy task and keep the result as the lab's first report. |
| 404 to 408 | 2 to 4 | Attention, the block and positions go into the lab, each with a switch and a test. |
| 409 | 5 | Kofi reviews the model code and sets his rule: every claim needs a test, and every comparison needs a seed noise floor. The behavioural causal-mask test, the overfit-one-batch test and the gradient check go in. |
| 412 | 6 | The base recipe is fixed (600 steps, AdamW, warm-up then cosine). Every later lesson trains it the same way. |
| 418 | 7 | You compare the lab's design with published configurations, and add a mixture-of-experts switch. |
| 421 | 8 | The corpus report: mixture, duplicates, contamination of the lookup questions, a trained tokeniser. |
| 426 | 9 | Hana asks what a model 1,000 times bigger (big, 805,777,408 parameters) would cost to train on hired GPUs on Earth. Her cap for any single hired run is $1,500. You fit a scaling law on a grid of small models and say what it can and cannot tell her. |
| 431 | 10 | Tariq reports that long transmissions are being split. You measure what is expensive in attention and what the KV cache costs as context grows. |
| 433 to 436 | 11 | The Earth link is down for three sols. The station can use only its own models, on its own CPUs. You build the decoding loop, the KV cache, speculative decoding, batching and quantisation, and serve base locally. |
| 440 to 446 | 12 | Maya asks for log cards: every maintenance log turned into the four-field card. You first measure prompting a large hosted model (through the relay, now back up), then fine-tune base, then decide. Her bar for shipping: all four fields exactly right on at least 90% of CARD_EVAL, with validation loss on the general corpus no more than 0.02 worse than base. |
| 447 | 13 | Isla needs the robots' cameras to tell what is on a test card and how many. You build a small vision transformer, then CLIP, then a captioner, on the shapes data. |
| 452 | 14 | Kofi asks: what has base actually learnt? You instrument it, find what copies names (or why it cannot yet), and compare with GPT-2 small. |
| 460 | 15 | Capstone. You write the lab's report for the station: what you built, what you measured, what it cost, and what you would not claim. Tag v1.0.0. |
One repository: which module adds which file
Everything you keep goes in one repository, tinyformer-lab, around a single Python package called tinyformer/. Lessons run in Colab, so their code sits in cells; each module's lab saves the pieces into the package (with %%writefile) and adds a test. Push the repository to your own GitHub account after each lab, because Colab's disk is wiped when the runtime stops.
| Module | Adds to tinyformer-lab (as written by the module's lab) | Tag |
|---|---|---|
| 1 | The repository: pyproject.toml, README.md, .gitignore, tinyformer/__init__.py, tinyformer/data.py (seeds and synthetic tasks from 06), experiments/m01_rnn_vs_attention.py, tests/test_m01_data.py | v0.1.0 |
| 2 | tinyformer/attention.py: attention by hand, the masks, multi-head, grouped and multi-query heads; experiments/m02_kv_heads.py; tests/test_m02_attention.py | v0.2.0 |
| 3 | tinyformer/config.py (ModelConfig), tinyformer/layers.py (feed-forward variants, norms), tinyformer/model.py (block, embeddings, output head), more shared blocks appended to tinyformer/data.py; experiments/m03_switches.py; tests/test_m03_block.py | v0.3.0 |
| 4 | tinyformer/positions.py: sinusoidal, learned, relative, ALiBi, rotary, and context extension; new fields in config.py; experiments/m04_positions.py; tests/test_m04_positions.py | v0.4.0 |
| 5 | tinyformer/init.py (five starting-weight schemes, or GPT-2 small's weights); four repairs edited into tinyformer/model.py; experiments/m05_precision.py; tests/test_m05_model.py (causal-mask behaviour, overfit one batch, finite differences) | v0.5.0 |
| 6 | tinyformer/train.py (the trainer, schedules, clipping, checkpoints, monitoring, run records); experiments/m06_base.py, which writes runs/base/ (metrics.csv, ckpt.pt, manifest.json); tests/test_m06_train.py | v0.6.0 |
| 7 | tinyformer/presets.py (encoder, decoder, encoder-decoder and published-config presets), tinyformer/moe.py; experiments/m07_moe.py; tests/test_m07_families.py | v0.7.0 |
| 8 | tinyformer/corpus.py (corpus report, deduplication, contamination check), tinyformer/tokenizer.py (byte pair encoding), tinyformer/packing.py, tinyformer/objectives.py; experiments/m08_objectives.py; tests/test_m08_pretrain.py | v0.8.0 |
| 9 | tinyformer/scaling.py (grid, fit, and BIG, the one definition of Hana's 1,000-times model), tinyformer/costs.py (memory, allocation and dollar arithmetic), tinyformer/parallel/ (__init__.py, launch.py, the one launcher every distributed script shares, then data.py, zero.py, tensor.py, pipeline.py); experiments/m09_hana.py; tests/test_m09_scale.py | v0.9.0 |
| 10 | tinyformer/attention_variants.py (sparse, banded, linear and tiled attention, a kernel switch), tinyformer/kvcache.py (KV cache cost and ways to shrink it), tinyformer/longctx.py (Lesson 4's long-context probes, coded transmissions, report card and retrieval helpers); experiments/m10_efficiency.py; tests/test_m10_efficiency.py | v0.10.0 |
| 11 | tinyformer/generate.py (one KV cache API, the request contract, decoders, constraints, speculative decoding), an optional cache argument added to Attention in model.py, tinyformer/serve/ (__init__.py, paged.py, batcher.py, scheduler.py, api.py, the FastAPI endpoint), tinyformer/compress.py (quantisation, pruning, distillation); experiments/m11_serving.py; tests/test_m11_serving.py | v0.11.0 |
| 12 | tinyformer/adapt/ (cards.py, sft.py, score.py, lora.py, prefs.py), evals/log_cards.py (Maya's bar as a runnable check), tests/test_m12_adapt.py, experiments/m12_adapt.py and its manifest | v0.12.0 |
| 13 | tinyformer/vision.py (camera test cards and one split, patches, one VisionEncoder shared by the classifier, CLIP and the captioner, and Lesson 3's video tubelet and sound spectrogram sketches); experiments/m13_vision.py; tests/test_m13_vision.py | v0.13.0 |
| 14 | tinyformer/analysis.py (tasks with a known answer including make_varied, hooks, exact ablation, patching, logit lens, probes), tinyformer/ssm.py (make_recall, the state-space mixer, short convolution); experiments/m14_analysis.py; tests/test_m14_analysis.py | v0.14.0 |
| 15 | tinyformer/capstone.py (cost ledger, three-seed runner, noise floor and verdict, report generator), tests/test_m15_capstone.py, experiments/m15_capstone.py, REPORT.md (generated from the run manifest, with a hand-written part) | v1.0.0 |
Each module's experiment script reproduces the module's main table. Results go in runs/, never inside tinyformer/.
Two things about the order. The Module 9 and Module 10 labs both start from v0.8.0 and touch separate files, so you can do either first; the Module 11 lab rebuilds both sets of files (each written only if missing) before it adds its own. From Module 11 on, a lab that finds no repository of yours rebuilds a stand-in of every earlier file it needs, so you can start at any later module.
What you keep from the old version of this course
This is the second version of the course. The first assumed a lab folder, a shipped pretrained model (models/base), a 9 MB generated corpus and an image dataset that learners did not have. This version keeps the ideas and makes everything reproducible inside a Colab notebook:
| Kept | Changed |
|---|---|
The configurable lab, tinyformer-lab, with every design choice a switch | The lab is built by the learner across Modules 1 to 15, not supplied |
| The generated Aurora Station corpus, with its known mixture | Five sources, not six (the inventory lists are gone); 1.8 million characters, not 9 MB; British spelling in the templates; generated in a code block in under a second |
| Maintenance logs that name a person then repeat the first name; assignment sheets with questions | Names on a sheet are unique, so every question has one answer; the lookup questions come with a target to score |
| The synthetic tasks (copy and friends) | copy, reverse and induction, defined in 06 |
| The log-to-card task for Module 12 | The card format and the evaluation set are fixed in 06 |
| The shapes images | 2,000 images, not 7,200, generated in code; each image holds one shape kind in one colour, so shape, colour, count and caption are unambiguous |
The shipped models/base (3,148,224 parameters, BPE vocabulary of 4,096 with 180 unused rows) | Replaced by the base recipe (800,512 parameters, character vocabulary of 69 with no unused rows), trained in each lesson that needs it; GPT-2 small and SmolLM2-135M for inspecting real, published models |
| Matched-parameter comparisons with a seed noise floor; every number labelled measured, cited or arithmetic | Unchanged |
| The model test suite (causal-mask behaviour, overfit one batch, finite differences) | Unchanged, built in Module 5 |
What "done" means
At the end of the course your repository can answer Kofi's question about any claim in it: show me the test, show me the run, show me the noise floor. If it can, you understand transformers well enough to build one, change one and say honestly what your change did.