Skip to main content
Milestone #11Not StartedPhase 6Advanced · Capstone25–40 hours19 required tasks

Pretrained Mini LLM

MyLLM v1 — Train your own decoder-only GPT language model from randomly initialized weights using your custom BPE tokenizer, memory-mapped dataset pipeline, and full pretraining engine

PythonPyTorchNumPy (memmap)Custom BPE TokenizerMixed Precision (BF16/FP16)AdamW & Cosine LR Schedule
Build Progress0 / 19 tasks (0%)

Hardware-Matched Educational Model Scales (MyLLM v1)

Choose a model scale matched to your hardware budget: (1) Tiny Educational Model (100K–1M parameters, e.g., 4 layers, d_model=128) for CPU or integrated laptops; (2) Small Single-GPU Model (5M–20M parameters, e.g., 6–8 layers, d_model=384) for consumer GPUs or free cloud notebooks; or (3) Optional Extended Model (30M–100M parameters, e.g., 12 layers, d_model=768) for dedicated GPU workstations. The exact same pipeline scales across all three tiers.

0%(0/19 steps)Not Started
Project Specification & Architecture

What You Will Build

This is the capstone engineering milestone of Phase 6 (`MyLLM v1`). You will bring together your custom BPE tokenizer (Project 8) and your Mini GPT architecture (Project 10) to train a complete decoder-only language model from randomly initialized weights. You will curate and clean a text dataset, perform document-level deduplication, split into leak-free train/validation/test sets, encode and pack documents into memory-mapped binary shards (`train.bin`, `val.bin`), run a mixed-precision pretraining loop with gradient accumulation, linear warmup + cosine learning rate decay, and checkpoint recovery, evaluate validation perplexity and sample generation across training steps, run a controlled ablation study, and package `MyLLM v1` with a Model Card and standalone CLI inference script.

Why This Project Matters in the AI/LLM Stack

Most developers only call hosted APIs or fine-tune existing checkpoints. Training `MyLLM v1` from random weights—watching cross-entropy loss drop from `ln(V)` (~9.0) down to coherent domain prose (~2.1), debugging learning rate warmup spikes, and verifying zero train-validation contamination—gives you end-to-end ownership of how foundation models are born.

Learning Objectives

  • Curate, clean, deduplicate, and split a raw text corpus at the document level without train-validation leakage.
  • Tokenize and pack documents with `<|eos|>` separators into contiguous `np.memmap` (`uint16`) binary shards.
  • Engineer a fault-tolerant PyTorch pretraining loop with gradient accumulation, mixed precision, gradient clipping, and checkpoint resumption.
  • Evaluate pretrained checkpoints quantitatively via validation perplexity, baseline comparisons, and controlled ablation experiments.
  • Package `MyLLM v1` with weights, tokenizer artifacts, `config.json`, a transparent Model Card, and a streaming CLI inference script.

Prerequisites

  • Phase 4 Project: BPE Tokenizer (`milestone-08-bpe-tokenizer`)
  • Phase 5 Project: Mini GPT Architecture (`milestone-10-mini-gpt-architecture`)
  • Phase 6 Modules 6.1–6.5: Dataset Engineering, Pretraining Pipeline, and Packaging

Required Mathematical Concepts

  • Autoregressive next-token cross-entropy loss and validation perplexity: PPL = exp(L_val)
  • Initial uniform random loss baseline: L_0 = ln(V)
  • Effective batch size in tokens: B_tokens = B_micro * GradAccumSteps * T
  • Linear warmup + cosine learning-rate decay schedule
  • Decoupled AdamW weight decay (2D matrix weights vs. 1D biases/norms)
Interactive Engineering Checklist

Step-by-Step Implementation Tasks (19 Steps)

Check off each engineering task as your code passes its acceptance criteria. Progress is saved automatically in your browser.

Stage 1 of 40/6 tasks complete

Dataset Curation, Cleaning, Deduplication & Binary Sharding

Select an open text corpus, clean extraction artifacts, deduplicate documents, create document-level splits, and pack tokenized IDs into memory-mapped binary files.

Core

A cohesive domain corpus (such as TinyStories, open educational textbooks, Project Gutenberg literature, or permissive Python files) allows a small model to learn grammar and domain structure rapidly.

Implementation: Download an openly licensed text dataset (at least 5–50 MB of raw text for laptop/single-GPU scales) and record its source URL, license, and SHA-256 hash in `data/manifest.json`.
Expected Outcome: Raw dataset downloaded locally with a documented provenance manifest.
Acceptance Check: `data/manifest.json` records dataset name, license, file count, and byte size.
Core

Corrupted UTF-8 bytes, HTML boilerplate, and repetitive control characters waste vocabulary capacity and distort loss.

Implementation: Write a cleaning pass in `data/prepare_data.py` that normalizes UTF-8 text, strips broken markup, and filters out documents shorter than 32 characters or with abnormal symbol-to-word ratios.
Expected Outcome: Clean document iterator yielding high-signal text records.
Acceptance Check: Cleaning script logs the total documents inspected, kept, and filtered.
Core

Splitting must occur at the document level after deduplication so identical passages never appear in both `train` and `val`.

Implementation: Compute a SHA-256 digest of each normalized document to remove exact duplicates, shuffle documents with a fixed seed (`seed=42`), and partition into 90% train, 5% validation, and 5% test documents.
Expected Outcome: Three disjoint document collections with zero cross-split overlap.
Acceptance Check: Set intersection of document hashes between `train`, `val`, and `test` is strictly `0`.
Core

Fitting the tokenizer strictly on the training split prevents vocabulary leakage from held-out validation documents.

Implementation: Train your BPE tokenizer (e.g., `vocab_size=4096` or `8192`) on the training split including `<|endoftext|>` (`<|eos|>`), and save `tokenizer.json`.
Expected Outcome: Serialized BPE tokenizer tailored to your corpus.
Acceptance Check: Tokenizer encodes and decodes sample validation documents with 100% round-trip accuracy and reports characters-per-token compression.
Intermediate

Concatenating tokenized documents separated by `<|eos|>` and writing them to disk as `uint16` binary arrays allows `np.memmap` to stream any dataset size with near-zero RAM.

Implementation: Encode all documents in `train`, `val`, and `test`, append `<|eos|>` after each document, and write `train.bin`, `val.bin`, and `test.bin` as contiguous `np.uint16` arrays alongside `dataset_meta.json`.
Expected Outcome: Compact binary shards on disk ready for zero-copy streaming.
Acceptance Check: `train.bin` and `val.bin` load instantaneously via `np.memmap(..., dtype=np.uint16, mode='r')`.
Core

Each training step samples `batch_size` random starting offsets `i` in `data` and slices `x = data[i : i + T]` and `y = data[i + 1 : i + T + 1]`.

Implementation: Write `get_batch(split, batch_size, block_size, device)` that opens the `np.memmap` shard and returns pinned, device-transferred `(x, y)` `torch.long` tensors.
Expected Outcome: Fast batch sampler supporting arbitrary context lengths `T`.
Acceptance Check: `x.shape == (B, T)`, `y.shape == (B, T)`, and `torch.equal(x[:, 1:], y[:, :-1])` is `True`.
Stage 2 of 40/4 tasks complete

Model Configuration & Optimizer Setup

Define hardware-matched model configurations, separate 2D vs. 1D parameter groups for AdamW, and implement the warmup-cosine learning rate scheduler.

Core

Matching parameter count (`100K–1M`, `5M–20M`, or `30M–100M`) and context length (`T=256` or `512`) to your hardware ensures fast iteration.

Implementation: Create preset YAML/JSON configs (`configs/tiny_1m.json`, `configs/small_15m.json`) and verify parameter count and analytic `ln(vocab_size)` initial loss at step 0.
Expected Outcome: Instantiated GPT model with verified parameter budget and healthy step-0 loss.
Acceptance Check: Untrained model initial loss on a random batch is within `±0.15` of `math.log(vocab_size)`.
Intermediate

Weight decay should regularize 2D projection/embedding matrices (`weight_decay=0.1`) but never 1D layer-norm scales or biases (`weight_decay=0.0`).

Implementation: Implement `configure_optimizers(model, weight_decay, learning_rate, betas)` that partitions parameters by `p.dim() >= 2` vs `p.dim() < 2` and logs total parameter counts in each group.
Expected Outcome: Properly partitioned `torch.optim.AdamW` optimizer.
Acceptance Check: 100% of trainable parameters belong to exactly one group, and all `LayerNorm`/`RMSNorm` and bias tensors have `weight_decay == 0.0`.
Core

Warming up the learning rate linearly from `0` to `max_lr` over the first 2–5% of steps prevents early Adam variance spikes from diverging random weights.

Implementation: Write `get_lr(step, warmup_steps, max_steps, max_lr, min_lr)` and unit-test its output at step `0`, `warmup_steps`, and `max_steps`.
Expected Outcome: Deterministic step-wise learning rate schedule function.
Acceptance Check: LR rises linearly during warmup, follows a smooth cosine curve to `min_lr = 0.1 * max_lr`, and stays at `min_lr` thereafter.
Core

Before launching a multi-hour pretraining run, verifying that the pipeline can memorize a single fixed batch down to `loss < 0.05` catches bugs in loss shifting, optimizer stepping, or masking.

Implementation: Add a `--sanity-overfit-one-batch` flag to `train.py` that trains on one static batch for 200 steps and asserts final loss `< 0.05`.
Expected Outcome: Verified gradient flow and optimization loop.
Acceptance Check: Single-batch loss drops monotonically from `~ln(V)` to `< 0.05` within 200 updates.
Stage 3 of 40/5 tasks complete

Pretraining Loop, Checkpointing & Live Telemetry

Implement the main training loop with gradient accumulation, mixed precision, gradient norm clipping, periodic validation evaluation, and resumable checkpoints.

Advanced

Gradient accumulation simulates a large effective token batch size (`B_micro * grad_accum_steps * T`) on limited VRAM, while `torch.amp.autocast` (`bfloat16`/`float16`) accelerates matmuls.

Implementation: In `train.py`, loop over `grad_accum_steps` micro-batches, divide each micro-batch loss by `grad_accum_steps` before `.backward()`, and wrap the forward pass in `torch.amp.autocast`.
Expected Outcome: Memory-efficient training step supporting arbitrary effective token batch sizes.
Acceptance Check: Accumulated gradients over `K` micro-batches of size `B` match a single batch of size `K * B` within floating-point tolerance.
Core

Clipping global gradient norm at `1.0` protects against unlucky data batches, and logging pre-clip `grad_norm` diagnoses training stability.

Implementation: Call `grad_norm = torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)` before `optimizer.step()` and log `step`, `train_loss`, `lr`, `grad_norm`, and `tokens_per_sec` to `runs/metrics.jsonl`.
Expected Outcome: Structured JSONL training telemetry for every step.
Acceptance Check: `runs/metrics.jsonl` records valid JSON lines with step, loss, perplexity, LR, grad_norm, and throughput.
Core

A single validation batch is noisy; averaging loss over `eval_iters` (e.g., 50 batches) in `model.eval()` + `torch.no_grad()` provides a reliable validation perplexity curve.

Implementation: Write `estimate_loss(model, eval_iters=50)` that computes mean loss and perplexity (`math.exp(loss)`) on both `train` and `val` splits every `eval_interval` steps.
Expected Outcome: Low-variance train and validation perplexity checkpoints.
Acceptance Check: Validation evaluation restores `model.train()` mode afterwards and logs `val_loss` and `val_ppl`.
Intermediate

Saving model weights, optimizer states, step counter, RNG states, and config allows training to resume seamlessly after preemption.

Implementation: Save `ckpt_latest.pt` and `ckpt_best.pt` (when `val_loss` improves) and implement `--resume` to continue training from the exact step and learning rate.
Expected Outcome: Resumable training checkpoints.
Acceptance Check: Resuming from step `k` produces the exact same step `k+1` learning rate and continues the loss curve smoothly.
Core

Sampling from a fixed set of 5 benchmark prompts at step 0, 25%, 50%, 75%, and 100% visually demonstrates the progression from random token salad to coherent prose.

Implementation: At each validation interval, generate 100 tokens from 5 fixed prompts (`temperature=0.8, top_p=0.9`) and append them to `runs/generations_log.md`.
Expected Outcome: Side-by-side chronological log of model generations across training.
Acceptance Check: `runs/generations_log.md` shows clear progression from random characters/subwords at step 0 to syntactically coherent continuations at convergence.
Stage 4 of 40/4 tasks complete

Evaluation, Ablation Study & MyLLM v1 Packaging

Compare validation perplexity against baselines, run a controlled ablation study, and export the self-contained `MyLLM v1` release bundle.

Intermediate

Comparing your trained model's test perplexity against a random uniform baseline (`V`) and a unigram/bigram frequency baseline proves how much structure the Transformer learned.

Implementation: Write `evaluate.py` to compute test-set perplexity for `ckpt_best.pt`, compare it against uniform random (`PPL = V`) and unigram token frequency baselines, and check 8-gram overlap with `train.bin`.
Expected Outcome: Quantitative evaluation report table.
Acceptance Check: Trained `MyLLM v1` achieves dramatically lower test perplexity than unigram/random baselines and generates novel sentences not copied verbatim from `train.bin`.
Advanced

Running two controlled training runs where only one hyperparameter changes (e.g., `block_size=128 vs 256` or `RoPE vs learned positional embeddings`) teaches scientific LLM experimentation.

Implementation: Run a short controlled ablation (at least 1,000 steps) comparing two configurations with identical data seed and parameter budget, and plot/tabulate their validation loss curves.
Expected Outcome: Documented ablation experiment table and loss comparison.
Acceptance Check: Ablation section in the evaluation report clearly states the hypothesis, controlled variables, and final validation perplexity of both runs.
Core

Anyone cloning your repository should be able to generate text from your trained model with a single terminal command.

Implementation: Create `generate.py --model-dir ./myllm_v1 --prompt '...' --max-tokens 150 --temperature 0.8 --top-k 40 --top-p 0.9` that loads `config.json`, `tokenizer.json`, and `model.pt` and streams generated tokens to stdout.
Expected Outcome: Self-contained CLI generator script.
Acceptance Check: Running `python generate.py --model-dir ./myllm_v1 --prompt 'Once upon a time'` streams generated tokens without importing training dependencies.
Core

A complete LLM release includes inference weights (stripped of optimizer state), `config.json`, `tokenizer.json`, evaluation report, and `MODEL_CARD.md`.

Implementation: Export the final release directory `myllm_v1/` and author `MODEL_CARD.md` documenting architecture specs, training corpus, token budget, validation/test perplexity, ablation findings, and known limitations.
Expected Outcome: Complete, reproducible `MyLLM v1` release artifact.
Acceptance Check: All 7 required `MyLLM v1` deliverables exist in the repository and pass reload verification.
Reference Architecture & Verification Code

Starter & Verification Code Examples

AdamW 2D vs. 1D Parameter Group Partitioning & Warmup-Cosine LR

Reference implementation for separating weight-decayed matrix parameters from non-decayed norms/biases and computing cosine learning rate.

import math
import torch
import torch.nn as nn

def configure_optimizers(model: nn.Module, weight_decay: float, lr: float, betas=(0.9, 0.95)):
    param_dict = {pn: p for pn, p in model.named_parameters() if p.requires_grad}
    decay_params = [p for n, p in param_dict.items() if p.dim() >= 2]
    nodecay_params = [p for n, p in param_dict.items() if p.dim() < 2]
    optim_groups = [
        {"params": decay_params, "weight_decay": weight_decay},
        {"params": nodecay_params, "weight_decay": 0.0},
    ]
    return torch.optim.AdamW(optim_groups, lr=lr, betas=betas)

def get_cosine_lr(step: int, warmup_steps: int, max_steps: int, max_lr: float, min_lr: float) -> float:
    if step < warmup_steps:
        return max_lr * (step + 1) / max(1, warmup_steps)
    if step >= max_steps:
        return min_lr
    decay_ratio = (step - warmup_steps) / max(1, (max_steps - warmup_steps))
    coeff = 0.5 * (1.0 + math.cos(math.pi * decay_ratio))
    return min_lr + coeff * (max_lr - min_lr)
Verification & Acceptance Suite

How to Test Your Implementation

Verify leak-free dataset sharding, gradient accumulation equivalence, single-batch memorization, and standalone `MyLLM v1` checkpoint reloading.

$ pytest tests/test_pretraining_pipeline.py -vTest Runner

Zero Train/Validation Document Leakage Check

Ensure validation perplexity reflects true generalization rather than duplicate documents.

Steps: Compute SHA-256 hashes of all raw documents in train and val splits and assert `len(train_hashes & val_hashes) == 0`.

Pass Criteria: Zero shared documents between splits.

Gradient Accumulation Equivalence Test

Verify that accumulating gradients over 4 micro-batches of size 2 equals 1 batch of size 8.

Steps: Fix RNG seed, run 1 step with `(B=8, accum=1)` in FP32 and 1 step with `(B=2, accum=4)`, and compare parameter gradients.

Pass Criteria: `torch.allclose(g1, g2, atol=1e-6)` is `True`.

Standalone Release Reload & Generation Test

Confirm the exported `myllm_v1/` bundle can be loaded in a fresh Python process.

Steps: Load `myllm_v1/` via `generate.py` with `temperature=0.0` (greedy) twice on the same prompt.

Pass Criteria: Both runs produce identical, coherent token sequences.

Overall Project Acceptance Criteria

  • Dataset pipeline deduplicates at the document level and verifies zero document overlap between `train.bin`, `val.bin`, and `test.bin`.
  • Pretraining pipeline verifies initial step-0 loss `≈ ln(vocab_size)` and passes the single-batch overfit sanity check (`loss < 0.05`).
  • Training loop implements AdamW 2D/1D parameter groups, linear warmup + cosine decay, gradient accumulation, and gradient norm clipping.
  • `MyLLM v1` is packaged with custom tokenizer, `config.json`, inference weights, evaluation + ablation report, `MODEL_CARD.md`, and `generate.py` CLI.
Progressive Spoiler-Free Guidance

Expandable Hints & Debugging Tips

Try implementing each stage on your own first. Expand a hint below if you want to verify tensor shapes, mathematical identities, or common edge cases.

Optional Bonus Extensions

Optional Stretch Challenges

Optional · Does Not Block Completion

Scaling Law Mini-Sweep (3 Model Sizes)

Train 3 model sizes (e.g., 500K, 2M, and 8M parameters) on the same dataset and plot final validation loss against total training FLOPs ($C \approx 6 N D$).

What You Learn: Empirical power-law scaling curves ($L(C)$) and compute-optimal allocation.

MinHash Near-Duplicate Filtering

Upgrade your exact SHA-256 deduplicator to a 5-gram MinHash LSH near-deduplicator that catches documents with >85% Jaccard similarity.

What You Learn: Web-scale fuzzy deduplication algorithms used in FineWeb and Dolma.

Expected Deliverable

What Your Completed Repository Should Contain

`MyLLM v1` — A complete self-trained language model release containing your custom BPE tokenizer, GPT architecture, data & pretraining pipeline, trained checkpoints, evaluation & ablation report, Model Card, and CLI inference script.

Deliverable Artifacts

  • Custom tokenizer (`tokenizer.json`) & memory-mapped dataset preparation script (`prepare_data.py`)
  • GPT-style architecture (`model.py`) & configuration files (`config.json`)
  • Pretraining pipeline (`train.py`) with AdamW, cosine LR, gradient accumulation, and checkpointing
  • Trained model checkpoints (`myllm_v1/model.pt`)
  • Quantitative evaluation & ablation report (`EVALUATION_REPORT.md`)
  • Standalone streaming CLI inference script (`generate.py`)
  • Reproducible documentation & transparent Model Card (`MODEL_CARD.md`)

After Completing This Project, You Should Be Able To...

  • Engineer a leak-free, deduplicated, memory-mapped pretraining dataset pipeline.
  • Train a GPT-style language model from random weights to coherent text generation.
  • Diagnose training curves, gradient norms, learning rate warmup, and validation perplexity.
  • Design and interpret controlled ablation experiments and package reproducible LLM artifacts.
Portfolio & Repository Tracking

GitHub Repository Submission

Link your public or private GitHub repository URL to keep a structured portfolio of your from-scratch AI builds. Stored locally in your browser (self-reported; no GitHub tokens or remote code execution).

Status: Not Linked

Suggested Repository: myllm-v1-pretraining-from-scratch

Recommended Directory Layout:

data/prepare_data.py
myllm/model.py
myllm/train.py
myllm/evaluate.py
generate.py
myllm_v1/config.json
myllm_v1/MODEL_CARD.md
EVALUATION_REPORT.md
tests/test_pretraining_pipeline.py
README.md

Recommended README Documentation Checklist

  • Include the training and validation loss/perplexity curve plot.
  • Show chronological sample generations from step 0, 25%, 50%, and 100%.
  • Include the ablation study comparison table.
  • Provide a single-command quickstart for `python generate.py`.
Local Engineering Notebook

Personal Implementation Notes