CourseTransformers: From Attention to Trained, Served and Interpreted Models · Course guide · part 4 of 111
Part 4 · Course guide

The Project: tinyformer-lab at Aurora Station

11 min read·13 Oct 2026

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

NameRoleIn the course
YouMachine-learning engineerBuild, train and measure everything in the lab
Maya LindqvistStation Systems LeadOwns the maintenance logs. Wants each log turned into a four-field card (Module 12) and makes the ship decision
Kofi MensahReliability EngineerReviews every claim the lab makes. His rule: no comparison without a seed noise floor and a test (Modules 5, 6, 15)
Hana AokiCompute OfficerOwns the station's compute and the budget for training time hired on Earth. Asks what every run costs (Modules 9, 11, 15)
Tariq HaddadComms OfficerOwns the link to Earth. Sets the limits on latency, bandwidth and what may be sent (Modules 10, 11, 12)
Isla DuarteRobotics EngineerOwns 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.

DataWhat it isSize
The Aurora corpusFive kinds of station document: maintenance logs, assignment sheets with questions, dialogues, greenhouse reports, procedures5,700 training and 300 validation documents, about 1.8 million characters
The character tokeniserOne token per character, plus <pad>, <doc> and <unk>69 tokens
Synthetic taskscopy, 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 questionsHeld-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 cardsA 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 1281,000 training and 200 evaluation pairs
Camera test cards32 by 32 pictures of one to three squares, circles or triangles in one colour, labelled shape, colour, count and caption2,000 images
The base modelThe 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 embeddings800,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 (SEED in 06). Datasets that must not overlap use SEED + 1, SEED + 2 and 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 times base). Module 9, Lesson 4 defines it and the Module 9 lab saves it as scaling.BIG. Earlier Module 9 lessons round it to "about 800 million" or 1,000 times base (800,512,000); big is 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.

SolModuleEvent
4001You 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 4082 to 4Attention, the block and positions go into the lab, each with a switch and a test.
4095Kofi 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.
4126The base recipe is fixed (600 steps, AdamW, warm-up then cosine). Every later lesson trains it the same way.
4187You compare the lab's design with published configurations, and add a mixture-of-experts switch.
4218The corpus report: mixture, duplicates, contamination of the lookup questions, a trained tokeniser.
4269Hana 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.
43110Tariq 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 43611The 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 44612Maya 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.
44713Isla 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.
45214Kofi asks: what has base actually learnt? You instrument it, find what copies names (or why it cannot yet), and compare with GPT-2 small.
46015Capstone. 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.

ModuleAdds to tinyformer-lab (as written by the module's lab)Tag
1The 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.pyv0.1.0
2tinyformer/attention.py: attention by hand, the masks, multi-head, grouped and multi-query heads; experiments/m02_kv_heads.py; tests/test_m02_attention.pyv0.2.0
3tinyformer/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.pyv0.3.0
4tinyformer/positions.py: sinusoidal, learned, relative, ALiBi, rotary, and context extension; new fields in config.py; experiments/m04_positions.py; tests/test_m04_positions.pyv0.4.0
5tinyformer/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
6tinyformer/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.pyv0.6.0
7tinyformer/presets.py (encoder, decoder, encoder-decoder and published-config presets), tinyformer/moe.py; experiments/m07_moe.py; tests/test_m07_families.pyv0.7.0
8tinyformer/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.pyv0.8.0
9tinyformer/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.pyv0.9.0
10tinyformer/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.pyv0.10.0
11tinyformer/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.pyv0.11.0
12tinyformer/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 manifestv0.12.0
13tinyformer/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.pyv0.13.0
14tinyformer/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.pyv0.14.0
15tinyformer/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:

KeptChanged
The configurable lab, tinyformer-lab, with every design choice a switchThe lab is built by the learner across Modules 1 to 15, not supplied
The generated Aurora Station corpus, with its known mixtureFive 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 questionsNames 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 12The card format and the evaluation set are fixed in 06
The shapes images2,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 arithmeticUnchanged
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.