LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 196 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/9
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningBayesian Inference from EvidenceStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/15
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageStatistical Learning and GeneralizationConformal Prediction and UncertaintyClustering and PCAProbabilistic Generative ModelsCore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingCausal Inference and CounterfactualsPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/25
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsContext EngineeringPrompt Injection DefenseResponsible AI GovernancePrivacy-Preserving Machine LearningData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BPrompt Optimization with DSPyModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Reproducible ML Study
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/17
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabJAX for PyTorch ResearchersContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineMixed Precision TrainingDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningTraining Run OperationsReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight Interpolation
🤖Advanced Agents & Retrieval0/16
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryRecursive Language Models (RLM)Multi-Agent OrchestrationCapstone: Production Agent
⚡Inference & Production Scale0/31
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceAccelerator Architecture Field GuideCorrect GPU Profiling and BenchmarkingGPU Kernel Performance Engineering LabGPU Parallel Primitives LabGPU Compilers and Kernel LanguagesGEMM From Naive CUDA to Tensor CoresLow-Precision GPU ProgrammingGPU Runtime Execution LabAI-Generated Kernel EngineeringModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingGPU Failure OperationsDistributed Inference Data PlaneServing Benchmark EngineeringA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning Agent System Design
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
🔬Project Deep Dives0/17
Deep Dive - vLLMDeep Dive - SkyRLDeep Dive - FlashAttentionDeep Dive - FlashInferDeep Dive - DeepGEMMDeep Dive - NCCLDeep Dive - MegatronDeep Dive - DeepSpeedDeep Dive - RayDeep Dive - MLflowDeep Dive - PyTorchDeep Dive - TransformersDeep Dive - SGLangDeep Dive - slimeDeep Dive - DeepEPDeep Dive - TinkerDeep Dive - Light-PEFT
Back to Topics
LearnML Algorithms & EvaluationDataset Pipelines and Data Quality
⚙️MediumMLOps & Deployment

Dataset Pipelines and Data Quality

Build versioned AI datasets with schema gates, grouped splits, contamination checks, and auditable receipts.

23 min read
Learning path
Step 46 of 196 in the full curriculum
PyTorch Training LoopsFeature Engineering for Production ML

Personalize this lesson

Adapt explanations and teaching visuals to your background and preferred voice.

Tickets 101 and 102 look like two separate training rows in an incident export. They differ only in leading spaces and letter case, yet both belong to conversation c-51. If 101 lands in the training partition while 102 lands in evaluation, the test score won't measure generalization to new incidents. It'll measure memorization of incident c-51's specific tokens.

The previous chapter trained a ticket router from pre-computed numerical tensors. Now trace those tensors backward: which raw records can become supervision, and which held-out records can an evaluation engineer actually trust? A dataset pipeline turns raw records into model inputs and records how it did so. That record supplies provenance: where each example came from, which transformations changed it, and which gates approved it. Cleaning, splitting, and contamination checks protect different parts of the same training claim.

Ingestion contracts: schema enforcement and type validation

The router consumes features derived from support tickets. Start one step earlier with exported messages and labels from a developer-support conversation dump:

FieldTypeMeaningWhy it matters
ticket_idintUnique exported row IDTraces rejects and downstream predictions back to source.
conversation_idstrThread ID for one incidentMust stay in one split to avoid conversation leakage.
textstrRaw operator messageBecomes model input after normalization.
labelstrstandard or escalateBecomes supervised target after human review.

Read these four exported rows as four distinct pipeline decisions:

TicketRaw messageLabelDiagnosis
101Build is still failingescalateValid reviewed example.
102 BUILD is still failing escalateSame message after safe normalization.
103Where is the deploy policy?standardValid reviewed example.
104SEV-123 timeline has no reviewed labelNoneMissing required label.

Ticket 104 presents the first breakdown: a message exists, but no reviewed label does. What should happen before normalization or splitting?

Start with an ingestion contract gate. In modern data architectures, validation operates across three distinct tiers:

  1. Structural gates: Enforce column presence, non-nullity, and strict data types. A float ticket ID or a string where an integer belongs gets rejected immediately.
  2. Domain gates: Enforce value bounds, regex patterns, and categorical membership. Here, labels must belong strictly to {"standard", "escalate"}.
  3. Distributional gates: Detect batch-level anomalies, such as unexpected null rates or drastic shifts in label ratios before data reaches disk.

Declarative validation libraries formalize these tiers. Pydantic models with strict typing (pydanticStrict2026, pydanticModels2026) reject silent type coercions, such as string "101" turning into int 101, or truthy strings turning into booleans. Pandera provides DataFrame-level schemas with column checks and statistical hypothesis gates for batch tabular data. Great Expectations runs declarative assertion suites in ingestion pipelines to intercept corrupted records before model ingestion.

Schema evolution requires constant vigilance. Upstream services frequently add fields (additive changes) or deprecate columns, widen types, and rename identifiers (breaking changes). A strict ingestion gate fails fast on breaking changes, isolating the malformed batch rather than letting corrupt records silently train your model.

01-schema-gate.py
1raw_rows = [ 2 {"ticket_id": 101, "conversation_id": "c-51", "text": "Build is still failing", "label": "escalate"}, 3 {"ticket_id": 102, "conversation_id": "c-51", "text": " BUILD is still failing ", "label": "escalate"}, 4 {"ticket_id": 103, "conversation_id": "c-52", "text": "Where is the deploy policy?", "label": "standard"}, 5 {"ticket_id": 104, "conversation_id": "c-53", "text": "SEV-123 timeline has no reviewed label", "label": None}, 6] 7required = {"ticket_id", "conversation_id", "text", "label"} 8labels = {"standard", "escalate"} 9 10def reject_reason(row: object) -> str | None: 11 if not isinstance(row, dict): 12 return "not_an_object" 13 if not required.issubset(row): 14 return "missing_field" 15 if type(row["ticket_id"]) is not int or row["ticket_id"] <= 0: 16 return "invalid_ticket_id" 17 conversation = row["conversation_id"] 18 if not isinstance(conversation, str) or not conversation.strip() or conversation != conversation.strip(): 19 return "invalid_conversation_id" 20 if not isinstance(row["text"], str) or not row["text"].strip(): 21 return "blank_text" 22 if not isinstance(row["label"], str) or row["label"] not in labels: 23 return "invalid_label" 24 return None 25 26accepted, rejected = [], [] 27for row in raw_rows: 28 reason = reject_reason(row) 29 ticket_id = row.get("ticket_id") if isinstance(row, dict) else None 30 if reason is None: 31 accepted.append(ticket_id) 32 else: 33 rejected.append((ticket_id, reason)) 34print("accepted:", accepted) 35print("rejected:", rejected)
Schema gate
1accepted: [101, 102, 103] 2rejected: [(104, 'invalid_label')]

The gate catches ticket 104 at the intake boundary. Schema validation can't prove that a label is correct, but it stops unreviewed or malformed rows from becoming supervision.

Deterministic normalization and exact fingerprinting

Rows 101 and 102 differ only because an export system injected spaces and changed letter casing. Text sanitization strips control characters, surrogate pairs, and non-printable bytes before text reaches downstream tokenizers.

Next comes Unicode normalization. The Unicode standard defines multiple canonical and compatibility forms: NFC, NFD, NFKC, and NFKD. NFKC (Compatibility Decomposition followed by Canonical Composition) replaces compatibility characters with standard equivalents. It collapses typographic ligatures like fi into fi, turns superscript ² into 2, and normalizes full-width characters into standard ASCII. Following NFKC with str.casefold() (which handles tricky multilingual casing like German ß mapping to ss) and whitespace collapsing exposes duplicate text reliably.

Conservative normalization has boundaries. Case-folding merges case-sensitive identifiers like Prod and prod, while compatibility normalization can alter code syntax. Never strip negation or environment names: build passed and build not passed must remain distinct records.

Once text is normalized, a deterministic SHA-256 hash becomes an exact fingerprint. It isn't a semantic similarity score or proof that two records describe the same event. Equal digests provide an efficient lookup key for duplicate detection; compare normalized strings directly when exact identity matters, since cryptographic hashes aren't mathematically collision-free.

02-normalize-and-fingerprint.py
1import hashlib 2import unicodedata 3 4def normalize_text(text: str) -> str: 5 normalized = unicodedata.normalize("NFKC", text) 6 return " ".join(normalized.casefold().split()) 7 8def fingerprint(text: str) -> str: 9 return hashlib.sha256(normalize_text(text).encode("utf-8")).hexdigest() 10 11messages = [ 12 "Build is still failing", 13 " BUILD is still failing ", 14 "Build is not failing", 15] 16for message in messages: 17 print(normalize_text(message), fingerprint(message)[:12]) 18print("first two duplicate:", fingerprint(messages[0]) == fingerprint(messages[1])) 19print("negation preserved:", fingerprint(messages[0]) != fingerprint(messages[2]))
Exact fingerprints
1build is still failing c53011fb9834 2build is still failing c53011fb9834 3build is not failing e038c1b99d05 4first two duplicate: True 5negation preserved: True

The printed hashes are shortened for display; identity comparisons always use the full 64-character hex digest. Record the Python runtime and Unicode database versions alongside the normalization rule when reproducible bytes matter across architectures.[1]Reference 1unicodedata: Unicode Databasehttps://docs.python.org/3/library/unicodedata.html[2]Reference 2hashlib: Secure Hashes and Message Digestshttps://docs.python.org/3/library/hashlib.html

Why normalize text before computing its exact fingerprint?

Answer

Normalization ensures harmless formatting differences like capitalization and extra spacing map to identical byte representations before hashing. It must stay conservative so semantic distinctions like negation stay intact.

Applying schema filtering and normalization to tickets 101 through 104 produces a clean separation: ticket 104 is rejected, 102 merges into 101, and 101 plus 103 proceed.

End-to-end data validation pipeline showing five sequential stages: Ingestion Schema Gate, Normalization and Exact Dedup, Privacy and Provenance, Grouped Split Partitioning, and Quality Gate with Manifest Receipt. Quarantined records branch downward with explicit error reasons.
The five-stage data validation pipeline. Raw records pass through strict schema gates, deterministic normalization, privacy redaction, and grouped split partitioning before signing into an immutable release manifest.

Duplicate labels and the quarantine boundary

Rows 201, 202, and 203 collapse to the same normalized text, but row 203 carries label standard while rows 201 and 202 carry escalate. Should the pipeline keep the first row, or quarantine the entire group?

A naive deduplicator picks whichever row arrived first ("first-in-wins"). That's an anti-pattern. Silent resolution sweeps critical flaws under the rug: annotation error, guideline drift, or missing features (like customer SLA tier or staging flag) that justified different routing decisions.

A defensive pipeline isolates the entire fingerprint group into an anomaly quarantine. Label conflict is evidence to investigate, not noise to discard.

03-conflicting-labels.py
1from collections import defaultdict 2import unicodedata 3 4def key(text: str) -> str: 5 clean = " ".join(unicodedata.normalize("NFKC", text).casefold().split()) 6 return clean 7 8rows = [ 9 {"ticket_id": 201, "text": "Migration blocked for 12 days", "label": "escalate"}, 10 {"ticket_id": 202, "text": " migration blocked for 12 days ", "label": "escalate"}, 11 {"ticket_id": 203, "text": "Migration blocked for 12 days", "label": "standard"}, 12 {"ticket_id": 204, "text": "Runbook link updated", "label": "standard"}, 13] 14groups = defaultdict(list) 15for row in rows: 16 groups[key(row["text"])].append(row) 17 18kept = [] 19quarantined = [] 20for duplicate_group in groups.values(): 21 observed_labels = {row["label"] for row in duplicate_group} 22 if len(observed_labels) > 1: 23 quarantined.extend(row["ticket_id"] for row in duplicate_group) 24 else: 25 kept.append(duplicate_group[0]["ticket_id"]) 26 27print("kept representative rows:", kept) 28print("quarantined label conflict:", quarantined)
Conflict quarantine
1kept representative rows: [204] 2quarantined label conflict: [201, 202, 203]

Grouping runs directly on the normalized string, so hash collisions can't fabricate a false label conflict. In this build, agreeing copies collapse only within the same conversation. Identical text appearing across separate conversations routes to human review, ensuring common response templates don't skew training weights.

Grouped splits and target leakage prevention

Passing schema validation and deduplication doesn't make an example safe for evaluation. Standard random splitting (like an 80/20 train/test split) fails in multi-turn dialogues, medical records, or user-session feeds.

When multiple records share an underlying entity, random splitting causes entity leakage. If turns 1 and 3 of incident c-51 train the model while turn 2 tests it, the model memorizes incident-specific server names and jargon. The test score measures recall of known incidents, not generalization to new ones.

Preventing entity leakage requires choosing the correct split unit. Split on conversation_id, not ticket_id. All records sharing an entity key must atomically land in the same partition.

Using a stable cryptographic hash (sha256(conversation_id) % 100) assigns each entity deterministically without requiring a centralized state store:

04-stable-grouped-split.py
1import hashlib 2 3def split_for_conversation(conversation_id: str) -> str: 4 bucket = int(hashlib.sha256(conversation_id.encode("utf-8")).hexdigest()[:8], 16) % 100 5 if bucket < 70: 6 return "train" 7 if bucket < 85: 8 return "validation" 9 return "test" 10 11rows = [ 12 {"ticket_id": 301, "conversation_id": "build-001"}, 13 {"ticket_id": 315, "conversation_id": "build-001"}, 14 {"ticket_id": 303, "conversation_id": "build-005"}, 15 {"ticket_id": 304, "conversation_id": "build-006"}, 16] 17assignments = [(row["ticket_id"], split_for_conversation(row["conversation_id"])) for row in rows] 18same_conversation_stays_together = assignments[0][1] == assignments[1][1] 19print("assignments:", assignments) 20print("same conversation stays together:", same_conversation_stays_together)
Grouped split
1assignments: [(301, 'train'), (315, 'train'), (303, 'validation'), (304, 'test')] 2same conversation stays together: True

Both turns of build-001 stay in train. build-005 hashes into validation and build-006 into test.

Stable hashing ensures repeatability, but doesn't guarantee balanced row counts or uniform label distributions. Large conversations contribute dozens of turns while small ones contribute one. Always audit partition sizes and class distributions after applying grouped splits.

Entity grouping addresses generalization across conversations, not across time. If records arrive chronologically, use out-of-time (OOT) validation splits where the test set is strictly bounded by a future timestamp. This prevents temporal leakage, where features computed from future events pollute training rows.

Two rows come from the same conversation. Why isn't a random row-level split enough?

Answer

Turns within a conversation share context, incident details, and entity jargon. Assigning the whole conversation_id to a single partition ensures evaluation measures generalization to completely unseen conversations.

Benchmark contamination and de-duplication audits

Grouped splits protect conversation boundaries. They don't prevent training data from containing locked benchmark questions and answers. In language model development, web crawlers ingest millions of pages that contain benchmark prompts, test suites, or discussion forums, leaving evaluation sets that no longer measure true reasoning on unseen tasks.

During GPT-3's pre-training, an 8-word to 13-word n-gram overlap filter removed benchmark test sets from training data (brown2020). However, a filtering bug left overlaps in the final dataset, and retraining wasn't economically feasible. For Wikipedia language modeling benchmarks and the Children's Book Test, contamination was nearly total, so OpenAI omitted those benchmark results entirely.

Start with an exact normalized overlap check between candidate training data and locked evaluation sets:

05-exact-contamination.py
1import hashlib 2import unicodedata 3 4def clean(text: str) -> str: 5 return " ".join(unicodedata.normalize("NFKC", text).casefold().split()) 6 7def fp(text: str) -> str: 8 return hashlib.sha256(clean(text).encode("utf-8")).hexdigest() 9 10training = ["migration blocked for 12 days", "runbook link updated"] 11locked_eval = ["MIGRATION blocked for 12 days", "incident review still pending"] 12train_keys = {fp(text) for text in training} 13collisions = [text for text in locked_eval if fp(text) in train_keys] 14 15print("exact collisions:", collisions) 16print("release evaluation:", len(collisions) == 0)
Exact contamination gate
1exact collisions: ['MIGRATION blocked for 12 days'] 2release evaluation: False

Exact matching is necessary, but insufficient on its own. A benchmark question might be paraphrased or formatted with minor edits while preserving the identical reasoning path.

Post-hoc black-box contamination checks (like Membership Inference Attacks or Min-K% log-probability scoring) often fail to detect contamination reliably, yielding near-chance AUC-ROC scores on frontier models. Preventative pre-training decontamination and rigorous n-gram filtering remain the only dependable defenses.

Near-duplicate detection: MinHash, shingling, and LSH

Consider two support tickets: build has not passed and my build has not passed. Their exact SHA-256 fingerprints are completely different. Yet they share three out of four adjacent word pairs.

Break text into contiguous n-grams of words or characters called shingles. For word pairs (n=2n=2n=2):

  • Document AAA: {"build has", "has not", "not passed"} (size 3)
  • Document BBB: {"my build", "build has", "has not", "not passed"} (size 4)

Measure their set overlap with Jaccard similarity:

J(A,B)=∣A∩B∣∣A∪B∣J(A, B) = \frac{|A \cap B|}{|A \cup B|}J(A,B)=∣A∪B∣∣A∩B∣​

For this pair, the intersection contains 3 shared shingles and the union contains 4 distinct shingles:

J(A,B)=34=0.75J(A, B) = \frac{3}{4} = 0.75J(A,B)=43​=0.75

A similarity of 0.75 crosses a 0.70 inspection threshold:

06-shingle-overlap.py
1def shingles(text: str, width: int = 2) -> set[tuple[str, ...]]: 2 if type(width) is not int or width < 1: 3 raise ValueError("shingle width must be positive") 4 words = text.casefold().split() 5 if not words: 6 return set() 7 if len(words) < width: 8 return {tuple(words)} 9 return {tuple(words[index:index + width]) for index in range(len(words) - width + 1)} 10 11def jaccard(left: str, right: str) -> float: 12 a = shingles(left) 13 b = shingles(right) 14 union = a | b 15 return len(a & b) / len(union) if union else 0.0 16 17train_text = "build has not passed" 18eval_text = "my build has not passed" 19score = jaccard(train_text, eval_text) 20print("shared pair score:", round(score, 2)) 21print("send to human review:", score >= 0.70)
Near-duplicate review queue
1shared pair score: 0.75 2send to human review: True

Computing all-pairs Jaccard similarity across millions of documents requires O(N2)O(N^2)O(N2) comparisons, which becomes impossible at scale. Broder's MinHash algorithm (broder1997) solves this scaling bottleneck.

MinHash uses KKK independent hash functions applied to each shingle. For each hash function hkh_khk​, compute:

sk(A)=min⁡w∈Ahk(w)s_k(A) = \min_{w \in A} h_k(w)sk​(A)=w∈Amin​hk​(w)

The probability that two documents produce the same minimum hash value equals their Jaccard similarity:

P(sk(A)=sk(B))=J(A,B)P\left(s_k(A) = s_k(B)\right) = J(A, B)P(sk​(A)=sk​(B))=J(A,B)

Concatenating KKK minimum hashes creates a compact signature vector. Locality-Sensitive Hashing (LSH) groups these KKK signatures into bbb bands of rrr rows (K=b×rK = b \times rK=b×r). Two documents sharing identical hashes in any single band become candidate pairs. This finds near-duplicates in sub-quadratic time.

Lee et al. (lee2022) showed that removing near-duplicates from web corpora reduced memorization by roughly 10x without harming downstream accuracy. Modern foundation model corpora like FineWeb (penedo2024, penedo2025fineweb2), Dolma (soldaini2024), and Llama 3 (touvron2024) run 5-gram MinHash LSH across billions of tokens to eliminate duplicated web content.

Data quality gate scorecard auditing candidate dataset: Tier 1 Structural Schema passes with 100% type conformance, Tier 2 Deduplication resolves 1 exact duplicate and quarantines 3 conflicting rows, Tier 3 Privacy flags 1 email for redaction, and Tier 4 Grouped Splits verifies zero entity leakage while flagging 1 near-duplicate benchmark candidate with Jaccard 0.75.
The pre-flight data quality gate scorecard. Automated assertion tiers audit schema conformance, deduplication, privacy boundaries, and target leakage to verify data invariants before model training.

Two messages have Jaccard shingle similarity 0.92. Should the pipeline delete one automatically?

Answer

No. The score flags a candidate near-duplicate. A human or policy gate must verify whether the examples share equivalent answers and labels before merging, discarding, or declaring contamination.

Privacy, PII detection, and provenance

Support tickets frequently contain Personally Identifiable Information (PII): developer emails, phone numbers, server IP addresses, authentication tokens, and internal ticket references. Training models on unredacted text creates data leak vulnerabilities where models reproduce private keys or employee contacts during generation.

PII detection employs a layered defense:

  1. Deterministic regex: Scans for structured patterns with fixed formats: email addresses, UUIDs, IPv4/IPv6 addresses, and credit card numbers.
  2. Named Entity Recognition (NER): Detects unstructured entities: human names, company names, and physical addresses.
  3. Dedicated de-identification SDKs: Microsoft Presidio (presidio2023) combines pattern recognizers, contextual rule engines, and spaCy NER to detect and de-identify sensitive spans across text streams.

Once detected, pipelines apply two redaction strategies:

  • Document-level dropping: If PII density is high (e.g. Dolma drops documents with more than 5 PII matches), the entire document gets removed (soldaini2024).
  • Span masking: For sparse PII, sensitive spans get replaced with typed surrogate tokens (<EMAIL>, <PHONE_NUMBER>, <PERSON>).
10-sensitive-data-quarantine.py
1import re 2 3email_pattern = re.compile(r"\b[\w.+-]+@[\w.-]+\.[A-Za-z]{2,}\b") 4rows = [ 5 {"ticket_id": 501, "text": "Incident INC-10234 mentions a private timeline"}, 6 {"ticket_id": 502, "text": "Send updates to [email protected] please"}, 7] 8 9no_email_match = [] 10needs_redaction_review = [] 11for row in rows: 12 if email_pattern.search(row["text"]): 13 needs_redaction_review.append(row["ticket_id"]) 14 else: 15 no_email_match.append(row["ticket_id"]) 16 17print("no email match in this narrow gate:", no_email_match) 18print("redaction review required:", needs_redaction_review)
Sensitive-data quarantine
1no email match in this narrow gate: [501] 2redaction review required: [502]

A regex non-match doesn't prove safety: row 501 still contains an internal incident identifier (INC-10234). Production pipelines enforce privacy reviews, access tiers, and verified redaction models before public release.

Data systems also distinguish provenance from lineage:

  • Provenance: Captures origin, custody, collection guidelines, and legal licensing.
  • Lineage: Maps the computational DAG: exact source snapshots, transform parameters, code commit SHAs, and output checksums (often emitted as OpenLineage events).

Dataset versioning bridges the two. Tools like DVC store lightweight pointer files in Git while syncing large binary chunks to content-addressable storage (CAS) like S3. Table formats like Delta Lake and Apache Iceberg provide ACID transactions, schema enforcement, and immutable snapshot time-travel over Parquet files.

Modern foundation model data pipelines: web filtering and synthetic gates

When building pre-training datasets from raw Common Crawl dumps, filtering is the single largest factor determining model quality (raffel2020, penedo2024, touvron2024). Raw web text is flooded with HTML boilerplate, navigation menus, SEO keyword soup, and machine-translated spam.

Modern pipelines apply a multi-tier filtering battery:

1. Heuristic text filtering

Rules based on document structure eliminate obvious junk without running expensive models:

  • Length bounds: Discard documents with fewer than 50 or more than 100,000 words.
  • Symbol-to-word ratio: Reject text where symbols or punctuation exceed 15% to 20% of all tokens (catches broken code dumps or terminal dumps).
  • Stopword thresholds: Natural language exhibits stable stopword distributions. A page lacking common stopwords (e.g. the, and, is) is usually a product list, keyword cluster, or scraped catalog.
  • Repetition filters: Remove lines or documents with repeating n-grams or duplicated paragraphs (catches looping web scrapers).

2. Model-based perplexity filtering

Heuristics catch structural garbage, but can't evaluate prose quality. High-scale pipelines train a lightweight reference language model (like a 5-gram KenLM or a compact transformer) on high-quality curated sources such as Wikipedia, textbooks, and vetted documentation (penedo2024).

The reference model scores the perplexity of each candidate document:

PPL(X)=exp⁡(−1N∑i=1Nlog⁡P(xi∣x<i))\text{PPL}(X) = \exp\left( -\frac{1}{N} \sum_{i=1}^N \log P(x_i \mid x_{<i}) \right)PPL(X)=exp(−N1​i=1∑N​logP(xi​∣x<i​))

Documents with extreme perplexity are discarded: high perplexity indicates incoherent babble or OCR noise, while abnormally low perplexity indicates repetitive boilerplate or templated spam.

3. Synthetic data quality gating

Modern instruction-tuning (SFT) and preference modeling (DPO/RLHF) rely heavily on synthetic data generated by frontier teacher models. Automated quality gates must vet synthetic records before they hit training pipelines:

  • Format and schema validation: Enforce JSON schema adherence, tag balance (<think>...</think>), and response length bounds.
  • Verifiable execution: For coding and math pipelines, execute generated code in sandboxed containers; reject solutions failing unit tests.
  • LLM-as-a-judge scoring: Prompt evaluator models to grade response completeness, truthfulness, and reasoning coherence, dropping outputs falling below strict quality thresholds.

Building a reproducible candidate artifact

Now assemble all verification mechanics into one deterministic pipeline. It validates raw records, normalizes text, resolves duplicate groups, assigns grouped splits, and writes JSONL outputs alongside a rejection log and signed manifest.

A candidate artifact means these mechanics executed successfully. It doesn't mean privacy or contamination review has signed off on release.

07-build-dataset-artifact.py
1import hashlib 2import json 3import platform 4import unicodedata 5from collections import Counter, defaultdict 6from pathlib import Path 7 8raw_rows = [ 9 {"ticket_id": 401, "conversation_id": "c-a", "text": "Build is still failing", "label": "escalate"}, 10 {"ticket_id": 402, "conversation_id": "c-a", "text": " BUILD is still failing ", "label": "escalate"}, 11 {"ticket_id": 403, "conversation_id": "c-i", "text": "Runbook page shows stale", "label": "standard"}, 12 {"ticket_id": 404, "conversation_id": "c-c", "text": "SEV-123 timeline has no reviewed label", "label": None}, 13 {"ticket_id": 405, "conversation_id": "c-002", "text": "Break-glass request will not open", "label": "standard"}, 14 {"ticket_id": 406, "conversation_id": "c-e", "text": "Migration ran twice in production", "label": "escalate"}, 15 {"ticket_id": 407, "conversation_id": "c-l", "text": "Deploy completed this morning", "label": "standard"}, 16 {"ticket_id": 408, "conversation_id": "build-003", "text": "Build stalled after approval", "label": "escalate"}, 17 {"ticket_id": 409, "conversation_id": "c-m", "text": "Access request pending review", "label": "standard"}, 18 {"ticket_id": 410, "conversation_id": "c-m", "text": "Access request pending review", "label": "escalate"}, 19 {"ticket_id": 411, "conversation_id": "c-x", "text": "Access policy page missing", "label": "standard"}, 20 {"ticket_id": 412, "conversation_id": "c-y", "text": "Access policy page missing", "label": "standard"}, 21] 22valid_labels = {"standard", "escalate"} 23required_fields = {"ticket_id", "conversation_id", "text", "label"} 24 25def normalize_text(text: str) -> str: 26 return " ".join(unicodedata.normalize("NFKC", text).casefold().split()) 27 28def text_fingerprint(text: str) -> str: 29 return hashlib.sha256(text.encode("utf-8")).hexdigest() 30 31def reject_reason(row: object) -> str | None: 32 if not isinstance(row, dict): 33 return "not_an_object" 34 if not required_fields.issubset(row): 35 return "missing_field" 36 if type(row["ticket_id"]) is not int or row["ticket_id"] <= 0: 37 return "invalid_ticket_id" 38 conversation = row["conversation_id"] 39 if not isinstance(conversation, str) or not conversation.strip() or conversation != conversation.strip(): 40 return "invalid_conversation_id" 41 if not isinstance(row["text"], str) or not row["text"].strip(): 42 return "blank_text" 43 if not isinstance(row["label"], str) or row["label"] not in valid_labels: 44 return "invalid_label" 45 return None 46 47def stable_split(conversation_id: str) -> str: 48 bucket = int(hashlib.sha256(conversation_id.encode("utf-8")).hexdigest()[:8], 16) % 100 49 if bucket < 70: 50 return "train" 51 if bucket < 85: 52 return "validation" 53 return "test" 54 55def prepare_rows(input_rows): 56 id_counts = Counter( 57 row["ticket_id"] for row in input_rows 58 if isinstance(row, dict) and type(row.get("ticket_id")) is int 59 ) 60 rejected = [] 61 prepared_by_text = defaultdict(list) 62 for row in input_rows: 63 reason = reject_reason(row) 64 ticket_id = row.get("ticket_id") if isinstance(row, dict) else None 65 if reason is None and id_counts[ticket_id] > 1: 66 reason = "duplicate_ticket_id" 67 if reason: 68 rejected.append({"ticket_id": ticket_id, "reason": reason}) 69 continue 70 text = normalize_text(row["text"]) 71 prepared_by_text[text].append({ 72 "ticket_id": ticket_id, 73 "conversation_id": row["conversation_id"], 74 "text": text, 75 "label": row["label"], 76 "text_sha256": text_fingerprint(text), 77 }) 78 79 kept = [] 80 for duplicate_group in prepared_by_text.values(): 81 labels = {row["label"] for row in duplicate_group} 82 conversations = {row["conversation_id"] for row in duplicate_group} 83 reason = ("label_conflict" if len(labels) > 1 else 84 "cross_conversation_duplicate" if len(conversations) > 1 else None) 85 if reason: 86 rejected.extend({"ticket_id": row["ticket_id"], "reason": reason} 87 for row in duplicate_group) 88 continue 89 duplicate_group.sort(key=lambda row: row["ticket_id"]) 90 representative = duplicate_group[0] 91 kept.append({**representative, "split": stable_split(representative["conversation_id"])}) 92 rejected.extend({"ticket_id": row["ticket_id"], "reason": "exact_duplicate"} 93 for row in duplicate_group[1:]) 94 95 kept.sort(key=lambda row: row["ticket_id"]) 96 rejected.sort(key=lambda row: (str(row["ticket_id"]), row["reason"])) 97 return kept, rejected 98 99kept, rejected = prepare_rows(raw_rows) 100assert len(kept) + len(rejected) == len(raw_rows) 101 102output = Path("artifacts") 103output.mkdir(exist_ok=True) 104rows_path = output / "dev_tickets.v1.jsonl" 105rejects_path = output / "dev_tickets.v1.rejects.jsonl" 106rows_path.write_text("".join(json.dumps(row, sort_keys=True) + "\n" for row in kept), encoding="utf-8") 107rejects_path.write_text("".join(json.dumps(row, sort_keys=True) + "\n" for row in rejected), encoding="utf-8") 108artifact_sha256 = hashlib.sha256(rows_path.read_bytes()).hexdigest() 109manifest = { 110 "dataset_id": "developer-ticket-routing", 111 "version": "v1", 112 "status": "candidate_not_released", 113 "pipeline_version": "nfkc-casefold-space__within-conversation-dedup__cross-group-review__v1", 114 "source_snapshot": "synthetic-ticket-fixture-v1", 115 "source_sha256": hashlib.sha256(json.dumps(raw_rows, sort_keys=True).encode("utf-8")).hexdigest(), 116 "python_version": platform.python_version(), 117 "unicode_version": unicodedata.unidata_version, 118 "split_policy": "sha256(conversation_id)[:8] modulo 100; 70/15/15 thresholds", 119 "rows_raw": len(raw_rows), 120 "rows_kept": len(kept), 121 "reject_reasons": dict(Counter(row["reason"] for row in rejected)), 122 "split_counts": dict(Counter(row["split"] for row in kept)), 123 "artifact_sha256": artifact_sha256, 124 "rejects_sha256": hashlib.sha256(rejects_path.read_bytes()).hexdigest(), 125 "pending_reviews": ["split_coverage", "locked_benchmark_overlap", "near_duplicates", "privacy_and_license"], 126} 127(output / "dev_tickets.v1.manifest.json").write_text( 128 json.dumps(manifest, indent=2, sort_keys=True) + "\n", 129 encoding="utf-8", 130) 131print("kept rows:", manifest["rows_kept"], "rejected:", manifest["reject_reasons"]) 132print("split counts:", manifest["split_counts"]) 133print("artifact digest prefix:", artifact_sha256[:16]) 134print("status:", manifest["status"])
Versioned artifact
1kept rows: 6 rejected: {'exact_duplicate': 1, 'invalid_label': 1, 'label_conflict': 2, 'cross_conversation_duplicate': 2} 2split counts: {'train': 3, 'validation': 2, 'test': 1} 3artifact digest prefix: ba384792199e9be7 4status: candidate_not_released

The manifest acts as a structured dataset datasheet (gebru2021). Gebru et al. propose datasheets that document motivation, composition, collection, and recommended uses so consumers can evaluate fit.

Notice the manifest explicitly tracks pending reviews: split coverage, benchmark overlap, near-duplicates, and privacy. Don't mark a dataset ready for release until those checks pass.

Verifying the candidate contract like downstream code

Downstream training code shouldn't blindly ingest files because they exist on disk. A production data loader executes pre-flight assertions before spending GPU compute:

  1. Zero group leakage: No conversation ID spans multiple splits.
  2. Deterministic split adherence: Every record matches the stable hash assignment.
  3. Cryptographic integrity: File bytes match artifact_sha256 and rejects_sha256 in the manifest.
  4. Structural validity: 100% of rows pass schema requirements.
  5. Fingerprint consistency: Recomputed SHA-256 fingerprints match row metadata.
  6. Accounting balance: kept + rejected == total raw rows.
08-verify-artifact.py
1import hashlib 2import json 3from collections import Counter, defaultdict 4 5rows = [json.loads(line) for line in rows_path.read_text(encoding="utf-8").splitlines()] 6saved_manifest = json.loads((output / "dev_tickets.v1.manifest.json").read_text(encoding="utf-8")) 7saved_rejects = [json.loads(line) for line in rejects_path.read_text(encoding="utf-8").splitlines()] 8groups = defaultdict(set) 9for row in rows: 10 groups[row["conversation_id"]].add(row["split"]) 11 12no_group_leakage = all(len(splits) == 1 for splits in groups.values()) 13split_rule_matches = all(row["split"] == stable_split(row["conversation_id"]) for row in rows) 14digest_matches = hashlib.sha256(rows_path.read_bytes()).hexdigest() == saved_manifest["artifact_sha256"] 15reject_digest_matches = hashlib.sha256(rejects_path.read_bytes()).hexdigest() == saved_manifest["rejects_sha256"] 16schema_valid = all(reject_reason(row) is None for row in rows) 17fingerprints_match = all( 18 row["text"] == normalize_text(row["text"]) 19 and row["text_sha256"] == text_fingerprint(row["text"]) 20 for row in rows 21) 22identities_unique = (len({row["ticket_id"] for row in rows}) == len(rows) 23 and len({row["text"] for row in rows}) == len(rows)) 24counts_match = ( 25 len(rows) == saved_manifest["rows_kept"] 26 and len(rows) + len(saved_rejects) == saved_manifest["rows_raw"] 27 and dict(Counter(row["split"] for row in rows)) == saved_manifest["split_counts"] 28 and dict(Counter(row["reason"] for row in saved_rejects)) == saved_manifest["reject_reasons"] 29) 30checks = { 31 "conversation and split rule": no_group_leakage and split_rule_matches, 32 "artifact and rejection digests": digest_matches and reject_digest_matches, 33 "row schema": schema_valid, 34 "recomputed fingerprints": fingerprints_match, 35 "unique IDs and normalized texts": identities_unique, 36 "manifest counts": counts_match, 37} 38for name, passed in checks.items(): 39 print(f"{name}: {passed}") 40if not all(checks.values()): 41 raise ValueError(f"candidate contract failed: {checks}")
Artifact contract check
1conversation and split rule: True 2artifact and rejection digests: True 3row schema: True 4recomputed fingerprints: True 5unique IDs and normalized texts: True 6manifest counts: True

Each assertion verifies an independent invariant: group isolation, byte identity, record structure, content fingerprints, uniqueness, and accounting. A loader that fails fast prevents silent corruption from invalidating an expensive training run.

Auditing distribution drift and split coverage

Even if every row passes structural checks, the dataset might still fail downstream needs. If the validation split contains zero escalate cases, validation accuracy can't evaluate whether your model detects high-priority incidents.

Always audit label coverage and feature distributions across splits:

09-split-coverage.py
1from collections import defaultdict 2 3required_labels = {"standard", "escalate"} 4coverage = defaultdict(set) 5for row in rows: 6 coverage[row["split"]].add(row["label"]) 7 8missing = { 9 split: sorted(required_labels - coverage[split]) 10 for split in ["train", "validation", "test"] 11 if required_labels - coverage[split] 12} 13print("coverage:", {split: sorted(values) for split, values in coverage.items()}) 14print("missing labels:", missing) 15print("label coverage passes:", not missing)
Coverage audit
1coverage: {'train': ['escalate'], 'validation': ['standard'], 'test': ['standard']} 2missing labels: {'train': ['standard'], 'validation': ['escalate'], 'test': ['escalate']} 3label coverage passes: False

This fixture fails label coverage: train contains only escalate examples while validation and test contain only standard examples.

In production, test distributions across partitions using statistical tests:

  • Categorical features: Chi-square test of independence or Population Stability Index (PSI).
  • Continuous features (e.g. sequence lengths): Two-sample Kolmogorov-Smirnov (KS) test or Wasserstein distance.

When a partition fails coverage, collect additional reviewed conversation groups or adopt stratified grouped splitting. Never cherry-pick individual records across group boundaries to artificially balance a split.

Tracking dataset versions across pipeline evolutions

Suppose pipeline v2 adds an annotation or updates the normalization regex. If model accuracy shifts, did the architecture improve, or did the underlying data change?

Assigning each dataset release an immutable SHA-256 digest and pipeline version makes performance shifts attributable:

11-version-comparison.py
1import hashlib 2import json 3 4def digest(rows: list[dict]) -> str: 5 payload = "\n".join(json.dumps(row, sort_keys=True) for row in rows) + "\n" 6 return hashlib.sha256(payload.encode("utf-8")).hexdigest() 7 8v1 = [{"text": "build missing", "label": "escalate"}] 9v2 = [*v1, {"text": "break-glass request broken", "label": "standard"}] 10 11print("v1:", digest(v1)[:12], "rows:", len(v1)) 12print("v2:", digest(v2)[:12], "rows:", len(v2)) 13print("version changed:", digest(v1) != digest(v2))
Version comparison
1v1: 6045599184e8 rows: 1 2v2: 551369eeb269 rows: 2 3version changed: True

Hugging Face Datasets computes an Arrow caching fingerprint over dataset state and each transformation step (huggingface_datasets). While Arrow caching manages local reuse, an immutable manifest digest binds a published dataset release to Git commit tags and experiment runs.

Mapping pipeline gates to enterprise AI systems

The mechanisms applied to our developer ticket fixture scale directly to foundation model pipelines:

Small lesson mechanismEnterprise AI and foundation model application
Ingestion schema gatePydantic strict schemas and Great Expectations suites reject malformed fine-tuning rows.
Unicode NFKC + casefoldStandardizes multilingual corpora and normalizes full-width tokens across web scrapes.
Conflicting label quarantineIsolates ambiguous human preferences before training Reward Models (RM) or Direct Preference Optimization (DPO).
Grouped split by conversationPrevents entity leakage across multi-turn sessions, document clusters, or patient records.
MinHash LSH near-deduplicationBroder's shingle MinHash filters trillions of duplicate web tokens across C4, Dolma, FineWeb, and Llama 3.
Benchmark decontaminationN-gram and log-probability overlap filters prevent test suite memorization across MMLU, GSM8K, and HumanEval.
PII detection and redactionPresidio NER and regex filters drop high-density PII documents and mask sensitive customer data.
Manifest and datasheetTracks cryptographic lineage, source snapshots, and pending audit reviews per release.
Distribution and drift auditEvaluates class balance and sequence length drift across train, validation, and test partitions.

Never quote an evaluation metric without naming the exact dataset artifact, split policy, and decontamination audit that produced it.

Controlled failure labs: break the pipeline on purpose

Run these targeted experiments against the runnable code blocks above to observe how specific assertions catch mutations:

  1. Simulate entity leakage: In 08-verify-artifact.py, append {**rows[0], "ticket_id": 999, "split": "test"} to rows before computing groups.
  2. Simulate bit rot or file tampering: In 08-verify-artifact.py, append a newline to rows_path after loading saved_manifest and before evaluating digest_matches.
  3. Evaluate boundary conditions in shingling: In 06-shingle-overlap.py, print jaccard("build", "build") and jaccard("", "").
  4. Violate grouped split integrity: In 04-stable-grouped-split.py, assign partitions by hashing str(row["ticket_id"]) rather than row["conversation_id"].

What each failure reveals

  1. no_group_leakage, identities_unique, and manifest counts fail simultaneously. Multiple assertions catch the corruption from different perspectives.
  2. digest_matches becomes False. Even an invisible trailing byte breaks cryptographic identity.
  3. A single matching word returns 1.0; two empty strings return 0.0. Blank strings must be caught by schema gates, not similarity estimators.
  4. Turns from conversation build-001 separate into different splits, causing entity leakage and inflating evaluation scores.

Carrying clean data into feature engineering

A dataset manifest gives reviewers verifiable lineage: raw sources enter, transformations and split boundaries act on them, and cryptographic digests lock the resulting bytes. Passing structural assertions is a prerequisite, not a guarantee that every evaluation requirement is met.

Clean partitions answer one core question: which records can meet during training and evaluation? Feature engineering introduces the next operational challenge: was each feature knowable at prediction time, and does it mean the same thing during live inference as it did during batch training?

Complete the lesson

Mastery Check

Answer every question, then check your score. Score 75% or higher to mark this lesson complete.

1.An ingestion contract receives a developer ticket export row where all four required columns exist, text is 'Database connection refused', and label is 'critical'. The reviewed production vocabulary allows only standard and escalate. What should the schema gate do?

Correct answer: Reject the row immediately as an invalid label before downstream normalization or splitting runs.

Ingestion schema contracts enforce domain validity. A label outside the approved vocabulary must be rejected at the boundary to prevent unreviewed supervision from silently corrupting training data.

2.A pipeline normalizes text with Unicode NFKC, case-folding, and whitespace collapse before generating SHA-256 fingerprints. Given messages Build is failing, BUILD is failing , and Build is not failing, what relationship holds among their fingerprints?

Correct answer: The first two share an identical fingerprint; the third produces a distinct hash because negation words are preserved.

NFKC, casefold, and whitespace collapse map cosmetic differences to identical bytes, giving messages 1 and 2 identical hashes. Conservative normalization preserves semantic tokens like 'not', keeping message 3 distinct.

3.Tickets 201 and 202 share identical normalized text. Ticket 201 is labeled escalate, while ticket 202 is labeled standard. Why is a 'first-in-wins' deduplication policy dangerous here?

Correct answer: It suppresses genuine supervision conflicts caused by annotation disagreement, shifting guidelines, or omitted features.

Silently keeping the first observed label hides real ambiguity. Identical text with conflicting labels indicates human annotation errors, policy changes, or missing context. The entire conflict group must be quarantined.

4.A support ticket dataset contains multi-turn customer conversations. An engineer splits rows randomly 80/20 into train and test sets. Why does this splitting strategy produce misleadingly high test metrics?

Correct answer: Turns from the same incident leak across train and test, allowing the model to memorize entity-specific tokens.

When multi-turn conversations split across partitions, the model encounters turns from the same incident in both train and test. It memorizes incident-specific server names and context, inflating test accuracy.

5.Why did OpenAI completely omit Wikipedia language-modeling benchmarks and the Children's Book Test from the official GPT-3 evaluation results?

Correct answer: A filtering bug allowed training data to heavily overlap with those benchmarks, turning evaluation into memorization recall.

Due to a filtering flaw, GPT-3's training data contained extensive overlaps with those benchmarks. Because retraining was impractical, the authors omitted the contaminated results rather than publishing inflated metrics.

6.Two support messages share 3 shingles out of 4 total unique shingles in their union. Their Jaccard similarity is 0.75, which crosses a 0.70 near-duplicate review threshold. What should the pipeline do?

Correct answer: Queue the pair for human review or policy inspection rather than deleting either row from the candidate set automatically.

High Jaccard similarity indicates near-duplicate candidates, not mathematical proof of redundancy. Automated deletion risks discarding legitimate distinct issues or valid template responses.

7.How does Broder's MinHash with Locality-Sensitive Hashing (LSH) make near-duplicate detection tractable across billions of web-crawl tokens?

Correct answer: It compresses documents into hash signatures and groups them into bands, reducing all-pairs quadratic comparisons to sub-quadratic candidate pairing.

Comparing all document pairs takes quadratic time. MinHash signatures estimate Jaccard similarity, and LSH banding hashes similar signatures into shared buckets, finding candidate pairs in sub-quadratic time.

8.An automated PII scanner detects an email address in ticket 502. The document contains no other sensitive entities. What is the recommended remediation under modern corpus curation practices?

Correct answer: Mask the email span with a typed surrogate token like <EMAIL> or route the record to review, preserving valid text.

For sparse PII, span-level redaction using typed surrogate tokens removes the sensitive data while retaining document utility. Document-level dropping is reserved for high PII density.

9.A downstream training job loads a published dataset JSONL file and its manifest. The recomputed SHA-256 digest of the JSONL file does not match the artifact_sha256 recorded in the manifest. What does this indicate?

Correct answer: The physical bytes of the data file have been altered, truncated, or corrupted since the manifest was originally signed.

A cryptographic digest check verifies byte-level integrity. If the recomputed hash differs from the manifest, the file contents have mutated, been truncated, or suffered corruption.

10.In foundation model data curation (e.g. FineWeb, Dolma, LLaMA), why do pipelines combine heuristic rules with KenLM perplexity filtering on web crawls?

Correct answer: Heuristics strip structural boilerplate and spam, while perplexity scoring against clean text discards incoherent prose.

Heuristic filters cheaply eliminate malformed documents, broken formatting, and repetition. Perplexity filtering against curated reference corpora (Wikipedia/books) removes low-quality, unnatural text at scale.

10 questions remaining.

Next Step
Continue to Feature Engineering for Production ML

You can now build a traceable candidate and distinguish passed checks from pending reviews. Next, make sure every prediction feature was knowable at decision time and means the same thing during training and live serving.

PreviousPyTorch Training Loops
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

unicodedata: Unicode Database

Python Software Foundation · 2026

https://docs.python.org/3/library/unicodedata.html

hashlib: Secure Hashes and Message Digests

Python Software Foundation · 2026

https://docs.python.org/3/library/hashlib.html

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.