Przejdź do głównej zawartości

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 ⭐

  1. Bias ku większościowej klasie: Model może nauczyć się przewidywać tylko większościową klasę.
  2. Myliące metryki: Accuracy może być wysoka, ale model może nie wykrywać mniejszościowej klasy.
  3. Niedostateczne trenowanie: Model ma za mało przykładów mniejszościowej klasy.
  4. 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

  1. Eksperymentuj z różnymi technikami - przetestuj różne metody oversampling i undersampling.

  2. Porównaj metryki - użyj różnych metryk do oceny modeli.

  3. Threshold tuning - znajdź optymalny próg decyzyjny.

  4. Ensemble methods - użyj ensemble z różnymi technikami balansowania.

  5. 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