Downsamples documents with no entity labels while keeping all positive samples, balancing class distribution in NER training without discarding entity-bearing examples.
Scanned 9/12/2026
Install to Claude Code
npx -y skills add wenmin-wu/ds-skills --skill negative-sample-downsampling --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Negative Sample Downsampling?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/wenmin-wu-negative-sample-downsampling)More formats (shields.io, HTML) on the badges page.
---
name: nlp-negative-sample-downsampling
description: >
Downsamples documents with no entity labels while keeping all positive samples, balancing class distribution in NER training without discarding entity-bearing examples.
---
# Negative Sample Downsampling
## Overview
In NER datasets, most documents contain no entities — in PII detection, 70-90% of essays are clean. Training on all of them wastes compute on easy negatives and biases the model toward predicting O (non-entity). Downsampling negative documents to 20-33% of their original count while keeping all positive documents rebalances the training set. This is simpler than token-level class weighting and more effective — the model sees more entity examples per epoch, improving recall by 2-5%.
## Quick Start
```python
import random
def downsample_negatives(data, keep_ratio=0.2, entity_label='O'):
"""Keep all positive docs, downsample negative docs."""
positives = []
negatives = []
for doc in data:
labels = doc['labels'] if isinstance(doc['labels'], list) else doc['labels'].tolist()
if set(labels) != {entity_label}:
positives.append(doc)
else:
negatives.append(doc)
n_keep = int(len(negatives) * keep_ratio)
random.shuffle(negatives)
sampled_negatives = negatives[:n_keep]
print(f"Positives: {len(positives)}, Negatives: {len(negatives)} -> {n_keep}")
return positives + sampled_negatives
# Alternative: filter function for HuggingFace datasets
def filter_no_entity(example, keep_ratio=0.2):
has_entity = set(example['labels']) != {'O'}
return has_entity or (random.random() < keep_ratio)
dataset = dataset.filter(filter_no_entity)
```
## Workflow
1. Split documents into positive (contains entities) and negative (all O)
2. Keep all positive documents
3. Randomly sample `keep_ratio` of negative documents
4. Combine and shuffle for training
## Key Decisions
- **keep_ratio**: 0.2-0.33 is typical; too low causes false positives on clean documents
- **Per-epoch resampling**: Resample negatives each epoch for diversity
- **vs class weights**: Downsampling is simpler and works at document level; class weights work at token level
- **Validation**: Do NOT downsample validation set — evaluate on the true distribution
## References
- [DeBERTa3base Training](https://www.kaggle.com/code/valentinwerner/915-deberta3base-training)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!