Training Data Engineering

You will understand where trillions of tokens come from, how a raw web crawl is turned into a training set by a dozen filtering stages, why deduplication is not optional, and how to mix sources so the model learns what you want it to.

Two teams train the same 7B architecture with the same optimizer on the same number of tokens. One model is noticeably better at everything. The difference is the data. Scaling laws from the last chapter tell you how many tokens to use, but they say nothing about which tokens, and by 2024 it had become clear that quality and mix matter as much as scale.

The problem is that the raw material is the public web, and the public web is mostly garbage: boilerplate, navigation menus, SEO spam, machine-translated junk, the same press release copied ten thousand times, and, scattered among it, a few percent of text worth learning from. Data engineering is the discipline of finding that few percent at petabyte scale without accidentally throwing away the good parts.

Where the data comes from

Every large training set is a mix of a handful of source types. Their character differs a lot, and so does how much of each exists.

  • Web crawl. Common Crawl is a nonprofit that has crawled the web since 2008 and publishes snapshots of raw HTML; a recent snapshot is a few billion pages and on the order of 100 TB of compressed WARC files. Nearly all web data in LLMs starts here. It is the largest source by far, and the dirtiest.
  • Code. Public repositories from GitHub, deduplicated and license-filtered (The Stack, Kocetkov et al., 2022; The Stack v2 at about 900B tokens). Code teaches precise syntax, long-range structure and, apparently, reasoning.
  • Books. Project Gutenberg (public domain), plus, in older datasets, shadow-library collections whose use has been litigated. Long, coherent, edited prose.
  • Papers. arXiv, PubMed Central, Semantic Scholar's peS2o. Dense technical text and math.
  • Reference. Wikipedia and its siblings. Small (English Wikipedia is only about 4B tokens) but very high quality, so it is often repeated several times.
  • Math. Math-heavy web pages (OpenWebMath, Paster et al., 2023, about 15B tokens), textbooks, competition problems.
  • Synthetic. Text generated by another model: textbook-style explanations, rewritten web pages, instruction data. Small in most pre-training mixes, growing fast.

Proportions in known open datasets

The table gives rough composition by tokens for four influential open datasets. The numbers are approximate; each paper reports them slightly differently (by bytes, by documents, or by effective tokens after upsampling), so treat them as the shape of the mix, not a spec.

DatasetSize (approx.)Composition (approx.)Notable for
The Pile (Gao et al., 2020)825 GiB, ~300B tokensWeb ~28%, academic (PubMed, arXiv, etc.) ~30%, books ~15%, code ~8%, dialogue/other ~19%The first widely used curated mix; 22 named sources with per-source weights.
RefinedWeb (Penedo et al., 2023)~5T tokens (600B released)100% filtered and deduplicated Common CrawlShowed web-only data, aggressively cleaned, matches curated mixes.
FineWeb / FineWeb-Edu (Penedo et al., 2024)15T / 1.3T tokens100% Common Crawl (96 snapshots); Edu subset selected by a quality classifierPublic ablations of every filter; the Edu classifier boosted knowledge benchmarks markedly.
Dolma (Soldaini et al., 2024)~3T tokensWeb ~75%, code ~13%, C4 ~6%, Reddit ~3%, papers ~2%, books and wiki under 1%Fully open toolkit and provenance; used to train OLMo.

Two things to notice. Even in the curated datasets, web text is the majority, because nothing else exists in the needed quantity. And the high-quality sources (Wikipedia, books, papers) are tiny relative to a 15T-token budget, which is why the mixing section below is mostly about how many times you can repeat them.

The pipeline

Here is the whole thing, from crawl to shards. Each box is a stage that a real team owns, tunes, and ablates. We walk through them in order.

raw crawl (WARC) text extractiontrafilatura language IDfastText URL / blocklistadult, spam, opt-out quality filtersGopher rules, classifiers dedupexact + MinHash PII / toxicityregex, classifiers decontaminaten-gram vs benchmarks tokenizeBPE, 128k mix & sampleweights per source pack8k sequences, doc mask shardto the trainers blue = cheap, per-document, embarrassingly parallel. yellow = the judgment calls. pink = needs the whole corpus at once (dedup) or external lists. green = turns text into training batches. Every stage is ablated: train a small model with and without it, compare. Books, code, papers and wiki enter at "mix & sample" after their own lighter pipelines.
Figure 1. The data pipeline. The top row is per-document filtering, which parallelizes trivially. Deduplication and decontamination need to see the whole corpus. The bottom-right turns clean text into fixed-length token sequences. The order matters: dedup after quality filtering is cheaper, because there is less to compare.

Text extraction

A crawled page is HTML: navigation bars, cookie banners, ads, footers, and somewhere in the middle, the article. Extraction pulls out the main content. Tools like trafilatura and resiliparse do this with heuristics about tag structure and text density. Common Crawl also ships a pre-extracted "WET" text format, but it keeps far more boilerplate; RefinedWeb and FineWeb both found that re-extracting from raw HTML with trafilatura produced measurably better models, and it is now standard. This one stage discards most of the bytes: HTML markup and boilerplate are the bulk of a page.

Language identification

A fastText classifier (Joulin et al., 2016) assigns each document a language and a confidence. English-only datasets keep documents scoring above about 0.65 for English; multilingual ones keep a set of languages with per-language thresholds. This is where roughly half of the remaining documents go, because roughly half the web is not in English.

URL and blocklist filtering

Before reading any content, drop domains on known adult, spam, malware and piracy lists (RefinedWeb used a 4.6M-domain blocklist plus URL-word scoring). Also honor opt-outs: domains that ask not to be crawled for AI training, and sites that have requested removal. This stage is cheap and removes a few percent, but a poorly built blocklist causes harm: Dodge et al. (2021) showed that C4's word-based filter disproportionately removed text about and by minority groups.

Quality filters: heuristics

The problem now is that the remaining documents are in English and not obviously spam, but many are still junk: lists of product codes, pages that are 90% bullet points, auto-generated text. Heuristic filters encode what junk looks like. The Gopher rules (Rae et al., 2021) are the canonical set, and every later pipeline uses some variant:

  • Keep documents with 50 to 100,000 words, mean word length 3 to 10 characters.
  • Drop if the symbol-to-word ratio (for # or ...) exceeds 0.1.
  • Drop if more than 90% of lines start with a bullet, or more than 30% end with an ellipsis.
  • Require that at least 80% of words contain an alphabetic character.
  • Require at least two of the stop words the, be, to, of, and, that, have, with.
  • Drop documents with excessive repetition: duplicate lines, paragraphs, or frequent n-grams above a threshold.

C4 (Raffel et al., 2020) adds line-level rules: keep only lines ending in terminal punctuation with at least five words, drop pages containing "lorem ipsum" or a curly brace (code) or any word from a bad-word list. FineWeb's ablations found that a few extra rules of this kind (fraction of lines ending in punctuation, fraction of short lines) each gave a small but real gain.

Quality filters: model-based

Heuristics can tell junk from prose, but not a great explanation from a mediocre one. The next step is to train a classifier for "quality" and keep the high scorers. GPT-3 did this with a linear classifier trained to distinguish WebText from raw crawl. FineWeb-Edu (Penedo et al., 2024) did it more directly: ask Llama 3 70B to rate 460k web pages on a 0 to 5 scale of "educational value for grade-school to college level", train a small embedding-based classifier on those labels, and keep pages scoring 3 or higher. That kept about 1.3T of 15T tokens and produced large gains on knowledge and reasoning benchmarks (MMLU, ARC) at the same token budget. Llama 3 reports the same approach with its own classifiers.

Common confusion

"Higher quality" does not mean "better for every purpose". Aggressive educational filtering improves MMLU but can reduce diversity, hurt on informal text and on some languages, and encode the labeler model's preferences. Every filter is a bet about what the downstream model should be good at, and the only way to settle it is to train small models on the filtered and unfiltered versions and look at the evals you care about. FineWeb's paper is worth reading precisely because it shows those ablations for every stage.

Deduplication

The web copies itself. Press releases, syndicated news, license boilerplate, product descriptions and Wikipedia mirrors appear thousands of times each. Lee et al. (2021) found that in C4, a corpus that was already filtered, over 3% of the tokens sat in near-duplicate documents, and that a single 61-word sentence appeared over 60,000 times.

Why it matters

Three reasons, in increasing order of seriousness. First, wasted compute: training on the same document twice teaches less than training on two different ones. Second, skewed distribution: a passage that appears 60,000 times is being upweighted 60,000x relative to what you intended. Third, memorization: Lee et al. showed that deduplication cut the rate at which models emitted training text verbatim by about 10x, and Carlini et al. (2022) showed that memorization of a sequence grows roughly log-linearly with the number of times it appeared. Memorized text is a privacy problem (Kandpal et al., 2022), a copyright problem, and a sign the model is doing lookup instead of learning. Lee et al. also found that deduplicated models reached the same accuracy in fewer steps.

Exact deduplication

The easy part: hash every document (or every line, or every 50-token span) and drop repeats. Line-level exact dedup catches boilerplate ("Click here to subscribe") that appears inside otherwise-unique pages. Exact matching misses the common case, though: the same article with a different byline, date, or footer.

Near-duplicate detection with MinHash

The problem: to find near-duplicates you want to compare every pair of documents, and with a billion documents that is $10^{18}$ comparisons. MinHash (Broder, 1997) is the trick that makes it linear. It has three steps.

Shingles. Represent each document as the set of its $n$-grams (for text, typically 5-word shingles; in the toy below we use 3-word shingles). Two documents that share most of their sentences share most of their shingles, regardless of small edits.

Jaccard similarity. The natural similarity between two sets is the size of their intersection over the size of their union.

$$J(A, B) = \frac{|A \cap B|}{|A \cup B|}$$

A Jaccard of 1 means identical shingle sets; 0 means nothing shared. Pipelines usually call documents near-duplicates above about 0.7 to 0.8.

MinHash. Computing $J$ exactly for every pair is still quadratic. But here is the trick: pick a random hash function $h$, and for each document record only the minimum hash value over its shingles. The probability that two documents have the same minimum equals their Jaccard similarity, exactly:

$$\Pr\big[\min_{s \in A} h(s) = \min_{s \in B} h(s)\big] = J(A, B)$$

Why: the minimum over $A \cup B$ lands on some shingle; the two minima agree exactly when that shingle is in both sets, and by symmetry of a random hash each shingle of the union is equally likely to be the minimum, so the chance is $|A \cap B| / |A \cup B|$. Use $k$ independent hash functions and you get a $k$-number signature per document; the fraction of positions where two signatures agree is an unbiased estimate of $J$ with standard error about $\sqrt{J(1-J)/k}$. Then, instead of comparing all pairs, the signatures are chopped into bands and hashed into buckets (locality-sensitive hashing); only documents that collide in some bucket are compared. FineWeb uses 5-word shingles and 112 hash functions in 14 bands of 8, which flags pairs above roughly 0.75 similarity.

Where it came from

Broder (1997) invented MinHash at AltaVista to find near-duplicate web pages in the search index; the same technique, essentially unchanged, now deduplicates the corpora that train language models a quarter-century later.

Worked example: shingles and Jaccard

Document A: "the cat sat on the mat". Document B: "the cat sat on a mat".

3-word shingles of A: {the cat sat, cat sat on, sat on the, on the mat}: 4 shingles.
3-word shingles of B: {the cat sat, cat sat on, sat on a, on a mat}: 4 shingles.

Intersection: {the cat sat, cat sat on}, size 2. Union: 6 distinct shingles. $J = 2/6 = 0.33$. One changed word in a six-word sentence broke half the shingles; on a 500-word page, one changed word breaks only 3 of ~500 shingles, and $J$ stays near 0.99.

With $k = 4$ hash functions, suppose the minima agree at 1 of 4 positions: estimate $0.25$. With $k = 100$, expect about 33 agreements, give or take 5. More hashes, tighter estimate.

MinHash in one line: hash every shingle, keep the smallest doc A shingles h=17 h=42 h=5 h=88 → min = 5 doc B shingles h=17 h=42 h=5 h=63 → min = 5 (agree!) The shared shingle (green) happened to have the smallest hash, so the minima agree. With a different hash function the smallest might be an unshared one (blue/yellow) and they would differ. P(agree) = shared / total distinct = 3 / 5 = 0.6 = Jaccard. Repeat with k hashes to estimate it.
Figure 2. One hash function, two documents. The minimum hash is a random "representative" shingle drawn uniformly from the union; the minima agree exactly when that representative is shared. That is why the agreement probability equals the Jaccard similarity.
Symbols
$A, B$ = shingle sets
$J$ = Jaccard similarity
$h_1 \dots h_k$ = random hash functions
$k$ = signature length
$b$ = bands, $r$ = rows per band
STEP 1
Shingle
Split each document into overlapping word $n$-grams and keep the set.
STEP 2
Sign
For each of $k$ hash functions, record the minimum hash over the shingles: a $k$-vector.
STEP 3
Band
Split the signature into $b$ bands of $r$ rows; bucket documents by each band's value.
STEP 4
Compare & drop
Only pairs sharing a bucket are candidates. Estimate $J$ from the signatures; drop one of each pair above the threshold (cluster with union-find).
InteractiveMinHash near-duplicate demoedit the texts

Edit either text. The exact Jaccard over 3-word shingles is computed directly; the MinHash estimate uses $k$ salted hash functions. Increase $k$ and watch the estimate settle onto the truth.

PII, toxicity and decontamination

PII and toxicity

Regular expressions catch the easy personal information: email addresses, phone numbers, IP addresses, which are replaced with placeholder tokens (Dolma does this). Toxicity is scored by a classifier and documents above a threshold are dropped or downweighted. Both stages are small in volume and large in consequence, and both involve trade-offs: a too-aggressive toxicity filter removes discussions about harmful topics along with harmful content, which then hurts the model's ability to recognize and refuse them later.

Decontamination against benchmarks

The problem: benchmark test sets are on the web. If MMLU questions are in the training data, the evaluation score is meaningless. Decontamination searches the corpus for overlap with every benchmark you plan to report and removes the matching documents. GPT-3 used 13-gram overlap; Llama and most current pipelines use 8- or 10-gram token overlap with a threshold on the fraction of the benchmark example covered. It is imperfect (paraphrases slip through, and it is only as good as your list of benchmarks), which is one reason the evaluation chapter spends time on held-out and freshly written evals.

From text to batches

Tokenization

The clean corpus is tokenized once, up front, with the BPE tokenizer from the tokenization chapter, and stored as arrays of integer ids. This is the point at which "how many tokens do we have" becomes a definite number, and it depends on the tokenizer: the same text is 15% fewer tokens with Llama 3's 128k vocabulary than with Llama 2's 32k.

Mixing and sampling weights

Now the sources come together. You have, say, 12T tokens of filtered web, 900B of code, 200B of math, 100B of books, 4B of Wikipedia, and a budget of 15T tokens. You assign each source a sampling weight and the loader draws batches accordingly. The weight determines two things: what fraction of the model's training is spent on that source, and how many times that source gets repeated.

Llama 3's reported mix is roughly 50% general knowledge, 25% mathematical and reasoning data, 17% code, and 8% multilingual, arrived at through scaling-law experiments on small models with different mixes. Upweighting code and math well past their natural share of the web is now standard, because both improve reasoning benchmarks out of proportion to their volume.

InteractiveData-mix designerset shares and total

Shares are normalized to 100%. Each source has a rough available size; the readout shows how many times each will be repeated. Bars turn red when a source passes four epochs.

Epochs and repetition: how many times can you reuse data?

The problem behind the mixing sliders: high-quality data is scarce, and budgets have outrun it. Can you just repeat it? Muennighoff et al. (2023) trained models with fixed compute on corpora of varying size, forcing repetition, and fit a scaling law for the effective value of repeated tokens. The finding: up to about 4 epochs, repeated data is nearly as good as fresh data; the loss is barely distinguishable. Beyond that, returns fall off quickly, and by around 40 epochs additional repeats are worth almost nothing. So repeating Wikipedia four times is fine and common; repeating it forty times just overfits.

They also found that when data is the constraint, it is better to train a somewhat smaller model for more epochs than the Chinchilla rule would suggest, and that mixing in code helps fill the gap without much penalty on natural-language evaluations.

Curriculum and the annealing phase

Data order matters too. Most of a run uses a fixed mix, but two adjustments have become standard. First, long documents are held back for the long-context stage at the end (see pre-02). Second, and more important, the final stretch of training, during the learning-rate decay, is run on an upsampled high-quality mix: more code, more math, more curated sources, and sometimes instruction-style data. This is called annealing. Llama 3 reports that annealing on a small amount of high-quality math data during the last 40M tokens of the 8B run improved GSM8K and MATH substantially, and they used the same trick as a cheap way to evaluate candidate data sources (Blakeney et al., 2024, study this directly). MiniCPM (Hu et al., 2024) mixes high-quality and SFT data into the decay phase of its WSD schedule for the same reason. The intuition: the model's final weights are determined disproportionately by what it sees while the learning rate is small, so that is where the best data should go.

Packing sequences and document masking

The trainer wants fixed-length sequences (8,192 tokens, say). Documents are not that length. The standard solution is to concatenate all documents with an end-of-text token between them and chop the stream into fixed windows. A window then usually contains the tail of one document, several whole ones, and the head of another.

The catch: with a plain causal mask, tokens of document 3 attend to tokens of documents 1 and 2 in the same window, which are unrelated. The model learns to ignore them, but it is wasted attention and can cause subtle problems. The fix is a document mask: in addition to the causal mask, block attention across document boundaries, so each token attends only within its own document. Llama 3 uses this and reports it matters little in standard pre-training but is important in the long-context stage, where a single 128k window can hold dozens of documents. Efficient attention kernels support it as a "variable-length" mode that also skips the blocked-out computation.

One packed window of 8 tokens, three documents doc 1 (blue), doc 2 (yellow), doc 3 (pink), EOS between causal mask only causal + document mask rows = query tokencols = key token left: token 6 (doc 3) canattend to docs 1 and 2 right: blocks on the diagonal,each doc sees only itself
Figure 3. Packing three documents into one window. With only the causal mask (left), every token can attend to everything before it, including unrelated documents. The document mask (right) keeps attention within each document's block. Kernels that support this also skip the empty regions, so it is faster, not slower.

Sharding

Finally the token stream is written as many fixed-size shards (memory-mapped binary files of a few hundred MB to a few GB each) with an index, so that thousands of data-loader workers can each read their assigned slice without coordination, and so that a restart from a checkpoint can resume at exactly the right token. Shuffling happens at the document level before packing and at the shard level during loading; the exact order is recorded so the run is reproducible. The systems chapter picks up from here.

The filter funnel

Put the numbers together and the pipeline is a funnel. The retention percentages below are illustrative, modeled on what RefinedWeb and FineWeb report for English web data; real values depend on the crawl, the language, and how aggressive each stage is set. The point is the shape: two stages (extraction and language ID) throw away most of the bytes, a model-based quality filter throws away most of the rest, and what remains is around 1% of what you started with.

InteractiveFilter-funnel visualizerstep through

Start with 100 TB of raw crawl and apply each stage. The bar is on a log scale, because a linear one would vanish by stage three.

A snapshot of 100 TB of crawl ends up as a few hundred billion tokens of training text. Since Common Crawl has published around a hundred snapshots (with heavy overlap between them, which dedup removes), that is how a 15T-token web corpus is assembled.

Synthetic data

If quality data is scarce, why not generate it? The Phi series (Gunasekar et al., 2023, "Textbooks Are All You Need") trained small models largely on model-written textbook-style text and exercises and got strong results on coding benchmarks, and Cosmopedia and similar datasets have made the recipe open. Synthetic data has clear advantages: you control the topic distribution, the format, and the quality; you can target gaps (rare languages, step-by-step math); and it does not run out.

The risks are just as clear. Synthetic text carries the generating model's biases, errors and stylistic tics, and a model trained on it inherits them. Trained recursively, model on model on model, the distribution narrows: Shumailov et al. (2024) call this model collapse, showing that the tails of the distribution (rare facts, unusual phrasings) disappear first, and each generation forgets a little more. The current consensus is that synthetic data is a supplement, valuable in targeted amounts and in the annealing phase, and that it must be mixed with, not substituted for, real data, with the generating model kept out of the loop of its own evaluation.

Licensing and ethics

A short section, because the issues are real and unsettled, and this course is not the place to settle them.

On one side: web-scale training uses text that was published to be read, and the models learn from it in a way that does not reproduce most of it; most jurisdictions have not ruled definitively, and several (Japan, the EU with an opt-out) permit text and data mining in some form. Common Crawl respects robots.txt, and pipelines increasingly honor opt-out signals.

On the other side: authors and publishers did not consent to this use, the models can and sometimes do reproduce training text verbatim, and the commercial value created is enormous and flows to the model builders. Several lawsuits (the New York Times against OpenAI and Microsoft, filed in December 2023, among others) are working through exactly these questions. The Pile's Books3 component, drawn from a shadow library, was removed after takedown requests, and datasets built since have been more careful about provenance: Dolma documents the source and license of every component, and the Stack respects repository licenses and offers opt-out.

Practically: know where every byte came from, keep provenance metadata through the whole pipeline, honor opt-outs, deduplicate (which reduces verbatim reproduction), and be honest in the model card. What you can legally do and what you should do are different questions, and the answer to the second one is a judgment your team has to own.

Companion code

code/lumen/data.py contains a small but complete version of the bottom of this pipeline: a tiny corpus, a loader that tokenizes it, packs documents into fixed-length sequences with end-of-text separators, builds the document mask, and shards the result. It also has a minimal MinHash implementation you can run on the toy corpus. The packing logic is the part people most often get wrong, so here it is.

import torch

def pack(docs, seq_len, eos_id):
    """docs: list of lists of token ids. Returns (tokens, doc_ids) of shape [n_seq, seq_len]."""
    stream, ids = [], []
    for d, doc in enumerate(docs):
        stream.extend(doc + [eos_id])
        ids.extend([d] * (len(doc) + 1))
    n = len(stream) // seq_len                      # drop the ragged tail
    tokens = torch.tensor(stream[: n * seq_len]).view(n, seq_len)
    doc_ids = torch.tensor(ids[: n * seq_len]).view(n, seq_len)
    return tokens, doc_ids

def document_mask(doc_ids):
    """[n_seq, L] -> [n_seq, L, L] bool: True where query i may attend key j."""
    L = doc_ids.size(1)
    causal = torch.tril(torch.ones(L, L, dtype=torch.bool))
    same_doc = doc_ids[:, :, None] == doc_ids[:, None, :]
    return causal[None] & same_doc

toks, dids = pack([[5, 6, 7], [8, 9], [10, 11, 12]], seq_len=8, eos_id=0)
print(toks, document_mask(dids)[0].int(), sep="\n")
tensor([[ 5, 6, 7, 0, 8, 9, 0, 10]]) tensor([[1, 0, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 1, 0, 0], [0, 0, 0, 0, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1]], dtype=torch.int32)

Three block-diagonal triangles, one per document. The second document's block is rows 4 to 6; the eighth token is the start of document 3 and can see only itself.

Practice

Exercise 1: build MinHash and check the estimator

Using the toy corpus in code/lumen/data.py, implement 5-word shingling, exact Jaccard, and MinHash signatures with $k$ hash functions. For one pair of documents, compute the estimate for $k \in \{8, 32, 128, 512\}$, 20 times each with different seeds, and plot the mean and standard deviation of the estimate against $k$. Confirm the standard deviation falls like $1/\sqrt{k}$ and matches $\sqrt{J(1-J)/k}$.

Solution sketch

Each hash position is a Bernoulli($J$) trial, so the mean of $k$ of them has variance $J(1-J)/k$. Use a salted string hash or a family $h_i(x) = (a_i x + b_i) \bmod p$ over integer shingle ids. Then add banding: with $b$ bands of $r$ rows, the probability that a pair with similarity $J$ collides in at least one band is $1 - (1 - J^r)^b$; plot this S-curve for FineWeb's $b = 14, r = 8$ and find the similarity at which it crosses 0.5 (about 0.69).

Exercise 2: does the document mask matter?

Train two copies of the small model from code/lumen/train.py on the packed toy corpus for 500 steps, one with the plain causal mask and one with the document mask from code/lumen/data.py. Compare validation loss measured on unpacked single documents. Then repeat with sequence length 4x longer (so each window holds more documents) and see whether the gap changes.

Solution sketch

At short sequence length with few documents per window, expect a small or invisible difference: the model learns to ignore tokens before the last EOS. At longer lengths, more of each window is cross-document and the masked version should be slightly better and train slightly faster in the early steps. This mirrors the Llama 3 finding that the mask matters most in the long-context stage.

Exercise 3: write and ablate a quality filter

Implement four Gopher rules (word count, mean word length, bullet-line fraction, stop-word presence) as a function on raw text. Apply them to any few thousand web documents you can obtain legitimately (for example a small sample of Common Crawl's WET files, or FineWeb's public sample on the Hugging Face Hub) and report what fraction each rule removes. Read 20 documents each rule removed. Which rule has the worst false-positive rate?

Solution sketch

The stop-word rule usually has the highest false-positive rate on short, legitimate, non-narrative pages (tables, recipes, poetry). The bullet rule is precise but misses much. This is the everyday experience of data engineering: every rule is a trade-off, and reading the rejects is how you tune it.

Check yourself
Two documents have Jaccard similarity 0.8 over their shingles. With one random hash function, what is the probability their MinHash values agree?
The minimum hash over the union picks a uniformly random shingle; the minima agree exactly when it is shared, which happens with probability |A ∩ B| / |A ∪ B| = J = 0.8.
According to Muennighoff et al. (2023), about how many epochs over the same data can you train before repeated tokens are worth noticeably less than fresh ones?
Up to ~4 epochs, repeated data is nearly as good as new data. Returns then diminish rapidly, and by ~40 epochs extra repeats add almost nothing.
What does a document mask do during packed training?
Packing concatenates documents; the document mask blocks attention across their boundaries, on top of the causal mask. Masking 15% is BERT; removing benchmark overlap is decontamination.
Why is the "annealing" phase run on upsampled high-quality data?
During learning-rate decay the model settles into its final minimum; data seen then has outsized influence. Llama 3 and MiniCPM both exploit this with a high-quality mix at the end.

Key takeaways

  • Almost all pre-training data starts as web crawl; curated sources (books, papers, Wikipedia) are tiny by comparison and get repeated.
  • The pipeline is extraction, language ID, blocklists, heuristic and model-based quality filters, dedup, PII and toxicity, decontamination, tokenization, mixing, packing, sharding; about 1% of the raw bytes survive.
  • Deduplicate: it saves compute, fixes the distribution, and cuts verbatim memorization roughly tenfold. MinHash makes near-duplicate detection linear via the identity P(minima agree) = Jaccard.
  • Every filter is a bet; ablate it by training small models with and without it.
  • Upsample code and math; repeat scarce sources up to about 4 epochs; anneal on the best data at the end.
  • Pack documents with EOS separators and a document mask; shard with an index for reproducible, resumable loading.
  • Synthetic data is a supplement with collapse risks; provenance and opt-outs are part of the engineering, not an afterthought.

Further reading