adversarial-validation-kaggle
Skill5 starsChanged 34 days agoMITView on GitHub
Correct implementation of adversarial validation for Kaggle competitions. Use when: (1) Selecting training samples that match test distribution, (2) Reducing synthetic data artifacts by filtering, (3) Preparing data subsets for better generalization. Covers train vs test classification, sample selection method, and common pitfalls.
In this repository63 other files it gives its agents.
- AGENTS.mdAGENTS.md
- agent-nurture-frameworkSkill
- agent-session-memory-protocolSkill
- arxiv-paper-searchSkill
- autogluon-firstSkill
- autogluon-preset-strategySkill
- autogluon-timeseries-strategySkill
- catboost-first-tabularSkill
- claudeceptionSkill
- code-competition-artifact-pipelineSkill
- competition-orchestration-multimodelSkill
- context-engineeringSkill
- controlled-submission-experimentSkill
- cross-competition-feature-transferSkill
- cv-lb-gap-acknowledgmentSkill
- domain-knowledge-constraints-trapSkill
- ensemble-model-correlation-trapSkill
- feature-engineering-saturation-detectionSkill
- gpu-readiness-assessmentSkill
- gsd-loop-engineeringSkill
- kaggle-cognitive-cost-optimizationSkill
- kaggle-competition-best-practicesSkill
- kaggle-competition-type-strategySkill
- kaggle-data-format-firstSkill
- kaggle-discussion-searchSkill
- kaggle-experiment-sopSkill
- kaggle-oof-lb-validation-protocolSkill
- kaggle-optimal-blendingSkill
- kaggle-submission-format-by-metricSkill
- kaggle-top-performer-replicationSkill
- knowledge-crystallization-feedback-loopSkill
- ladder-drift-and-meta-aware-regressionSkill
- learned-value-beats-heuristic-augmentationSkill
- llm-competition-pipelineSkill
- mcts-pipeline-searchSkill
- mem0-dynamic-graph-memorySkill
- memory-hierarchy-managementSkill
- ml-sweet-spot-principleSkill
- model-ensemble-negative-weight-effectSkill
- multi-agent-rolesSkill
- multi-level-aggregation-overfittingSkill
- okf-visualize-knowledgeSkill
- onnx-minimal-network-designSkill
- per-category-modeling-backfireSkill
- progressive-verification-debuggingSkill
- ptcg-2ply-bounded-searchSkill
- ptcg-bc-large-model-submissionSkill
- retail-eda-frameworkSkill
- rmsle-zero-threshold-asymmetrySkill
- runtime-reflexionSkill
- sc-tir-mathematical-reasoningSkill
- self-evolving-skillsSkill
- skill-governance-and-automationSkill
- spatiotemporal-graph-feature-engineeringSkill
- store-sales-darts-chronos-blendSkill
- tabular-polynomial-features-breakthroughSkill
- three-layer-wisdom-extractionSkill
- time-series-walk-forward-validationSkill
- trueskill-simulation-competition-strategySkill
- ts-day-specific-forecastingSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 6 are distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 137
If using external/real data (e.g., UCI dataset):
- Distinctive: line 181
Wrong: Use percentile threshold
- Distinctive: line 244
After implementing adversarial validation, check:
- Distinctive: line 250
Score Distribution: Should be roughly normal, not bimodal
- Distinctive: line 258
LB vs OOF Gap: Should decrease after adversarial validation
- Distinctive: line 286
Rule of thumb: Start with 27% (17K from 63K), adjust based on:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Adversarial Validation Implementation
Problem
Adversarial validation is a powerful technique for improving model generalization, but it's frequently implemented incorrectly. The wrong approach can lead to worse performance instead of better.
Real Case: Initial implementation used "train vs real data" (wrong) and "percentile-based filtering" (wrong), resulting in 0.90457 AUC vs baseline 0.95513. Corrected implementation (train vs test, sort-based) achieved 0.97058 AUC.
Performance Impact:
- Wrong method (train vs UCI): 0.90457 AUC (-5% vs baseline)
- Correct method (train vs test): 0.97058 AUC (+1.5% vs baseline)
Context / Trigger Conditions
Use adversarial validation when:
- Working with Kaggle Playground Series (synthetic data)
- Training and test distributions may differ
- Need to improve model generalization
- OOF score is good but LB score drops
Symptoms you need adversarial validation:
- High OOF AUC (>0.95) but much lower LB AUC
- Model overfits training distribution
- Synthetic data has GAN artifacts
- Large dataset (>100K) with potential low-quality samples
Common misconceptions:
- ❌ "Adversarial validation distinguishes real vs synthetic data"
- ✅ "Adversarial validation distinguishes training vs test distribution"
Solution
Core Principle
Goal: Identify and keep training samples that are most similar to the test set. This ensures your model trains on data that matches the evaluation distribution.
Step 1: Prepare Adversarial Dataset
# Combine train and test sets
adv_train = train[features].copy()
adv_train['is_test'] = 0 # Label: training set
adv_test = test[features].copy()
adv_test['is_test'] = 1 # Label: test set
adv_combined = pd.concat([adv_train, adv_test], axis=0, ignore_index=True)Critical: Distinguish train vs test, NOT train vs real/external data!
Step 2: Train Adversarial Classifier
from sklearn.model_selection import StratifiedKFold
import lightgbm as lgb
lgb_params = {
'objective': 'binary',
'metric': 'auc',
'num_leaves': 31,
'max_depth': 6,
'learning_rate': 0.05,
'n_estimators': 500,
'verbosity': -1,
'n_jobs': 10
}
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
oof_pred = np.zeros(len(adv_combined))
for tr, val in skf.split(adv_combined, adv_combined['is_test']):
train_data = lgb.Dataset(adv_combined.iloc[tr][features],
label=adv_combined.iloc[tr]['is_test'])
val_data = lgb.Dataset(adv_combined.iloc[val][features],
label=adv_combined.iloc[val]['is_test'])
model = lgb.train(lgb_params, train_data, num_boost_round=500,
valid_sets=[val_data],
callbacks=[lgb.early_stopping(stopping_rounds=50)])
oof_pred[val] = model.predict(adv_combined.iloc[val][features])
auc_score = roc_auc_score(adv_combined['is_test'], oof_pred)
print(f"Adversarial AUC: {auc_score:.5f}")AUC Interpretation:
- AUC ≈ 0.50: Train and test distributions are very similar (good!)
- AUC ≈ 0.60-0.70: Some differences exist (filtering helps)
- AUC > 0.80: Significant distribution shift (filtering critical)
Step 3: Score and Select Training Samples
# Predict "test-likeness" for all training samples
train_copy = train.copy()
train_copy['test_likeness'] = model.predict(train[features])
# CORRECT METHOD: Sort by score and take top N samples
# This directly selects the N most "test-like" training samples
TARGET_SAMPLES = 16000 # Adjust based on your needs
train_sorted = train_copy.sort_values('test_likeness', ascending=False)
train_purified = train_sorted.head(TARGET_SAMPLES).copy()
print(f"Selected: {len(train_purified)} samples")
print(f"Score range: [{train_purified['test_likeness'].min():.4f}, "
f"{train_purified['test_likeness'].max():.4f}]")WRONG METHOD (Do NOT use):
# ❌ Percentile-based filtering
threshold = np.percentile(train_copy['test_likeness'], 60)
train_filtered = train_copy[train_copy['test_likeness'] > threshold]Why wrong: Percentile method keeps samples above a threshold, which may include many medium-scoring samples and exclude high-scoring ones if distribution is skewed.
Step 4: Add External Data (Optional)
If using external/real data (e.g., UCI dataset):
# Add AFTER adversarial filtering
original_data = pd.read_csv('uci_dataset.csv')
combined_train = pd.concat([train_purified, original_data], axis=0)
print(f"Final dataset: {len(combined_train)} samples")
print(f" - Purified synthetic: {len(train_purified)}")
print(f" - Original data: {len(original_data)}")Step 5: Verify Results
# Compare before and after
oof_before = train_model(train[features], y)
oof_after = train_model(combined_train[features], combined_y)
print(f"Before adversarial: {oof_before:.5f}")
print(f"After adversarial: {oof_after:.5f}")
print(f"Improvement: {oof_after - oof_before:.5f}")Common Pitfalls
Pitfall 1: Wrong Classification Target
Wrong: Distinguish train (synthetic) vs UCI (real data)
# ❌ This removes "synthetic-looking" samples
adv_train['is_real'] = 0
adv_uci['is_real'] = 1Correct: Distinguish train vs test
# ✅ This identifies "test-like" samples
adv_train['is_test'] = 0
adv_test['is_test'] = 1Pitfall 2: Percentile-Based Filtering
Wrong: Use percentile threshold
# ❌ May exclude high-scoring samples
threshold = np.percentile(train_scores, 60)
train_filtered = train[train_scores > threshold]Correct: Sort and take top N
# ✅ Guaranteed to keep highest-scoring samples
TARGET_SAMPLES = 16000
train_filtered = train.sort_values('test_likeness', ascending=False).head(TARGET_SAMPLES)Pitfall 3: Incorrect Sample Size Estimation
Example Error: "17万 samples" (170,000) vs Actual "17,000 samples"
Symptoms:
- Results don't match paper/guide despite correct implementation
- Sample size confusion between 17K and 170K
Prevention:
# Always verify sample size from source
# "17万" could mean:
# - 170,000 (十七万)
# - 17,000 (一万七千)
# Check PDF/guide for exact number and context
# Look for: "Training data: 17,303 samples" (not 170,303)Pitfall 4: Filtering Before Target Encoding
Wrong order:
- Adversarial validation → filter
- Target encoding on filtered data
- Add external data
Correct order:
- Adversarial validation → filter
- Add external data
- Target encoding on combined data
Why: Target encoding statistics should include external data for better estimates.
Performance Comparison
Real Case Study (Kaggle S6E2 Heart Disease):
| Method | OOF AUC | vs Baseline |
| Baseline (630K all data) | 0.95513 | - |
| Train vs UCI (wrong target) | 0.90457 | -5.1% ❌ |
| Train vs Test (percentile) | 0.95098 | -0.4% ❌ |
| Train vs Test (sort, 170K) | 0.95551 | +0.04% ⚠️ |
| Train vs Test (sort, 17K) | 0.97058 | +1.6% ✅ |
Key Insight: Small sample size (17K) with correct method beats large sample (170K or 630K) with wrong method.
Verification
After implementing adversarial validation, check:
- AUC Score: Adversarial AUC should be 0.50-0.70 (not 0.99+)
- 0.99+ indicates you're distinguishing wrong categories
- 0.50 indicates train/test already similar
- Score Distribution: Should be roughly normal, not bimodal
print(f"Mean: {train_scores.mean():.4f}") print(f"Median: {train_scores.median():.4f}") print(f"Min: {train_scores.min():.4f}") print(f"Max: {train_scores.max():.4f}") - LB vs OOF Gap: Should decrease after adversarial validation
gap_before = oof_score - lb_score gap_after = oof_score_purified - lb_score_purified print(f"Gap reduced by: {gap_before - gap_after:.5f}")
Notes
When Adversarial Validation Works Best
Ideal conditions:
- Large synthetic dataset (>100K samples)
- Suspected GAN artifacts or distribution shift
- High OOF but low LB (overfitting to train distribution)
Less effective when:
- Small dataset (<10K samples)
- Train/test already well-matched (adversarial AUC ≈ 0.50)
- Using cross-validation correctly (already reduces overfitting)
Sample Size Selection
Guidelines for choosing TARGET_SAMPLES:
- Large datasets (>500K): 10-30% (50K-150K)
- Medium datasets (100K-500K): 20-40% (20K-200K)
- Small datasets (<100K): 50-80% (50K-80K)
Rule of thumb: Start with 27% (17K from 63K), adjust based on:
- Adversarial AUC (higher → more aggressive filtering)
- Dataset quality (lower quality → more filtering)
- Computational budget (smaller → faster training)
Integration with Other Techniques
Adversarial validation works well with:
- Target encoding: Apply after filtering (on combined data)
- Feature selection: More effective on purified data
- Ensemble methods: Stacking benefits from consistent train/test distribution
Does NOT replace:
- Cross-validation: Still needed for model evaluation
- Feature engineering: Still need domain-specific features
- Hyperparameter tuning: Still need to optimize model parameters
References
Verified On:
- Kaggle Playground Series S6E2 (Heart Disease)
- XGBoost, LightGBM, CatBoost (all compatible)
- Datasets: 630K → 17K samples (36.5x reduction)
Real-World Validation:
- Baseline: 0.95513 OOF AUC (630K samples)
- Adversarial (17K): 0.97058 OOF AUC (+1.55%)
- LB improvement: Expected +0.01-0.02 over baseline
Related Skills:
- ml-research-validation: Validating research claims
- kaggle-playground-external-data-validation: External data integration
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

