Use SMOTE when the minority class is severely under‑represented and you can afford synthetic samples; use class‑weighting when you need to preserve the original data distribution or when online/incremental learning is required.
Step‑by‑step comparison
1. Split data – train_test_split(..., stratify=y, random_state=42).
2. SMOTE pipeline –
from imblearn.over_sampling import SMOTE
from sklearn.pipeline import Pipeline
smote = SMOTE(sampling_strategy='auto', k_neighbors=5, random_state=7)
clf = Pipeline([('smote', smote), ('model', LogisticRegression(max_iter=1000))])
clf.fit(X_train, y_train)3. Class‑weight pipeline –
from sklearn.linear_model import LogisticRegression
clf = LogisticRegression(class_weight='balanced', max_iter=1000)
clf.fit(X_train, y_train)4. Evaluate – compute roc_auc_score(y_test, prob) and precision_recall_fscore_support.
Quick decision table
| Criterion | SMOTE | Class weights |
|------------------------------|--------------------------------------|-----------------------------------|
| Preserve original samples? | No (creates synthetic points) | Yes |
| Works with streaming data? | No (needs full data) | Yes (fit incremental models) |
| Risk of over‑fitting | Higher if k_neighbors too large | Lower, but may bias loss scale |
| Hyper‑parameter load | sampling_strategy, k_neighbors | None (except optional weight dict) |
| Library support (2026) | imblearn, smote-variants | Native in sklearn, xgboost, lightgbm |
Threshold tips
- Set sampling_strategy to the ratio that yields a minority proportion of ~0.3‑0.4 for most tree models.
- For class_weight, if you need a custom ratio, pass a dict like {0:1, 1:10} where the weight equals 1/prop.
Gotcha
If you apply SMOTE before the train‑test split, synthetic points can leak information about the test set, inflating performance metrics; always oversample after splitting.