Tag Archive for: Decision Tree

Wieviele Trainungsbeispiele benötigen Lernverfahren? (1/2)

Kurz nach der Jahrtausendwende begann das Zeitalter der digitalen Daten. Seitdem übertrifft die Menge der digitalen Daten die der Analogen [HL11] und dem Maschinellen Lernen stehen enorme Datenmengen zur Verfügung. Unter dem Buzzword „big data“ wird dabei meist nur das reine Volumen gesehen, andere Faktoren, wie die Frequenz mit der die Daten zu verarbeiten sind und die Variabilität der Formate werden oft vernachlässigt, obwohl auch solche Daten unter „big data“ zusammengefasst werden. Betrachtet man das Volumen dann spielen zwei Faktoren eine zentrale Rolle, die das „big“ von „big data“ ausmachen: die Anzahl der Beispieldatensätze und – und dies wird häufig übersehen – die Anzahl der Eigenschaften mit denen die Beispieldaten beschrieben werden.
Wenn von „big data“ gesprochen wird, wird dabei oft angenommen, dass genügend Datensätze vorhanden sind. Für bestimmte Anwendungen jedoch, müssen die Daten in unterschiedliche Gruppen unterschieden werden, um beim Lernen nicht Äpfel und Birnen in einen Topf zu werfen. In solchen Fällen kann es leicht passieren, dass pro Gruppe zu wenig Beispieldaten vorhanden sind und die Frage an Bedeutung gewinnt: „Reichen die Datensätze eigentlich aus, um ein Vorhersagemodel mit einer gewissen Mindestgüte zu lernen?“.
Leider gibt es bisher keine einfache Antwort auf diese Frage, da diese neben der Anzahl der Eigenschaften – der Dimensionalität – der Daten, von der Struktur des Datenraums, der Verteilung der Daten in diesem Raum, dem verwendeten Lernverfahren, der Ausdrucksfähigkeit seiner Hypothesenrepräsentation und seiner endgültigen Parametrisierung abhängt. In der “Computational Learning Theory” wurden jedoch Ansätze zur Abschätzungen von Untergrenzen erarbeitet, die, unter der Annahme idealer Lernverfahren, zu mindestens eine Aussage über die benötigte Mindestmenge an Trainingsdaten gestatten.
Ziel dieses Beitrags ist es auf möglichst anschauliche Art und Weise anhand eines praktischen Beispiels zu zeigen, welchen Einfluss die Dimensionalität der Daten auf die Abschätzung der Anzahl der benötigten Beispiele für das Erlernen von Vorhersagemodellen – genauer einfachen Klassifikationsmodellen[1] – hat und welche Methoden hierfür existieren. In diesem ersten Teil liegt das Hauptaugenmerk auf endlichen Daten- und Hypothesenräumen und wir werden sehen, dass selbst für eine kleine Anzahl von Eigenschaften – sprich Dimensionen – nützliche Aussagen nur für sehr einfache Hypothesenrepräsentationen möglich sind. Im zweiten Teil werden wir einen Abschätzungsansatz betrachten, der die „Unterscheidungsstärke“ unterschiedlicher Lernverfahren berücksichtigt und mit dem auch Abschätzungen für unendliche Daten- und Hypothesenräume möglich werden.

Anwendungsbeispiel

Betrachten wir das Beispiel eines Online-Shops, der Produkte über das Internet verkauft und dessen Produkte klassifiziert werden sollen. Wie die Produkte klassifiziert werden sollen ist für unsere Betrachtungen unerheblich, was wir aber im Kopf haben sollten: der Absatz unterschiedlicher Produkte folgt einer Potenzverteilung. Eine kleine Zahl von Produkten wird sehr häufig verkauft, so dass für sie viele Datensätze existieren (solche Produkte werden gewöhnlicher Weise in konventionellen Geschäften vertrieben, die nur begrenzte Lagerkapazitäten haben). Der Großteil der Produkte wird jedoch eher seltener umgesetzt (auch als „long tail“ bezeichnet), so dass die Anzahl ihrer Datensätze gering ist; u.U. so gering, dass für sie keine verlässlichen Vorhersagemodelle erlernbar sind.

Zur Illustration gehen wir davon aus, dass in dem Online-Shop Produkte von 500 Marken verkauft werden und diese Produkte neben ihrer Marke durch ihre Größe (10 mögliche Werte), ihre Farbe (20 mögliche Werte), die ersten drei Ebenen der Google Produktkategorien (auf der dritten Ebene 500 mögliche Werte) und ihren Preis (im Bereich 0,49 – 100 €) beschrieben werden.

In diesem Kontext besitzt die Antwort auf die Frage: „Wie viele Daten werden überhaupt für ein Lernverfahren benötigt?“ offensichtlich konkreten Nutzen,

  • da wir abschätzen können, ob für ein konkretes Produkt überhaupt ein sinnvolles Vorhersagemodell erlernbar ist,
  • da wir aus der Abschätzung auf die Dauer der Datensammlung schließen können und
  • um ggf. die Daten von selten verkauften Produkten inhaltlich oder zeitlich zu aggregieren.

Was uns vorweg klar sein sollte

Die Daten, die wir zum Erlernen von Vorhersagemodellen verwenden, werden durch Eigenschaften (normalerweise als Feature, in der Statistik auch als Variablen bezeichnet) beschrieben. Die Eigenschaften werden in beobachtete und abhängige Eigenschaften (im Maschinellen Lernen auch als Label bezeichnet) unterschieden. Die Wertebereiche der Eigenschaften können in endliche und unendliche Wertebereich unterschieden werden.

Wir können nicht erwarten, dass ein Lernverfahren ein 100%ig korrektes Modell erlernt. Lernverfahren versuchen durch einen induktiven Schluss aus Daten ein Vorhersagemodell zu ermitteln. Da die zur Verfügung stehende Datenmenge immer begrenzt sein wird und die Daten damit realistischer Weise unvollständig sein werden, Messfehler und Inkonsistenzen enthalten können, kann auch ein erlerntes Modell niemals 100%ig korrekt sein.

Viele unterschiedliche Modelle können konsistent mit den verfügbaren Daten sein. Ziel des Lernverfahrens ist es daher mit den verfügbaren Daten das bestmögliche Vorhersagemodell zu ermitteln.

Wir müssen in Kauf nehmen, dass unbekannte, zukünftige oder ungewöhnliche Daten zu fehlerhaften Vorhersagen führen. Zum Lernzeitpunkt ist nur ein Ausschnitt aller Daten verfügbar. Zukünftig erhobene Daten können Veränderungen unterliegen oder es können bisher noch nicht gesehene Fälle auftreten, auf die das erlernte Modell nicht mehr richtig passt.

Aus diesen Fakten ergibt sich die einzig realistische Annahme: ein gutes Lernverfahren soll mit großer Wahrscheinlichkeit eine gute Näherung des richtigen Vorhersagemodells erlernen.

Anzahl benötigter Trainingsfälle

Zur Abschätzung der Anzahl benötigter Trainingsfälle – als Beispielkomplexität (sample complexity) bezeichnet – wurden in der Computational Learning Theory unterschiedliche Ansätze entwickelt. Diese Ansätze beschreiben für idealisierte Lernverfahren unter welchen Bedingungen probabilistisch, approximativ, korrektes Lernen (PAC learning) effizient möglich ist. Grundlegend für die Einsetzbarkeit dieser Ansätze ist die Unterscheidung, ob das Lernen in einem endlichen oder unendlichen Hypothesenraum erfolgt, und ob das Lernverfahren konsistente Hypothesen oder nur näherungsweise Hypothesen, z.B. beim Vorliegen von Messfehlern, zu den Daten erlernen kann.

Endliche Datenräume

Sofern die Daten nur durch nominelle Eigenschaften mit endlichen Wertebereichen beschrieben werden[2], lässt sich die Größe des Datenraums relativ einfach bestimmen. Die folgende Tabelle beschreibt für die wichtigsten nominellen Eigenschaftstypen Größenfaktoren, die im Folgenden zur vereinheitlichten Darstellung verwendet werden:

Type
t
Fehlende Werte (NA) ? Größe des Wertebereichs
n
Größenfaktor g(t)
Boolean Nein 2 2
Boolean Ja 2 3
Nominal (Menge) Nein n_t n_t
Nominal (Menge) Ja n_t n_t+1

Die Größe eines endlichen d-dimensionalen Datenraums D kann allgemein mit folgender Formel bestimmt werden |D| = \prod_{i=1}^d{g(t_i)}.

Das Lernproblem besteht darin: aus einer Teilmenge von Trainingsbeispielen S  aus dem Datenraum D, i.e. S \subset D, die ein Trainer dem Lernverfahren vorgibt, um Zielkonzept c zu erlernen, eine Hypothese aus dem Hypothesenraum h \in H des Lernverfahrens zu ermitteln, welche (möglichst) alle positiven Beispiel S_p  umfasst und (möglichst) alle negativen Beispiele S_n  ausschließt.

Einfache Hypothesenrepräsentation

Die einfachste Hypothesenrepräsentation, in der Lernen, welches über einfaches Erinnern hinausgeht, sinnvoll ist, sind Disjunktionen von Bool’schen Eigenschaften. Eine Beispielanwendung für die diese Repräsentation Sinn macht, ist das Erkennen von Spam-Emails anhand des Vorliegens unterschiedlicher alternativer Eigenschaften, die Spam-Emails charakterisieren. Der Hypothesenraum dieser Sprache besitzt eine Größe von |H| = 2^d [FoDS18]. Ein Beispiel für ein verbreitetes Lernverfahren, das eine Hypothesenrepräsentation dieses Typs nutzt, ist Naive Bayes.

Beliebige nominelle Eigenschaften können durch One-Hot- oder Dummy-Encoding als Bool’sche Variablen kodiert werden. Damit ergibt sich zum Erlernen von Disjunktionen kodierter, Bool’scher Eigenschaften die Größe des Hypothesenraums als |H| = 2^{\sum_{i=1}^d{g(t_i)}}.

Um unser Produktbeispiel in dieser Sprache zu repräsentieren, müssen die Eigenschaften geeignet kodiert werden, z.B. durch One-Hot- oder Dummy-Encoding, bei dem jeder Wert einer Eigenschaft durch eine neue bool’sche Variable kodiert wird. Hieraus ergeben sich im Fall von One-Hot-Encoding 500+10+20+500+9941=10.971 und im Fall von Dummy-Encoding 499+9+19+499+9940=10.966 neue Bool’sche Eigenschaften.

Eigenschaftsvektoren (Feature-Vektoren, bzw. Konjunktionen von Eigenschaften) stellen die nächstkomplexere Repräsentationssprache dar, die, solange sie nicht um ein Konstrukt zur Verallgemeinerung erweitert wird, sehr unspektakulär ist, da Beispiele mit ihr lediglich erinnert werden. Erst wenn ein „don’t care“-Symbol, wie z.B. „?“, für beliebige Eigenschaftswerte hinzugefügt wird, wird die extremste Form von Generalisierung möglich, die von einzelnen Werten gleich auf alle Werte generalisiert [ML97]. Durch das „don’t care“-Symbol wird der Größenfaktor g um einen weiteren Wert erhöht. Für diese Repräsentation beträgt die Größe des Hypothesenraums  über rein bool‘schen Eigenschaften (inkl. „don’t care“)  |H| = 3^d und für allgemeine endliche Eigenschaften|H| = \prod_{i=1}^d{(g(t_i)+1)}. Diese Repräsentation ist sehr eingeschränkt und erlaubt es nur einzelne und keine kombinierten Konzepte zu erlernen. Sie ist daher eigentlich nur von theoretischem Interesse und wird – soweit bekannt – in keinem praktisch eingesetzten Lernverfahren genutzt.

Interessanter ist eine Verallgemeinerung dieser Repräsentationssprache, die k-CNF (konjunktive Normalform), die aus einer Konjunktion von Disjunktionen der Länge k besteht, die sowohl polynomielle Beispiel- als auch Zeitkomplexität besitzt [ML97] und für die ein effizienter Algorithmus existiert. Diese Repräsentation lässt sich auch auf einen d-dimensionalen Eigenschaftsvektor übertragen, in dem für jede Eigenschaft Generalisierungen über beliebige Teilmengen erlaubt werden. Die Größe des Hypothesenraums dieser Sprache beträgt |H| = \prod_{i=1}^d{2^{g(t_i)}} = 2^{\sum_{i=1}^d{g(t_i)}}. Mit dieser Sprache können alle Eigenschaften zwar separat auf beliebige Teilmengen generalisiert werden, Korrelationen zwischen Eigenschaften werden jedoch nicht berücksichtigt.

Für Repräsentationssprachen, die keinerlei Einschränkungen machen, besitzt der Hypothesenraum für Daten mit d bool‘schen Eigenschaften eine Größe von |H| = 2^{2^d}. Auf beliebige endliche Eigenschaften übertragen, kann diese Aussage zu |H| = 2^{|D|} = 2^{\prod_{i=1}^d{g(t_i)}} verallgemeinert werden.

Wie aus diesen Abschätzungen ersichtlich wird, hat die Dimensionalität d der Daten einen direkten Einfluss auf die Größe des Hypothesenraums und damit auf die Anzahl der von einem Lernverfahren zu berücksichtigenden Konzepte.

Realistische Hypothesenrepräsentation

Bis auf einfache Disjunktionen bool’scher Eigenschaften, sind einfache Hypothesenrepräsentationen entweder zu ausdrucksschwach, so dass nützliche Konzepte kaum ausdrückbar sind, oder zu ausdrucksstark, so dass Lernen in vertretbarer nicht-exponentieller Zeit nicht möglich ist. Die gängigen Lernverfahren, wie k-Nearest Neighbors, Naive Bayes, Decision Trees, Random Forrests, AdaBoost, XGBoost, Logistic Regression, Support Vector Machines und Neuronale Netze, etc. beschränken durch spezifische Annahmen (inductive bias) den Hypothesenraum, um so nützliche Konzepte in vernünftiger Zeit zu erlernen.

Leider lassen sich nur für wenige der real eingesetzten Verfahren Abschätzungen für die Größe des Hypothesenraums finden.

Verfahren |H| Parameter
Boolean-coded Naive Bayes 2^{\sum_{i=1}^d{g(t_i)}}
Boolean-coded Decision Trees[3] 2^{\sum_{i=1}^d{g(t_i)}}
Boolean-coded Decision Trees with limited depth [4] 2(2^k-1)(1+log_2{⁡\sum_{i=1}^d{g(t_i)}} ) +1 k = Tiefenbegrenzung

Lernen eines zu allen Trainingsdaten konsistenten Konzepts (aka Overfitting)

Unter der Annahme eines idealen Lernalgorithmus, kann die Größe des Hypothesenraums dazu verwendet werden die Anzahl der Trainingsdaten m die ein „konsistenter Lernalgorithmus“[5] benötigt, um ein beliebiges Konzept mit einem maximalen Fehler \epsilon und einer Unsicherheit \delta (bzw. einer Wahrscheinlichkeit von 1 - \delta ) zu erlernen, abgeschätzt werden mit[6]

    \[m \geq \frac{1}{\epsilon}(ln{(|H|)} + ln{(\frac{1}{\delta})})\]

Nehmen wir für unser Beispielszenario an Produkt A wird stündlich im Durchschnitt 100 mal verkauft und Produkt B wird jeden Tag im Schnitt nur 10 mal verkauft.  Zur Vereinfachung nehmen wir weiter an, die Produkte werden jeden Tag – egal ob Wochentag oder Wochenende – nur zwischen 6:00 und 20:00 Uhr verkauft. Pro Monat erhalten wir für Produkt A 42.000 Datensätze und für Produkt B 300 Datensätze.

Der Datenraum D hat eine Größe von |D| = 500*10*20*500*9941 \approx 497 Mrd. Punkten. Mit einer einfachen bool’schen Kodierung ergibt sich d = 500+10+20+500+9951 = 10.971 und |H| = 2^{10.961}.

Wollten wir Datensätze dieser Produkte mit einem Fehler \epsilon von maximal 10% und einer maximalen Unsicherheit \delta = 5% – wie auch immer – klassifizieren, so würden wir für den Einsatz von Naive Bayes oder unbegrenzten DecisionTrees mindestens 76.145 Datensätze benötigen. Weder die monatlichen Daten von Produkt A noch Produkt B würden ausreichen.

Mit einem tiefenbeschränkten Entscheidungsbaum-Verfahren mit 5 Stufen, sind, ungeachtet der Qualität des Lernergebnisses, die Daten von Produkt A und B ausreichend, um die Anforderungen an \epsilon und \delta einzuhalten, da nur mindestens 91 Datensätze benötigt werden.

Ein, dieser Abschätzung zugrundeliegender, idealer Lernalgorithmus, ist jedoch für praktische Anwendungen unrealistisch, da er zwar für die Trainingsdaten ein konsistentes Konzept ermitteln würde, welches aber bei unbekannten, neuen Daten versagen kann. Der angenommene Lernalgorithmus unterliegt der „Überanpassung“ (overfitting).

Nichts desto trotz ist diese Abschätzungsformel hilfreich, da sie eine Aussage erlaubt, wie viele Trainingsbeispiele im besten Fall ausreichen, um mit einem idealen Lernverfahren ein Konzept mit einem maximalen Fehler von \epsilon und einer Unsicherheit von höchstens \delta zu erlernen, das in der genutzten Hypothesenrepräsentation ausdrückbar ist.

Agnostisches Lernen eines Konzeptes, das möglichst gut zu den Trainingsdaten passt

Überanpassung wollen wir in der Regel vermeiden, damit die erlernten Vorhersagemodelle auch auf unbekannte, fehlerbehaftete oder teilweise inkonsistente Daten anwendbar sind. Anders ausgedrückt: das zu erlernende Konzept c kann etwas außerhalb des Hypothesenraums liegen, der durch das eingesetzte Lernverfahren erfasst wird. Dies bedeutet, dass wir im Hypothesenraum des Lernverfahrens nur eine Näherung c' erlernen können, die möglichst gut sein sollte. Solch ein – als agnostisch bezeichnetes – Lernverfahren muss daher bestrebt sein den Fehler zwischen den Trainingsdaten und dem Fehler der sich durch das Erlernen der Näherung c' ergibt möglichst klein zu halten.

Auch hierfür kann, unter der Annahme eines idealen Lernalgorithmus, die Größe des Hypothesenraums dazu verwendet werden die Anzahl der Trainingsdaten m die ein „agnostisches Lernverfahren“ benötigt, um eine gute Näherung an das zu erlernende Konzept in einem endlichen Hypothesenraum mit einem maximalen Fehler \epsilon und einer Unsicherheit \delta (bzw. einer Wahrscheinlichkeit von 1 - \delta) zu erlernen, abgeschätzt werden mit[6]

    \[m \geq \frac{1}{2\epsilon^2}(ln{(|H|)} + ln{(\frac{2}{\delta})})\]

Auf das Beispiel angewendet müsste sich – unter der Annahme gleicher Rahmenbedingungen – die Mindestzahl von Trainingsbeispielen auf m = 490 belaufen. D.h. die Daten von Produkt A könnten zum Lernen der Klassifikation verwendet werden, die Datenmenge für Produkt B wäre jedoch nicht ausreichend.

Folgerung

Mit diesem ersten Beitrag haben wir anhand eines kleinen realen Beispiels gezeigt, wie sich für einen idealen Lernalgorithmus über die Betrachtung der Größe endlicher Hypothesenräume, die Mindestanzahl der benötigten Trainingsbeispiel abschätzen lässt.

Auch wenn es sich hierbei um eine idealisierte Betrachtung handelt, erlauben solche Abschätzungen Aussagen darüber, wann Lernverfahren nur mit einem größeren Fehler behaftet einsetzbar sind.

Diese Betrachtung erstreckte sich bisher nur über endliche Eigenschaften und berücksichtigt die Komplexität der Hypothesenrepräsentation – eine der wesentlichen Eigenschaften eines Lernverfahrens – noch nicht. Dies wird Thema des zweiten Teils sein, in dem wir sehen werden, wie sich Abschätzung auf der Basis der – sogenannten – Vapnik-Chervonenkis-Dimension (VC-Dimension) für viele gängige Klassen von Lernverfahren einsetzen lassen.

Fußnoten

[1] Wir betrachten hierbei nur rein binäre, binomiale resp. Bool’sche Klassifikationsprobleme, deren Aussagen sich jedoch auch auf multinomiale Klassifikation und reell-wertige Vorhersagemodelle übertragen lassen (siehe [ESL09], Seite 238).

[2] Unendlich, überabzählbare Eigenschaften lassen sich in Abhängigkeit vom Anwendungsproblem und der erforderlichen Genauigkeit oft diskretisieren und als ordinale Daten oder Intervalle ganzer Zahlen repräsentieren, wie z.B. Alter, Körpergröße, Längen, Temperatur, und Zeitintervalle usw., wenn es ausreichend ist diese mit einer Genauigkeit von Jahren, cm, mm, Zehntelgrad oder Sekunden zu erfassen.

[3] Vollausgebaute Decision Trees unterliegen der Gefahr der „Überanpassung“ (overfitting) und werden in der Regel gestutzt, um dies zu vermeiden. Die Abschätzung stellt daher die Obergrenze dar.

[4] http://www.cs.cmu.edu/~guestrin/Class/10701/slides/learningtheory-bigpicture.pdf  und https://www.autonlab.org/_media/tutorials/pac05.pdf (Letzter Zugriff: 10.3.2018)

[5] Ein „konsistenter Lernalgorithmus“ erlernt Hypothesen, die – wann immer möglich – perfekt zu den Trainingsdaten passen [ML97].

[6] Details zur Ableitung der beschriebenen Untergrenzen finden sich u.a. in [ML97], [FoML12] oder [FoDS18].

Referenzen

[HL11] „The World’s Technological Capacity to Store, Communicate, and Compute Information“, M. Hilbert, P. López, Science 332, 60, 2011, http://www.uvm.edu/pdodds/files/papers/others/2011/hilbert2011a.pdf (letzter Zugriff: 14. März 2018)

[ESL09] “The Elements of Statistical Learning”, T. Hastie, R. Tibshirani, J. Friedman, 2nd Edition, Springer, 2009.

[ML97] „Machine Learning“, T. Mitchell, McGraw-Hill, 1997.

[FoML12] „Foundations of Machine Learning“, M. Mohri, A. Rostamizadeh, A. Talwalkar, The MIT Press, 2012.

[FoDS18] „Foundations of Data Science“, A. Blum, J. Hopcroft, R. Kannan, Cornell University, https://www.cs.cornell.edu/jeh/book.pdf, Jan. 4th, 2018 (letzter Zugriff: 14. März 2018)

ID3-Algorithmus: Ein Rechenbeispiel

Dieser Artikel ist Teil 3 von 4 der Artikelserie Maschinelles Lernen mit Entscheidungsbaumverfahren und nun wollen wir einen Entscheidungsbaum aus Daten herleiten, jedoch ohne Programmierung, sondern direkt auf Papier (bzw. HTML :-).

Folgender Datensatz sei gegeben:

Zeile Kundenart Zahlungsgeschwindigkeit Kauffrequenz Herkunft Zahlungsmittel: Rechnung?
 1  Neukunde  niedrig  niedrig  Inland  false
 2  Neukunde  niedrig  niedrig  Ausland  false
 3  Stammkunde  niedrig  niedrig  Inland  true
 4  Normalkunde  mittel  niedrig  Inland  true
 5  Normalkunde  hoch  hoch  Inland  true
 6  Normalkunde  hoch  hoch  Ausland  false
 7  Stammkunde  hoch  hoch  Ausland  true
 8  Neukunde  mittel  niedrig  Inland  false
 9  Neukunde  hoch  hoch  Inland  true
 10  Normalkunde  mittel  hoch  Inland  true
 11  Neukunde  mittel  hoch  Ausland  true
 12  Stammkunde  mittel  niedrig  Ausland  true
 13  Stammkunde  niedrig  hoch  Inland  true
 14  Normalkunde  mittel  niedrig  Ausland  false

Gleich vorweg ein Disclaimer: Der Datensatz ist natürlich überaus klein, ja gerade zu winzig. Dafür würden wir in der Praxis niemals einen Machine Learning Algorithmus einsetzen. Dennoch bleiben wir besser übersichtlich und nachvollziehbar mit diesen 14 Zeilen. Das Lernziel dieser Übung ist es, ein Gefühl für die Erstellung von Entscheidungsbäumen zu erhalten.
Zu beachten ist ferner, dass dieser Datensatz bereits aggregiert ist, denn eigentlich nummerisch abbildbare Daten wurden in Klassen zusammengefasst.

Das Ziel:

Der Datensatz spielt wieder, welchem Kunden (ID) bisher die Zahlung per Rechnung erlaubt und nicht widerrufen wurde. Das Ziel soll sein, eine Vorhersage darüber zu machen zu können, wann ein Kunde per Rechnung zahlen darf und wann nicht (dann per Vorkasse).

Der Algorithmus:

Wir verwenden den ID3-Algorithmus in seiner Reinform. Der ID3-Algorithmus ist der gängigste Algorithmus zum Aufbau datengetriebener Entscheidungsbäume und es gibt mehrere Abwandlungen. Die Vorgehensweise des Algorithmus wird in dem Teil 2 der Artikelserie Entscheidungsbaum-Algorithmus ID3 erläutert.

1. Schritt: Auswählen des Attributes mit dem höchsten Informationsgewinn

Der Informationsgewinn eines Attributes (A) im Sinne des ID3-Algorithmus ist die Differenz aus der Entropie (E(S)) (siehe Teil 1 der Artikelserie Entropie, ein Maß für die Unreinheit in Daten) des gesamten Datensatzes (S) und der Summe aus den gewichteten Entropien des Attributes für jeden einzelnen Wert (Value i), der im Attribut vorkommt:
IG(S, A) = H(S) - \sum_{i=1}^n \frac{\bigl|S_i\bigl|}{\bigl|S\bigl|} \cdot H(S_i)

1.1 Gesamt-Entropie des Datensatzes berechnen

Erstmal schauen wir uns die Entropie des gesamten Datensatzes an. Die Entropie bezieht sich dabei auf das gewünschte Klassifikationsergebnis, also ist die Zahlung via Rechnung erlaubt oder nicht? Diese Frage wird entweder mit true oder false beantwortet.

H(S) = - \frac{9}{14} \cdot \log_2(\frac{9}{14}) - \frac{5}{14} \cdot \log_2(\frac{5}{14})  = 0.94

1.2 Berechnung der Informationsgewinne aller Attribute

Berechnen wir nun also die Informationsgewinne über alle Spalten.

Attribut Subset Count(true) Count(false)
Kundenart “Neukunde” 2 3
“Stammkunde” 4 0
“Normalkunde” 3 2

Wir zerlegen den gesamten Datensatz gedanklich in drei Kategorien der Kundenart und berechnen die Entropie bezogen auf das Klassifikationsziel:

H(S_{Neukunde}) = - \frac{2}{5} \cdot \log_2(\frac{2}{5}) - \frac{3}{5} \cdot \log_2(\frac{3}{5})  = 0.97

H(S_{Stammkunde}) = - \frac{4}{4} \cdot \log_2(\frac{4}{4}) - \frac{0}{4} \cdot \log_2(\frac{0}{4})  = 0.00

H(S_{Normalkunde}) = - \frac{3}{5} \cdot \log_2(\frac{3}{5}) - \frac{2}{5} \cdot \log_2(\frac{2}{5})  = 0.97

Zur Erinnerung, der Informationsgewinn (Information Gain) wird wie folgt berechnet:

    \[ IG(S, A_{Kundenart}) =  - \sum_{i=1}^n \frac{\bigl|S_i\bigl|}{\bigl|S\bigl|} \cdot H(S_i) \]

Angewendet auf das Attribut “Kundenart”…

    \[ IG(S, A_{Kundenart}) =  H(S) - \frac{\bigl|S_{Neukunde}\bigl|}{\bigl|S\bigl|} \cdot H(S_{Neukunde}) - \frac{\bigl|S_{Stammkunde}\bigl|}{\bigl|S\bigl|} \cdot H(S_{Stammkunde}) - \frac{\bigl|S_{Normalkunde}\bigl|}{\bigl|S\bigl|} \cdot H(S_{Normalkunde}) \]

… erhalten wir der Formal nach folgenden Informationsgewinn:

    \[ IG(S, A_{Kundenart}) =  0.94 - \frac{5}{14} \cdot 0.97 - \frac{4}{14} \cdot 0.00 - \frac{5}{14} \cdot 0.97 = 0.247 \]

Nun für die weiteren Spalten:

Attribut Subset Count(true) Count(false)
Zahlungsgeschwindigkeit “niedrig” 2 2
“mittel” 4 2
“schnell” 3 1

Entropien für die “Zahlungsgeschwindigkeit”:

H(S_{niedrig}) = - \frac{2}{4} \cdot \log_2(\frac{2}{4}) - \frac{2}{4} \cdot \log_2(\frac{2}{4})  = 1.00

H(S_{mittel}) = - \frac{4}{6} \cdot \log_2(\frac{4}{6}) - \frac{2}{6} \cdot \log_2(\frac{2}{6})  = 0.92

H(S_{schnell}) = - \frac{3}{4} \cdot \log_2(\frac{3}{4}) - \frac{1}{4} \cdot \log_2(\frac{1}{4})  = 0.81

So berechnen wir wieder den Informationsgewinn:

    \[ IG(S, A_{Zahlungsgeschwindigkeit}) =  H(S) - \frac{\bigl|S_{niedrig}\bigl|}{\bigl|S\bigl|} \cdot H(S_{niedrig}) - \frac{\bigl|S_{mittel}\bigl|}{\bigl|S\bigl|} \cdot H(S_{mittel}) - \frac{\bigl|S_{schnell}\bigl|}{\bigl|S\bigl|} \cdot H(S_{schnell}) \]

Einsatzen und ausrechnen:

    \[ IG(S, A_{Zahlungsgeschwindigkeit}) =  0.94 - \frac{4}{14} \cdot 1.00 - \frac{6}{14} \cdot 0.92 - \frac{4}{14} \cdot 0.81 = 0.029 \]

Und nun für die Spalte “Kauffrequenz”:

Attribut Subset Count(true) Count(false)
Kauffrequenz “niedrig” 3 4
“hoch” 6 1

Entropien:

H(S_{niedrig}) = - \frac{3}{7} \cdot \log_2(\frac{3}{7}) - \frac{4}{7} \cdot \log_2(\frac{4}{7})  = 0.99

H(S_{hoch}) = - \frac{6}{7} \cdot \log_2(\frac{6}{7}) - \frac{1}{7} \cdot \log_2(\frac{1}{7})  = 0.59

Informationsgewinn:

    \[ IG(S, A_{Kauffrequenz}) =  H(S) - \frac{\bigl|S_{niedrig}\bigl|}{\bigl|S\bigl|} \cdot H(S_{niedrig}) - \frac{\bigl|S_{hoch}\bigl|}{\bigl|S\bigl|} \cdot H(S_{hoch}) \]

Einsetzen und Ausrechnen:

    \[ IG(S, A_{Kauffrequenz}) =  0.94 - \frac{7}{14} \cdot 1.00 - \frac{7}{14} \cdot 0.59 = 0.150 \]

Und last but not least die Spalte “Herkunft”:

Attribut Subset Count(true) Count(false)
Herkunft “Inland” 6 2
“Ausland” 3 3

Entropien:

H(S_{Inland}) = - \frac{6}{8} \cdot \log_2(\frac{6}{8}) - \frac{2}{8} \cdot \log_2(\frac{2}{8})  = 0.81

H(S_{Ausland}) = - \frac{3}{6} \cdot \log_2(\frac{3}{6}) - \frac{3}{6} \cdot \log_2(\frac{3}{6})  = 1.00

Informationsgewinn:

    \[ IG(S, A_{Herkunft}) =  H(S) - \frac{\bigl|S_{Inland}\bigl|}{\bigl|S\bigl|} \cdot H(S_{Inland}) - \frac{\bigl|S_{Ausland}\bigl|}{\bigl|S\bigl|} \cdot H(S_{Ausland}) \]

Einsetzen und Ausrechnen:

    \[ IG(S, A_{Herkunft}) =  0.94 - \frac{8}{14} \cdot 0.81 - \frac{6}{14} \cdot 1.00 = 0.05 \]

2. Schritt: Anlegen des Wurzel-Knotens

Der Informationsgewinn ist für das Attribut “Kundenart” am größten, daher entscheiden wir uns im Sinne des ID3-Algorithmus für dieses Attribut als Wurzel-Knoten.

3. Schritt: Rekursive Wiederholung (!!!)

Nun stellt sich natürlich die Frage: Wie geht es weiter?

Der Algorithmus kann eigentlich nur eines: Einen Wurzelknoten finden. Diesen Vorgang müssen wir nun nur noch rekursiv wiederholen, und das tun wir wie folgt.

Der Datensatz wurde bereits aufgeteilt in die drei Kundenarten. Für jede Kundenart ergibt sich jeweils ein Subset mit den verbleibenden Attributen. Für alle drei Subsets erstellen wir dann wieder einen Wurzelknoten, so dass ein neuer Ast entsteht.

3.1 Erster Rekursionsschritt

Machen wir also weiter und bestimmen wir das nächste Attribut nach der Kundenart, für die Fälle Kundenart = “Neukunde”:

Zeile Kundenart Zahlungsgeschwindigkeit Kauffrequenz Herkunft Zahlungsmittel: Rechnung?
 1  Neukunde  niedrig  niedrig  Inland  false
 2  Neukunde  niedrig  niedrig  Ausland  false
 8  Neukunde  mittel  niedrig  Inland  false
 9  Neukunde  hoch  hoch  Inland  true
 11  Neukunde  mittel  hoch  Ausland  true

Die Entropie des Gesamtdatensatzes (ja, es ist für diesen Schritt betrachtet der gesamte Datensatz!) ist wie folgt:

H(S_{Neukunde}) = - \frac{2}{5} \cdot \log_2(\frac{2}{5}) - \frac{3}{5} \cdot \log_2(\frac{3}{5})  = 0.97

Die Entropie ist weit weg von einer bestimmten Wahrscheinlichkeit (nahe der Gleichverteilung). Daher müssen wir hier nochmal ansetzen und losrechnen:

Entropien für “Zahlungsgeschwindigkeit” bei Neukunden:

H(S_{niedrig}) = 0.00

H(S_{mittel}) = 1.00

H(S_{hoch}) = 0.00

Informationsgewinn des Attributes “Zahlungsgeschwindigkeit” bei Neukunden:

    \[ IG(S_{Neukunde},A_{Zahlungsgeschwindigkeit}) = 0.97 - \frac{3}{5} \cdot 0.00 - \frac{2}{5} \cdot 1.00 -  \frac{1}{5} \cdot 0.00 = 0.57 \]

Betrachtung der Spalte “Kauffrequenz” bei Neukunden:

Entropien für “Kauffrequenz” bei Neukunden:

H(S_{niedrig}) = 0.00

H(S_{hoch}) = 0.00

Informationsgewinn des Attributes “Kauffrequenz” bei Neukunden:

    \[ IG(S_{Neukunde},A_{Kauffrequenz}) = 0.97 - \frac{3}{5} \cdot 0.00 - \frac{2}{5} \cdot 0.00 = 0.97 \]

Betrachtung der Spalte “Herkunft” bei Neukunden:

Entropien für “Herkunft” bei Neukunden:

H(S_{Inland}) = 0.92

H(S_{hoch}) = 1.00

Informationsgewinn des Attributes “Herkunft” bei Neukunden:

    \[ IG(S_{Neukunde},A_{Herkunft}) = 0.97 - \frac{3}{5} \cdot 0.92 - \frac{2}{5} \cdot 1.00 = 0.018 \]

Wir entscheiden uns also für das Attribut “Kauffrequenz” als Ast nach der Entscheidung “Neukunde”, denn dieses Attribut bring uns den größten Informationsgewinn und trennt uns die Unterscheidung für oder gegen das Zahlungsmittel “Rechnung” eindeutig auf.

3.1 Zweiter Rekursionsschritt

Was passiert mit der Kundenart “Stammkunde”?

Zeile Kundenart Zahlungsgeschwindigkeit Kauffrequenz Herkunft Zahlungsmittel: Rechnung?
 3  Stammkunde  niedrig  niedrig  Inland  true
 7  Stammkunde  hoch  hoch  Ausland  true
 12  Stammkunde  mittel  niedrig  Ausland  true
 13  Stammkunde  niedrig  hoch  Inland  true

Die Antwort ist einfach: Nichts!
Wer ein Stammkunde ist, dem wurde stets die Zahlung per Rechnung erlaubt.

H(S_{Stammkunde}) = 0.0

3.1 Dritter Rekursionsschritt

Fehlt nun nur noch die Frage nach der Unterscheidung von Normalkunden.

Zeile Kundenart Zahlungsgeschwindigkeit Kauffrequenz Herkunft Zahlungsmittel: Rechnung?
 4  Normalkunde  mittel  niedrig  Inland  true
 5  Normalkunde  hoch  hoch  Inland  true
 6  Normalkunde  hoch  hoch  Ausland  false
 14  Normalkunde  mittel  niedrig  Ausland  false

Zwar ist die Entropie des Subsets der Normalkunden…

H(S_{Normalkunde}) = 1.0

… denkbar schlecht, da maximal. Aber wir können genauso vorgehen, wie wir es bei dem Subset der Neukunden getan haben. Ich nehme es nun aber vorweg: Wenn wir uns den Datensatz näher ansehen, erkennen wir, dass wir diese Gesamtentropie von 1.0 für das Subset “Normalkunde” nicht mit den Attributen “Kauffrequenz” oder “Zahlungsgeschwindigkeit” reduzieren können, da dieses auch für sich betrachtet in Entropien der Größe 1.0 erhalten werden. Das Attribut “Herkunft” hingegen teilt den Datensatz sauber in true und false auf:

Somit ist der Informationsgewinn für das Attribut “Herkunft” am größten und wir haben unseren Baum komplett und – glücklicherweise – eindeutig bestimmen können!

Ergebnis: Der Entscheidungsbaum

Somit haben wir den Entscheidungsbaum über den ID3-Algorithmus erstellt, der eine Auskunft darüber macht, ob einem Kunden die Zahlung über Rechnung (statt Vorkasse) erlaubt wird:

true = Rechnung als Zahlungsmittel erlaubt
false = Rechnung als Zahlungsmittel nicht erlaubt

Ensemble Learning

Stellen Sie sich vor, Sie haben die Frage Ihres Lebens vor sich. Die korrekte Beantwortung dieser Frage wird Ihr Leben positiv beeinflussen, andernfalls negativ. Aber Sie haben Glück: Sie dürfen einen Experten, den Sie auswählen dürfen, um Rat fragen oder Sie dürfen eine annonyme Gruppe, sagen wir 1.000 Personen, um Rat fragen. Welchen Rat würden Sie sich einholen? Die einzelne Experten-Meinung oder die aggriegierte Antwort einer ganzen Gruppe von Menschen?
Oder wie wäre es mit einer Gruppe von Experten?

Ensemble Learning

Beim Einsatz eines maschinellen Lernalgorithmus auf ein bestimmtes Problem kann durchaus eine angemessene Präzision (Accuracy, eine Quote an Prädiktionsergebnissen, die als korrekt einzustufen sind) erzielt werden, doch oftmals reicht die Verlässlichkeit eines einzelnen Algorithmus nicht aus. Algorithmen können mit unterschiedlichen Parametern verwendet werden, die sich bei bestimmten Daten-Situationen verschieden auswirken. Bestimmte Algorithmen neigen zur Unteranpassung (Underfitting), andere zur Überanpassung (Overfitting).

Soll Machine Learning für den produktiven Einsatz mit bestmöglicher Zuverlässigkeit entwickelt und eingesetzt werden, kommt sinnvollerweise Ensemble Learning zum Einsatz. Beim Ensemble Learning wird ein Ensemble (Kollektiv von Prädiktoren) gebildet um ein Ensemble Average (Kollektivmittelwert) zu bilden. Sollte also beispielsweise einige Klassifizierer bei bestimmten Daten-Eingaben in ihren Ergebnissen ausreißen, steuern andere Klassifizierer dagegen. Ensemble Learning kommt somit in der Hoffnung zum Einsatz, dass eine Gruppe von Algorithmen ein besseres Ergebnis im Mittel erzeugen als es ein einzelner Algorithmus könnte.

Ich spreche nachfolgend bevorzugt von Klassifizierern, jedoch kommt Ensemble Learning auch bei der Regression zum Einsatz.

Voting Classifiers (bzw. Voting Regressors)

Eine häufige Form – und i.d.R. auch als erstes Beispiel eines Ensemble Learners – ist das Prinzip der Voting Classifiers. Das Prinzip der Voting Classifiers ist eine äußerst leicht nachvollziehbare Idee des Ensemble Learnings und daher vermutlich auch eine der bekanntesten Form der Kollektivmittelwert-Bildung. Gleich vorweg: Ja, es gibt auch Voting Regressors, jedoch ist dies ein Konzept, das nicht ganz ohne umfassendere Aggregation auf oberster Ebene auskommen wird, daher wäre für die Zwecke der akkurateren Regression eher das Stacking (siehe unten) sinnvoll.

Eine häufige Frage im Data Science ist, welcher Klassifizierer für bestimmte Zwecke die besseren sind: Entscheidungsbäume, Support-Vector-Machines, k-nächste-Nachbarn oder logistische Regressionen?

Warum nicht einfach alle nutzen? In der Tat wird genau das nicht selten praktiziert. Das Ziel dieser Form des Ensemble Learnings ist leicht zu erkennen: Die unterschiedlichen Schwächen aller Algorithmen sollen sich – so die Hoffnung – gegenseitig aufheben. Alle Algorithmen (dabei können auch mehrere gleiche Algorithmen mit jedoch jeweils unterschiedlichen Paramtern gemeint sein, z. B. mehrere knN-Klassifizierer mit unterschiedlichen k-Werten und Dimensionsgewichtungen) werden auf dasselbe Problem hin trainiert.

Stacking

Bei der Prädiktion werden entweder alle Klassifizierer gleich behandelt oder unterschiedlich gewichtet (wobei größere Unterschiede der Gewichtungen unüblich, und vermutlich auch nicht sinnvoll, sind). Entsprechend einer Ensemble-Regel werden die Ergebnisse aller Klassifizierer aggregiert, bei Klassifikation durch eine Mehrheitsentscheidung, bei Regression meistens durch Durchschnittsbildung oder (beim Stacking) durch einen weiteren Regressor.

Abgesehen davon, dass wir mit dem Ensemble-Klassifizierer bzw. Regressoren vermutlich bessere Ergebnisse haben werden, haben wir nun auch eine weitere Information hinzubekommen: Eine Entropie über die Wahrscheinlichkeit. Bestenfalls haben alle Klassifizierer die gleiche Vorhersage berechnet, schlechtestensfalls haben wir ein Unentschieden. So können wir Vorhersagen in ihrer Aussagekraft bewerten. Analog kann bei Regressionen die Varianz der Ergebnisse herangezogen werden, um das Ergebnis in seiner Aussagekraft zu bewerten.

Betrachtung im Kontext von: Eine Kette ist nur so stark, wie ihr schwächstes Glied

Oft heißt es, dass Ensemble Learning zwar bessere Ergebnisse hervorbringt, als der schwächste Klassifizier in der Gruppe, aber auch schlechtere als der beste Klassifizierer. Ist Ensemble Learning also nur ein Akt der Ratlosigkeit, welcher Klassifizierer eigentlich der bessere wäre?

Ja und nein. Ensemble Learning wird tatsächlich in der Praxis dazu verwendet, einzelne Schwächen abzufangen und auch Ausreißer-Verhalten auf bisher andersartiger Daten abzuschwächen. Es ist ferner jedoch so, dass Ensemble Learner mit vielen Klassifizieren sogar bessere Vorhersagen liefern kann, als der beste Klassifizierer im Programm.

Das liegt an dem Gesetz der großen Zahlen, dass anhand eines Beispiels verdeutlicht werden kann: Bei einem (ausbalanzierten) Münzwurf liegt die Wahrscheinlichkeit bei genau 50,00% dafür, Kopf oder Zahl zu erhalten. Werfe ich die Münze beispielsweise zehn Mal, erhalte ich aber vielleicht drei Mal Kopf und sieben mal Zahl. Werfe ich sie 100 Mal, erhalte ich vielleicht 61 Mal Kopf und 39 Mal Zahl. Selbst nur 20 Mal die Zahl zu erhalten, wäre bei nur 100 Würfen gar nicht weit weg von unwahrscheinlich. Würde ich die Münze jedoch 10.000 Male werfen, würde ich den 50% schon sehr annähern, bei 10 Millionen Würfen wird sich die Verteilung ganz sicher als Gleichverteilung mit 50,0x% für Kopf oder Zahl einpendeln.

Nun stellt man sich (etwas überspitzt, da analog zu den Wünzwürfen) nun einen Ensemble Learner mit einer Gruppe von 10.000 Klassifiziern vor. Und angenommen, jeder einzelne Klassifizierer ist enorm schwach, denn eine richtige Vorhersage trifft nur mit einer Präzision von 51% zu (also kaum mehr als Glücksspiel), dann würde jedoch die Mehrheit der 10.000 Klassifizierer (nämlich 51%) richtig liegen und die Mehrheitsentscheidung in den absolut überwiegenden Fällen die korrekte Vorhersage treffen.

Was hingehen in diesem Kontext zutrifft: Prädiktionen via Ensemble Learning sind zwangsläufig langsam. Durch Parallelisierung der Klassifikation kann natürlich viel Zeit eingespart werden, dann ist das Ensemble Learning jedoch mindestens immer noch so langsam, wie der langsamste Klassifizierer.

Bagging

Ein Argument gegen den Einsatz von gänzlich verschiedenen Algortihmen ist, dass ein solcher Ensemble Learner nur schwer zu verstehen und einzuschätzen ist (übrigens ein generelles Problem im maschinellen Lernen). Bereits ein einzelner Algorithmus (z. B. Support Vector Machine) kann nach jedem Training alleine auf Basis der jeweils ausgewählten Daten (zum Training und zum Testen) recht unterschiedlich in seiner Vorhersage ausfallen.

Bagging (kurze Form von Bootstrap Aggregation) ist ein Ensemble Learning Prinzip, bei dem grundsätzlich der gleiche Algorithmus parallel mit unterschiedlichen Aufteilungen der Daten trainiert (und natürlich getestet) wird. Die Aufteilung der Daten kann dabei komplett (der vollständige Datensatz wird verteilt und verwendet) oder auch nur über Stichproben erfolgen (dann gibt es mehrfach verwendete Datenpunkte, aber auch solche, die überhaupt nicht verwendet werden). Das Ziel ist dabei insbesondere, im Endergebnis Unter- und Überanpassung zu vermeiden. Gibt es viele Dichte-Cluster und Ausreißer in den Daten, wird nicht jeder Klassifizierer sich diesen angepasst haben können. Jede Instanz der Klassifizierer erhält weitgehend unterschiedliche Daten mit eigenen Ausreißern und Dichte-Clustern, dabei darf es durchaus Überschneidungen bei der Datenaufteilung geben.

Pasting

Pasting ist fast genau wie Bagging, nur mit dem kleinen aber feinen Unterschied, dass sich die Datenaufteilung nicht überschneiden darf. Wird ein Datenpunkt durch Zufallsauswahl einem Klassifizierer zugewiesen, wird er nicht mehr für einen anderen Klassifizierer verwendet. Über die Trainingsdaten des einen Klassifizierers verfügt demnach kein anderer Klassifizierer. Die Klassifizierer sind somit völlig unabhängig voneinander trainiert, was manchmal explizit gewollt sein kann. Pasting setzt natürlich voraus, dass genug Daten vorhanden sind. Diese Voraussetzung ist gleichermaßen auch eine Antwort auf viele Probleme: Wie können große Datenmengen schnell verarbeitet werden? Durch die Aufteilung ohne Überschneidung auf parallele Knoten.

Random Forest

Random Forests sollten an dieser Stelle im Text eigentlich nicht stehen, denn sie sind ein Beispiel des parallelen Ensembles bzw. des Voting Classifiers mit Entscheidungsbäumen (Decision Trees). Random Forests möchte ich an dieser Stelle dennoch ansprechen, denn sie sind eine äußerst gängige Anwendung des Baggings oder (seltener) auch des Pastings für Entscheidungsbaumverfahren. Die Datenmenge wird durch Zufall aufgeteilt und aus jeder Aufteilung heraus wird ein Entscheidungsbaum erstellt. Eine Mehrheitsentscheidung der Klassifikationen aller Bäume ist das Ensemble Learning des Random Forests.

Random Forest ist ein Verfahren der Klassifikation oder Regression, das bereits so üblich ist, dass es mittlerweile längst in (fast) allen Machine Learning Bibliotheken implemeniert ist und – dank dieser Implementierung – in der Anwendung nicht komplizierter, als ein einzelner Entscheidungsbaum.

Stacking

Stacking ist eine Erweiterung des Voting Classifiers oder Voting Regressors um eine höhere Ebene (Blending-Level), die die beste Aggregation der Einzel-Ergebnisse erlernt. An der Spitze steht beim Stacking (mindestens) ein weiterer Klassifikator oder Regressor

Stacking ist insbesondere dann sinnvoll, wenn die Ergebnisse der einzelnen Algorithmen sehr unterschiedlich ausfallen können, was bei der Regression – da stetige Werte statt wenige Klassen – nahezu immer der Fall ist. Stacking-Algorithmen können sogar mehrere Schichten umfassen, was ihr Training wesentlich schwieriger gestaltet.

Boosting (Sequential Ensemble Learning)

Bagging, Pasting und Stacking sind parallele Verfahren des Ensemble Learning (was nicht bedeutet, dass die parallel dargestellten Algorithmen in der Praxis nicht doch sequenziell abgearbeitet werden). Zwangsweise sequenziell durchgeführt wird hingegen das Boosting, bei dem wir schwache Klassifizierer bzw. Regressoren durch Iteration in ihrem Training verstärken wollen. Boosting kann somit als eine Alternative zum Deep Learning gesehen werden. Während beim Deep Learning ein starker Algorithmus durch ein mehrschichtiges künstliches neuronales Netz dafür entworfen und trainiert wird, um ein komplexes Problem zu lösen (beispielsweise Testerkennung [OCR]), können derartige Herausforderungen auch mit schwächeren Klassifikatoren unter Einsatz von Boosting realisiert werden.

Boosting bezieht sich allein auf das Training und ist aus einer Not heraus entstanden: Wie bekommen wir bessere Prädiktionen mit einem eigentlich schwachen Lernalgorithmus, der tendenziell Unteranpassung erzeugt? Boosting ist eine Antwort auf Herausforderungen der Klassifikation oder Regression, bei der ein Algorithmus iterativ, also in mehreren Durchläufen, durch Anpassung von Gewichten trainiert wird.

Eines der bekanntesten Boosting-Verfahren ist AdaBoost. Der erste Schritt ist ein normales Training. Beim darauffolgenden Testen zeigen sich Klassifikations-/Regressionsfehler. Die fehlerhaft vorhergesagten Datenpunkte werden dann für einen nächsten Durchlauf höher gewichtet. Diese Iteration läuft einige Male, bis die Fehlerquote sich nicht mehr verbessert.

Bei AdaBoost werden falsch vorhergesagte Datensätze im jeweils nächsten Durchlauf höher gewichtet. Bei einem alternativen Boosing-Verfahren, dem Gradient Boosting (auf Basis der Gradientenmethode), werden Gewichtungen explizit in Gegenrichtung des Prädiktionsfehlers angepasst.

Was beispielsweise beim Voting Classifier der Random Forest ist, bei dem mehrere Entscheidungsbäume parallel arbeiten, sind das Äquvivalent beim Boosting die Gradient Boosted Trees, bei denen jeder Baum nur einen Teil der Daten akkurat beschreiben kann, die sequentielle Verschachtelung der Bäume jedoch auch herausfordernde Klassifikationen meistert.

Um bei dem Beispiel der Entscheidungsbäume zu bleiben: Sowohl Random Forests als auch Gradient Boosted Trees arbeiten grundsätzlich mit flachen Bäumen (schwache Klassifikatoren). Gradient Boosted Trees können durch die iterative Verstärkung generell eine höhere Präzision der Prädiktion erreichen als Random Forests, wenn die Feature- und Parameter-Auswahl bereits zu Anfang sinnvoll ist. Random Forests sind hingegen wiederum robuster bei der Feature- und Parameter-Auswahl, verstärken sich jedoch nicht gegenseitig, sondern sind in ihrem Endergebnis so gut, wie die Mehrheit der Bäume.

Buchempfehlungen

Mehr zum Thema Machine Learning und Ensemble Learning gewünscht? Folgende zwei Buchempfehlungen bieten nicht nur Erklärungen, sondern demonstrieren Ensemble Learning auch mit Beispiel-Code mit Python Scikit-Learn.

Hands-On Machine Learning with Scikit-Learn and TensorFlow: Concepts, Tools, and Techniques for Building Intelligent Systems Machine Learning mit Python: Das Praxis-Handbuch für Data Science, Predictive Analytics und Deep Learning (mitp Professional)

Lineare Regression in Python mit Scitkit-Learn

Die lineare Regressionsanalyse ist ein häufiger Einstieg ins maschinelle Lernen um stetige Werte vorherzusagen (Prediction bzw. Prädiktion). Hinter der Regression steht oftmals die Methode der kleinsten Fehlerquadrate und die hat mehr als eine mathematische Methode zur Lösungsfindung (Gradientenverfahren und Normalengleichung). Alternativ kann auch die Maximum Likelihood-Methode zur Regression verwendet werden. Wir wollen uns in diesem Artikel nicht auf die Mathematik konzentrieren, sondern uns direkt an die Anwendung mit Python Scikit-Learn machen:

Haupt-Lernziele:

  • Einführung in Machine Learning mit Scikit-Learn
  • Lineare Regression mit Scikit-Learn

Neben-Lernziele:

  • Datenvorbereitung (Data Preparation) mit Pandas und Scikit-Learn
  • Datenvisualisierung mit der Matplotlib direkt und indirekt (über Pandas)

Was wir inhaltlich tun:

Der Versuch einer Vorhersage eines Fahrzeugpreises auf Basis einer quantitativ-messbaren Eigenschaft eines Fahrzeuges.


Die Daten als Download

Für dieses Beispiel verwende ich die Datei “Automobil_data.txt” von Kaggle.com. Die Daten lassen sich über folgenden Link downloaden, nur leider wird ein (kostenloser) Account benötigt:
https://www.kaggle.com/toramky/automobile-dataset/downloads/automobile-dataset.zip
Sollte der Download-Link unerwartet mal nicht mehr funktionieren, freue ich mich über einen Hinweis als Kommentar 🙂

Die Entwicklungsumgebung

Ich verwende hier die Python-Distribution Anaconda 3 und als Entwicklungs-Umgebung Spyder (in Anaconda enthalten). Genauso gut funktionieren jedoch auch Jupyter Notebook, Eclipse mit PyDev oder direkt die IPython QT-Console.


Zuerst einmal müssen wir die Daten in unsere Python-Session laden und werden einige Transformationen durchführen müssen. Wir starten zunächst mit dem Importieren von drei Bibliotheken NumPy und Pandas, deren Bedeutung ich nicht weiter erläutern werde, somit voraussetze.

import matplotlib.pyplot as plt  # Die Nr.1 der Bibliotheken zur Datenvisualisierung
import numpy as np               # Bibliothek "Nummerisches Python"
import pandas as pd              # Bibliothek "Panel Data"

Wir nutzen die Pandas-Bibliothek, um die “Automobile_data.txt” in ein pd.DataFrame zu laden.

dataSet = pd.read_csv("Automobile_data.txt",  # Hier liegt die Datei im selben Verzeichnis wie das Python-Skript!
                      delimiter = ',',
                      thousands = None,
                      decimal = '.')

Schauen wir uns dann die ersten fünf Zeilen in IPython via dataSet.head().

In : dataSet.head()
Out: 
   symboling normalized-losses         make fuel-type aspiration num-of-doors  \
0          3                 ?  alfa-romero       gas        std          two   
1          3                 ?  alfa-romero       gas        std          two   
2          1                 ?  alfa-romero       gas        std          two   
3          2               164         audi       gas        std         four   
4          2               164         audi       gas        std         four   

    body-style drive-wheels engine-location  wheel-base  ...    engine-size  \
0  convertible          rwd           front        88.6  ...            130   
1  convertible          rwd           front        88.6  ...            130   
2    hatchback          rwd           front        94.5  ...            152   
3        sedan          fwd           front        99.8  ...            109   
4        sedan          4wd           front        99.4  ...            136   

   fuel-system  bore  stroke compression-ratio horsepower  peak-rpm city-mpg  \
0         mpfi  3.47    2.68               9.0        111      5000       21   
1         mpfi  3.47    2.68               9.0        111      5000       21   
2         mpfi  2.68    3.47               9.0        154      5000       19   
3         mpfi  3.19     3.4              10.0        102      5500       24   
4         mpfi  3.19     3.4               8.0        115      5500       18   

  highway-mpg  price  
0          27  13495  
1          27  16500  
2          26  16500  
3          30  13950  
4          22  17450  

[5 rows x 26 columns]

Hinweis: Der Datensatz hat viele Spalten, so dass diese in der Darstellung mit einem Backslash \ umgebrochen werden.

Gleich noch eine weitere Ausgabe dataSet.info(), die uns etwas über die Beschaffenheit der importierten Daten verrät:

In : dataSet.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 205 entries, 0 to 204
Data columns (total 26 columns):
symboling            205 non-null int64
normalized-losses    205 non-null object
make                 205 non-null object
fuel-type            205 non-null object
aspiration           205 non-null object
num-of-doors         205 non-null object
body-style           205 non-null object
drive-wheels         205 non-null object
engine-location      205 non-null object
wheel-base           205 non-null float64
length               205 non-null float64
width                205 non-null float64
height               205 non-null float64
curb-weight          205 non-null int64
engine-type          205 non-null object
num-of-cylinders     205 non-null object
engine-size          205 non-null int64
fuel-system          205 non-null object
bore                 205 non-null object
stroke               205 non-null object
compression-ratio    205 non-null float64
horsepower           205 non-null object
peak-rpm             205 non-null object
city-mpg             205 non-null int64
highway-mpg          205 non-null int64
price                205 non-null object
dtypes: float64(5), int64(5), object(16)
memory usage: 41.7+ KB

Einige Spalten entsprechen hinsichtlich des Datentypes nicht der Erwartung. Für die Spalten ‘horsepower’ und ‘peak-rpm’ würde ich eine Ganzzahl (Integer) erwarten, für ‘price’ hingegen eine Fließkommazahl (Float), allerdings sind die drei Spalten als Object deklariert. Mit Trick 17 im Data Science, der Anzeige der Minimum- und Maximum-Werte einer zu untersuchenden Datenreihe, kommen wir dem Übeltäter schnell auf die Schliche:

dataSet['horsepower'].min()
Out: '100'

dataSet['horsepower'].max()
Out: '?'

Datenbereinigung

Für eine Regressionsanalyse benötigen wir nummerische Werte (intervall- oder ratioskaliert), diese möchten wir auch durch richtige Datentypen-Deklaration herstellen. Nun wird eine Konvertierung in den gewünschten Datentyp jedoch an den (mit ‘?’ aufgefüllten) Datenlücken scheitern.

Schauen wir uns doch einmal die Datenreihen an, in denen in der Spalte ‘peak-rpm’ Fragezeichen stehen:

dataSet[dataSet['peak-rpm'] == '?'][['engine-type', 'num-of-cylinders']]
Out: 
    engine-type num-of-cylinders
130         ohc             four
131         ohc             four

Zwei Datenreihen sind vorhanden, bei denen ‘peak-rpm’ mit einem ‘?’ aufgefüllt wurde. Nun könnten wir diese Datenreihen einfach rauslöschen. Oder mit sinnvollen (im Sinne von wahrscheinlichen) Werten auffüllen. Vermutlichen haben beide Einträge – beide sind OHC-Motoren mit 4 Zylindern – eine ähnliche Drehzahl-Angabe wie vergleichbare Motoren. Mit folgendem Quellcode, gruppieren wir die Spalten ‘engine-type’ und ‘num-of-cylinders’ und bilden für diese Klassen den arithmetischen Mittelwert (.mean()) für die ‘peak-rpm’.

dataSet_rpm = dataSet[dataSet['peak-rpm'] != '?'][['engine-type', 'num-of-cylinders','peak-rpm']]
dataSet_rpm['peak-rpm'] = dataSet_rpm['peak-rpm'].astype(float)
dataSet_rpm_grouped = dataSet_rpm.groupby(['engine-type', 'num-of-cylinders'])
dataSet_rpm_grouped['peak-rpm'].mean()

Und schauen wir uns das Ergebnis an:

dataSet_rpm_grouped['peak-rpm'].mean()
Out: 
engine-type  num-of-cylinders
dohc         four                5700.000000 -- 
             six                 5050.000000
dohcv        eight               5750.000000
l            four                4668.181818
             three               5100.000000
ohc          five                5081.818182
             four                5155.468750
             six                 4821.428571
ohcf         four                4775.000000
             six                 5900.000000
ohcv         eight               4625.000000
             six                 5212.500000
             twelve              5000.000000
rotor        two                 6000.000000
Name: peak-rpm, dtype: float64

Ein Vier-Zylinder-OHC-Motor hat demnach durchschnittlich einen Drehzahl-Peak von 5155 Umdrehungen pro Minute. Ohne nun (fahrlässigerweise) auf die Verteilung in dieser Klasse zu achten, nehmen wir einfach diesen Schätzwert, um die zwei fehlende Datenpunkte zu ersetzen.

Wir möchten jedoch die Original-Daten erhalten und legen ein neues DataSet (dataSet_c) an, in welches wir die Korrekturen vornehmen:

dataSet_c = dataSet.copy()   # das "c"-Anhängsel steht für "corrected"

Nun können wir die fehlenden Peak-RPM-Einträge mit unserem Schätzwert ersetzen:

dataSet_c.loc[dataSet_c['peak-rpm'] == '?', 'peak-rpm'] = 5155

Was bei einer Drehzahl-Angabe noch funktionieren mag, ist für anderen Spalten bereits etwas schwieriger: Die beiden Spalten ‘price’ und ‘horsepower’ sind ebenfalls vom Typ Object, da sie ‘?’ enthalten. Verzichten wir einfach auf die betroffenen Zeilen:

dataSet_c = dataSet_c[dataSet_c['price'] != '?']                    # entsprechende Zeilen herausfiltern
dataSet_c['price'] = dataSet_c['price'].astype(float)               # Typ-Konvertierung zu Float

dataSet_c = dataSet_c[dataSet_c.horsepower != '?']                  # entsprechende Zeilen herausfiltern
dataSet_c['horsepower'] = dataSet_c['horsepower'].astype(float)     # Typ-Konvertierung in Int

Datenvisualisierung mit Pandas

Wir wollen uns nicht lange vom eigentlichen Ziel ablenken, dennoch nutzen wir die Visualisierungsfähigkeiten der Pandas-Library (welche die Matplotlib inkludiert), um uns dann die Anzahlen an Einträgen nach Hersteller der Fahrzeuge (Spalte ‘make’) anzeigen zu lassen:

dataSet_grouped_make = dataSet_c.groupby('make')
dataSet_grouped_make['make'].count().plot(kind = 'bar', figsize = (10, 10))
plt.show()    # Besser jedes Plot abschließen! Auch wenn es in Pandas entstanden ist.

Oder die durchschnittliche PS-Zahl nach Hersteller:

(dataSet_c.groupby('make'))['horsepower'].mean().plot(kind = 'barh',
                                                      title = 'Mean Horsepower',
                                                      figsize = (10, 10))
plt.show()

Vorbereitung der Regressionsanalyse

Nun kommen wir endlich zur Regressionsanalyse, die wir mit Scikit-Learn umsetzen möchten. Die Regressionsanalyse können wir nur mit intervall- oder ratioskalierten Datenspalten betreiben, daher beschränken wir uns auf diese. Die “price”-Spalte nehmen wir jedoch heraus und setzen sie als unsere Zielgröße fest.

""" ----- Vorbereitung für die Regressionsanalyse ----- """
cols_ratio = ['horsepower', 'wheel-base', 'length', 'width', 'height', 'curb-weight', 'engine-size', 'compression-ratio', 'city-mpg', 'highway-mpg']
cols_target = ['price']

dataSet_ratio = dataSet_c.loc[:, cols_ratio]
dataSet_target = dataSet_c[cols_target]

Interessant ist zudem die Betrachtung vorab, wie die einzelnen nummerischen Attribute untereinander korrelieren. Dafür nehmen wir auch die ‘price’-Spalte wieder in die Betrachtung hinein und hinterlegen auch eine Farbskala mit dem Preis (höhere Preise, hellere Farben).

grr = pd.plotting.scatter_matrix(dataSet_c[cols_target + cols_ratio]
                                 ,c = dataSet_target
                                 ,figsize=(15, 15)
                                 ,marker = 'o'
                                 ,hist_kwds={'bins' : 20}
                                 ,s = 60
                                 ,alpha = 0.8)
plt.show()

Die lineare Korrelation ist hier sehr interessant, da wir auch nur eine lineare Regression beabsichtigen.

Wie man in dieser Scatter-Matrix recht gut erkennen kann, scheinen einige Größen-Paare nahezu perfekt zu korrelieren, andere nicht.

Korrelation…

  • …nahezu perfekt linear: highway-mpg vs city-mpg (mpg = Miles per Gallon)
  • … eher nicht gegeben: highway-mpg vs height
  • … nicht linear, dafür aber nicht-linear: highway-mpg vs price

Nun, wir wollen den Preis eines Fahrzeuges vorhersagen, wenn wir eine andere quantitative Größe gegeben haben. Auf den Preis bezogen, erscheint mir die Motorleistung (Horsepower) einigermaßen linear zu korrelieren. Versuchen wir hier die lineare Regression und setzen somit die Spalte ‘horsepower’ als X und ‘price’ als y fest.

X = dataSet_ratio[['horsepower']] # doppelte [], da eine Liste von Spalten zu übergeben ist
y = dataSet_c[cols_target]

Die gängige Konvention ist übrigens, X groß zu schreiben, weil hier auch mehrere x-Dimensionen enthalten sein dürfen (multivariate Regression). y hingegen, ist stets nur eine Zielgröße (eine Dimension).

Die lineare Regression ist ein überwachtes Verfahren des maschinellen Lernens, somit müssen wir unsere Prädiktionsergebnisse mit Test-Daten testen, die nicht für das Training verwendet werden dürfen. Scitkit-Learn (oder kurz: sklearn) bietet hierfür eine Funktion an, die uns das Aufteilen der Daten abnimmt:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y,
                                                    test_size = 0.3,     # 70% der Daten für das Training
                                                    random_state = None) # bei Bedarf kann hier "dem Zufall auf die Sprünge geholfen" werden

Zu beachten ist dabei, dass die Daten vor dem Aufteilen in Trainings- und Testdaten gut zu durchmischen sind. Auch dies übernimmt die train_test_split-Funktion für uns, nur sollte man im Hinterkopf behalten, dass die Ergebnisse (auf Grund der Zufallsauswahl) nach jedem Durchlauf immer wieder etwas anders aussehen.

Lineare Regression mit Scikit-Learn

Nun kommen wir zur Durchführung der linearen Regression mit Scitkit-Learn, die sich in drei Zeilen trainieren lässt:

""" ----- Lineare Regressionsanalyse ------- """

from sklearn.linear_model import LinearRegression   # importieren der Klasse

lr = LinearRegression()                             # instanziieren der Klasse

lr.fit(X_train, y_train)                            # trainieren

Aber Vorsicht! Bevor wir eine Prädiktion durchführen, wollen wir festlegen, wie wir die Güte der Prädiktion bewerten wollen. Die gängigsten Messungen für eine lineare Regression sind der MSE und R².

MSE = \frac{\sum_{i=1}^n (y_i - \hat{y_i})^2}{n}

Ein großer MSE ist schlecht, ein kleiner gut.

R^2 = 1 - \frac{MSE}{Var(y)}= \frac{\frac{1}{n} \cdot \sum_{i=1}^n (y_i - \hat{y_i})^2}{\frac{1}{n} \cdot \sum_{i=1}^n (y_i - \hat{\mu_y})^2}

Ein kleines R² ist schlecht, ein großes R² gut. Ein R² = 1.0 wäre theoretisch perfekt (da der Fehler = 0.00 wäre), jedoch in der Praxis unmöglich, da dieser nur bei absolut perfekter Korrelation auftreten würde. Die Klasse LinearRegression hat eine R²-Messmethode implementiert (score(x, y)).

print('------ Lineare Regression -----')
print('Funktion via sklearn: y = %.3f * x + %.3f' % (lr.coef_[0], lr.intercept_))
print("Alpha: {}".format(lr.intercept_))
print("Beta: {}".format(lr.coef_[0]))
print("Training Set R² Score: {:.2f}".format(lr.score(X_train, y_train)))
print("Test Set R² Score: {:.2f}".format(lr.score(X_test, y_test)))
print("\n")

Die Ausgabe (ein Beispiel!):

------ Lineare Regression -----
Funktion via sklearn: y = 170.919 * x + -4254.701     # Die Funktion ist als y = 171 * x - 4254.7
Alpha: [-4254.70114803]                               # y-Achsenschnitt bei x = 0
Beta: [ 170.91919086]                                 # Steigung der Gerade
Training Set R² Score: 0.62                           
Test Set R² Score: 0.73

Nach jedem Durchlauf ändert sich mit der Datenaufteilung (train_test_split()) das Modell etwas und auch R² schwankt um eine gewisse Bandbreite. Berauschend sind die Ergebnisse dabei nicht, und wenn wir uns die Regressionsgerade einmal ansehen, wird auch klar, warum:

plt.figure(figsize=(10,10))
plt.scatter(X_train, y_train, color = 'blue')                 # Blaue Punkte sind Trainingsdaten
plt.scatter(X_test, y_test, color = 'green')                  # Grüne Punkte sind Testdaten
plt.plot(X_train, lr.predict(X_train), color = 'red')         # Hier ensteht die Gerade (x, y) = (x, lr.predict(x)
plt.xlabel(X_train.columns[0])
plt.ylabel(cols_target[0])
plt.show()

Bei kleineren Leistungsbereichen, etwa bis 100 PS, ist die Preis-Varianz noch annehmbar gering, doch bei höheren Leistungsbereichen ist die Spannweite deutlich größer. (Nachträgliche Anmerkung vom 06.05.2018: relativ betrachtet, bleibt der Fehler über alle Wertebereiche ungefähr gleich [relativer Fehler]. Die absoluten Fehlerwerte haben jedoch bei größeren x-Werten so eine Varianz der möglichen y-Werte, dass keine befriedigenden Prädiktionen zu erwarten sind.)

Egal wie wir eine Gerade in diese Punktwolke legen, wir werden keine befriedigende Fehlergröße erhalten.

Nehmen wir einmal eine andere Spalte für X, bei der wir vor allem eine nicht-lineare Korrelation erkannt haben: “highway-mpg”

X = dataSet_ratio[['highway-mpg']]
y = dataSet_c[cols_target]

Wenn wir dann das Training wiederholen:

------ Lineare Regression -----
Funktion via sklearn: y = -868.787 * x + 40575.036
Alpha: [ 40575.03556055]
Beta: [-868.7869183]
Training Set R² Score: 0.49
Test Set R² Score: 0.40

Die R²-Werte sind nicht gerade berauschend, und das erklärt sich auch leicht, wenn wir die Trainings- und Testdaten sowie die gelernte Funktionsgerade visualisieren:

Die Gerade lässt sich nicht wirklich gut durch diese Punktwolke legen, da letztere eher eine Kurve als eine Gerade bildet. Im Grunde könnte eine Gerade noch einigermaßen gut in den Bereich von 22 bis 43 mpg passen und vermutlich annehmbare Ergebnisse liefern. Die Wertebereiche darunter und darüber jedoch verzerren zu sehr und sorgen zudem dafür, dass die Gerade auch innerhalb des mittleren Bereiches zu weit nach oben verschoben ist (ggf. könnte hier eine Ridge-/Lasso-Regression helfen).

Richtig gute Vorhersagen über nicht-lineare Verhältnisse können jedoch nur mit einer nicht-linearen Regression erreicht werden.

Nicht-lineare Regression mit Scikit-Learn

Nicht-lineare Regressionsanalysen erlauben es uns, nicht-lineare korrelierende Werte-Paare als Funktion zu erlernen. Im folgenden Scatter-Plot sehen wir zum einen die gewohnte lineare Regressionsgerade (y = a * x + b) in rot, eine polinominale Regressionskurve dritten Grades (y = a * x³ + b * x² + c * x + d) in violet sowie einen Entscheidungsweg einer Entscheidungsbaum-Regression in gelb.

Nicht-lineare Regressionsanalysen passen sich dem Verlauf der Punktwolke sehr viel besser an und können somit in der Regel auch sehr gute Vorhersageergebnisse liefern. Ich ziehe hier nun jedoch einen Gedankenstrich, liefere aber den Quellcode für die lineare Regression als auch für die beiden nicht-linearen Regressionen mit:

Python Script Regression via Scikit-Learn

Weitere Anmerkungen

  • Bibliotheken wie Scitkit-Learn erlauben es, machinelle Lernverfahren schnell und unkompliziert anwenden zu können. Allerdings sollte man auch verstehen, wei diese Verfahren im Hintergrund mathematisch arbeiten. Diese Bibliotheken befreien uns also nicht gänzlich von der grauen Theorie.
  • Statt der “reinen” lineare Regression (LinearRegression()) können auch eine Ridge-Regression (Ridge()), Lasso-Regression (Lasso()) oder eine Kombination aus beiden als sogenannte ElasticNet-Regression (ElasticNet()). Bei diesen kann über Parametern gesteuert werden, wie stark Ausreißer in den Daten berücksichtigt werden sollen.
  • Vor einer Regression sollten die Werte skaliert werden, idealerweise durch Standardisierung der Werte (sklearn.preprocessing.StandardScaler()) oder durch Normierung (sklearn.preprocessing.Normalizer()).
  • Wir haben hier nur zwei-dimensional betrachtet. In der Praxis ist das jedoch selten ausreichend, auch der Fahrzeug-Preis ist weder von der Motor-Leistung, noch von dem Kraftstoffverbrauch alleine abhängig – Es nehmen viele Größen auf den Preis Einfluss, somit benötigen wir multivariate Regressionsanalysen.

Entscheidungsbaum-Algorithmus ID3

Dieser Artikel ist Teil 2 von 4 der Artikelserie Maschinelles Lernen mit Entscheidungsbaumverfahren.

Entscheidungsbäume sind den Ingenieuren bestens bekannt, um Produkte hierarchisch zu zerlegen und um Verfahrensanweisungen zu erstellen. Die Data Scientists möchten ebenfalls Verfahrensanweisungen erstellen, jedoch automatisiert aus den Daten heraus. Auf diese Weise angewendet, sind Entscheidungsbäume eine Form des maschinellen Lernens: Die Maschine soll selbst einen Weg finden, um ein Objekt einer Klasse zuzuordnen.

Der ID3-Algorithmus

Den ID3-Algorithmus zu verstehen lohnt sich, denn er ist die Grundlage für viele weitere, auf ihn aufbauende Algorithmen. Er ist mit seiner iterativen und rekursiven Vorgehensweise auch recht leicht zu verstehen, er darf nur wiederum nicht in seiner Wirkung unterschätzt werden. Die Vorgehensweise kann in drei wesentlichen Schritten zerlegt werden, wobei der erste Schritt die eigentliche Wirkung (mit allen Vor- und Nachteilen) entfaltet:

  1. Schritt: Auswählen des Attributes mit dem höchsten Informationsgewinn
    Betrachte alle Attribute (Merkmale) des Datensatzes und bestimme, welches Attribut die Daten am besten klassifiziert.
  2. Schritt: Anlegen eines Knotenpunktes mit dem Attribut
    Sollten die Ergebnisse unter diesem Knoten eindeutig sein (1 unique value), speichere es in diesem Knotenpunkt und springe zurück.
  3. Schritt: Rekursive Fortführung dieses Prozesses
    Andernfalls zerlege die Daten jedem Attribut entsprechend in n Untermengens (subsets), und wiederhole diese Schritte für jede der Teilmengen.

Der Informationsgewinn (Information Gain) – und wie man ihn berechnet


Der Informationsgewinn eines Attributes (A) im Sinne des ID3-Algorithmus ist die Differenz aus der Entropie (E(S)) (siehe Teil 1 der Artikelserie: Entropie, ein Maß für die Unreinheit in Daten) des gesamten Datensatzes (S) und der Summe aus den gewichteten Entropien des Attributes für jeden einzelnen Wert (Value i), der im Attribut vorkommt:
IG(S, A) = E(S) - \sum_{i=1}^n \frac{\bigl|S_i\bigl|}{\bigl|S\bigl|} \cdot E(S_i)

Wie die Berechnung des Informationsgewinnes funktioniert, wird Teil 3 dieser Artikel-Reihe (erscheint in Kürze) zeigen.

Die Vorzüge des ID3-Algorithmus – und die Nachteile

Der Algorithmus ist die Grundlage für viele weitere Algorithmen. In seiner Einfachheit bringt er gewisse Vorteile – die ihn vermutlich zum verbreitesten Entscheidungsbaum-Algorithmus machen – mit sich, aber hat auch eine Reihe von Nachteilen, die bedacht werden sollten.

Vorteile Nachteile
  • leicht verständlich und somit schnell implementiert
  • stellt eine gute Basis für Random Forests dar
  • alle Attribute spielen eine Rolle, der Baum wird aber tendenziell klein, da der Informationsgewinn die Reihenfolge vorgibt
  • funktioniert (mit Anpassungen) auch für Mehrfachklassifikation
  • aus der Reihenfolge durch den Informationsgewinn entsteht nicht unbedingt der beste bzw. kleinste Baum unter allen Möglichkeiten. Es ist ein Greedy-Algorithmus und somit “kurzsichtig”
  • die Suche nach Entscheidungsregeln ist daher auch nicht vollständig/umfassend
  • da der Baum via ID3 solange weiterwachsen soll, bis die Daten so eindeutig wie möglich erklärt sind, wird Overfitting geradezu provoziert

Overfitting (Überanpassung) beachten und vermeiden

Aus Daten heraus generierte Entscheidungsbäume neigen zur Überanpassung. Das bedeutet, dass sich die Bäume den Trainingsdaten soweit anpassen können, dass sie auf diese perfekt passen, jedoch keine oder nur noch einen unzureichende generalisierende Beschreibung mehr haben. Neue Daten, die eine höhere Vielfältigkeit als die Trainingsdaten haben können, werden dann nicht mehr unter einer angemessenen Fehlerquote korrekt klassifiziert.

Vorsicht vor Key-Spalten!

Einige Attribute erzwingen eine Überanpassung regelrecht: Wenn beispielsweise ein Attribut wie „Kunden-ID“ (eindeutige Nummer pro Kunde) einbezogen wird, haben wir – bezogen auf das Klassifikationsergebnis – für jeden einzelnen Wert in dem Attribut eine Entropie von 0 zu erwarten, denn jeder ID beschreibt einen eindeutigen Fall (Kunde, Kundengruppe etc.). Daraus folgt, dass der Informationsgewinn für dieses Attribut maximal wird. Hier würde der Baum eine enorme Breite erhalten, die nicht hilfreich wäre, denn jeder Wert (IDs) bekäme einen einzelnen Ast im Baum, der zu einem eindeutigen Ergebnis führt. Auf neue Daten (neue Kundennummern) ist der Baum nicht anwendbar, denn er stellt keine generalisierende Beschreibung mehr dar, sondern ist nur noch ein Abbild der Trainingsdaten.

Prunning – Den Baum nachträglich kürzen

Besonders große Bäume sind keine guten Bäume und ein Zeichen für Überanpassung. Eine Möglichkeit zur Verkleinerung ist das erneute Durchrechnen der Informationsgewinne und das kürzen von Verzweigungen (Verallgemeinerung), sollte der Informationsgewinn zu gering sein. Oftmals wird hierfür nicht die Entropie oder der Gini-Koeffizient, sondern der Klassifikationsfehler als Maß für die Unreinheit verwendet.

Random Forests als Overfitting-Allheilmittel

Bei Random Forests (eine Form des Ensemble Learning) handelt es sich um eine Gemeinschaftsentscheidung der Klassenzugehörigkeit über mehrere Entscheidungsbäume. Diese Art des “demokratischen” Machine Learnings wird auch Ensemble Learning genannt. Werden mehrere Entscheidungsbäume unterschiedlicher Strukturierung zur gemeinsamen Klassifikation verwendet, wird die Wirkung des Overfittings einzelner Bäume in der Regel reduziert.

Entropie – Und andere Maße für Unreinheit in Daten

Dieser Artikel ist Teil 1 von 4 der Artikelserie Maschinelles Lernen mit Entscheidungsbaumverfahren.

Hierarchische Klassifikationsmodelle, zu denen das Entscheidungsbaumverfahren (Decision Tree) zählt, zerlegen eine Datenmenge iterativ oder rekursiv mit dem Ziel, die Zielwerte (Klassen) im Rahmen des Lernens (Trainingsphase des überwachten Lernens) möglichst gut zu bereiningen, also eindeutige Klassenzuordnungen für bestimmte Eigenschaften in den Features zu erhalten. Die Zerlegung der Daten erfolgt über einen Informationsgewinn, der für die Klassifikation mit einem Maß der Unreinheit berechnet wird (im nächsten Artikel der Serie werden wir die Entropie berechnen!) Read more

Maschinelles Lernen mit Entscheidungsbaumverfahren – Artikelserie

Das Entscheidungsbaumverfahren (Decision Tree) ist eine verbreitete Möglichkeit der Regression oder Klassifikation über einen vielfältigen Datensatz. Das Verfahren wird beispielsweise dazu verwendet, um die Kreditwürdigkeit von Bankkunden zu klassifizieren oder auch, um eine Funktion zur Vorhersage einer Kaufkraft zu bilden.

Sicherlich hat beinahe jeder Software-Entwickler bereits einen Entscheidungsbaum (meistens binäre Baumstrukturen) programmiert und auch Maschinenbauingenieure benutzen Entscheidungsbäume, um Konstruktionsstrukturen darzustellen. Im Data Science haben Entscheidungsbäume allerdings eine etwas andere Bedeutung, denn ein Data Scientist befasst sich weniger mit dem manuellen Erstellen von solchen Baumstrukturen, sondern viel mehr mit Algorithmen, die ausreichend gute (manchmal: best mögliche) Baumstrukturen automatisch aus eine Menge mehr oder weniger bekannter Daten heraus generieren, die dann für eine automatische Klassifikation bzw. Regression dienen können.

Entscheidungsbäume sind also eine Idee des überwachten maschinellen Lernens, bei der Algorithmen zum Einsatz kommen, die aus einer Datenmenge heraus eine hierarchische Struktur von möglichst wenigen Entscheidungswegen bilden. Diese Datenmenge stellt eine sogenannte Trainingsstichprobe dar. Meiner Erfahrung nach werde Entscheidungsbäume oftmals in ihrer Mächtigkeit, aber auch in ihrer Komplexität unterschätzt und die Einarbeitung fiel mehr selbst schwerer, als ich anfangs annahm: In der Praxis stellt das Verfahren den Data Scientist vor viele Herausforderungen.

In dieser Artikelserie wird es vier nachfolgende Teile geben (Verlinkung erfolgt nach Veröffentlichung):