A sub-400M-parameter language model trained from scratch — and, more to the
point, the training infrastructure around it: distributed-ready training loop,
crash-safe checkpoint/resume, fault tolerance, observability, and a verifiable
data pipeline. Built from first principles in PyTorch, no training frameworks.
Not a fine-tune. Not a wrapper. Random init → a working LM, trained by code in
this repo. The model is standard-modern; the systems work is the point.
Three configs, one stack
Config
Params
Tokens
Optimizer
Data
Schedule
Purpose
configs/base_124m.json
114M
~3B
AdamW
FineWeb-Edu
warmup+cosine
v1 — original portfolio run
configs/base_152m_v2.json
152M
~15B
Muon+AdamW hybrid
SmolLM-corpus 83/17
WSD (MiniCPM)
v1.5 — actual hero run (18L × 768d, ReLU², soft-cap, Liger)
configs/base_350m.json
363M
(15B)
Muon+AdamW
SmolLM-corpus
WSD
reference only — pivoted away from (activation-memory ceiling on 40GB)
The v1.5 hero run (152M) is designed around two claims: (1) direct token-
matched comparison vs Pythia-160M (same width, 50% more depth, better data,
better optimizer); (2) approaching Pythia-410M at the same token count
despite being 2.7× smaller — the "beats above weight" framing. Eight things
differ from v1: deeper architecture (18L vs 12L), seq_len 2048 (vs 1024), Muon
hybrid (vs AdamW), WSD schedule (vs cosine), Liger fused linear+CE, z-loss,
ReLU² FFN (vs SwiGLU), final logit soft-cap.
Why this exists
This is a portfolio project for an LLM training-infrastructure role. The
interesting problems in training large models aren't the architecture (well
understood) — they're the systems: making multi-day runs reliable, resumable,
observable, and fast on the hardware you have. So this repo is deliberately
weighted toward operational excellence over architectural novelty.
Architecture (src/matilda/model.py)
A modern dense decoder-only transformer — the same recipe as Llama/Qwen-class
models. Shape is a runtime knob:
atomic writes; saves model+opt+sched+step+RNG+dataloader position; a killed run resumes to a loss curve identical to the uninterrupted one (< 1e-6, tested)
Fault tolerance
train.py
NaN/Inf guard (skip+log+abort-after-N); SIGTERM → checkpoint-and-exit for spot-instance death
Observability
monitor.py
MFU (incl. attention FLOPs), tokens/s, rolling step-time (catches throttling), grad-norm, peak GPU mem → always-on metrics.jsonl + optional W&B
full config + git SHA logged per run; deterministic seeding
Results
Validated (RTX 3090): 30/30 tests pass on GPU, smoke + bit-for-bit resume
clean, 53.4% MFU at batch_size=24 with torch.compile (BS≥28 OOMs on the
vocab projection — the expected memory hotspot).
Training run + ablations: pending the A100 run. The ablation harness
(scripts/ablate.py) emits docs/ABLATIONS.md — a controlled comparison, one
change per row:
35 tests run on CPU in ~2 min. Highlights: overfit-single-batch (the model can
learn), causal-mask-no-leak (no future-token leakage), bit-for-bit resume,
NaN-skip-then-recover, shard checksum corruption detection, Muon overfit, WSD
three-phase shape, z-loss equals CE + lse² when on. BASE_350M shape test skips
on CPU dev boxes without liger-kernel.