Januar 12, 2026

Imbalanced Data

Eines der häufigsten Probleme beim Trainieren von Machine Learning Modellen sind unbalancierte Daten, also ein Datensatz, in dem die Klassen stark ungleich verteilt sind. Wenn das beim Modelltraining nicht entsprechend berücksichtigt wird kann es dazu führen, dass das Metriken verzerrt werden und das Modell nicht optimal trainiert wird.

Grundsätzlich kann man zwei Typen von Datensätzen unterscheiden:

  • Balanced Dataset: Jede Klasse im Datensatz ist ungefähr gleich stark vertreten. Beispiel: 50% Klasse A, 50% Klasse B.
  • Imbalanced Dataset: Eine Klasse dominiert die andere(n) deutlich. Beispiel: 95% Klasse A, 5% Klasse B.

Ein klassisches Beispiel für ein imbalanced dataset ist die Betrugserkennung bei Kreditkartentransaktionen – betrügerische Transaktionen machen oft weniger als 1% der Daten aus.

Warum sind Imbalanced Datasets problematisch?

Ein klassischer Machine-Learning-Algorithmus (z. B. Logistic Regression, Random Forest, SVM) versucht, die Gesamtgenauigkeit zu maximieren, beispielsweise indem die Accuracy optimiert wird. Bei stark ungleich verteilten Klassen kann das zu irreführenden Ergebnissen führen:

Beispiel: Wenn 95% der Daten Klasse A sind, erreicht ein Modell, das immer Klasse A vorhersagt, 95% Accuracy – aber es erkennt niemals Klasse B.

Daher sind Accuracy und ähnliche Metriken bei unbalancierten Datensätzen oft nicht aussagekräftig.

Beispiel in Python

Wir simulieren ein realistisches Szenario mit stark unbalancierten Daten. In unserem Beispiel wird ein Datensatz so erzeugt, dass 95% aller Fälle in Klasse 0 fallen und nur 5% in Klasse 1.

import pandas as pd
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split

# Erstelle einen stark unbalancierten Datensatz
X, y = make_classification(n_samples=5000, n_features=10,
                           n_classes=2, weights=[0.95, 0.05],
                           random_state=42)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

Wenn man auf diesen Daten nun ein einfaches Modell ohne Anpassungen trainiert , hier ein Random Forest Modell, dann wird lediglich die Gesamtgenauigkeit optimiert. D.h. das Modell wird in den meisten Fällen die Klasse 1 vorhersagen.

from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix

clf = RandomForestClassifier(random_state=42)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)

print(classification_report(y_test, y_pred))
# Ausgabe des Classification Reports
              precision    recall  f1-score   support

           0       0.96      0.99      0.98      1398
           1       0.84      0.50      0.63       102

    accuracy                           0.96      1500
   macro avg       0.90      0.75      0.80      1500
weighted avg       0.96      0.96      0.95      1500

Im Classification Report kann man erkennen, dass die Vorhersage für Klasse 2 nicht funktioniert (Mehr zu den Metriken findet man hier).

Precision: 96% aller als Klasse 0 vorhergesagten Datenpunkte korrekt klassifiziert. Wird Klasse 1 vorhergesagt, dann liegt das Modell noch in 84% aller Fälle richtig.

Recall: 99% aller Datensätze in Klasse 0 wurden auch richtig als Klasse 0 erkannt. Von den Datensätzen der Klasse 1 wurden allerdings nur 50% richtig erkannt. D.h. die Hälfte der Minderheitsklassen wird übersehen (False Negatives). Gerade wenn es sich bei der Minderheitsklasse um die eigentlich interessante Klasse handelt (Beispiel Betrugserkennung), ist es natürlich äußerst ungünstig, wenn nur die Hälfte aller Fälle erkannt werden.

F1-Score: Der F1 Score für Klasse 1 fällt mit 63% deutlich schlechter aus wie für Klasse 0. Er zeigt also, dass der noch relativ gute Wert für Precision die schlechten Recall Werte nicht kompensiert.

Accuracy: Bei unbalancierten Daten ist Accuracy häufig nicht aussagekräftig. In diesem Beispiel scheint die Gesamtgenauigkeit mit 0,96 sehr hoch zu sein, auch wenn fast die Hälfte aller Datensätze der Klasse 1 nicht korrekt klassifiziert wurden.

Macro Average (einfache Mittelwert pro Klasse), zeigt ebenfalls eher mittelmäßig Ergebnisse. Der Micro Average (der nach support, also der Anzahl der Fälle pro Klasse gewichtet), zeigt hingegen ähnlich wie Accuracy fälschlicherweise gute Ergebnisse.

Precision-Recall AUC

PR-AUC (Precision-Recall AUC) ist eine Variante der ROC-Kurve bzw. von ROC-AUC, welche besonders bei stark unbalancierten Daten nützlich ist. Genau wie bei ROC-AUC wird für jeden möglichen Schwellwert eine Klassifikation erzeugt. Allerdings wird auf der x-Achse Precision und auf der y-Achse der Recall geplottet. PR-AUC misst dann die Fläche unter der Kurve mit Fokus auf die positive Klasse. Sie ist stärker durch False Positives als durch False Negatives beeinflusst und dadurch sensitiv für unbalancierte Daten.

Wichtig ist, dass PR-AUC keinen festen Referenzwert hat. Die Baseline für PR-AUC ist die Anzahl positiver Werte durch die Gesamtanzahl an Datensätzen. D.h. bei 5% positiver Klassen läge ein Zufallsmodell bei einem PR-AUC von 0,05. Der Modell muss also immer im Verhältnis zu diesem Baseline-Wert interpretiert werden.

from sklearn.metrics import precision_recall_curve, average_precision_score

precision, recall, thresholds = precision_recall_curve(y_test, y_score)

pr_auc = average_precision_score(y_test, y_score)
print(f"PR-AUC: {pr_auc:.3f}")

Strategien zum Umgang mit Imbalanced Datasets

Für die Arbeit mit unbalancierten Daten gibt es mit imbalenced learn (imblearn) eine häufig verwendete Erweiterung von scikit-learn. Das Paket ist voll kompatibel mit sklearn und verwendet die gleiche Art von APIs.

1. Resampling-Methoden

a) Oversampling der Minderheitsklasse

Bei diesem Verfahren wird der Datensatz balanciert indem man die Minderheitsklasse künstlich vergrößert. Das funktioniert am einfachsten durch das zufällige Kopieren der bestehenden Minderheitsbeispiele, so lange bis die Klassen ausgeglichen sind.

from imblearn.over_sampling import RandomOverSampler

ros = RandomOverSampler(random_state=42)
X_res, y_res = ros.fit_resample(X_train, y_train)
print(pd.Series(y_res).value_counts())

Nachteil dieser Methode ist es, dass nur wenige Datensätze mehrfach dupliziert werden. Dadurch werden diese von fortgeschrittenen Modellen schnell „auswendig“ gelernt und es entsteht ein hohes Overfitting-Risiko.

Eine Alternative ist es, neue Minderheitsbeispiele synthetisch zu erzeugen. Das geht beispielsweise durch Interpolation zwischen bestehenden Nachbarn. D.h. neue Datenpunkte werden synthetisch so erzeugt, dass ihre Werte zwischen den Werten von echten Datensätzen liegen.

Dadurch reduziert man Overfitting, erzeugt allerdings evtl. unrealistische Daten. Auch kann das Verfahren bei kategorialen Features problematisch werden.

from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=42)
X_res, y_res = smote.fit_resample(X_train, y_train)
print(pd.Series(y_res).value_counts())

b) Undersampling der Mehrheitsklasse

Ein Teil der Mehrheitsklasse wird entfernt, um die Balance herzustellen. Nachteil dieser Methode ist, dass sie die Gesamtmenge an Daten deutlich reduziert und daher zu Informationsverlust führen kann.

from imblearn.under_sampling import RandomUnderSampler

rus = RandomUnderSampler(random_state=42)
X_res, y_res = rus.fit_resample(X_train, y_train)
print(pd.Series(y_res).value_counts())

2. Algorithmische Ansätze

Viele moderne Verfahren unterstützen Klassengewichte. Die Minderheitsklasse wird global stärker gewichtet und Fehler auf dieser Klasse sind dadurch beim Training teurer als Fehler auf der Mehrheitsklasse. Das Verfahren hat gegenüber dem Sampling deutlich weniger Nachteile und sollte daher bevorzugt verwendet werden.

clf = RandomForestClassifier(class_weight='balanced', random_state=42)
clf.fit(X_train, y_train)

Wenn das Modell Klassengewichte nicht unterstützt, dann kann häufig auch mit sample weights gearbeitet werden. Das gewichten auf Basis von einzelnen Datenpunkten ermöglicht theoretisch sogar noch größere Flexibilität da jedem Datenpunkt ein eigenes Gewicht zugewiesen werden kann.

clf.fit(X_train, y_train, sample_weight=sample_weights)

3. Anpassung der Entscheidungsschwelle

Statt eines Standard-Schwellenwerts (z. B. 0.5) kann der Schwellenwert optimiert werden, um Recall oder Precision zu verbessern. D.h. man sucht einen Schwellwert, bei dem Precision und Recall besser ausbalanciert sind. Das Verfahren nennt man Threshold Moving (auch Decision Threshold Adjustment). Dabei werden alle möglichen Schwellwerte ausprobiert und der Schwellwert mit dem maximalen F1 Score verwendet.

from sklearn.metrics import precision_recall_curve
import numpy as np

y_scores = clf.predict_proba(X_test)[:,1]
precision, recall, thresholds = precision_recall_curve(y_test, y_scores)

optimal_idx = np.argmax(2 * (precision * recall) / (precision + recall))
optimal_threshold = thresholds[optimal_idx]
print(f"Optimaler Schwellenwert: {optimal_threshold:.2f}")

Fazit

Der Umgang mit unbalancierten Daten ist eine der zentralen Herausforderungen im Data Science. Ein fundiertes Verständnis der Datenverteilung, der richtigen Metriken und Techniken (wie Resampling, Klassengewichte und Schwellenwertoptimierung) ist entscheidend, um robuste und faire Modelle zu entwickeln.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert