Inhalt des Skriptums seit dem Sommersemester 2025
- Startseite
- Allgemeine Informationen zur Lehrveranstaltung
- Einfaches Python Setup und Wichtige Informationen
- 01 Einführung in algorithmisches Denken & LLMs
- 02 Algorithmenanalyse und Komplexitätstheorie
- 03 Vektoren, Matrizen und Vektorisierung in Python
- 04 Differenzieren und Integrieren in Python: Analytische und Numerische Methoden
- 05 Arbeiten mit Daten: Vorbereitung, Visualisierung, Analyse
- 06 Graphalgorithmen: Darstellungen, Traversierungen, Kürzeste Wege und Spannbäume
- 07 Gradient Descent, Stochastische Optimierung, Simulated Annealing, Genetische Algorithmen
- 08 Schwärme und Emergenz
- 09 Monte-Carlo-Methoden
- 10 Machine-Learning Grundlagen
Hier finden Sie die Kapitel aus den Vergangenen Jahren (legacy):
- 0. Python Einführung und Checkup
- 1. Einführung und einfache Algorithmen
- 2. Differenzieren und Integrieren
- 3. Vektoren, Matrizen und Vektorisierung in Python
- 4. Datenanalyse bzw. Datenauswertung
- 5. Grundlagen der Optimierung und Gradient Descent
- 6. Stochastische Optimierung und Genetische Algorithmen
- 7. Monte-Carlo-Methoden – Simulation und Integration
- 8. Monte-Carlo-Methoden, Teil 2 – Monte-Carlo-Integration, Teil 2 und Random Walk
- 9. Unsupervised Machine Learning: Clustering von Daten
- 10. Supervised Machine Learning: Grundlagen
- 11. Einführung in künstliche neuronale Netzwerke
Die Jupyter-Notebooks zur Legacy-Variante der Lehrveranstaltung finden Sie im zugehörigen GitHub-Repository.
10: Grundlagen des Machine-Learning¶
Willkommen zu einem der spannendsten und einflussreichsten Bereiche der modernen Informatik und Mathematik, dem Machine Learning! In dieser Lektion tauchen wir in die Welt der lernenden Algorithmen ein – Systeme, die aus Daten Muster erkennen, Vorhersagen treffen und Entscheidungen automatisieren können.
Machine Learning ist heute überall um uns herum: von Empfehlungssystemen bei Netflix und Spotify über Spracherkennung in Smartphones bis hin zu medizinischen Diagnosesystemen und autonomen Fahrzeugen. Die Fähigkeit, Maschinen das “Lernen” beizubringen, hat unsere Welt grundlegend verändert und wird dies auch weiterhin tun, und zwar in rasantem Tempo.
Aufbau auf vorherigen Inhalten¶
In dieser Einheit wird noch mehr als sonst deutlich, wie viel der bisherigen Algorithmen, Ideen und Konzepte aus den vorangegangenen Einheiten hier einfließen. Hier sind die wichtigsten:
- Vektoren und Matrizen (Einheit 3): Ob Inputdaten, Outputdaten oder die Gewichte in künstlichen neuronalen Netzwerken: Hier arbeitet man zentral mit Vektoren, Matrizen und höherdimensionaleren Arrays
- Arbeiten mit Daten (Einheit 5): Die Datenvorbereitung und -analyse ist fundamental für erfolgreiches Machine Learning und eigentlich das, wo am meisten Arbeit begraben wird, wenn es darum geht, das Modell noch besser zu machen
- Graphen (Einheit 6): Die Netzwerk-Topologie in künstlichen neuronalen Netzwerken ist ein ausgezeichnetes Beispiel für die Relevanz eines guten Verständnisses von Graphen
- Optimierung (Einheit 7): Machine Learning ist im Kern ein Optimierungsproblem, denn wir suchen die besten Parameter für unsere Modelle, und die werden optimiert, z.B. mittels Stochastic Gradient Descent
Nach dem Studium dieser Einheit sollten Sie zumindest folgende drei der wichtigsten Punkte hier verstehen:¶
- Was Machine Learning ist und wie es funktioniert: Was bedeutet “Lernen” in diesem Kontext?
- Die Unterschiede zwischen Supervised und Unsupervised Learning sowie deren Anwendungsgebiete
- Warum Daten für Machine Learning so wichtig sind und worauf man achten muss
10.1 Einführung in das Machine-Learning (ML)¶
Bevor wir mit GPUs und künstlichen neuronalen Netzwerken auf irgendwelche Daten stürzen, müssen wir einiges an Begriffen klären. Keine Sorge, vieles davon kennen Sie bereits, es bekommt hier nur ein paar neue Bezeichnungen oder Namen.
10.1.1 Grundbegriffe: Wie und was lernt eine Maschine?¶
Machine Learning ist ein Teilbereich der Künstlichen Intelligenz (KI), bei dem Computer-Algorithmen entwickelt werden, die automatisch aus Erfahrungen lernen und sich verbessern können, ohne explizit für jede spezifische Aufgabe programmiert zu werden. Den Unterschied zur traditionellen Programmierung könnte man dabei ganz kurz wie folgt zusammenfassen:
- Traditionelle Programmierung: Input + Programm → Output
- Machine Learning: Input (+ Output) → Programm (Modell) → Vorhersage/Analyse
Bei der traditionellen Programmierung schreiben wir explizite Regeln und Anweisungen. Beim Machine Learning hingegen zeigen wir dem Computer Beispiele (z.B. Input-Output-Paare beim Supervised Learning) und lassen ihn die zugrundeliegenden Muster und Regeln selbst erkennen. Die Maschine lernt dabei im Wesentlichen eine Funktion $f$, die im einfachsten Fall Eingaben (Input) auf Ausgaben (Output) abbildet:
$$f: X \rightarrow Y$$
Dabei kann es sich um verschiedene Arten von Funktionen handeln:
- Klassifikation: Zuordnung von Objekten (Inputs) zu Output-Kategorien (z.B. Erkennung von Spam vs. Nicht-Spam)
- Regression: Vorhersage von kontinuierlichen Werten (z.B. Immobilienpreise auf der Basis von Lage, qm, Zimmer, etc.)
- Clustering: Gruppierung ähnlicher Objekte ohne vorgegebene Kategorien und ohne vorgegebene Outputs
Der Lernprozess geht dabei normalerweise etwa folgendermaßen vor sich: Aus Rohdaten wird ein bereinigter und gut strukturierter Datensatz erzeugt, der für das Machine-Learning verwendet werden soll. Je nach Problemstellung wird dann ein ML-Modell “trainiert” (wie genau, das sehen wir weiter unten). Das Modell wird nach Abschluss des Trainings bzw. schon währenddessen in regelmäßigen Abständen verschiedenen Tests und Evaluierungen unterzogen, um zu sehen, wie gut es die Daten bereits beschreibt, und ob es idealerweise generalisieren kann (auch dazu kommen wir unten noch genauer).
10.1.2 Die Rolle von Daten im Machine-Learning¶
Im Machine Learning sind Daten das Fundament des Lernprozesses. Ohne qualitativ hochwertige und ausreichende Daten kann selbst der beste Algorithmus nicht erfolgreich sein. Der Ausspruch “Garbage in, garbage out” gilt hier besonders. Verschiedene Aspekte von Daten sind hier wichtig, insbesondere die Rollen, die verschiedene Teile der verewndeten Daten spielen.
Zunächst: Features sind die Eingangsvariablen oder Attribute, die zur Beschreibung jener Objekte verwendet werden, die die Daten repräsentieren. Sie sind die “Bausteine”, aus denen ein ML-Algorithmus lernt. Typische Beispiele für Features sind folgende Variablen, aber der Fantasie sind hier keine Grenzen gesetzt:
- Bei der Hauspreisvorhersage: Größe, Lage, Anzahl Zimmer, Baujahr
- Bei der Spam-Erkennung: Anzahl bestimmter Wörter, Absender-Domain, E-Mail-Länge
- Bei der Bilderkennung: Pixelwerte, Farben, Formen, Texturen
Am anderen Ende des Lern-Algorithmus stehen die Labels. Diese sind die Ausgangsvariablen oder Zielwerte, die vorhergesagt werden sollen. Sie sind nur beim Supervised Learning bekannt. Beispiele für mögliche Labels für die gerade beschriebenen Inputs/Features könnten folgende Werte sein:
- Hauspreise (kontinuierlicher Wert)
- Spam/Nicht-Spam (kategorisch)
- Krankheitsdiagnose (kategorisch)
Um Daten genauer zu beschreiben, unterscheidet man die folgenden Typen und Eigenschaften:
Numerische Daten:
- Kontinuierlich: Können jeden Wert in einem Bereich annehmen (z.B. Temperatur, Gewicht)
- Diskret: Nur bestimmte Werte möglich (z.B. Anzahl Kinder, Würfelaugen)
Kategorische Daten:
- Nominal: Keine natürliche Reihenfolge (z.B. Farben, Länder)
- Ordinal: Mit natürlicher Reihenfolge (z.B. Schulnoten, Größenkategorien)
Zeitreihendaten: Daten mit zeitlicher Komponente
Textdaten: Unstrukturierte Textinformationen (z.B. Email-Inhalt)
Bild- und Multimediadaten: Pixelwerte, Audio-Signale, Videos
Wenn man ein Bisschen über diese Typen nachdenkt, stellt man fest, dass es meist möglich ist, bestimmte Typen in andere umzuwandeln, sollte das aus irgendeinem Grund nötig sein. Z.B. kann man Kategorien erzeugen/definieren, indem man kontinuierliche Zahlenwerte in Intervalle einteilt.
Abseits der Daten, die in ein Modell während des Trainings gefüttert werden, gibt es noch weitere Parameter, die an dieser Stelle erwähnt werden sollen. Während alle Parameter in einem ML-Modell durch das Training an optimale Werte angepasst werden (diese sind die sogenannten “freien” Parameter, von denen auch immer die Rede ist, wenn man z.B. hört, wie “groß” ein bestimmtes Modell ist), gibt es noch vorab fixierte Parameter, die zwar eingestellt werden können, um das Modell zu verbessern, aber nicht während eines Trainingsruns selbst.
Diese nennt man Hyperparameter und meint damit Konfigurationseinstellungen des Lernalgorithmus, die also vor dem Training festgelegt werden. Typische Beispiele für Hyperparameter sind:
- Lernrate bei Gradient Descent
- Anzahl der Cluster bei K-Means (siehe unten)
- Tiefe eines Entscheidungsbaums (siehe unten)
- Anzahl der Neuronen bzw. die genaue Netzwerktopologie in einem neuronalen Netzwerk
Im folgenden werden wir uns zunächst einen Überblick über verschiedene Arten des Machine-Learnings verschaffen, und danach werden wir sehen, wie Daten bei den einzelnen Zugängen zum Einsatz kommen.
10.1.3 Supervised Learning im Kurzüberblick¶
Zunächst zum einfachen Grundprinzip: Supervised Learning (überwachtes Lernen) ist wie Lernen mit eineR LehrerIn. Der Algorithmus erhält gelabelte Trainingsdaten, das heißt, sowohl die Eingaben (Features) als auch die korrekten Ausgaben (Labels) sind bekannt. Die Hoffnung ist, ein Modell zu trainieren, das diese Zusammenhänge möglichst korrekt, aber auch mit der Fähigkeit zu generalisieren, lernen kann. Der Lernprozess läuft dabe folgendermaßen ab:
- Training: Der Algorithmus lernt aus gelabelten Beispielen, indem die Modellparameter aus dem jeweils gemachten Fehler heraus angepasst werden.
- Validation: Laufende Überprüfung der Leistung auf separaten Validierungsdaten, bereits während das Training läuft. Dies dient hauptsächlich dazu, das schlichte Auswendiglernen der Trainingsdaten durch das Modell zu verhindern.
- Testing: Finale Bewertung nach beendetem Training auf völlig ungesehenen Testdaten (der Moment der Wahrheit).
- Anwendung: Vorhersagen auf neue, ungelabelte Daten, also der Einsatz des Modells für den geplanten Zweck.
Wie bereits kurz angeklungen, unterscheidet man im wesentlichen zwei Ziele des Trainings: Klassifikation und Regression. Bei der Klassifikation wird versucht, Objekte diskreten Kategorien oder Klassen korrekt zuzuordnen. Man unterscheidet hier weiter zwischen binärer Klassifikation (Spam/Nicht-Spam, Krank/Gesund, etc.) und Mehrklassen-Klassifikation (Handschriftenerkennung der Ziffern 0-9, Bildkategorisierung, etc.).
Einige interessante Klassifikationsalgorithmen sind z.B.:
- Entscheidungsbäume
- Support Vector Machines (SVM)
- Naive Bayes
- k-Nearest Neighbors (k-NN)
- Logistische Regression
Im Prinzip kann man am Ende jedes ML-Modells eine Einheit einbauen, die Kategorien als Vorhersage ausgibt.
Bei der Regression hingegen wird versucht, kontinuierliche numerische Werte möglichst genau vorherzusagen. Regression lässt sich überall anwenden, wo man solche kontinuierlichen Zielwerte hat. Alle ML-Modelle können wiederum einen kontinuierlichen Output hervorbringen, wenn sie entsprechend angepasst werden.
10.1.4 Unsupervised Learning im Kurzüberblick¶
Unsupervised Learning (unüberwachtes Lernen) ist wie Lernen, aber diesmal ohne LehrerIn. Der Algorithmus erhält nur die Eingabedaten ohne korrekte Antworten und muss selbstständig versteckte Muster und Strukturen in den Daten entdecken. Mögliche Ziele für Unsupervised Learning sind daher an diese Herausforderung angepasst, z.B.:
- Mustererkennung: Identifikation wiederkehrender Strukturen
- Datenexploration: Verstehen der Datenverteilung und -struktur
- Dimensionsreduktion: Vereinfachung komplexer Datenräume
- Feature Learning: Automatische Erkennung relevanter Merkmale in den Eingabedaten
Eine spezielle Form des Unsupervised Learning ist das Clustering. Es teilt Daten in Gruppen oder Cluster ein, wobei Objekte innerhalb eines Clusters ähnlicher sind (bzw. sein sollten) als Objekte verschiedener Cluster. So etwas ist z.B. im Kundensegmentierung im Marketing oder für Genanalysen in der Bioinformatik interessant.
Beim Clustering kommen verschiedene Algorithmen zum Einsatz, je nach der Art der Daten und der Zielsetzung:
- K-Means: Partitioniert die Daten in eine vorgegebene Zahl von Clustern, indem ähnliche Eigenschaften iterativ zusammengefasst werden.
- Hierarchisches Clustering: Baut eine Hierarchie von Clustern aus den Daten zusammen und verwendet dafür der Reihe nach ein Maß für die Ähnlichkeit zwischen einzelnen Datenpunkten.
- DBSCAN: Dichtebasierte Clustering-Methode, die sich lokal von einem Punkt zum nächsten weiter durch den Datensatz vorarbeitet, bis alle ähnlichen Datenpunkte gruppiert sind. Kann gut mit Ausreißern umgehen.
Eine weitere spezielle Art des Unsupervised Learning ist die Anomaly Detection. Dabei identifiziert man ungewöhnliche oder verdächtige Datenpunkte, die nicht dem normalen Muster entsprechen. Das kann so einfach sein, wie Punkte außerhalb von $2\sigma$ einer Gaußverteilung zu markieren. Typische Anwendungsmöglichkeiten sind:
- Betrugserkennung im Finanzwesen
- Netzwerksicherheit (Intrusion Detection)
- Qualitätskontrolle in der Produktion
- Medizinische Diagnose
Insgesamt ist Unsupervised Learning also sehr vielfältig, obwohl man aufgrund der fehlenden Labels vom Gegenteil ausgehen könnte.
10.1.5 Andere Paradigmen für Machine Learning¶
Supervised und Unsupervised Learning sind zwar die grundlegenden Ausprägungen von Machine-Learning, es gibt aber noch weitere interessante Varianten. Die wichtigsten davon besprechen wir hier kurz.
Beim Reinforcement Learning (Verstärkenden Lernen) lernt ein Agent durch Trial-and-Error mit Belohnungen und Bestrafungen. Der Agent interagiert dabei mit einer Umgebung und lernt durch Feedback, welche Aktionen zu positiven Ergebnissen führen. Das Feedback kann unmittelbar sein, aber auch erst nach langer Zeit bekannt werden (wie z.B. am Ende einer Partie eines Strategiespiels wie Schach oder Go). Wichtig zu verstehen sind hier folgende Begriffe:
- Agent: Das lernende System bzw. ML-Modell in diesem Fall
- Environment: Die Umgebung, in der der Agent agiert, sie muss grundsätzlich bekannt, wahrnehmbar oder simulierbar sein.
- State: Aktueller Zustand der Umgebung
- Actions: Mögliche Handlungen des Agenten, sind je nach Spiel- oder Simulationsprinzip sehr unterschiedlich, aber grundsätzlich vorgegeben, denn sie sind als mögliche Outputs des ML-Modells (Agenten) vorab implementiert.
- Reward: Belohnung oder Bestrafung für eine Aktion bzw. für ein Simulations-/Spielergebnis, die dann rückwärts durch alle Schritte propagiert wird.
Bekannte Anwendungen oder Beispiele sind AlphaGo bzw. AlphaZero für die Welt der Strategiespiele, aber auch in der Robotik und dem autonomen Fahren (bzw. extremeren Formen der autonomen Bewegung wie Fliegen) kommt Reinforcement Learning, hauptsächlich in simulierten Umgebungen, zum Einsatz.
Als nächstes ist Bayesian Inference eine Erwähnung wert. Sie basiert auf dem Satz von Bayes und berücksichtigt Unsicherheiten in den Vorhersagen durch Wahrscheinlichkeitsverteilungen. Hier macht das ML-Modell nicht unbedingt aus klaren Inputs auch eindeutige Outputs, sondern der Input ist eine Prior Distribution, in der vorab-Annahmen über die Parameter stecken. Der Output des Modells entspricht dann einer Posterior Distribution, die einer aktualisierten Erwartung an das betrachtete System nach “Beobachtung” eines (Trainings-)Datensatzes entspricht.
Konkrete Vorteile dieser Art von Machine Learning sind die Quantifizierung von Unsicherheit, die im System bzw. den Daten vorkommt, die Möglichkeit, Vorwissen zu berücksichtigen (und zwar über die Prior Distribution), sowie eine gewisse Robustheit, wenn man nur wenige Trainigsdaten zur Verfügung hat.
Analogiebasiertes Lernen schließlich nutzt Ähnlichkeiten zwischen bekannten und neuen Problemen, um Lösungsstrategien zu übertragen. Das Grundprinzip dabei ist die Identifikation struktureller Ähnlichkeiten, auf deren Basis man dann Lösungsmuster auf neue Bereiche übertragen und anpassen kann. Transfer Learning kann man als so ein Beispiel bezeichnen, obwohl der Ansatz viel allgemeiner einsetzbar ist.
Und hier ganz zum Schluss noch ein paar weitere Begriffe in diesem Zusammenhang:
- Semi-Supervised Learning: Die verfügbaren Daten sind eine Kombination aus gelabelten und ungelabelten Daten
- Self-Supervised Learning: Lernen von Repräsentationen, die beim Lernen aus den Daten selbst entstehen
- Few-Shot Learning: Lernen mit sehr wenigen Beispielen
- Meta-Learning: “Lernen zu lernen”
Insgesamt ist Machine Learning ein faszinierendes und sich schnell entwickelndes Feld, das unser Verständnis von Intelligenz und Automatisierung revolutioniert. Die verschiedenen Paradigmen bieten unterschiedliche Ansätze für verschiedene Arten von Problemen, und oft werden mehrere Techniken kombiniert, um optimale Ergebnisse zu erzielen.
10.2 Supervised Learning¶
Nachdem wir uns einen Überblick über die verschiedenen Machine Learning Paradigmen verschafft haben, tauchen wir nun tiefer in das Supervised Learning ein – den Bereich des Machine Learning, der in der Praxis am häufigsten angewendet wird und die besten Ergebnisse liefert, wenn gelabelte Trainingsdaten verfügbar sind.
10.2.1 Datenvorbereitung¶
Die Datenvorbereitung (Data Preprocessing) ist oft der zeitaufwändigste und kritischste Schritt im gesamten Machine Learning Prozess. Erfahrene Data Scientists verbringen typischerweise den Löwenanteil ihrer Zeit mit der Datenvorbereitung. Der Grund ist einfach: Selbst der beste Algorithmus kann keine guten Ergebnisse liefern, wenn die Eingangsdaten schlecht aufbereitet sind.
Was genau man jetzt mit “schlecht aufbereiteten Daten” meint, kann verschieden sein, je nach Ursache. Hier sind die wichtigsten Schritte der Datenvorbereitung, die dafür sorgen sollen, dass alles astrein ist (einiges davon kennen Sie bereits aus unserer Einheit zur Arbeit mit Daten):
1. Datensammlung und -import Klingt alles logisch, ist es auch:
- Zusammenführung von Daten aus verschiedenen Quellen
- Handling verschiedener Dateiformate (CSV, JSON, Datenbanken)
- Überprüfung der Datenintegrität und -vollständigkeit
2. Explorative Datenanalyse (EDA)
- Verstehen der Datenstruktur und -verteilungen
- Identifikation von Mustern und Anomalien
- Visualisierung der Daten zur ersten Einschätzung Hier verschwimmt übrigens auch die Grenze zum Unsupervised Machine Learning etwas.
3. Behandlung fehlender Werte (Missing Values) Hier kann man verschieden vorgehen, um am Ende einen vollständigen Datensatz ohne Lücken zu erhalten:
- Entfernen: Löschen von Zeilen/Spalten mit zu vielen fehlenden Werten
- Imputation: Ersetzen durch Mittelwert, Median, Modus oder etwas ausgefeiltere Methoden
- Prediction: Vorhersage fehlender Werte basierend auf anderen Features
4. Behandlung von Ausreißern (Outliers) Ausreißer gehören im Prinzip zu jedem Datensatz dazu. Manchmal sind sie sogar die interessanten Elemente. Daher kann man die folgenden Schritte setzen oder auch nicht, je nach Zweck der Daten.
- Identifikation: Durch statistische Methoden oder Visualisierung
- Entscheidung: Entfernen, transformieren oder beibehalten
- Domain Knowledge: Berücksichtigung fachspezifischen Wissens
5. Feature Engineering Hier beginnt man bereits, den Datensatz aktiv zu verändern oder zu erweitern. War mal eine Kunst, ist aber in Zeiten des Deep Learning nicht mehr unbedingt nötig.
- Feature Creation: Ableitung neuer Features aus bestehenden
- Feature Selection: Auswahl der relevantesten Features
- Feature Transformation: Mathematische Transformationen (log, sqrt, etc.)
6. Encoding kategorischer Variablen Encoding ist, vor allem für moderne ML-Modelle wie auch LLMs gängige Praxis geworden. Diese mathematische Formulierung egal welcher Daten hilft kräftig dabei, die Daten in gängige ML-Modelle einspeisen zu können.
- One-Hot Encoding: Umwandlung nominaler Kategorien in binäre Features (also de facto in Vektoren mit Nullen und einer Eins)
- Label Encoding: Numerische Kodierung ordinaler Kategorien
- Target Encoding: Kodierung basierend auf der Zielvariable
7. Skalierung und Normalisierung Hilft, wie bereits bei der Optimierung erwähnt, dabei, in mehrdimensionalen Problemen Features mit verschiedenen Skalen vergleichbar zu machen.
- Min-Max Scaling: Skalierung auf einen bestimmten Bereich (z.B. 0-1)
- Standard Scaling: Mittelwert 0, Standardabweichung 1
- Robust Scaling: Weniger anfällig für Ausreißer
8. Aufteilung der Daten Das ist beim Supervised Learning unerlässlich und wird von vornherein von allen gängigen Software-Lösungen unterstützt. Üblich ist Train-Validation-Test-Aufteilung. Die Teile werden wie folgt verwendet:
- Training Set: Zum Trainieren des Modells (typisch 60-70%)
- Validation Set: Zur Validierung bzw. für Hyperparametertuning und Modellauswahl (typisch 15-20%)
- Test Set: Zur finalen Evaluation (typisch 15-20%) Wichtig ist hier, dass die Aufteilung ausgewogen erfolgt, d.h. es sollten von allen Kategorien aliquote Anteile in Train, Validation und Test landen. Man verteilt die Daten üblicherweise zufällig, was bereits ein guter Ansatz ist, aber bei Klassen mit sehr wenigen Elementen nicht ausreicht – in solchen Fällen muss man auf diese Minderheiten besonderes Augenmerk legen.
10.2.2 Der typische Trainingszyklus beim Supervised Learning: Training, Validation, Testing, Evaluation¶
Der Supervised Learning Lern-Prozess folgt einem strukturierten Zyklus, der systematisches Experimentieren und kontinuierliche Verbesserung ermöglicht. Manchmal wird die Validierung etwas nachlässig behandelt, davon ist aber abzuraten.
Phase 1: Training (Lernen), idealerweise bereits gekoppelt mit Validation
In der Trainingsphase lernt das Modell aus den gelabelten Trainingsdaten. Dabei passiert folgendes: Die freien Parameter des Modells werden angepasst, indem die Features (X_train) in das Modell geschickt werden und die Ergebnisse des forward pass durch das Modell mit den tatsächichen zugehörigen Labels (y_train) verglichen werden. Dieser Vergleich passiert mit Hilfe einer sogenannten Kosten-Funktion bzw. Loss-Funktion, und ja, das entspricht genau dem Prinzip einer Zielfunktion bei der Optimierung (denn das passiert hier ja auch).
Die Optimierung betrifft hier die freien Modellparameter, die so lange angepasst werden, bis man “zufrieden” ist. Dazu beobachtet man den Wert der Kosten-Funktion, der sich im Allgemeinen während des Trainings verringert. Genauer gesagt berechnet man diesen Funktionswert auf den Trainingsdaten und zusätzlich auf den Validation-Daten, die nicht zum Lernen verwendet werden. Auf diese Weise sieht man, wenn das Modell beginnt, den Trainingsdatensatz “auswendig” zu lernen und die Validierungsdaten nicht so gut wiederzugeben wie die Trainingsdaten. An dieser Stelle ist es Zeit, das Training abzubrechen.
Wichtige Konzepte beim Training:
- Epochen: Anzahl der Durchläufe des Optimierungsalgorithmus durch den gesamten Trainingsdatensatz
- Batch Size: Die Anzahl der Datenpunkte aus dem Datensatz, die gleichzeitig vom Modell verarbeitet werden. Größere Batch-Sizes brauchen mehr Speicher, kleinere mehr Zeit.
- Learning Rate: Die Schrittgröße bei der Parameteranpassung, das kennen wir bereits vom Gradient-Descent Algorithmus
Die gleichzeitig mit dem Training laufende Validierung dient zunächst der Entscheidung, wann der Trainingsrun gestoppt wird. Sie dient aber dadurch auch der Modellauswahl und Hyperparameter-Optimierung, denn bei verschiedenen Hyperparameter-Werten kann (und wird) das Training unterschiedlich effektiv sein. So können sich verschiedene Runs z.B. in der erreichten Genauigkeit auf dem Validierungsdatensatz oder in ihrer Fähigkeit zu generalisieren unterscheiden.
Phase 2: Testing (Testen)
Nach abgeschlossenem Training mit Validation liefert der Test eine unvoreingenommene Schätzung der Modellleistung, denn die dafür verwendeten Daten sind ja sowohl von Training als auch von Validation verschieden. Dabei kann man nun verschiedene Modelle hinsichtlich ihrer Leisung auf komplett neuen Daten vergleichen. Man sollte allerdings die Testdaten nicht für Modellauswahl oder Hyperparameter-Tuning verwenden! Beim Test wird insbesondere wieder die Kostenfunktion berechnet und mit den Werten von Training und Validation verglichen.
Phase 3: Evaluation (Bewertung)
Die Evaluation beurteilt schließlich die Qualität des Modells anhand verschiedener Metriken, die über die doch etwas unintuitive Kostenfunktion hinausgehen:
Für Klassifikation:
- Accuracy: Anteil korrekter Vorhersagen
- Precision: Anteil der als zur Zielkategorie gehörig vorhergesagten Datenpunkte, die tatsächlich der Zielkategorie angehören
- Recall: Anteil der Datenpunkte aus der Zielkategorie, die auch als solche vorhergesagt wurden
- F1-Score: Harmonisches Mittel aus Precision und Recall
- Confusion Matrix: Detaillierte Aufschlüsselung aller Vorhersagekombinationen (false/true positives/negatives)
Für Regression:
- Mean Absolute Error (MAE): Durchschnittlicher absoluter Fehler
- Mean Squared Error (MSE): Durchschnittlicher quadratischer Fehler
- Root Mean Squared Error (RMSE): Quadratwurzel des MSE
Der iterative Verbesserungszyklus führt dann insgesamt wiederholt über alle Phasen, so lange bis man zufrieden ist oder das gesamte Projektbudget aufgebraucht ist.
10.2.3 Machine-Learning als Optimierungsproblem¶
An dieser Stelle möchte ich nocheinmal erwähnen, dass Machine-Learning generell ein Optimierungsproblem ist. Das wird beim Supervised Learning unmittelbar deutlich, wenn man einen Label mit Modell-Ergebnissen vergleichen kann und daraus eine Richtung für die Optimierung der Modellparameter berechnet wird. Aber auch sonst folgt ML grundsätzlich dem Prinzip der Optimierung. Die konkrete Aufgabenstellung ist dann im Allgemeinen nur noch, etwas (eine konkrete Funktion) zu finden, die man optimieren kann.
10.2.4 Beispiel: Trainieren eines Entscheidungsbaums¶
Nach all dieser grauen Theorie wollen wir uns nun endlich einem Beispiel zuwenden. Dazu bietet sich ein Entscheidungsbaum als ML-Modell an, weil man hier intuitiv nachvollziehen kann, was im Modell passiert und wie das Ergebnis zu interpretieren ist. Entscheidungsbäume sind also intuitive und interpretierbare Modelle, die Entscheidungen durch eine Folge von Ja/Nein-Fragen treffen. Sie eignen sich sowohl für Klassifikation als auch für Regression.
Die Struktur eines Entscheidungsbaums sieht im Ganzen tatsächlich aus wie ein Baum. Dabei gibt es folgende Begriffe zu beachten:
- Root Node: Wurzelknoten mit der ersten Entscheidung
- Internal Nodes: Innere Knoten mit weiteren Entscheidungskriterien
- Leaf Nodes: Blätter mit den finalen Vorhersagen
- Branches: Äste, die die Entscheidungswege repräsentieren
Um Overfitting (das Auswendiglernen der Trainingsdaten) zu vermeiden, kann man verschiedene Hyperparameter einschränken. Am wichtigsten ist dabei aber die maximale Tiefe (also Maximallänge der Äste) des Baumes. Wir lassen uns unseren Entscheidungsbaum samt Datensatz und Lernen mit der Package Scikit-Learn von Gemini 2.5 Pro zusammenbauen, so wie auch die anderen Codebeispiele dieser Einheit.
# prevent library-related crashes
import os
os.environ['MKL_NUM_THREADS'] = '1'
os.environ['NUMEXPR_NUM_THREADS'] = '1'
os.environ['OMP_NUM_THREADS'] = '1'
# standard imports
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.metrics import accuracy_score, classification_report
# --- 1. Generate a Suitable Dataset ---
# make_moons is great for visualizing decision boundaries.
n_samples_total = 1000 # Generate more samples
X_sample, y_sample = make_moons(n_samples=n_samples_total, noise=0.25, random_state=42)
feature_names = ["Feature 1", "Feature 2"]
target_names = ["Class 0", "Class 1"]
# --- 2. Split Data into Train, Validation, and Test Sets ---
# First, split into training + validation (80%) and test (20%)
X_train_val, X_test, y_train_val, y_test = train_test_split(
X_sample, y_sample, test_size=0.2, random_state=42, stratify=y_sample
)
# Then, split training + validation into training (60% of original) and validation (20% of original)
X_train, X_val, y_train, y_val = train_test_split(
X_train_val, y_train_val, test_size=0.25, random_state=42, stratify=y_train_val # 0.25 * 0.8 = 0.2
)
print(f"Total generated samples: {X_sample.shape[0]}")
print(f"Training set shape: {X_train.shape}")
print(f"Validation set shape: {X_val.shape}")
print(f"Test set shape: {X_test.shape}")
# --- 3. Visualize the Data Splits ---
plt.figure(figsize=(18, 5))
plt.subplot(1, 3, 1)
scatter_train = plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='viridis', edgecolor='k', s=50, alpha=0.7)
plt.title(f"Training Data ({len(X_train)} samples)")
plt.xlabel(feature_names[0])
plt.ylabel(feature_names[1])
plt.legend(handles=scatter_train.legend_elements()[0], labels=list(target_names))
plt.grid(True)
plt.subplot(1, 3, 2)
scatter_val = plt.scatter(X_val[:, 0], X_val[:, 1], c=y_val, cmap='viridis', edgecolor='k', s=50, alpha=0.7)
plt.title(f"Validation Data ({len(X_val)} samples)")
plt.xlabel(feature_names[0])
plt.ylabel(feature_names[1])
plt.legend(handles=scatter_val.legend_elements()[0], labels=list(target_names))
plt.grid(True)
plt.subplot(1, 3, 3)
scatter_test = plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap='viridis', edgecolor='k', s=50, alpha=0.7)
plt.title(f"Test Data ({len(X_test)} samples)")
plt.xlabel(feature_names[0])
plt.ylabel(feature_names[1])
plt.legend(handles=scatter_test.legend_elements()[0], labels=list(target_names))
plt.grid(True)
plt.tight_layout()
plt.show()
# --- 4. Set up Decision Tree, Train, and Display Accuracy vs. Max Depth ---
max_depths = range(1, 16) # Explore depths, can go deeper for more complex data
train_accuracies = []
val_accuracies = []
for depth_iter in max_depths: # Renamed to avoid conflict
dt_classifier_iter = DecisionTreeClassifier(max_depth=depth_iter, random_state=42)
dt_classifier_iter.fit(X_train, y_train)
y_train_pred_iter = dt_classifier_iter.predict(X_train)
train_accuracies.append(accuracy_score(y_train, y_train_pred_iter))
y_val_pred_iter = dt_classifier_iter.predict(X_val)
val_accuracies.append(accuracy_score(y_val, y_val_pred_iter))
plt.figure(figsize=(10, 6))
plt.plot(max_depths, train_accuracies, 'o-', label='Training Accuracy')
plt.plot(max_depths, val_accuracies, 's-', label='Validation Accuracy')
plt.xlabel("Max Depth of Tree")
plt.ylabel("Accuracy")
plt.title("Training and Validation Accuracy vs. Max Tree Depth")
plt.legend()
plt.grid(True)
plt.xticks(max_depths)
plt.show()
# Choose a max_depth based on the plot. Let's pick one that is reasonably good but not too overfit.
# And another one that is clearly deeper.
chosen_max_depth = 4 # Adjusted based on typical make_moons behavior
dt_classifier = DecisionTreeClassifier(max_depth=chosen_max_depth, random_state=42)
dt_classifier.fit(X_train, y_train)
print(f"\n--- Initial Decision Tree (max_depth={chosen_max_depth}) ---")
# --- 5. Show Predictions Compared to Labels with Decision Boundaries ---
def plot_decision_boundary_and_predictions(clf, X_data, y_data, y_pred_data, title, ax):
# Create a mesh to plot the decision boundary
x_min, x_max = X_data[:, 0].min() - 0.5, X_data[:, 0].max() + 0.5
y_min, y_max = X_data[:, 1].min() - 0.5, X_data[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
np.arange(y_min, y_max, 0.02))
Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
ax.contourf(xx, yy, Z, alpha=0.3, cmap='viridis')
# Plot correctly classified points
correct_mask = (y_pred_data == y_data)
ax.scatter(X_data[correct_mask, 0], X_data[correct_mask, 1],
c=y_data[correct_mask], cmap='viridis', marker='o',
edgecolor='black', s=50, label='Correctly Classified', alpha=0.9)
# Plot misclassified points
ax.scatter(X_data[~correct_mask, 0], X_data[~correct_mask, 1],
c=y_data[~correct_mask], cmap='viridis', marker='o', # Same marker
edgecolor='red', linewidth=2, s=70, label='Misclassified', alpha=0.9) # Red border for incorrect
ax.set_title(f"{title} (Accuracy: {accuracy_score(y_data, y_pred_data):.2f})")
ax.set_xlabel(feature_names[0])
ax.set_ylabel(feature_names[1])
# Create custom legend handles if needed, or rely on labels
handles, labels = ax.get_legend_handles_labels()
# Filter out duplicate labels for a cleaner legend (if contourf adds one)
# For this specific scatter setup, standard legend should be fine.
ax.legend(loc='lower left') # Adjust legend position
ax.grid(True, linestyle='--', alpha=0.7)
y_train_pred = dt_classifier.predict(X_train)
y_val_pred = dt_classifier.predict(X_val)
y_test_pred = dt_classifier.predict(X_test)
fig, axes = plt.subplots(1, 3, figsize=(24, 7)) # Increased figure size for better boundary plots
plot_decision_boundary_and_predictions(dt_classifier, X_train, y_train, y_train_pred, "Training Set", axes[0])
plot_decision_boundary_and_predictions(dt_classifier, X_val, y_val, y_val_pred, "Validation Set", axes[1])
plot_decision_boundary_and_predictions(dt_classifier, X_test, y_test, y_test_pred, "Test Set", axes[2])
plt.tight_layout()
plt.show()
# --- 6. Visualize the Resulting Decision Tree ---
plt.figure(figsize=(18, 12)) # Adjust size as needed
plot_tree(dt_classifier,
filled=True,
rounded=True,
class_names=list(target_names),
feature_names=list(feature_names),
fontsize=10)
plt.title(f"Decision Tree Visualization (max_depth={chosen_max_depth})")
plt.show()
print("\nEvaluation Metrics for Initial Tree (max_depth=", chosen_max_depth, ") on Test Set:")
print(classification_report(y_test, y_test_pred, target_names=list(target_names)))
# --- 7. Train with a Deeper Tree and Compare ---
deeper_max_depth = 10 # Adjusted for make_moons, can lead to clear overfitting
dt_classifier_deep = DecisionTreeClassifier(max_depth=deeper_max_depth, random_state=42)
dt_classifier_deep.fit(X_train, y_train)
print(f"\n--- Deeper Decision Tree (max_depth={deeper_max_depth}) ---")
y_train_pred_deep = dt_classifier_deep.predict(X_train) # For visualization
y_val_pred_deep = dt_classifier_deep.predict(X_val) # For visualization
y_test_pred_deep = dt_classifier_deep.predict(X_test) # For metrics
# Visualize predictions for the deeper tree
fig, axes_deep = plt.subplots(1, 3, figsize=(24, 7))
plot_decision_boundary_and_predictions(dt_classifier_deep, X_train, y_train, y_train_pred_deep, f"Deeper Tree (d={deeper_max_depth}) - Training", axes_deep[0])
plot_decision_boundary_and_predictions(dt_classifier_deep, X_val, y_val, y_val_pred_deep, f"Deeper Tree (d={deeper_max_depth}) - Validation", axes_deep[1])
plot_decision_boundary_and_predictions(dt_classifier_deep, X_test, y_test, y_test_pred_deep, f"Deeper Tree (d={deeper_max_depth}) - Test", axes_deep[2])
plt.tight_layout()
plt.show()
# Visualize the Deeper Decision Tree
plt.figure(figsize=(30, 20)) # May need a very large figure for a deep tree
plot_tree(dt_classifier_deep,
filled=True,
rounded=True,
class_names=list(target_names),
feature_names=list(feature_names),
fontsize=7, # Smaller font for complex trees
max_depth=5) # Optionally, only display top N levels if tree is too massive
plt.title(f"Deeper Decision Tree Visualization (max_depth={deeper_max_depth}, showing top 5 levels if large)")
plt.show()
print("\nEvaluation Metrics for Deeper Tree (max_depth=", deeper_max_depth, ") on Test Set:")
print(classification_report(y_test, y_test_pred_deep, target_names=list(target_names)))
# --- Summary Comparison of Metrics ---
metrics_initial = classification_report(y_test, y_test_pred, target_names=list(target_names), output_dict=True)
metrics_deep = classification_report(y_test, y_test_pred_deep, target_names=list(target_names), output_dict=True)
comparison_df = pd.DataFrame({
f'Initial Tree (depth={chosen_max_depth})': {
'accuracy': metrics_initial['accuracy'],
'macro_avg_precision': metrics_initial['macro avg']['precision'],
'macro_avg_recall': metrics_initial['macro avg']['recall'],
'macro_avg_f1-score': metrics_initial['macro avg']['f1-score']
},
f'Deeper Tree (depth={deeper_max_depth})': {
'accuracy': metrics_deep['accuracy'],
'macro_avg_precision': metrics_deep['macro avg']['precision'],
'macro_avg_recall': metrics_deep['macro avg']['recall'],
'macro_avg_f1-score': metrics_deep['macro avg']['f1-score']
}
}).T
print("\n--- Comparison of Test Set Metrics ---")
print(comparison_df)
# Also compare training accuracies to highlight overfitting
train_acc_initial = accuracy_score(y_train, dt_classifier.predict(X_train))
train_acc_deep = accuracy_score(y_train, dt_classifier_deep.predict(X_train))
print(f"\nTraining Accuracy (Initial Tree, depth={chosen_max_depth}): {train_acc_initial:.3f}")
print(f"Training Accuracy (Deeper Tree, depth={deeper_max_depth}): {train_acc_deep:.3f}")
print(f"\nTest Accuracy (Initial Tree, depth={chosen_max_depth}): {metrics_initial['accuracy']:.3f}")
print(f"Test Accuracy (Deeper Tree, depth={deeper_max_depth}): {metrics_deep['accuracy']:.3f}")
if train_acc_deep > train_acc_initial and metrics_deep['accuracy'] < metrics_initial['accuracy']:
print("\nThe deeper tree shows signs of overfitting (higher train accuracy, lower test accuracy).")
elif train_acc_deep > train_acc_initial and metrics_deep['accuracy'] <= metrics_initial['accuracy'] + 0.01: # small tolerance
if metrics_deep['accuracy'] < metrics_initial['accuracy']:
print("\nThe deeper tree might be slightly overfitting or providing diminishing returns on the test set.")
else:
print("\nThe deeper tree has higher training accuracy and similar/slightly better test accuracy.")
else:
print("\nObserve the training and test accuracies to assess overfitting.")
Total generated samples: 1000 Training set shape: (600, 2) Validation set shape: (200, 2) Test set shape: (200, 2)


--- Initial Decision Tree (max_depth=4) ---


Evaluation Metrics for Initial Tree (max_depth= 4 ) on Test Set:
precision recall f1-score support
Class 0 0.87 0.97 0.92 100
Class 1 0.97 0.86 0.91 100
accuracy 0.92 200
macro avg 0.92 0.92 0.91 200
weighted avg 0.92 0.92 0.91 200
--- Deeper Decision Tree (max_depth=10) ---


Evaluation Metrics for Deeper Tree (max_depth= 10 ) on Test Set:
precision recall f1-score support
Class 0 0.93 0.95 0.94 100
Class 1 0.95 0.93 0.94 100
accuracy 0.94 200
macro avg 0.94 0.94 0.94 200
weighted avg 0.94 0.94 0.94 200
--- Comparison of Test Set Metrics ---
accuracy macro_avg_precision macro_avg_recall \
Initial Tree (depth=4) 0.915 0.920083 0.915
Deeper Tree (depth=10) 0.940 0.940176 0.940
macro_avg_f1-score
Initial Tree (depth=4) 0.914742
Deeper Tree (depth=10) 0.939994
Training Accuracy (Initial Tree, depth=4): 0.917
Training Accuracy (Deeper Tree, depth=10): 0.997
Test Accuracy (Initial Tree, depth=4): 0.915
Test Accuracy (Deeper Tree, depth=10): 0.940
Observe the training and test accuracies to assess overfitting.
Die Bezeichnungen mit “macro” meinen hier übrigens einfache arithmetische Mittel der jeweiligen Größen über alle Klassen.
10.3 Unsupervised Learning¶
Während beim Supervised Learning ein “Lehrer” in Form von gelabelten Daten zur Verfügung steht, müssen wir beim Unsupervised Learning versteckte Strukturen und Muster in den Daten ohne vorgegebene Antworten entdecken. Dies macht Unsupervised Learning sowohl herausfordernder als auch in gewisser Weise natürlicher und gleichzeitig mächtiger – schließlich lernen Menschen oft auch durch selbstständige Exploration und Mustererkennung, und das mit Erfolg.
10.3.1 Clustering von Daten: Grundprinzipien¶
Clustering ist der Prozess der Gruppierung von Datenpunkten in Cluster (Gruppen), sodass Punkte innerhalb eines Clusters einander ähnlicher sind als Punkte in verschiedenen Clustern. Es ist eine der fundamentalsten Aufgaben im Unsupervised Learning. Die grundlegendste Funktion, die man vorab klären bzw. definieren muss, ist, was genau mit “ähnlich” gemeint ist. Das kann z.B. eine Euklidsche Distanz sein, muss es aber nicht. Im Allgemeinen ist Ähnlichkeit ein sehr dehnbarer Begriff.
Das Herzstück jedes Clustering-Algorithmus ist also ein Ähnlichkeitsmaß oder eine Distanzfunktion. Häufige Distanzmetriken sind die bereits erwähnte Euklidische Distanz, die Manhattan Distanz (die rechteckigen “Straßen”-Grids folgt), oder die Cosinus-Distanz, die auf dem Winkel zwischen Vektoren basiert. Als Cluster-Qualitätskriterien kennt man hauptsächlich solche, die im Cluster bzw. zwischen den Clustern definiert sind:
Intra-Cluster-Ähnlichkeit: Punkte innerhalb eines Clusters sollen sich ähnlich sein
- Minimale Streuung: Geringe Varianz innerhalb der Cluster
- Hohe Dichte: Viele Punkte in einem kompakten Bereich
Inter-Cluster-Unterschiede: Cluster sollen sich voneinander unterscheiden
- Maximale Trennung: Große Distanzen zwischen Cluster-Zentren
- Geringe Überlappung: Klare Abgrenzung zwischen Clustern
Als typische Cluster-Formen und -eigenschaften haben sich folgende Merkmale herauskristallisiert: Sphärische Cluster sind, wie der Name schon sagt, rund und ideal für K-Means. Beliebige Formen werden besser durch DBSCAN erkannt, weil dieser Algorithmus im Prinzip den Cluster entlanggeht, bis alle Punkte zugeordnet sind. Wenn es Cluster in den Clustern gibt, eignet sich hierarchisches Clustering am besten. Gibt es verschiedenförmige Cluster mit unterschiedlichen Dichten, muss man einfach sein Glück versuchen und herausfinden, welcher Algorithmus am besten mit den Daten zurecht kommt.
Clustering kann mitunter auch grundsätzlich herausfordernd sein. Das kommt daher, dass man eigentlich immer etwas (genauer gesagt, Hyperparameter) fixieren muss. Das einfachste Beispiel dafür ist die Anzahl der Cluster, die man z.B. bei K-Means vorgeben muss. Bei DBSCAN ist es eine Dichte und eine Größenskala für die Kernelemente. Eine weitere Herausforderung, die wir bereits aus anderen Zusammenhängen kennen, ist der Fluch der Dimensionalität, d.h. je höherdimensional das Problem, desto schwieriger ist generell die Datenverarbeitung.
Auch Rauschen, also Daten, die eigentlich zu keinem Cluster gehören, können ein Problem sein. Für solche Daten empfehlen sich dichte-basierte Algorithmen wie DBSCAN, siehe unten.
10.3.2 Cluster-Algorithmen: K-Means vs. DBSCAN¶
Als zwei Paradebeispiele möchte ich Ihnen hier K-Means und DBSCAN kurz vorstellen. K-Means ist einer der bekanntesten und am häufigsten verwendeten Clustering-Algorithmen. Er partitioniert die Daten in k vordefinierte Cluster. Hier ist der K-Means Algorithmus:
1. Initialisierung:
- Wähle Anzahl k der Cluster
- Platziere k Zentroide zufällig im Datenraum
2. Wiederhole bis Konvergenz:
a) Assignment Step:
- Ordne jeden Datenpunkt dem nächstliegenden Zentroid zu
b) Update Step:
- Berechne neue Zentroide als Mittelwert aller zugeordneten Punkte
3. Konvergenz:
- Wenn sich Zentroide nicht mehr signifikant bewegen
- Oder maximale Iterationszahl erreicht
Vorteile von K-Means:
- Einfachheit: Leicht zu verstehen und implementieren
- Effizienz: O(n·k·i·d) Komplexität (n=Punkte, k=Cluster, i=Iterationen, d=Dimensionen)
- Skalierbarkeit: Funktioniert gut mit großen Datensätzen
- Garantierte Konvergenz: Algorithmus konvergiert immer
Nachteile von K-Means:
- k muss vorab gewählt werden: Nicht immer trivial
- Gut für sphärische Cluster: Funktioniert aber eventuell schlecht bei anderen Formen
- Sensitive to Initialization: Verschiedene Startpunkte → verschiedene Ergebnisse
- Ausreißer-sensitiv: Zentroide werden stark durch Outlier beeinflusst
Der Algorithmus DBSCAN identifiziert Cluster basierend auf der lokalen Datendichte und kann so relativ einfach Cluster beliebiger Form finden. Die Kernkonzepte von DBSCAN sind:
1. ε-Nachbarschaft: Alle Punkte innerhalb Radius ε von einem Punkt 2. MinPts: Mindestanzahl Punkte in der ε-Nachbarschaft 3. Core Point: Punkt mit mindestens MinPts Nachbarn 4. Border Point: Nicht-Core-Punkt in der Nachbarschaft eines Core-Points 5. Noise Point: Weder Core noch Border Point
Und hier ist der DBSCAN Algorithmus:
1. Beginne bei einem beliebigen unbesuchten Punkt und besuche ihn
2. Für jeden unbesuchten Punkt p:
a) Markiere p als besucht
b) Finde alle Nachbarn von p in ε-Distanz
c) Wenn |Nachbarn| < MinPts:
- Markiere p als Noise (vorerst)
d) Sonst:
- Erstelle neuen Cluster C
- Füge p zu C hinzu
- Für jeden Nachbarn n von p:
- Wenn n unbesucht: wiederhole Prozess rekursiv
- Wenn n keinem Cluster zugeordnet: füge n zu C hinzu
Vorteile von DBSCAN:
- Beliebige Cluster-Formen: Nicht auf sphärische Cluster beschränkt
- Automatische Cluster-Anzahl: k muss nicht vorab gewählt werden
- Noise Detection: Identifiziert Ausreißer als Noise
- Robustheit: Weniger anfällig für Ausreißer
Nachteile von DBSCAN:
- Parameter-Sensitivität: ε und MinPts müssen sorgfältig gewählt werden
- Varying Densities: Probleme bei Clustern unterschiedlicher Dichte
- Hochdimensionale Daten: Distanzkonzept wird weniger bedeutsam
- Memory Requirements: Kann speicherintensiv bei großen Datensätzen sein
Hier noch ein kurzer Vergleich von K-Means und DBSCAN:
| Aspekt | K-Means | DBSCAN |
|---|---|---|
| Cluster-Form | Sphärisch | Beliebig |
| Cluster-Anzahl | Muss gewählt werden | Automatisch |
| Ausreißer | Problematisch | Robuste Behandlung |
| Parameter | k | ε, MinPts |
| Komplexität | O(nki) | O(n log n) |
| Determinismus | Nein (Init-abhängig) | Ja |
10.3.3 Spezialfall für Distanzmatrizen: Agglomeratives Hierarchisches Clustering¶
Als nächstes möchte ich Ihnen noch etwas zeigen, das Sie eventuell dann brauchen, wenn Ihre Daten zwar keine Koordinaten haben, Sie aber trotzdem einen Abstand zwischen zwei Datenpunkten (also eine Distanzmatrix für den gesamten Datensatz) berechnen können. So ein Algorithmus ist das Hierarchische Clustering.
Hierarchisches Clustering erstellt eine Hierarchie von Clustern in Form eines Baums (Dendrogramm), anstatt eine flache Partitionierung wie K-Means oder DBSCAN. Es gibt zwei Hauptansätze: agglomerativ (bottom-up) und divisiv (top-down). Hier sehen wir uns nur das erste Beispiel an. Beim agglomerativen Ansatz beginnt jeder Datenpunkt als eigener Cluster und wird schrittweise mit dem nächstähnlichen Cluster verschmolzen.
Hier einmal der Agglomerative Algorithmus:
1. Initialisierung:
- Jeder Datenpunkt bildet einen eigenen Cluster
- Berechne Distanzmatrix zwischen allen Punkten
2. Wiederhole bis nur ein Cluster übrig:
a) Finde das Paar von Clustern mit kleinster Distanz
b) Verschmelze diese beiden Cluster
c) Aktualisiere die Distanzmatrix
d) Dokumentiere die Verschmelzung für das Dendrogramm
3. Ergebnis:
- Dendrogramm mit vollständiger Cluster-Hierarchie
- Wahl der finalen Cluster-Anzahl durch "Schnitt" im Baum
Soviel zum Clustering Algorithmus selbst. Dabei ist aber die Art, wie die Distanz zwischen Clustern berechnet wird (die Linkage), noch nicht fixiert, diese bestimmt aber die Form der resultierenden Cluster entscheidend mit. Hier sind die wichtigsten Möglichkeiten. Zunächst gibt es die Single Linkage, die einfach die kürzeste Entfernung von zwei Punkten aus zwei Clustern als die Clusterdistanz festlegt. Analog einfach funktioniert die Complete Linkage, bei der man die größte Entfernung irgendwelcher Punkte aus den Clustern als Distanz verwendet.
Üblicher ist allerdings die Average Linkage, bei der der Durchschnitt aller Punkt-zu-Punkt Distanzen verwendet wird, oder z.B. auch die Ward Linkage, welche die Varianz-Zunahme bei Verschmelzung zweier Cluster minimiert. Letztere hat zwar Vorteile, ist aber nur für Euklidische Distanzen anwendbar. Das bedeutet auch, dass sie für jene Fälle ausscheidet, bei denen ein Riesenvorteil des hierarchischen Clusterings zum Tragen kommt: Wenn man, wie bereits erwähnt, nämlich keine Koordinaten zur Verfügung hat, sondern nur eine Distanzmatrix.
Beim hierarchischen Clustering erhält man außerdem die Möglichkeit, die Hierarchie in einem Dendrogramm abzubilden. Dort sieht man, wie die Blätter, die ursprünglichen Datenpunkte, über diverse innere Knoten, wo die Verschmelzungen von Clustern stattfinden, zu einem riesigen gesamten Cluster (alle Daten zusammen) zusammengebaut würden, wenn man das wollte. Und hier kommen wir gleich zu einer anschaulichen Sache: Je nach gewünschter Cluster-Anzahl oder Distanz-Parameter sieht man durch eine nachträglich eingeführte Schnittlinie, wie die Cluster zustande kommen. Wir werden uns das unten in einem Beispiel ansehen.
Vorteile des Hierarchischen Clustering:
- Vollständige Information: Alle möglichen Cluster-Strukturen sind sichtbar
- Keine k-Wahl vorab nötig: Die Cluster-Anzahl kann nachträglich bestimmt werden
- Determinismus: Reproduzierbare Ergebnisse
- Interpretierbarkeit: Das Dendrogramm zeigt alle Cluster-Beziehungen
Nachteile des Hierarchischen Clustering:
- Komplexität: O(n³) Zeit, O(n²) Speicher
- Skalierbarkeit: Problematisch für sehr große Datensätze
- Sensitivität: Frühe Entscheidungen sind nicht korrigierbar
- Outlier-Einfluss: Ausreißer können die gesamte Struktur beeinflussen
10.3.4 Beispiel: Clustern von Beispieldaten¶
Hier möchte ich Ihnen nun wieder an Beispieldaten zeigen, was Clustering zu leisten im Stande ist. Wir sehen uns konkret die drei besprochenen Algorithmen im Vergleich an.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons, make_blobs # make_blobs is not used but kept for easy switching
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram, linkage
import pandas as pd # For potential tabular summaries if needed, not used in plots
# --- 0. Setup and Data Generation ---
# Using make_moons as it's good for showing differences between algorithms
n_samples = 200
X, y_true = make_moons(n_samples=n_samples, noise=0.1, random_state=42)
# Standardize the data - good practice for many clustering algorithms, esp. DBSCAN (eps) and K-Means
X_scaled = StandardScaler().fit_transform(X)
# Updated Helper Function for Plotting Clusters
predefined_colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b']
noise_color = 'silver'
def plot_clusters(X_data, labels, title, ax, centers=None, algorithm_name=""):
unique_labels = np.sort(np.unique(labels))
plot_handles = []
plot_labels_list = []
for k_label in unique_labels:
is_noise = (k_label == -1 and algorithm_name == "DBSCAN")
# Assign color and marker
if is_noise:
current_color = noise_color
marker = 'x'
label_text = 'Noise'
else:
color_idx = k_label if k_label >= 0 else 0
current_color = predefined_colors[color_idx % len(predefined_colors)]
marker = 'o'
label_text = f'Cluster {k_label}'
class_member_mask = (labels == k_label)
xy = X_data[class_member_mask]
# Conditional edgecolor
scatter_kwargs = {
's': 50,
'c': [current_color], # Pass color as a list for a single color to avoid colormap interpretation
'marker': marker,
'label': label_text,
'alpha': 0.8
}
if not is_noise: # Only add edgecolor for non-noise points (e.g., marker 'o')
scatter_kwargs['edgecolor'] = 'k'
handle = ax.scatter(xy[:, 0], xy[:, 1], **scatter_kwargs)
plot_handles.append(handle)
plot_labels_list.append(label_text)
if centers is not None:
center_handle = ax.scatter(centers[:, 0], centers[:, 1], s=250, marker='P',
c='red', edgecolor='black', label='Centroids', alpha=0.9)
plot_handles.append(center_handle)
plot_labels_list.append('Centroids')
ax.set_title(title, fontsize=14)
ax.set_xlabel("Feature 1 (Scaled)", fontsize=12)
ax.set_ylabel("Feature 2 (Scaled)", fontsize=12)
ax.legend(loc='best', fontsize=10)
ax.grid(True, linestyle='--', alpha=0.6)
print(f"Generated {n_samples} samples using make_moons with noise=0.1 and scaled them.")
# --- 1. K-Means Clustering ---
print("\n--- K-Means Clustering ---")
# We expect 2 clusters for make_moons
k_values = [2, 3, 4] # Number of clusters to try
fig_kmeans, axes_kmeans = plt.subplots(1, len(k_values), figsize=(5 * len(k_values), 5))
if len(k_values) == 1: axes_kmeans = [axes_kmeans] # Ensure axes_kmeans is iterable
for i, k in enumerate(k_values):
# print(k, "clusters")
kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
labels_kmeans = kmeans.fit_predict(X_scaled)
centers_kmeans = kmeans.cluster_centers_
plot_clusters(X_scaled, labels_kmeans, f'K-Means (k={k})', axes_kmeans[i], centers=centers_kmeans, algorithm_name="KMeans")
plt.tight_layout()
plt.show()
# --- 2. DBSCAN Clustering ---
print("\n--- DBSCAN Clustering ---")
# For DBSCAN, `eps` and `min_samples` are important.
# `eps` is the maximum distance between two samples for one to be considered as in the neighborhood of the other.
# `min_samples` is the number of samples in a neighborhood for a point to be considered as a core point.
# These values are dataset-dependent. For scaled make_moons with noise=0.1:
eps_values = [0.2, 0.3, 0.4] # Epsilon values to try
min_samples_fixed = 5
fig_dbscan, axes_dbscan = plt.subplots(1, len(eps_values), figsize=(5 * len(eps_values), 5))
if len(eps_values) == 1: axes_dbscan = [axes_dbscan]
for i, eps in enumerate(eps_values):
dbscan = DBSCAN(eps=eps, min_samples=min_samples_fixed)
labels_dbscan = dbscan.fit_predict(X_scaled)
plot_clusters(X_scaled, labels_dbscan, f'DBSCAN (eps={eps}, min_pts={min_samples_fixed})', axes_dbscan[i], algorithm_name="DBSCAN")
num_clusters = len(np.unique(labels_dbscan)) - (1 if -1 in labels_dbscan else 0)
axes_dbscan[i].set_title(f'DBSCAN (eps={eps}, min_pts={min_samples_fixed})\nFound {num_clusters} clusters + Noise', fontsize=12)
plt.tight_layout()
plt.show()
# --- 3. Hierarchical Agglomerative Clustering ---
print("\n--- Hierarchical Agglomerative Clustering ---")
linkage_methods = ['ward', 'complete', 'average'] # 'ward' minimizes variance, 'complete' uses max distance, 'average' uses avg distance
n_clusters_hierarchical = 2 # We expect 2 clusters for make_moons
# Plot Dendrograms
fig_dendrogram, axes_dendrogram = plt.subplots(1, len(linkage_methods), figsize=(6 * len(linkage_methods), 5.5))
if len(linkage_methods) == 1: axes_dendrogram = [axes_dendrogram]
print("Plotting Dendrograms...")
for i, method in enumerate(linkage_methods):
# Calculate linkage matrix
linked_matrix = linkage(X_scaled, method=method)
dendrogram(linked_matrix,
ax=axes_dendrogram[i],
orientation='top',
distance_sort='descending',
show_leaf_counts=False, # True can be very cluttered for n_samples > 30
truncate_mode='lastp', # Show only the last p merged clusters
p=12) # Number of merged clusters to show at the bottom
axes_dendrogram[i].set_title(f'Dendrogram ({method} linkage)', fontsize=14)
axes_dendrogram[i].set_xlabel("Sample index or (Cluster size)", fontsize=12)
axes_dendrogram[i].set_ylabel("Distance", fontsize=12)
axes_dendrogram[i].grid(axis='y', linestyle='--', alpha=0.6)
plt.tight_layout()
plt.show()
# Plot Cluster Assignments from Hierarchical Clustering
fig_hierarchical, axes_hierarchical = plt.subplots(1, len(linkage_methods), figsize=(5 * len(linkage_methods), 5))
if len(linkage_methods) == 1: axes_hierarchical = [axes_hierarchical] # Ensure iterable
print("\nPlotting Cluster Assignments from Hierarchical Clustering...")
for i, method in enumerate(linkage_methods):
# Ward linkage is only defined for euclidean distance
# For other linkages, affinity can be specified if not euclidean.
# AgglomerativeClustering uses euclidean by default.
if method == 'ward' and X_scaled.shape[0] < 2: # Ward requires at least 2 samples
axes_hierarchical[i].text(0.5, 0.5, 'Ward linkage requires >=2 samples', horizontalalignment='center', verticalalignment='center')
axes_hierarchical[i].set_title(f'Hierarchical ({method} linkage)', fontsize=14)
continue
hac = AgglomerativeClustering(n_clusters=n_clusters_hierarchical, linkage=method)
labels_hac = hac.fit_predict(X_scaled)
plot_clusters(X_scaled, labels_hac, f'Hierarchical ({method} linkage)\n{n_clusters_hierarchical} clusters', axes_hierarchical[i], algorithm_name="HAC")
plt.tight_layout()
plt.show()
print("\n--- End of Unsupervised Clustering Demo ---")
Generated 200 samples using make_moons with noise=0.1 and scaled them. --- K-Means Clustering ---

--- DBSCAN Clustering ---

--- Hierarchical Agglomerative Clustering --- Plotting Dendrograms...

Plotting Cluster Assignments from Hierarchical Clustering...

--- End of Unsupervised Clustering Demo ---
10.4 Übungsaufgabe: Supervised Learning mit Scikit-Learn¶
Nachdem wir die theoretischen Grundlagen des Supervised Learning kennengelernt haben, ist es Zeit, diese Konzepte in der Praxis anzuwenden! In dieser Übung werden wir das Wine Quality Dataset verwenden, um die Qualität von portugiesischem Vinho Verde Wein basierend auf seinen chemischen Eigenschaften vorherzusagen.
Hier sind die Details zu diesem Datensatz:
This dataset is public available for research. The details are described in [Cortez et al., 2009]. Please include this citation if you plan to use this database:
P. Cortez, A. Cerdeira, F. Almeida, T. Matos and J. Reis. Modeling wine preferences by data mining from physicochemical properties. In Decision Support Systems, Elsevier, 47(4):547-553. ISSN: 0167-9236.
Available at: [@Elsevier] http://dx.doi.org/10.1016/j.dss.2009.05.016
[Pre-press (pdf)] http://www3.dsi.uminho.pt/pcortez/winequality09.pdf
Hier zunächst der Code, um den Datensatz für Rotwein zu laden. Kommentieren Sie die entsprechenden Zeilen aus oder ein, um den zweiten Datensatz für Weißwein zu laden.
import pandas as pd
import os
# Define the expected column names based on your provided string
# (Pandas should pick these up automatically from the header row if the CSV is well-formed)
# column_names = [
# "fixed acidity", "volatile acidity", "citric acid", "residual sugar",
# "chlorides", "free sulfur dioxide", "total sulfur dioxide", "density",
# "pH", "sulphates", "alcohol", "quality"
# ]
# Define the file path
file_path = os.path.join('data','winequality-red.csv')
# file_path = os.path.join('data','winequality-white.csv')
try:
# Read the CSV file. Based on your column string, the delimiter is likely a semicolon.
wine_df = pd.read_csv(file_path, sep=';')
print("Successfully loaded winequality-red.csv")
print("\n--- First 5 Rows (head) ---")
print(wine_df.head())
print("\n--- Statistical Summary (describe) ---")
print(wine_df.describe())
print("\n--- Data Info (dtypes and non-null counts) ---")
print(wine_df.info())
except FileNotFoundError:
print(f"Error: The file '{file_path}' was not found.")
print("Please make sure the file is in the correct directory or provide the full path.")
except Exception as e:
print(f"An error occurred while reading or processing the file: {e}")
Successfully loaded winequality-red.csv
--- First 5 Rows (head) ---
fixed acidity volatile acidity citric acid residual sugar chlorides \
0 7.4 0.70 0.00 1.9 0.076
1 7.8 0.88 0.00 2.6 0.098
2 7.8 0.76 0.04 2.3 0.092
3 11.2 0.28 0.56 1.9 0.075
4 7.4 0.70 0.00 1.9 0.076
free sulfur dioxide total sulfur dioxide density pH sulphates \
0 11.0 34.0 0.9978 3.51 0.56
1 25.0 67.0 0.9968 3.20 0.68
2 15.0 54.0 0.9970 3.26 0.65
3 17.0 60.0 0.9980 3.16 0.58
4 11.0 34.0 0.9978 3.51 0.56
alcohol quality
0 9.4 5
1 9.8 5
2 9.8 5
3 9.8 6
4 9.4 5
--- Statistical Summary (describe) ---
fixed acidity volatile acidity citric acid residual sugar \
count 1599.000000 1599.000000 1599.000000 1599.000000
mean 8.319637 0.527821 0.270976 2.538806
std 1.741096 0.179060 0.194801 1.409928
min 4.600000 0.120000 0.000000 0.900000
25% 7.100000 0.390000 0.090000 1.900000
50% 7.900000 0.520000 0.260000 2.200000
75% 9.200000 0.640000 0.420000 2.600000
max 15.900000 1.580000 1.000000 15.500000
chlorides free sulfur dioxide total sulfur dioxide density \
count 1599.000000 1599.000000 1599.000000 1599.000000
mean 0.087467 15.874922 46.467792 0.996747
std 0.047065 10.460157 32.895324 0.001887
min 0.012000 1.000000 6.000000 0.990070
25% 0.070000 7.000000 22.000000 0.995600
50% 0.079000 14.000000 38.000000 0.996750
75% 0.090000 21.000000 62.000000 0.997835
max 0.611000 72.000000 289.000000 1.003690
pH sulphates alcohol quality
count 1599.000000 1599.000000 1599.000000 1599.000000
mean 3.311113 0.658149 10.422983 5.636023
std 0.154386 0.169507 1.065668 0.807569
min 2.740000 0.330000 8.400000 3.000000
25% 3.210000 0.550000 9.500000 5.000000
50% 3.310000 0.620000 10.200000 6.000000
75% 3.400000 0.730000 11.100000 6.000000
max 4.010000 2.000000 14.900000 8.000000
--- Data Info (dtypes and non-null counts) ---
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1599 entries, 0 to 1598
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 fixed acidity 1599 non-null float64
1 volatile acidity 1599 non-null float64
2 citric acid 1599 non-null float64
3 residual sugar 1599 non-null float64
4 chlorides 1599 non-null float64
5 free sulfur dioxide 1599 non-null float64
6 total sulfur dioxide 1599 non-null float64
7 density 1599 non-null float64
8 pH 1599 non-null float64
9 sulphates 1599 non-null float64
10 alcohol 1599 non-null float64
11 quality 1599 non-null int64
dtypes: float64(11), int64(1)
memory usage: 150.0 KB
None
Unser Dataset enthält 1599 Rotwein-Samples mit 11 chemischen Features:
- Säuren: Fixed acidity, volatile acidity, citric acid
- Zucker und Salze: Residual sugar, chlorides
- Schwefelverbindungen: Free/total sulfur dioxide, sulphates
- Physikalische Eigenschaften: Density, pH, alcohol
Die Zielvariable ist eine Qualitätsbewertung von 0-10, unter “quality”.
Die Schritte für diese Übungsaufgabe sind¶
- Nehmen Sie sich etwas Zeit für die Daten und versuchen Sie, ein Gefühl für den Datensatz zu bekommen
- Diskutieren Sie mit Ihrem LLM zunächst, ob bereits mit Unsupervised ML-Methoden sinnvolle Erkenntnisse über die Daten gewonnen werden könnten
- Diskutieren Sie dann mit Ihrem LLM, welche ML-Modelle hier für Supervised Learning in Frage kommen; das Supervised Learning ist die Hauptaufgabenstellung
- Trainieren Sie ein Supervised Modell (oder auch mehrere verschiedene, die Sie geeignet finden)
- Sehen Sie sich die Evaluations-Metriken an und Visualisieren Sie die Ergebnisse, um ein Gefühl dafür zu bekommen, was gut funktioniert hat und was nicht
- Versuchen Sie, wenn möglich, belegbare sinnvolle Schlussfolgerungen darüber zu ziehen, welche Eigenschaften für die Qualität des Weins wichtig oder sogar wesentlich sind. Kann man die Ergebnisse irgendwie praktisch verwenden?
- Wenn Zeit bleibt, vergleichen Sie die Situation für Rot- und Weißweine. Sind Ihre Schlussfolgerungen auf beide Arten anwendbar? Gibt es generell irgendwelche Unterschiede zwischen rot und weiß?
Viel Vergnügen und gutes Gelingen!



























































































