
Nemo Curator
- 398 installs
- 11.2k repo stars
- Updated June 16, 2026
- orchestra-research/ai-research-skills
nemo-curator is a data curation skill that deduplicates large text corpora with exact, fuzzy, and semantic methods so developers avoid training or indexing redundant documents in fine-tuning and RAG pipelines.
About
nemo-curator is an Orchestra Research agent skill for ML engineers preparing training or RAG corpora who must remove duplicate and near-duplicate documents at scale. The skill configures NeMo Curator modules including ExactDuplicates with md5 or sha256 hashing, FuzzyDuplicates using MinHash plus LSH with configurable hash permutations, and semantic deduplication for paraphrased content. Exact deduplication runs roughly 16× faster on GPU versus CPU according to the guide. Developers reach for nemo-curator when fine-tuning datasets contain repeated crawled pages, RAG indexes return redundant chunks, or corpus size bloats storage and training cost. The workflow selects the right dedup tier by match type and corpus scale.
- Exact deduplication on id/text fields with MD5 or SHA256 hashing
- Fuzzy near-duplicate removal via MinHash + LSH with tunable Jaccard threshold (default 0.8)
- Semantic deduplication using sentence-transformers embeddings and cosine similarity
- Documented GPU speedups (~16× exact dedup vs CPU; fuzzy pass 120h → 7.5h on 8TB-scale workloads)
- Configurable fuzzy parameters: num_hashes 128–512 (default 260), num_buckets 10–50 (default 20)
Nemo Curator by the numbers
- 398 all-time installs (skills.sh)
- +35 installs in the week ending Jul 18, 2026 (Skillselion tracking)
- Ranked #499 of 2,066 Data Science & ML skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/orchestra-research/ai-research-skills --skill nemo-curatorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 398 |
|---|---|
| repo stars | ★ 11.2k |
| Security audit | 2 / 3 scanners passed |
| Last updated | June 16, 2026 |
| Repository | orchestra-research/ai-research-skills ↗ |
How do you deduplicate text corpora for LLM training?
Deduplicate large text corpora before fine-tuning or RAG so developers do not train or index redundant documents.
Who is it for?
ML engineers cleaning large text corpora before fine-tuning or RAG who need exact, fuzzy, and semantic deduplication at scale.
Skip if: Small hand-curated datasets where manual review suffices or teams needing real-time streaming dedup rather than batch corpus jobs.
When should I use this skill?
Training or RAG corpora contain duplicate crawled pages, near-duplicate paragraphs inflate dataset size, or GPU-accelerated batch dedup is required.
What you get
Deduplicated text dataset with exact, fuzzy, and semantic duplicate records removed and documented hash settings.
- deduplicated text dataset
- dedup configuration with hash and LSH settings
By the numbers
- Exact deduplication runs ~16× faster on GPU vs CPU
- FuzzyDuplicates supports 260 MinHash permutations for near-match detection
Files
NeMo Curator - GPU-Accelerated Data Curation
NVIDIA's toolkit for preparing high-quality training data for LLMs.
When to use NeMo Curator
Use NeMo Curator when:
- Preparing LLM training data from web scrapes (Common Crawl)
- Need fast deduplication (16× faster than CPU)
- Curating multi-modal datasets (text, images, video, audio)
- Filtering low-quality or toxic content
- Scaling data processing across GPU cluster
Performance:
- 16× faster fuzzy deduplication (8TB RedPajama v2)
- 40% lower TCO vs CPU alternatives
- Near-linear scaling across GPU nodes
Use alternatives instead:
- datatrove: CPU-based, open-source data processing
- dolma: Allen AI's data toolkit
- Ray Data: General ML data processing (no curation focus)
Quick start
Installation
# Text curation (CUDA 12)
uv pip install "nemo-curator[text_cuda12]"
# All modalities
uv pip install "nemo-curator[all_cuda12]"
# CPU-only (slower)
uv pip install "nemo-curator[cpu]"Basic text curation pipeline
from nemo_curator import ScoreFilter, Modify
from nemo_curator.datasets import DocumentDataset
import pandas as pd
# Load data
df = pd.DataFrame({"text": ["Good document", "Bad doc", "Excellent text"]})
dataset = DocumentDataset(df)
# Quality filtering
def quality_score(doc):
return len(doc["text"].split()) > 5 # Filter short docs
filtered = ScoreFilter(quality_score)(dataset)
# Deduplication
from nemo_curator.modules import ExactDuplicates
deduped = ExactDuplicates()(filtered)
# Save
deduped.to_parquet("curated_data/")Data curation pipeline
Stage 1: Quality filtering
from nemo_curator.filters import (
WordCountFilter,
RepeatedLinesFilter,
UrlRatioFilter,
NonAlphaNumericFilter
)
# Apply 30+ heuristic filters
from nemo_curator import ScoreFilter
# Word count filter
dataset = dataset.filter(WordCountFilter(min_words=50, max_words=100000))
# Remove repetitive content
dataset = dataset.filter(RepeatedLinesFilter(max_repeated_line_fraction=0.3))
# URL ratio filter
dataset = dataset.filter(UrlRatioFilter(max_url_ratio=0.2))Stage 2: Deduplication
Exact deduplication:
from nemo_curator.modules import ExactDuplicates
# Remove exact duplicates
deduped = ExactDuplicates(id_field="id", text_field="text")(dataset)Fuzzy deduplication (16× faster on GPU):
from nemo_curator.modules import FuzzyDuplicates
# MinHash + LSH deduplication
fuzzy_dedup = FuzzyDuplicates(
id_field="id",
text_field="text",
num_hashes=260, # MinHash parameters
num_buckets=20,
hash_method="md5"
)
deduped = fuzzy_dedup(dataset)Semantic deduplication:
from nemo_curator.modules import SemanticDuplicates
# Embedding-based deduplication
semantic_dedup = SemanticDuplicates(
id_field="id",
text_field="text",
embedding_model="sentence-transformers/all-MiniLM-L6-v2",
threshold=0.8 # Cosine similarity threshold
)
deduped = semantic_dedup(dataset)Stage 3: PII redaction
from nemo_curator.modules import Modify
from nemo_curator.modifiers import PIIRedactor
# Redact personally identifiable information
pii_redactor = PIIRedactor(
supported_entities=["EMAIL_ADDRESS", "PHONE_NUMBER", "PERSON", "LOCATION"],
anonymize_action="replace" # or "redact"
)
redacted = Modify(pii_redactor)(dataset)Stage 4: Classifier filtering
from nemo_curator.classifiers import QualityClassifier
# Quality classification
quality_clf = QualityClassifier(
model_path="nvidia/quality-classifier-deberta",
batch_size=256,
device="cuda"
)
# Filter low-quality documents
high_quality = dataset.filter(lambda doc: quality_clf(doc["text"]) > 0.5)GPU acceleration
GPU vs CPU performance
| Operation | CPU (16 cores) | GPU (A100) | Speedup |
|---|---|---|---|
| Fuzzy dedup (8TB) | 120 hours | 7.5 hours | 16× |
| Exact dedup (1TB) | 8 hours | 0.5 hours | 16× |
| Quality filtering | 2 hours | 0.2 hours | 10× |
Multi-GPU scaling
from nemo_curator import get_client
import dask_cuda
# Initialize GPU cluster
client = get_client(cluster_type="gpu", n_workers=8)
# Process with 8 GPUs
deduped = FuzzyDuplicates(...)(dataset)Multi-modal curation
Image curation
from nemo_curator.image import (
AestheticFilter,
NSFWFilter,
CLIPEmbedder
)
# Aesthetic scoring
aesthetic_filter = AestheticFilter(threshold=5.0)
filtered_images = aesthetic_filter(image_dataset)
# NSFW detection
nsfw_filter = NSFWFilter(threshold=0.9)
safe_images = nsfw_filter(filtered_images)
# Generate CLIP embeddings
clip_embedder = CLIPEmbedder(model="openai/clip-vit-base-patch32")
image_embeddings = clip_embedder(safe_images)Video curation
from nemo_curator.video import (
SceneDetector,
ClipExtractor,
InternVideo2Embedder
)
# Detect scenes
scene_detector = SceneDetector(threshold=27.0)
scenes = scene_detector(video_dataset)
# Extract clips
clip_extractor = ClipExtractor(min_duration=2.0, max_duration=10.0)
clips = clip_extractor(scenes)
# Generate embeddings
video_embedder = InternVideo2Embedder()
video_embeddings = video_embedder(clips)Audio curation
from nemo_curator.audio import (
ASRInference,
WERFilter,
DurationFilter
)
# ASR transcription
asr = ASRInference(model="nvidia/stt_en_fastconformer_hybrid_large_pc")
transcribed = asr(audio_dataset)
# Filter by WER (word error rate)
wer_filter = WERFilter(max_wer=0.3)
high_quality_audio = wer_filter(transcribed)
# Duration filtering
duration_filter = DurationFilter(min_duration=1.0, max_duration=30.0)
filtered_audio = duration_filter(high_quality_audio)Common patterns
Web scrape curation (Common Crawl)
from nemo_curator import ScoreFilter, Modify
from nemo_curator.filters import *
from nemo_curator.modules import *
from nemo_curator.datasets import DocumentDataset
# Load Common Crawl data
dataset = DocumentDataset.read_parquet("common_crawl/*.parquet")
# Pipeline
pipeline = [
# 1. Quality filtering
WordCountFilter(min_words=100, max_words=50000),
RepeatedLinesFilter(max_repeated_line_fraction=0.2),
SymbolToWordRatioFilter(max_symbol_to_word_ratio=0.3),
UrlRatioFilter(max_url_ratio=0.3),
# 2. Language filtering
LanguageIdentificationFilter(target_languages=["en"]),
# 3. Deduplication
ExactDuplicates(id_field="id", text_field="text"),
FuzzyDuplicates(id_field="id", text_field="text", num_hashes=260),
# 4. PII redaction
PIIRedactor(),
# 5. NSFW filtering
NSFWClassifier(threshold=0.8)
]
# Execute
for stage in pipeline:
dataset = stage(dataset)
# Save
dataset.to_parquet("curated_common_crawl/")Distributed processing
from nemo_curator import get_client
from dask_cuda import LocalCUDACluster
# Multi-GPU cluster
cluster = LocalCUDACluster(n_workers=8)
client = get_client(cluster=cluster)
# Process large dataset
dataset = DocumentDataset.read_parquet("s3://large_dataset/*.parquet")
deduped = FuzzyDuplicates(...)(dataset)
# Cleanup
client.close()
cluster.close()Performance benchmarks
Fuzzy deduplication (8TB RedPajama v2)
- CPU (256 cores): 120 hours
- GPU (8× A100): 7.5 hours
- Speedup: 16×
Exact deduplication (1TB)
- CPU (64 cores): 8 hours
- GPU (4× A100): 0.5 hours
- Speedup: 16×
Quality filtering (100GB)
- CPU (32 cores): 2 hours
- GPU (2× A100): 0.2 hours
- Speedup: 10×
Cost comparison
CPU-based curation (AWS c5.18xlarge × 10):
- Cost: $3.60/hour × 10 = $36/hour
- Time for 8TB: 120 hours
- Total: $4,320
GPU-based curation (AWS p4d.24xlarge × 2):
- Cost: $32.77/hour × 2 = $65.54/hour
- Time for 8TB: 7.5 hours
- Total: $491.55
Savings: 89% reduction ($3,828 saved)
Supported data formats
- Input: Parquet, JSONL, CSV
- Output: Parquet (recommended), JSONL
- WebDataset: TAR archives for multi-modal
Use cases
Production deployments:
- NVIDIA used NeMo Curator to prepare Nemotron-4 training data
- Open-source datasets curated: RedPajama v2, The Pile
References
- [Filtering Guide](references/filtering.md) - 30+ quality filters, heuristics
- [Deduplication Guide](references/deduplication.md) - Exact, fuzzy, semantic methods
Resources
- GitHub: https://github.com/NVIDIA/NeMo-Curator ⭐ 500+
- Docs: https://docs.nvidia.com/nemo-framework/user-guide/latest/datacuration/
- Version: 0.4.0+
- License: Apache 2.0
Deduplication Guide
Complete guide to exact, fuzzy, and semantic deduplication.
Exact deduplication
Remove documents with identical content.
from nemo_curator.modules import ExactDuplicates
# Exact deduplication
exact_dedup = ExactDuplicates(
id_field="id",
text_field="text",
hash_method="md5" # or "sha256"
)
deduped = exact_dedup(dataset)Performance: ~16× faster on GPU vs CPU
Fuzzy deduplication
Remove near-duplicate documents using MinHash + LSH.
from nemo_curator.modules import FuzzyDuplicates
fuzzy_dedup = FuzzyDuplicates(
id_field="id",
text_field="text",
num_hashes=260, # MinHash permutations (more = accurate)
num_buckets=20, # LSH buckets (more = faster, less recall)
hash_method="md5",
jaccard_threshold=0.8 # Similarity threshold
)
deduped = fuzzy_dedup(dataset)Parameters:
num_hashes: 128-512 (default 260)num_buckets: 10-50 (default 20)jaccard_threshold: 0.7-0.9 (default 0.8)
Performance: 16× faster on 8TB dataset (120h → 7.5h)
Semantic deduplication
Remove semantically similar documents using embeddings.
from nemo_curator.modules import SemanticDuplicates
semantic_dedup = SemanticDuplicates(
id_field="id",
text_field="text",
embedding_model="sentence-transformers/all-MiniLM-L6-v2",
embedding_batch_size=256,
threshold=0.85, # Cosine similarity threshold
device="cuda"
)
deduped = semantic_dedup(dataset)Models:
all-MiniLM-L6-v2: Fast, 384 dimsall-mpnet-base-v2: Better quality, 768 dims- Custom models supported
Comparison
| Method | Speed | Recall | Use Case |
|---|---|---|---|
| Exact | Fastest | 100% | Exact matches only |
| Fuzzy | Fast | ~95% | Near-duplicates (recommended) |
| Semantic | Slow | ~90% | Paraphrases, rewrites |
Best practices
1. Start with exact dedup - Remove obvious duplicates 2. Use fuzzy for large datasets - Best speed/quality trade-off 3. Semantic for high-value data - Expensive but thorough 4. GPU acceleration required - 10-16× speedup
Quality Filtering Guide
Complete guide to NeMo Curator's 30+ quality filters.
Text-based filters
Word count
from nemo_curator.filters import WordCountFilter
# Filter by word count
dataset = dataset.filter(WordCountFilter(min_words=50, max_words=100000))Repeated content
from nemo_curator.filters import RepeatedLinesFilter
# Remove documents with >30% repeated lines
dataset = dataset.filter(RepeatedLinesFilter(max_repeated_line_fraction=0.3))Symbol ratio
from nemo_curator.filters import SymbolToWordRatioFilter
# Remove documents with too many symbols
dataset = dataset.filter(SymbolToWordRatioFilter(max_symbol_to_word_ratio=0.3))URL ratio
from nemo_curator.filters import UrlRatioFilter
# Remove documents with many URLs
dataset = dataset.filter(UrlRatioFilter(max_url_ratio=0.2))Language filtering
from nemo_curator.filters import LanguageIdentificationFilter
# Keep only English documents
dataset = dataset.filter(LanguageIdentificationFilter(target_languages=["en"]))
# Multiple languages
dataset = dataset.filter(LanguageIdentificationFilter(target_languages=["en", "es", "fr"]))Classifier-based filtering
Quality classifier
from nemo_curator.classifiers import QualityClassifier
quality_clf = QualityClassifier(
model_path="nvidia/quality-classifier-deberta",
batch_size=256,
device="cuda"
)
# Filter low-quality (threshold > 0.5 = high quality)
dataset = dataset.filter(lambda doc: quality_clf(doc["text"]) > 0.5)NSFW classifier
from nemo_curator.classifiers import NSFWClassifier
nsfw_clf = NSFWClassifier(threshold=0.9, device="cuda")
# Remove NSFW content
dataset = dataset.filter(lambda doc: nsfw_clf(doc["text"]) < 0.9)Heuristic filters
Full list of 30+ filters:
- WordCountFilter
- RepeatedLinesFilter
- UrlRatioFilter
- SymbolToWordRatioFilter
- NonAlphaNumericFilter
- BulletsFilter
- WhiteSpaceFilter
- ParenthesesFilter
- LongWordFilter
- And 20+ more...
Best practices
1. Apply cheap filters first - Word count before GPU classifiers 2. Tune thresholds on sample - Test on 10k docs before full run 3. Use GPU classifiers sparingly - Expensive but effective 4. Chain filters efficiently - Order by cost (cheap → expensive)
Related skills
How it compares
Choose nemo-curator for GPU-scale batch text dedup pipelines rather than single-file regex or manual spreadsheet deduplication.
FAQ
What deduplication methods does nemo-curator support?
nemo-curator supports NeMo Curator exact deduplication via md5 or sha256 hashing, fuzzy deduplication with MinHash and LSH, and semantic deduplication for paraphrased content. Each tier targets a different duplicate similarity level.
How fast is NeMo Curator exact deduplication on GPU?
The nemo-curator guide reports exact deduplication runs roughly 16× faster on GPU versus CPU. GPU acceleration matters most on large crawled corpora before fine-tuning or RAG index builds.
Is Nemo Curator safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.