Zenith
Stable · v1.0A from-scratch generative NLP library — decoder-only transformer language models, causal-LM training, and text generation, built on PyTorch tensor primitives.
pip install "zenith-nlp[all]"Overview
Zenith is a small, correct, readable implementation of the modern decoder-only
stack, held to library standards. The architecture is hand-written and modern;
PyTorch supplies only autograd, containers, and optimizers. It is the generative counterpart to Polaris — Polaris encodes,
Zenith generates — but fully independent: Polaris interop is an optional extra
(zenith-nlp[polaris]), never a dependency.
It works: a 10.7M-parameter from-scratch Llama-style decoder, trained in ~10 minutes on a MacBook (MPS), reaches 2.08 bits/char on tiny-shakespeare — matching the well-known nanoGPT baseline.
Problem
The modern decoder-only stack — rotary embeddings, RMSNorm, SwiGLU, KV caching, speculative decoding, LoRA — is scattered across papers and production codebases too large to learn from. Nothing small and readable takes training, inference, and serving seriously as a library at the same time. Zenith exists to fill that gap.
Implemented (v1.0)
- Decoder-only transformer (
DecoderLM) — configurable Llama-style (RoPE, RMSNorm, SwiGLU, weight-tied embeddings) or GPT-2-style (LayerNorm, learned positions, GELU), with an optional fused SDPA attention path. - Tokenizers — a dependency-free byte-level tokenizer and a from-scratch, vectorized byte-level BPE; both lossless.
- Generation (
Generator) — greedy, temperature, top-k, nucleus, beam search, repetition penalty, streaming, and a KV cache with rollback; plus greedy-exact speculative decoding: a small draft model proposes tokens, the target verifies — output provably identical to greedy, with up to 3.7× fewer target forward passes. - Training (
CausalLMTrainer) — warmup/cosine schedule, gradient clipping, AMP, gradient accumulation, LoRA adapters (zenith.peft),torchrun-native DDP, MLflow tracking, and deterministic runs. - Instruction tuning (
zenith.instruct) — chat template + supervised fine-tuning with response-only loss masking;zenith chat --instructruns the resulting mini chat model (documented in a model card). - int8 quantization — weight-only, ~4× smaller inference weights.
- Serving & CLI — FastAPI service with SSE streaming, plus
zenith generate,chat,console,serve,eval, and Hydra-configured training and sweeps.
Benchmarks
All measured on laptop-scale hardware (Apple Silicon, MPS) and reproducible from scripts in the repo:
- Headline — 10.7M Llama-style decoder: 2.08 bits/char on tiny-shakespeare (nanoGPT reference: ~2.1).
- Architecture ablation — same recipe and size, Llama-style vs GPT-2-style: 2.08 vs 2.11 bits/char, with the modern stack converging roughly 2× faster (best validation at epoch 10 vs 17). The honest caveat: at this scale the floor is set by data, not architecture — the modern stack buys convergence speed.
- Scaling study — 0.6M → 10.7M params, one recipe: each ~3× in parameters buys −0.19, then −0.06, then −0.01 bits/char, flattening into the ~1 MB corpus's data floor.
- text8 — the same 10.7M model on a 5 MB subset of the standard char-LM corpus: 1.78 bits/char, reported honestly as a subset.
Design decisions
- Correctness before speed — the fused SDPA path is numerically equivalent to the reference attention; speculative decoding is greedy-exact by construction.
- RoPE correct under caching — rotary positions are implemented to be consistent between full-sequence training and one-token-at-a-time cached inference.
- Everything optional is an extra — MLflow, the CLI, serving, and Polaris interop are lazily imported optional dependencies; the core stays lean.
- Checkpoints are self-describing — model + tokenizer save and load together, LoRA-aware, with backward compatibility for older architecture configs.
Lessons learned
- The scaling study's flattening curve was the clearest lesson: past ~5M parameters on a ~1 MB corpus, capacity stops paying — the next unit of quality costs data, not parameters.
- Exactness constraints make optimizations shippable: "speculative output equals greedy output" and "SDPA equals reference attention" turn performance work into testable engineering.
- The modern architecture bundle (RoPE, RMSNorm, SwiGLU) bought convergence speed more than final quality at this scale — an honest, measured result that reading papers alone would not have produced.
Roadmap
QLoRA and FSDP for larger-scale training, sweep-result aggregation, and a larger CUDA-scale benchmark beyond the laptop-scale text8 subset.