2025-02-08 · 3 min read

Auditing Dataset Quality Before Training

An audit catches dataset problems before you invest compute hours. The good ones are quick and signal obvious issues.

Three levels of validation

Format validation: JSON schema, required fields, template test.

Statistical audit: Token length distribution, class balance, duplication rate.

Human review sample: 20–30 random examples, sanity check on quality.

Format validation

import json
from datasets import load_dataset

def validate_format(jsonl_path, required_fields=None):
    """Check JSONL is valid JSON and has required fields."""
    if required_fields is None:
        required_fields = ["output"]

    errors = []
    for i, line in enumerate(open(jsonl_path)):
        try:
            obj = json.loads(line)
            for field in required_fields:
                if field not in obj:
                    errors.append(f"Line {i}: missing '{field}'")
        except json.JSONDecodeError as e:
            errors.append(f"Line {i}: invalid JSON: {e}")

    if errors:
        print(f"Format errors (first 10):")
        for err in errors[:10]:
            print(f"  {err}")
        return False

    print(f"✓ Format validation passed ({i+1} records)")
    return True

validate_format("data.jsonl", required_fields=["instruction", "output"])

Statistical audit

from transformers import AutoTokenizer
import json
import numpy as np

def audit_dataset(jsonl_path, model_name="Qwen/Qwen2.5-7B-Instruct"):
    """Compute token distribution and summary stats."""
    tokenizer = AutoTokenizer.from_pretrained(model_name)

    records = [json.loads(line) for line in open(jsonl_path)]
    lengths = []

    for record in records:
        text = record.get("output", "")
        tokens = len(tokenizer.encode(text))
        lengths.append(tokens)

    lengths = np.array(lengths)

    print("Token length statistics:")
    print(f"  Count: {len(lengths)}")
    print(f"  Mean: {lengths.mean():.0f}")
    print(f"  Median (p50): {np.percentile(lengths, 50):.0f}")
    print(f"  p95: {np.percentile(lengths, 95):.0f}")
    print(f"  p99: {np.percentile(lengths, 99):.0f}")
    print(f"  Max: {lengths.max()}")

    # Warn about outliers
    p99_val = np.percentile(lengths, 99)
    outliers = sum(lengths > p99_val * 1.5)
    if outliers > len(lengths) * 0.05:
        print(f"  ⚠ {outliers} examples ({100*outliers/len(lengths):.1f}%) are extreme outliers")

    # Check for duplicates
    seen = set()
    dupes = 0
    for record in records:
        key = record.get("output", "")
        if key in seen:
            dupes += 1
        seen.add(key)

    if dupes > 0:
        print(f"  ⚠ {dupes} duplicate outputs detected ({100*dupes/len(records):.1f}%)")

    return {
        "count": len(lengths),
        "mean_tokens": lengths.mean(),
        "max_tokens": lengths.max(),
        "duplicates": dupes,
    }

audit_dataset("data.jsonl")

Example output:

Token length statistics:
  Count: 450
  Mean: 125
  Median (p50): 110
  p95: 280
  p99: 450
  Max: 2847
  ⚠ 8 examples (1.8%) are extreme outliers

Human review sample

import json
import random

def sample_for_review(jsonl_path, n=20):
    """Draw n random examples for manual inspection."""
    records = [json.loads(line) for line in open(jsonl_path)]
    sample = random.sample(records, min(n, len(records)))

    for i, record in enumerate(sample, 1):
        print(f"\n=== Sample {i} ===")
        print(f"Input: {record.get('instruction', '')}")
        print(f"Output: {record.get('output', '')[:200]}...")

sample_for_review("data.jsonl", n=20)

Red flags and fixes

Red flag Possible cause Fix
p99 tokens >> p95 Long tail outliers Truncate at p95; filter > 2000 tokens
> 5% duplicates Poor dedup Run dedup again; check keys used
Max tokens > 4000 Out-of-context examples Filter to max 2048 tokens; split long texts
Very short p50 Sparse outputs Check if expected; may be fine for classification
Biased class distribution Imbalanced labels Undersample majority class or oversample minority

PII check

import re
import json

def find_pii(jsonl_path):
    """Detect email, phone, names (basic)."""
    email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
    phone_pattern = r'\b\d{3}[-.\s]?\d{3}[-.\s]?\d{4}\b'

    issues = []
    for i, line in enumerate(open(jsonl_path)):
        record = json.loads(line)
        text = record.get("output", "") + " " + record.get("instruction", "")

        if re.search(email_pattern, text):
            issues.append(f"Line {i}: email detected")
        if re.search(phone_pattern, text):
            issues.append(f"Line {i}: phone number detected")

    if issues:
        print(f"⚠ PII detected ({len(issues)} records):")
        for issue in issues[:5]:
            print(f"  {issue}")
    else:
        print("✓ No obvious PII found")

find_pii("data.jsonl")

Complete audit script

python audit.py \
  --input data.jsonl \
  --model Qwen/Qwen2.5-7B-Instruct \
  --sample-size 20

The script returns a JSON summary:

{
  "format_valid": true,
  "total_records": 450,
  "mean_tokens": 125,
  "max_tokens": 2847,
  "duplicates": 0,
  "pii_detected": false,
  "outliers": 8,
  "warnings": []
}

When to stop auditing

Audit stops when: - Format is valid - No obvious PII - Length distribution is reasonable (no 90% of examples being outliers) - Duplicate rate < 5%

Do not spend days perfecting the dataset before a single training run. Train, evaluate, then decide if more data prep is needed.