unbalanced_data
czym są niezbalansowane dane? ⭐
- Niezbalansowane dane to sytuacja, gdy klasy w zbiorze danych mają bardzo różne liczby próbek.
- Jest to powszechny problem w rzeczywistych zastosowaniach ML.
- Przykłady: wykrywanie oszustw (99% normalne, 1% oszustwa), diagnoza chorób (95% zdrowi, 5% chorzy).
- Standardowe metryki jak accuracy mogą być mylące dla niezbalansowanych danych.
- Wymaga specjalnych technik do radzenia sobie z problemem.
problemy z niezbalansowanymi danymi ⭐
- Bias ku większościowej klasie: Model może nauczyć się przewidywać tylko większościową klasę.
- Myliące metryki: Accuracy może być wysoka, ale model może nie wykrywać mniejszościowej klasy.
- Niedostateczne trenowanie: Model ma za mało przykładów mniejszościowej klasy.
- Błędne oceny: Standardowe metryki nie odzwierciedlają rzeczywistej wydajności.
przykładowy kod (niezbalansowane dane)
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import confusion_matrix, classification_report
from imblearn.over_sampling import SMOTE, RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler
from imblearn.combine import SMOTEENN
import matplotlib.pyplot as plt
import seaborn as sns
# Generowanie niezbalansowanych danych
np.random.seed(42)
n_samples = 10000
# Generowanie cech
feature1 = np.random.normal(0, 1, n_samples)
feature2 = np.random.normal(0, 1, n_samples)
# Tworzenie niezbalansowanego targetu (95% klasa 0, 5% klasa 1)
# Klasa 1 ma nieco inne rozkłady cech
class_0_mask = np.random.choice([True, False], n_samples, p=[0.95, 0.05])
class_1_mask = ~class_0_mask
# Modyfikacja cech dla klasy 1
feature1[class_1_mask] += 2
feature2[class_1_mask] += 2
# Target
y = class_1_mask.astype(int)
# Tworzenie DataFrame
data = pd.DataFrame({
'feature1': feature1,
'feature2': feature2,
'target': y
})
print("Rozkład klas:")
print(data['target'].value_counts())
print(f"Proporcje: {data['target'].value_counts(normalize=True)}")
# Wizualizacja rozkładu
plt.figure(figsize=(12, 4))
plt.subplot(1, 3, 1)
plt.scatter(data[data['target'] == 0]['feature1'],
data[data['target'] == 0]['feature2'],
alpha=0.6, label='Klasa 0', s=20)
plt.scatter(data[data['target'] == 1]['feature1'],
data[data['target'] == 1]['feature2'],
alpha=0.8, label='Klasa 1', s=50)
plt.title('Oryginalne dane')
plt.legend()
# Podział danych
X = data[['feature1', 'feature2']]
y = data['target']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
print(f"\nRozkład w zbiorze treningowym:")
print(pd.Series(y_train).value_counts())
1. problem z podstawowym modelem
# Trenowanie podstawowego modelu
model_basic = RandomForestClassifier(random_state=42)
model_basic.fit(X_train, y_train)
# Przewidywanie
y_pred_basic = model_basic.predict(X_test)
# Ocena
print("Wyniki podstawowego modelu:")
print(f"Accuracy: {accuracy_score(y_test, y_pred_basic):.3f}")
print(f"Precision: {precision_score(y_test, y_pred_basic):.3f}")
print(f"Recall: {recall_score(y_test, y_pred_basic):.3f}")
print(f"F1-Score: {f1_score(y_test, y_pred_basic):.3f}")
# Macierz pomyłek
cm_basic = confusion_matrix(y_test, y_pred_basic)
plt.subplot(1, 3, 2)
sns.heatmap(cm_basic, annot=True, fmt='d', cmap='Blues')
plt.title('Macierz pomyłek - podstawowy model')
plt.ylabel('Rzeczywiste')
plt.xlabel('Przewidywane')
2. techniki radzenia sobie z niezbalansowaniem
a) Oversampling (SMOTE)
# SMOTE - Synthetic Minority Over-sampling Technique
smote = SMOTE(random_state=42)
X_train_smote, y_train_smote = smote.fit_resample(X_train, y_train)
print(f"\nRozkład po SMOTE:")
print(pd.Series(y_train_smote).value_counts())
# Trenowanie modelu z SMOTE
model_smote = RandomForestClassifier(random_state=42)
model_smote.fit(X_train_smote, y_train_smote)
# Przewidywanie
y_pred_smote = model_smote.predict(X_test)
# Ocena
print("\nWyniki z SMOTE:")
print(f"Accuracy: {accuracy_score(y_test, y_pred_smote):.3f}")
print(f"Precision: {precision_score(y_test, y_pred_smote):.3f}")
print(f"Recall: {recall_score(y_test, y_pred_smote):.3f}")
print(f"F1-Score: {f1_score(y_test, y_pred_smote):.3f}")
b) Undersampling
# Random Undersampling
under_sampler = RandomUnderSampler(random_state=42)
X_train_under, y_train_under = under_sampler.fit_resample(X_train, y_train)
print(f"\nRozkład po undersampling:")
print(pd.Series(y_train_under).value_counts())
# Trenowanie modelu z undersampling
model_under = RandomForestClassifier(random_state=42)
model_under.fit(X_train_under, y_train_under)
# Przewidywanie
y_pred_under = model_under.predict(X_test)
# Ocena
print("\nWyniki z undersampling:")
print(f"Accuracy: {accuracy_score(y_test, y_pred_under):.3f}")
print(f"Precision: {precision_score(y_test, y_pred_under):.3f}")
print(f"Recall: {recall_score(y_test, y_pred_under):.3f}")
print(f"F1-Score: {f1_score(y_test, y_pred_under):.3f}")
c) Kombinacja technik
# SMOTEENN - kombinacja SMOTE i ENN
smoteenn = SMOTEENN(random_state=42)
X_train_combined, y_train_combined = smoteenn.fit_resample(X_train, y_train)
print(f"\nRozkład po SMOTEENN:")
print(pd.Series(y_train_combined).value_counts())
# Trenowanie modelu z kombinacją
model_combined = RandomForestClassifier(random_state=42)
model_combined.fit(X_train_combined, y_train_combined)
# Przewidywanie
y_pred_combined = model_combined.predict(X_test)
# Ocena
print("\nWyniki z SMOTEENN:")
print(f"Accuracy: {accuracy_score(y_test, y_pred_combined):.3f}")
print(f"Precision: {precision_score(y_test, y_pred_combined):.3f}")
print(f"Recall: {recall_score(y_test, y_pred_combined):.3f}")
print(f"F1-Score: {f1_score(y_test, y_pred_combined):.3f}")
3. porównanie wszystkich metod
# Porównanie wyników
results = {
'Basic': {
'accuracy': accuracy_score(y_test, y_pred_basic),
'precision': precision_score(y_test, y_pred_basic),
'recall': recall_score(y_test, y_pred_basic),
'f1': f1_score(y_test, y_pred_basic)
},
'SMOTE': {
'accuracy': accuracy_score(y_test, y_pred_smote),
'precision': precision_score(y_test, y_pred_smote),
'recall': recall_score(y_test, y_pred_smote),
'f1': f1_score(y_test, y_pred_smote)
},
'Undersampling': {
'accuracy': accuracy_score(y_test, y_pred_under),
'precision': precision_score(y_test, y_pred_under),
'recall': recall_score(y_test, y_pred_under),
'f1': f1_score(y_test, y_pred_under)
},
'SMOTEENN': {
'accuracy': accuracy_score(y_test, y_pred_combined),
'precision': precision_score(y_test, y_pred_combined),
'recall': recall_score(y_test, y_pred_combined),
'f1': f1_score(y_test, y_pred_combined)
}
}
# Wizualizacja porównania
comparison_df = pd.DataFrame(results).T
plt.figure(figsize=(12, 6))
comparison_df.plot(kind='bar', figsize=(12, 6))
plt.title('Porównanie metod radzenia sobie z niezbalansowaniem')
plt.xlabel('Metoda')
plt.ylabel('Wartość')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.tight_layout()
plt.show()
print("\nPorównanie wszystkich metod:")
print(comparison_df)
inne techniki radzenia sobie z niezbalansowaniem
1. Zmiana metryk oceny
- Używaj F1-Score, ROC-AUC, Precision-Recall AUC
- Unikaj accuracy dla niezbalansowanych danych
2. Zmiana wag klas
# Wagi klas w Random Forest
model_weighted = RandomForestClassifier(
class_weight='balanced', # lub {0: 1, 1: 10}
random_state=42
)
3. Threshold tuning
# Dostosowanie progu decyzyjnego
y_pred_proba = model_smote.predict_proba(X_test)[:, 1]
thresholds = np.arange(0.1, 0.9, 0.1)
for threshold in thresholds:
y_pred_threshold = (y_pred_proba > threshold).astype(int)
f1 = f1_score(y_test, y_pred_threshold)
print(f"Threshold {threshold:.1f}: F1-Score {f1:.3f}")
praktyczne ćwiczenia
-
Eksperymentuj z różnymi technikami - przetestuj różne metody oversampling i undersampling.
-
Porównaj metryki - użyj różnych metryk do oceny modeli.
-
Threshold tuning - znajdź optymalny próg decyzyjny.
-
Ensemble methods - użyj ensemble z różnymi technikami balansowania.
-
Real-world data - przetestuj na rzeczywistych niezbalansowanych zbiorach danych.
dobre praktyki
- Wybór metryki: Używaj F1-Score lub ROC-AUC zamiast accuracy.
- Walidacja: Używaj stratified cross-validation.
- Testowanie: Testuj różne techniki na swoim zbiorze danych.
- Interpretacja: Uważaj na overfitting przy oversampling.
polecane źródła
- Handling Imbalanced Data – Towards Data Science
- SMOTE Tutorial – Machine Learning Mastery
- Imbalanced Data Guide – scikit-learn
- Dealing with Imbalanced Data – Kaggle