Skip to content

From GPT to Production

Pretraining at scale

Data, compute, and the next-token objective

How a Transformer swallows trillions of tokens of raw internet and, with no human labels at all, teaches itself to model language - one gradient step at a time.

You already know the two halves of this puzzle. From Lesson 37 you know the objective - read the tokens so far, predict the next one, and grade the bet with cross-entropy. From Lesson 11 you know the learning rule - nudge every weight against its gradient. Pretraining is nothing more than these two ideas run at absurd scale: a data pipeline that feeds trillions of tokens through the same loop, millions of times, until the loss stops falling. This lesson is about what breaks - and what you must tune - when that loop gets enormous.

The fuel: web-scale text, deduplicated

A frontier model is not shown a curated textbook. It is shown a firehose: snapshots of the open web (Common Crawl), code repositories, books, encyclopedias, and forums - cleaned, filtered for quality, and stitched into one gigantic stream of tokens. The single most important cleaning step is deduplication: the raw web is full of near-identical copies (mirrors, boilerplate, reposts), and letting the model see the same passage thousands of times wastes compute and encourages brittle memorization. Dedup, quality filtering, and blending the right mixture of sources are where a huge fraction of real pretraining effort goes.

Why the labels are free

Supervised learning needs humans to tag every example - expensive, and it caps you at however much text people can label. Pretraining sidesteps this entirely with a self-supervised trick: the label for "The cat sat on the ___" is already sitting in the text - it is the very next word. Every position in every document is simultaneously an input and a free training target. That is why the data can scale to trillions of tokens: the internet labels itself.

One objective, a trillion times over

The entire training signal is the next-token cross-entropy you met in Lesson 37 - the same cross-entropy that first appeared as a cost function in Lesson 17 - now averaged over the whole corpus of tokens:

The pretraining loss

Every token contributes one term: the model's log-probability of the token that actually came next, given everything before it. There is no other objective, no reward model, no human in the loop - just "how surprised were you by the real text, on average." Minimizing over (the billions of weights) is what pretraining means. And the way we minimize it is the way we minimize everything: gradient descent, with the gradient supplied by backprop.

Scaling this loop introduces knobs that did not matter on a single neuron. Because the weights start random, the very first gradients are huge and chaotic, so we warm up the learning rate from zero over the first few thousand steps, then decay it back down (usually on a cosine) so the model can settle into a sharp minimum instead of bouncing around it:

Run the loop yourself

Below is a live pretraining run. Press Run and watch the tokens-seen counter race into the billions while the loss curve falls along a power-law-ish descent toward the dashed ideal floor (the best loss this data can reach - a preview of scaling laws). Then reshape it: drag the learning rate too high and the loss spikes and diverges; drag it too low and it crawls, never catching the floor. Shrink the batch and the curve turns jagged with gradient noise; grow it and it smooths. Finally, rebalance the data mixture and watch the floor itself rise or fall.

Pretraining training-loop simulator Run - reshape with the sliders
training lossideal floor (data limit)learning-rate schedule
learning-rate presets:
web80%
code10%
books10%

A balanced blend (approx 60% web, 25% code, 15% books) reaches the lowest floor; pile everything into one source and the floor rises.

tokens seen0
loss-
effective η3.0e-4
best @ budget2.99

Press Run to pretrain on 300.0B tokens.

The "aha": the same curve, three ways to ruin it

Hit 3e-4 - sweet spot and the loss glides smoothly to the floor - that is a healthy run. Now hit 3e-3 - diverge: as warmup ramps the learning rate up, the loss suddenly rockets off the top and the run is dead - millions of dollars of compute, wasted, which is why real runs clip gradients and babysit the loss curve. Hit 3e-5 - crawl and it barely descends: safe, but you would need vastly more tokens to get anywhere. The loss-vs-tokens curve is the single dashboard that pretraining engineers stare at for weeks - a smooth descent means it is working; a spike means stop everything.

Warmup is not optional at scale

Notice how, at the very start of every run, the loss barely moves - that flat shoulder is warmup. With freshly-randomized weights, the loss surface near the start is treacherous; a full-size step would launch you into a bad region you can never recover from. So we creep in with a tiny learning rate and ramp up only once the weights are sane. Skip warmup on a big model and the loss often diverges in the first hundred steps - the same knife-edge you felt rolling the ball downhill in Lesson 11, now with hundreds of billions of weights all moving at once.

The loop, in real code

Here is the whole machine, stripped to essentials. Note three scale-only tricks: AdamW (an adaptive optimizer with decoupled weight decay, the default for LLMs); gradient accumulation, which sums gradients over several micro-batches so you can simulate a giant batch that would never fit in one GPU's memory; and checkpointing, so a crash three weeks into a run resumes instead of restarting. Distributed training simply replicates this loop across thousands of GPUs, averaging gradients between them each step.

# one pretraining loop, scaled to trillions of tokens
opt = torch.optim.AdamW(model.parameters(), lr=3e-4,
                        betas=(0.9, 0.95), weight_decay=0.1)
sched = warmup_cosine(opt, warmup=2000, total=600_000)  # steps

model.train()
for step, batch in enumerate(loader):          # batch: (B, T) token ids
    # gradient accumulation: fake a huge batch on limited memory
    for micro in batch.split(micro_bs):
        logits = model(micro[:, :-1])
        loss = F.cross_entropy(                   # the ONLY objective
            logits.reshape(-1, V),
            micro[:, 1:].reshape(-1)
        ) / accum_steps
        loss.backward()                          # backprop accumulates grads
    torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)  # tame spikes
    opt.step(); sched.step(); opt.zero_grad()   # one AdamW update
    if step % 1000 == 0:
        save_checkpoint(model, opt, step)      # so a crash resumes here
One pretraining loop, scaled to trillions of tokens

Check yourself

During pretraining, setting the peak learning rate far too high most often makes the loss:

Pretraining needs no human labels because the training target is:

Recall: why do we warm the learning rate up from zero and then decay it back down?

Nail down the two-phase schedule. Try to state it, then check.

Lock it in

  • Pretraining is self-supervised next-token prediction run at absurd scale: trillions of tokens, one objective, gradient descent.
  • The internet labels itself - every position is simultaneously an input and a free target, which is why no human annotations are needed.
  • Warmup ramps the learning rate from zero to avoid diverging on chaotic early gradients; cosine decay settles the model into a sharp minimum.
  • AdamW, gradient accumulation, and checkpointing are the plumbing that keeps the old gradient-descent machinery from blowing up at scale.
  • The data mixture (web, code, books) and deduplication determine the floor the loss can reach - no amount of compute overcomes bad data.

Primary source

Stanford's CS336: Language Modeling from Scratch walks the entire pretraining stack - data curation and dedup, the training loop, optimizers, learning-rate schedules, and distributed training - at exactly this level of detail. For a from-scratch, run-it-yourself companion in code, Sebastian Raschka's "Build a Large Language Model (From Scratch)" implements the pretraining loop, AdamW, and checkpointing line by line.

Ask your teacher

Here is the connection worth holding onto: the optimizer stepping this loss curve is plain gradient descent (Lesson 11), and every gradient it needs is computed by backpropagation (Lesson 22) over billions of weights - the same machinery you learned on a single neuron, just enormous. Nothing new was invented for scale; we only added the plumbing (warmup, AdamW, accumulation, dedup, thousands of GPUs) that keeps that old machinery from blowing up. Ask me how AdamW differs from vanilla SGD, why bigger batches let you push the learning rate higher, or how gradients are averaged across a thousand machines each step.