domain-knowledge-constraints-trap
Skill5 starsChanged 35 days agoMITView on GitHub
Avoid applying domain knowledge constraints that hurt ML model performance. Use when: (1) Considering medical/physical/logical constraints on training data, (2) Adversarial validation AUC changes significantly after applying constraints (>0.10 shift), (3) CV score drops after adding "reasonable" domain rules, (4) Train/test distributions become misaligned after filtering.
In this repository63 other files it gives its agents.
- AGENTS.mdAGENTS.md
- adversarial-validation-kaggleSkill
- 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
- 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
4 rules. About 0 are common, 4 are distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
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 93
If you MUST apply constraints:
- Distinctive: line 265
If you filter training data but not test data
- Distinctive: line 285
Don't change adversarial AUC significantly
- Distinctive: line 295
Instead of filtering, consider:
≈ “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
Domain Knowledge Constraints Trap
Problem
Applying domain knowledge "constraints" or "rules" to filter training data can seem like a good idea for improving model quality, but often destroys model performance by changing the data distribution and removing valuable information.
Real Case: Adding 4 medical constraints to heart disease prediction:
- Expected: Improve model with medical domain knowledge
- Actual: CV AUC dropped from 0.970 to 0.943 (-2.8%)
- Root cause: Adversarial AUC changed from 0.501 → 0.661 (distribution shift)
Context / Trigger Conditions
Use this skill when:
- Working on ML projects with domain experts providing "rules"
- Considering filtering data based on physical/medical/logical constraints
- Adversarial validation shows train/test distribution change after filtering
- CV score decreases after adding "reasonable" constraints
Symptoms:
- Domain experts say "this data point is impossible"
- Filtering "anomalies" or "outliers" based on domain rules
- CV score drops after adding constraints
- Train/test distributions become different (adversarial AUC ≠ 0.5)
Common Trap Examples:
- Medical: "Heart rate can't exceed 220 - age" → Removes valid extreme cases
- Physical: "Temperature can't be negative" → Removes sensor errors AND valid extremes
- Business: "Customer can't spend >$10K/month" → Removes high-value outliers
- Temporal: "Events can't happen in the future" → Removes data entry errors AND valid edge cases
Solution
Step 1: Quantify Distribution Impact
Before applying constraints, check adversarial validation:
from sklearn.model_selection import StratifiedKFold
import lightgbm as lgb
from sklearn.metrics import roc_auc_score
def check_adversarial_auc(train_df, test_df, features):
"""Check if train/test distributions are similar"""
adv_train = train_df[features].copy()
adv_train['is_test'] = 0
adv_test = test_df[features].copy()
adv_test['is_test'] = 1
adv_combined = pd.concat([adv_train, adv_test], axis=0)
# Train classifier to distinguish train vs test
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({'objective': 'binary', 'verbosity': -1},
train_data, num_boost_round=100,
valid_sets=[val_data],
callbacks=[lgb.early_stopping(stopping_rounds=10)])
oof_pred[val] = model.predict(adv_combined.iloc[val][features])
auc = roc_auc_score(adv_combined['is_test'], oof_pred)
return auc
# Before constraints
auc_before = check_adversarial_auc(train, test, features)
print(f"Adversarial AUC (before): {auc_before:.5f}")
# Output: 0.50111 ≈ 0.5 (distributions are similar) ✅Step 2: Apply Constraints Cautiously
If you MUST apply constraints:
def apply_constraints_with_validation(df, constraints):
"""Apply constraints and measure impact"""
df_filtered = df.copy()
removed = []
for constraint_name, constraint_func in constraints:
before = len(df_filtered)
df_filtered = constraint_func(df_filtered)
after = len(df_filtered)
removed_count = before - after
removed.append((constraint_name, removed_count))
print(f"{constraint_name}: removed {removed_count} samples")
total_removed = len(df) - len(df_filtered)
print(f"Total removed: {total_removed} ({100*total_removed/len(df):.1f}%)")
# Check adversarial AUC after filtering
auc_after = check_adversarial_auc(df_filtered, test, features)
print(f"Adversarial AUC (after): {auc_after:.5f}")
# Warn if distribution changed significantly
if abs(auc_after - 0.5) > 0.10:
print("⚠️ WARNING: Distribution changed significantly!")
print(" Constraints may be removing test-like samples.")
print(" Consider NOT applying these constraints.")
return df_filtered, removedStep 3: Validate Impact on Model Performance
Always train with and without constraints to compare:
# Train WITHOUT constraints (baseline)
model_baseline = train_model(train, features)
cv_baseline = cross_val_score(model_baseline, X, y, cv=5, scoring='roc_auc')
print(f"CV (no constraints): {cv_baseline.mean():.5f}")
# Apply constraints
train_constrained, removal_stats = apply_constraints_with_validation(train, constraints)
# Train WITH constraints
model_constrained = train_model(train_constrained, features)
cv_constrained = cross_val_score(model_constrained,
X_constrained, y_constrained,
cv=5, scoring='roc_auc')
print(f"CV (with constraints): {cv_constrained.mean():.5f}")
# Compare
if cv_constrained.mean() < cv_baseline.mean():
print("❌ Constraints HURT performance!")
print(f" Drop: {cv_baseline.mean() - cv_constrained.mean():.5f}")
else:
print("✅ Constraints HELP performance")Step 4: Decision Framework
Should you apply domain constraints?
| Condition | Action |
| Adversarial AUC change < 0.05 | ✅ Safe to apply |
| Adversarial AUC change 0.05-0.10 | ⚠️ Proceed with caution |
| Adversarial AUC change > 0.10 | ❌ DO NOT apply |
| CV score drops | ❌ DO NOT apply |
| Removes >20% of data | ❌ DO NOT apply |
| Removes >5% of data | ⚠️ Validate carefully |
Real Example:
Adversarial AUC before constraints: 0.501 ✅
Adversarial AUC after constraints: 0.661 ❌ (Distribution changed!)
CV before constraints: 0.970
CV after constraints: 0.943 (-2.8%)
Conclusion: DO NOT apply these medical constraintsVerification
Good Signs (constraints are safe):
- Adversarial AUC stays close to 0.5 (±0.05)
- CV score improves or stays same
- Removed samples are true errors (verified by domain expert)
- Train/test distributions remain similar
Bad Signs (constraints are harmful):
- Adversarial AUC moves away from 0.5 (>0.10)
- CV score drops significantly
- Many "edge case" samples are removed
- Train/test distributions become different
Example
Medical Constraints for Heart Disease:
def apply_medical_constraints(df):
"""Apply 4 medical domain constraints"""
df_filtered = df.copy()
# Constraint 1: Max HR ≤ 220 - Age
valid_hr = df_filtered['Max HR'] <= (220 - df_filtered['Age'])
removed_1 = (~valid_hr).sum()
df_filtered = df_filtered[valid_hr]
print(f"Heart rate constraint: removed {removed_1} samples")
# Constraint 2: Young low-risk patients don't have high vessel count
young_high_risk = (
(df_filtered['Age'] < 40) &
(df_filtered['Vessels'] >= 3) &
(df_filtered['Cholesterol'] < 160)
)
removed_2 = young_high_risk.sum()
df_filtered = df_filtered[~young_high_risk]
print(f"Young high-risk constraint: removed {removed_2} samples")
# Constraint 3: ST depression consistency
st_inconsistent = (
(df_filtered['ST_depression'] > 2) &
(df_filtered['ST_slope'] == 1)
)
removed_3 = st_inconsistent.sum()
df_filtered = df_filtered[~st_inconsistent]
print(f"ST consistency constraint: removed {removed_3} samples")
# Constraint 4: Thallium-Vessels consistency
thal_vessels_inconsistent = (
(df_filtered['Thallium'] == 7) &
(df_filtered['Vessels'] > 0)
)
removed_4 = thal_vessels_inconsistent.sum()
df_filtered = df_filtered[~thal_vessels_inconsistent]
print(f"Thallium-Vessels constraint: removed {removed_4} samples")
total_removed = len(df) - len(df_filtered)
print(f"Total: {total_removed} samples removed ({100*total_removed/len(df):.1f}%)")
return df_filtered
# Apply constraints
train_constrained = apply_medical_constraints(train)
# Check impact on distribution
auc_before = 0.501 # Train/test are similar
auc_after = check_adversarial_auc(train_constrained, test, features)
# Result: 0.661 ❌ Distribution changed!
# Check impact on model performance
cv_before = 0.96998
cv_after = 0.94258 # Dropped by 0.027!
# Conclusion: Medical constraints are HARMFUL
# Decision: DO NOT use themNotes
Why Domain Constraints Can Fail
1. Edge Cases Are Real
- "Impossible" values may be valid edge cases
- Removing them hurts model's ability to handle extremes
- Example: High heart rate in athlete with heart condition
2. Train/Test Mismatch
- If you filter training data but not test data
- Model trains on "clean" data but tested on "real" data
- Performance drops due to distribution shift
3. Information Loss
- Even "impossible" values contain signal
- They help model learn decision boundaries
- Removing them reduces model capacity
4. Over-Filtering
- Domain experts are often too conservative
- "That can't be right" → data entry error
- But it's actually a rare but valid case
When Constraints ARE Appropriate
Safe to use:
- Clear data entry errors (e.g., negative age)
- Impossible physical values (e.g., heart rate = 1000)
- Verified with domain experts AND validated empirically
- Don't change adversarial AUC significantly
Require validation:
- "Unusual but possible" values
- Removing >5% of data
- Change train/test distribution
- Drop CV performance
Alternatives to Hard Constraints
Instead of filtering, consider:
1. Weighting Examples
# Down-weight outliers instead of removing
sample_weights = np.ones(len(train))
sample_weights[outliers] = 0.5 # Down-weight, don't remove
model.fit(X, y, sample_weight=sample_weights)2. Robust Models
# Use models that handle outliers well
from sklearn.ensemble import HistGradientBoostingClassifier
model = HistGradientBoostingClassifier() # More robust to outliers3. Feature Engineering
# Add indicator for "edge case" instead of filtering
train['is_unusual'] = (
(train['Max HR'] > (220 - train['Age'])) |
(train['Cholesterol'] > 400)
).astype(int)
# Model learns to handle unusual cases4. Post-Processing Rules
# Apply domain rules AFTER prediction, not before
pred = model.predict(X)
# If prediction says "high risk" but HR is normal, flag for review
high_risk_unusual_hr = pred & (X['Max HR'] < 100)References
Verified On:
- Kaggle Playground Series S6E2 (Heart Disease)
- Medical constraints reduced CV AUC from 0.970 to 0.943
- Adversarial AUC shift from 0.501 to 0.661 confirmed distribution change
Related Skills:
- adversarial-validation-implementation: For checking train/test distribution alignment
- kaggle-auc-binary-submission-bug: For proper AUC submission format
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.

