FAQ

Data strategy

Deduplicating training data, and why it is the cheapest win

Exact, near and cross-split duplicates — what each costs you, and the MinHash settings the major corpora actually published.

Deduplication is the highest-value-per-hour operation in data preparation. It costs compute rather than judgement, every major corpus does it, and skipping it silently inflates your benchmark scores.

Three kinds of duplicate, three different harms

Worth separating, because they need different detection and cause different problems.

A grid of eight portrait paintings in two rows, among them Vermeer's Girl with a Pearl Earring, a Van Gogh self-portrait and Leonardo's Lady with an Ermine. Two identical images of the Mona Lisa are outlined in red, and a brighter version of the Mona Lisa with red sleeves and a clear landscape behind her is outlined in orange.
imagededup's example search over eight portraits: two copies of the same Mona Lisa image are framed in red, and a different painting of the same sitter in orange. The red pair is an unambiguous duplicate; whether the orange one counts is a decision about what the dataset is for, which is exactly what a threshold encodes. Image: idealo/imagededup · Apache-2.0 repository
Nine pairs of small images in three groups labelled exact duplicate, near-duplicate and very similar, with a test image above each training image: a fish, a rocket and a lion that are identical; flowers and an animal that are slightly shifted; a bicycle, a fish and a bridge that look alike.
CIFAR-100 test images (top) above their nearest training images (bottom), grouped as exact duplicates, near-duplicates and very similar images. The authors find such duplicates for 3.3% of CIFAR-10 and 10% of CIFAR-100 test images, so part of every reported score was measured on images the model had effectively already seen. Image: Figure 1 of the article · CC BY 4.0 · from Barz & Denzler 2020, Journal of Imaging, “Do We Train on Test Data? Purging CIFAR of Near-Duplicates”, doi:10.3390/jimaging6060041

Exact duplicates. Byte-identical records. Simple to detect, trivially cheap, and common in anything assembled from multiple sources. They waste compute and overweight whatever they contain.

Near duplicates. The same content with minor differences — a reformatted document, a re-uploaded image, boilerplate with one field changed, the same article on two sites. These are the bulk of real duplication and need fuzzy matching to find.

Cross-split duplicates. The same or near-same item in both training and evaluation. This is the one that actually damages you, because it inflates your metrics. Your model has seen the test answers and your evaluation is measuring memorisation. Every benchmark number you report is then wrong in the flattering direction.

Exact and near duplicates cost efficiency. Cross-split duplicates cost you the ability to know whether anything works — check that one even if you skip the others.

What the big corpora did

The published pipelines give you tested defaults rather than guesses.

FineWeb used MinHash with 5-gram shingles and 112 hash functions, arranged as 14 buckets of 8 hashes, targeting roughly 75% similarity — and applied it per crawl dump independently rather than globally, which is an unusual choice worth noting if you are copying the recipe.

RedPajama-Data-v2 flagged exact duplicates with a Bloom filter over content hashes, and ships MinHash signatures computed with 128 hash functions so that you can add fuzzy matching at the similarity level you choose. By the figures in its paper, dropping those exact duplicates takes the head and middle partitions from 32.8 billion documents and 50.7 trillion tokens to 20.8 billion and 30.4 trillion — roughly 40% of the tokens removed.

That 40% figure is the one to carry around. Web-derived data is massively redundant, and if your pipeline is not removing a substantial fraction you should check whether it is working at all.

exact first, then near, then cross-split
import hashlib, re
from datasketch import MinHash, MinHashLSH

def norm(t):                                  # normalise before hashing
    return re.sub(r"\s+", " ", t.strip().lower())

def shingles(t, k=5):
    w = norm(t).split()
    return {" ".join(w[i:i+k]) for i in range(max(1, len(w) - k + 1))}

# 1. exact - cheap, do it first, it shrinks the problem
seen, rows = set(), []
for r in corpus:
    h = hashlib.sha256(norm(r["text"]).encode()).hexdigest()
    if h not in seen:
        seen.add(h); rows.append(r)
print(f"exact: {len(corpus)} -> {len(rows)}")

# 2. near-duplicates via MinHash LSH
lsh, kept = MinHashLSH(threshold=0.75, num_perm=128), []
for i, r in enumerate(rows):
    m = MinHash(num_perm=128)
    for s in shingles(r["text"]):
        m.update(s.encode())
    if not lsh.query(m):                      # nothing similar already kept
        lsh.insert(str(i), m); kept.append(r)
print(f"near: {len(rows)} -> {len(kept)}")

# 3. cross-split - the one that matters. Always TRAIN side loses.
eval_lsh = MinHashLSH(threshold=0.70, num_perm=128)
for j, e in enumerate(eval_set):
    m = MinHash(num_perm=128)
    for s in shingles(e["text"]):
        m.update(s.encode())
    eval_lsh.insert(f"e{j}", m)

clean, leaked = [], 0
for r in kept:
    m = MinHash(num_perm=128)
    for s in shingles(r["text"]):
        m.update(s.encode())
    if eval_lsh.query(m):
        leaked += 1                           # drop from TRAIN, keep eval intact
    else:
        clean.append(r)
print(f"cross-split leaks removed from train: {leaked}")

Choosing a threshold

Similarity thresholds are a judgement call and the consequences are asymmetric, which should guide you.

Too aggressive and you discard legitimately distinct items that happen to share structure — a real risk with templated text, short records, or code, where boilerplate dominates the token overlap. Too lenient and near-duplicates survive.

The published corpora cluster around 0.70 to 0.80 for text, which is a reasonable starting range. For cross-split checking, bias lower — be more aggressive — because a false positive costs you one training example and a false negative costs you the integrity of your evaluation.

Inspect the pairs your threshold catches. Print twenty pairs at the boundary and decide whether you would call them duplicates. Ten minutes calibrates the threshold far better than reasoning about it, and it is the step that catches a badly wrong setting before it removes half your data.

A line chart titled "Effect of threshold (num_perm=128)": the probability that a pair is returned by an LSH query against its true Jaccard similarity, for thresholds 0.3, 0.5, 0.7 and 0.9 with their band and row settings (b=37 r=3, b=25 r=5, b=14 r=9, b=5 r=25). Each is an S-shaped curve rising around its threshold, with measured points over a dashed theoretical curve.
MinHash LSH does not cut cleanly at its threshold: set to 0.7, it catches pairs at true similarity 0.65 about a quarter of the time and pairs at 0.8 about nine times in ten. That soft edge is why printing pairs near the boundary calibrates a setting better than the number alone. Image (cropped): ekzhu/datasketch · MIT repository

Images, audio and code need different methods

Text shingling does not transfer, and each modality has a standard approach.

Images. Perceptual hashing catches resizes, recompressions and minor crops cheaply. Embedding similarity catches semantic near-duplicates — the same scene from a slightly different angle — which perceptual hashing misses and which matters for cross-split leakage. Video frames are the acute case: consecutive frames are near-identical, so a random split over frames guarantees leakage, and you must split by clip.

Audio. Audio fingerprinting for exact and near matches. The same trap as video applies: split by recording or speaker, never by segment.

Code. Exact and token-level matching after normalising whitespace and comments. Code is extraordinarily duplicated — vendored dependencies, forks, generated files, the same snippet copied across thousands of repositories. Expect to remove a lot, and consider excluding obviously generated and vendored paths before you start.

A line chart titled "Impact of Deduplication Threshold on Remaining Data": remaining data in per cent against deduplication threshold epsilon from 0.00 to 0.20. The curve drops from 100% to about 76% by 0.01, to about 58% at 0.05, about 32% at 0.10 and about 4% at 0.20.
Embedding-based deduplication of video clips, where two clips count as duplicates if their cosine similarity is at least 1 − ε. The share kept falls from about three-quarters at ε = 0.01 to roughly a third at 0.1 and under 5% at 0.2, so for media the threshold is a decision about what the dataset is, not a detail. Image: NVIDIA-NeMo/Curator · Apache-2.0 repository

Keep the counts

Log how many records each deduplication stage removed, and keep that with the dataset's provenance record — see our provenance post.

Two reasons. It is how you notice that a pipeline change silently stopped working, which happens and is otherwise invisible. And it is how you explain the composition of a mixture later, when someone asks why the model over-weights a particular source.

The counts also tell you something about your sources. A source that loses 90% to deduplication was mostly repetition, which is useful information about whether to include it next time.

A bar chart with four bars in increasingly light green, x-axis 'Data curation pipeline stage' reading 'Raw extracted CC', '+ Text cleaning', '+ Deduplication' and '+ Quality Filtering', and y-axis 'Average accuracy (%) over various downstream tasks' rising from about 47.5 to about 50.9.
Downstream accuracy after each stage of a text curation pipeline. Deduplication is the largest single jump here — larger than the text cleaning before it and the quality filter after it. Image: NVIDIA-NeMo/Curator · Apache-2.0 repository

Questions people ask

Should I deduplicate before or after filtering?

Exact deduplication first, because it is cheap and shrinks everything downstream. Quality filtering next. Near-duplicate detection after that, since it is the expensive stage and you want it running over the smallest possible set. Cross-split checking last, immediately before training.

Does deduplication hurt if my data is naturally repetitive?

Legitimate repetition — common phrases, standard forms, boilerplate that genuinely occurs at that frequency in your domain — is signal, and aggressive deduplication can flatten it. Deduplicate at the document level rather than the passage level, and calibrate your threshold by inspecting boundary pairs.

How do I check for leakage against a benchmark I did not build?

Run the same near-duplicate check between your training data and the benchmark's public test set. If you find overlap, you cannot report that benchmark honestly without saying so — which is one of several reasons a private evaluation set is worth building.

Is deduplication enough to prevent memorisation?

It reduces it substantially, since repeated content is memorised most readily, but it does not eliminate it — models can memorise singly-occurring content, particularly distinctive strings. If memorisation is a specific concern, it needs its own testing.

Stop hunting. Start searching.

Tens of thousands of curated datasets, searchable in plain language — with the licence and size checked before you download anything.

Search Dataset Finder →