Predictive Maintenance mit Python:
 Ausfallerkennung mit DecisionTreeClassifier

Dies Tutorial zeigt, wie in einem Predictive Maintenance-Szenario Ausfälle mit Hilfe des DecisionTree-Klassifikators der Python-Bibliothek Scikit-Learn vorhergesagt werden können. Wir verwenden einen Automotive-Datensatz automotive_data.csv mit 22 Merkmalen, die Sensor-Messwerte am Motor enthalten, und einer Zielvariablen: Ausfall. Die Fragestellung lautet: "Bei welcher Kombination von Merkmalen tritt ein Ausfall ein?".

  Motivation

Bei der Durchführung der Datenanalyse im Rahmen der Predictive Maintenance sind Entscheidungsbäume ein wichtiges Modell für die Vorhersage von Ausfällen. Während im produktiven Einsatz optimierte Klassifikatoren wie Random Forest eingesetzt werden, ist das Entscheidungsbaum-Modell wegen seiner einfachen Interpretierbarkeit eine gute Wahl, um eine erste Datenanalyse durchzuführen und die Daten zu verstehen.

Voraussetzung für den Einsatz eines Entscheidungsbaum-Modells ist das Vorliegen von Vergangenheitsdaten, bei denen bekannt ist, bei welcher Merkmalskombination ein Ausfall eingetreten ist.

Hier verwenden wir die Funktionen der Python-Bibliothek Scikit-Learn, sowie Graphviz für die Visualisierung des Entscheidungsbaums, und setzen als Entwicklungsumgebung Visual Studio Code ein.

Warum Scikit-Learn?

Scikit-Learn ist eine der wichtigen Python-Bibliotheken für Machine Learning und bietet Unterstützung für die üblichen Schritte des Überwachten und Unüberwachten Lernens: Datenvorbereitung, Trainingsphase und Modellevaluation, ebenso leistungsstarke Algorithmen für Klassifikations-, Regressions- und Clustering-Probleme.

Warum graphviz?

Graphviz ist ein Open-Source-Programmpaket zur Visualisierung von Graphen, das in scikit-learn mittels der Funktion export_graphviz() für die Visualisierung von Entscheidungs­bäumen verwendet wird. Die mit graphviz erzeugten Entscheidungs­bäume können in verschiedenen Bildformaten gespeichert werden, insbesondere auch im SVG-Format, und haben damit eine bessere Qualität als die Default-Visualisierung mit der sklearn-Funktion plot_tree().

  Übersicht

Demo-PY4 ist in 8 Abschnitte gegliedert. Zunächst wird der Datensatz beschrieben und die Fragestellung formuliert, die mit der Datenanalyse beantwortet werden soll. Danach wird die Funktionsweise von Entscheidungsbäumen kurz vorgestellt, sowie die Vorbereitung der Entwicklungsumgebung und Installation benötigter Bibliotheken. In den folgenden Abschnitten erfolgt die Erstellung eines Entscheidungsbaum-Vorhersagemodells, danach die Visualisierung, zuletzt die Ermittlung von Performance-Kennzahlen.

Interaktive Visualisierung des Entscheidungsbaums
Interaktive Visualisierung des Entscheidungsbaums.


Die Tutorials können unten als Python-Skript (*.py) oder als Jupyter Notebook (*.ipynb) heruntergeladen werden.


1 Der Automotive-Datensatz

Der Automobildatensatz ist eine csv-Datei, die aus 648 Beobachtungen besteht. Jede Beobachtung / Zeile enthält numerische Sensorwerte zu insgesamt 22 Merkmalen, sowie die Spalte Ausfall, die nur zwei Werte annimmt: 0 für "kein Ausfall", und 1 für "Ausfall". Die erhobenen Merkmalswerte stammen aus Temperatur- und Druckmessungen sowie Mengenangaben zum Kraftstoff und zu Abgasdämpfen, die an verschiedenen Stellen im Motor erfasst wurden. Weiterhin enthält der Datensatz acht Merkmale zu den Lambdasonden, die an jeweils einem Zylinder im Motor Messwerte liefern und in 2 Bänke unterteilt sind. Z.B. das Merkmal Lambdasonde32 gibt den Sensorwert vom dritten Zylinder innerhalb der zweiten Bank an. Als Trennzeichen für die Spalten wird das Semikolon verwendet.

Ausfall;Kuehlmitteltemp;EinspritzmKurz;EinspritzmLang;Kraftstoffdruck;Ansaugkrdruck;Drosselklstellung;Einlasslufttemp;LS11;LS21;LS22;LS41;LS12;LS22.1;LS32;LS42;Kraftstoffleitdruck;Abgasrueckf;Kraftstoffdampfsaeub.;Kraftstoffeinsatz;EVAPDruck;Luftdruck;KatTemp
0;3.0;7.0;27.7;12.0;4.1;2.9;2.0;1.4;1.5;1.3;1.0;0.8;0.5;1.7;1.6;1.2;1.4;1.0;1.0;0.4;0.3;0.2
0;7.0;27.2;12.5;4.9;3.1;2.6;1.7;1.4;1.4;1.4;0.8;0.8;0.5;2.4;2.4;2.2;2.5;2.4;2.8;3.0;3.0;3.1
0;3.3;28.5;8.9;5.0;3.7;3.1;1.9;1.5;1.5;1.4;1.1;0.9;0.9;1.5;1.5;1.3;1.2;1.1;1.1;0.3;0.3;0.1
1;1.8;2.4;3.1;10.7;18.6;11.4;11.5;1.2;1.8;1.5;1.2;0.6;0.5;2.4;2.2;0.2;0.3;0.6;0.7;0.7;0.9;0.5
0;11.2;21.1;18.0;7.8;3.0;2.2;0.8;0.5;0.4;0.2;0.4;0.2;3.1;0.7;1.2;1.8;1.1;1.9;0.8;2.6;2.3;2.7
 Automotive-Datensatz automotive_data_648.csv

  Der Automotive-Datensatz ist nicht ganz balanciert: denn die Anzahl der Beobachtungen mit Label "Kein Ausfall" beträgt etwa 2/3, die der Beobachtungen mit Label "Ausfall" nur ein Drittel der Zeilen. Diese Information ist wichtig für die Art, wie der Entscheidungsbaum konfiguriert wird und welche Performance-Kennzahlen relevant sind. Bei balancierten Entscheidungsbäumen kann die Kennzahl Accuracy als Performance-Kennzahl verwendet werden, sie ist ein Maß für den Anteil an falschen Klassifikationen. Bei nicht balancierten Datensatzen, die nur wenige Ausfälle enthalten, sind andere Kennzahlen zu verwenden, genauer: Recall.

  Dieser Automotive-Datensatz enthält keine Timestamps, d.h. die Beobachtungen stammen nicht aus regelmäßig nacheinanderfolgenden Messungen. Es liegen lediglich Vergangenheitsdaten vor, bei denen bekannt ist, bei welcher Kombination von Sensorwerten ein Ausfall aufgetreten ist. Für derartige Datensätzen können Machine Learning-Algorithmen wie Entscheidungsbäume, Random Forest oder Isolation Forest eingesetzt werden, es sind jedoch keine Zeitreihenanalysen möglich. Datensätze, die in der Produktion auftreten, und als Zeitreihen erfasst werden, enthalten seltener Ausfälle und sind damit nicht balanciert.

2 Die Fragestellung

Uns interessiert, welche Merkmalskombinationen, d.h. welches Zusammenspiel der Sensorwerte, zu einem Ausfall des Motors führen. Die Frage, die der Entscheidungsbaum beantworten soll, lautet also: Welche Kombination von Merkmalen wird zu einem Ausfall führen? Werden alle Merkmale einen Einfluss auf den Ausfall haben und falls nein, mit welchem Gewicht werden welche Merkmale einen Ausfall bewirken?

3 Ablauf der Datenanalyse

Die Datenanalyse für die Predictive Maintenance läuft in fünf Schritten ab, wobei einige der Teilschritte je nach Daten und ausgewähltem Modell auch entfallen können. Z.B. ist bei einem Entscheidungsbaum-Modell keine Normalisierung der Daten erforderlich.

4 Was ist ein Entscheidungsbaum?

Ein binärer Entscheidungsbaum ist ein Klassifikationsmodell, mit dessen Hilfe eine Ja-Nein Fragestellung beantwortet werden kann. In einem Predictive Maintenance-Szenario soll z.B. vorhergesagt werden, ob bei einer bestimmten Kombination von Messungen ein Ausfall eintritt oder nicht. Diese "Vorhersage" ist strenggenommen eine Klassifikation, also eine Funktion / Zuordnung, die die Merkmale einer Beobachtung auf die korrekten Zustände ("Ausfall" oder "Kein Ausfall") abbildet. Das Modell entsteht, indem die Datentabelle, die bei der Datenerhebung erfasst wurde, um eine Spalte ergänzt wird, die Zielvariable genannt wird und die die Bewertung des Zustands enthält. Für die Vergangenheits­daten ist die Bewertung bekannt, für neue Daten wird die Bewertung durch das Modell vorhergesagt.

Ein Entscheidungsbaum besteht aus einer Wurzel, Knoten und Blättern, wobei jeder Knoten eine Entscheidungsregel und jedes Blatt eine Antwort auf die Fragestellung darstellt. Um eine Klassifikation eines einzelnen Datenobjektes abzulesen, geht man vom Wurzelknoten entlang des Baumes abwärts. Bei jedem Knoten wird ein Merkmal abgefragt und eine Entscheidung über die Auswahl des folgenden Knoten getroffen. Dies wird so lange fortgesetzt, bis man ein Blatt erreicht. Das Blatt entspricht der Klassifikation.

Mini-Beispiel
Zur Veranschaulichung betrachten wir einen Datensatz mit Sensor-Messwerten mit nur zwei Merkmalen: Temperatur und Druck und einer Zielvariablen: Ausfall. Der Entscheidungsbaum für das Mini-Beispiel gibt eine Antwort auf die Frage, bei welcher Kombination von Werten für die Merkmale temp und druck das Gerät ausfallen wird.

Datensatz mit Zielvariable

id temp druck ausfall
1normalhochnein
2hochnormalnein
3hochhochja
4hochnormalja
5hochhoch?

Die Zeilen 1 bis 4 enthalten die Vergangen­heits­daten mit bekannter Bewertung der Ziel­variablen. Zeile 5 enthält eine neue Beobachtung, für die auf Basis des Entscheidungs­baums der Ausfall vorhergesagt wird.

Entscheidungsbaum

Entscheidungsbaum

Wenn die Temperatur hoch und der Druck hoch ist, wird ein Ausfall vorhergesagt. Wenn die Temperatur normal ist, wird kein Ausfall eintreten.


Wie entsteht der Entscheidungsbaum?
Die genaue Form des Entscheidungsbaums entsteht durch den Trainingsprozess des überwachten Lernens. Die Input-Daten werden in Trainingsdaten und Testdaten unterteilt. Auf Basis der Trainingsdaten wird mittels eines passenden Algorithmus (CART, C4.5 oder ID3) der Entscheidungsbaum erstellt, dessen Güte mit Hilfe von Performance-Kennzahlen (z.B. Vertrauenswahrscheinlichkeit, Genauigkeit, Trefferquote) ermittelt wird. Der Trainingsprozess wird solange wiederholt, bis das Modell eine gewünschte Performance erreicht. Danach kann es für die Vorhersage auf neuen Datensätzen verwendet werden.


5 Vorbereitung der Entwicklungsumgebung

Als Vorbereitung für die Umsetzung der Machine Learning Pipeline mit Python muss zunächst die Entwicklungsumgebung Visual Studio Code installiert werden, sowie eine neuere Python-Version und drei Python-Bibliotheken für Datenanalyse und Machine Learning: NumPy, Pandas und ScikitLearn.

 Grundlagen der Python-Programmierung mit Visual Studio Code werden in Demo-PY1: Python-Tutorial beschrieben.
 Die Verwendung benötigter Python-Bibliotheken wie Numpy, Pandas und Scikit-Learn werden im dritten Teil des Python-Tutorials "Python-Bibliotheken" genauer beschrieben.
 Die Details der Verwendung von Jupyter Notebooks sind im Tutorial Jupyter Notebooks verwenden beschrieben.

Für die Programmierung des Entscheidungsbaum-Vorhersagemodells kann anschließend entweder ein Python-Skript oder ein Jupyter Notebook erstellt werden. Um in Visual Studio Code mit Jupyter Notebooks arbeiten zu können, muss die Jupyter Extension installiert werden. Das Arbeiten mit Jupyter Notebook hat den Vorteil, dass man Codezellen separat ausführen kann, dies ist vor allem beim Ausprobieren neuer Funktionen nützlich.

Benötigte Programm­bibliotheken
  NumPy wird für die Speicherung und Verarbeitung der Daten in mehrdimensionale Arrays benötigt.
  Pandas wird für das Lesen des Datensatzes aus der csv-Datei in eine interne Datenstruktur (DataFrame) und statistische Auswertung der Daten benötigt.
  Scikit-Learn bildet gängige Machine Learning-Algorithmen ab und besteht aus Modulen, die die Funktionen entsprechend der Machine Learning-Pipeline gruppieren. Das Modul tree enthält Klassen wie DecisionTreeClassifier und RandomForest. Das Modul preprocessing enthält Funktionen für Datenvorbereitung, z.B. Skalierung der Daten. Das sklearn-Modul metrics enthält Funktionen, mit denen man die Güte eines Vorhersagemodells bewerten kann. Wir importieren nur diejenigen Scikit-Learn Klassen und Funktionen, die in unserem Anwendungsfall benötigt werden, insbesondere: DecisionTreeClassifier, train_test_split, cross_validate, KFold,

Scikit-Learn wird laufend weiterentwickelt, z.B. sind einige Funktionen aus dem Modul metrics erst ab der Version 1.7 verfügbar. Die Version der Scikit-Learn Installation kann im Terminal überprüft werden, mit dem folgenden Befehl:

pip show scikit-learn

6 Umsetzung der Machine Learning Pipeline

Die Machine Learning Pipeline zur Erstellung eines Entscheidungsbaum-Vorhersagemodells besteht in der Abfolge der der Schritte: 1. Daten einlesen und vorbereiten, 2. Zielvariable und Trainingsdaten festlegen, 3. Vorhersagemodell erstellen, 4. Performance evaluieren, 5. Modell auf neue Daten anwenden.

6-1 Datenvorbereitung: Daten einlesen

Im Schritt "Datenvorbereitung" werden die Daten aus der csv-Datei automotive_data_648.csv eingelesen und statistisch untersucht.

Python-Code: "Daten einlesen"
import numpy as np
import pandas as pd
# Daten aus csv-Datei einlesen
file = "https://www.elab2go.de/demo-py4/automotive_data_648.csv"
df = pd.read_csv(file, header=0, index_col=None, sep = ";")

# Überprüfe Größe des Arrays
print(f"Anzahl Zeilen {df.shape[0]}, Anzahl Spalten: {df.shape[1]}")
# Überprüfe Ausfallverteilung
print("Ausfallverteilung")
print(df['Ausfall'].value_counts())
Ausgabe:
Anzahl Zeilen 648, Anzahl Spalten: 23

Ausfallverteilung:
 Ausfall
0    420
1    228

Verwendete Funktionen
  Mit import werden benötigte Bibliotheken importiert. In Python kann mit Hilfe der import-Anweisung entweder eine komplette Programmbibliothek importiert werden, oder nur einzelne Funktionen (from-import-Anweisung). Beim Import werden für die jeweiligen Bibliotheken oder Funktionen Alias-Namen vergeben: für Numpy vergeben wird den Alias np, für Pandas vergeben wir den Alias pd.
  Die Pandas-Funktion read_csv() liest Daten aus einer CSV-Datei in ein DataFrame df ein. Sie erhält als ersten Parameter den Namen der einzulesenden Datei, hier als URL angegeben. Falls die Datei lokal in demselben Ordner wie das Skript gespeichert wird, kann auch einfach der Dateiname angegeben werden, ohne Pfadangaben. Die weiteren Parameter sind Optionen, die den Import steuern.
  header=0 bedeutet, dass die erste Zeile der CSV-Datei Spaltenüberschriften enthält.
  index_col=0 bedeutet, dass die erste Spalte Zeilenüberschriften enthält.
  sep = ";" bedeutet, dass ein Semikolon als Trennzeichen (engl. separator) verwendet wird.
 df.shape ermittelt die Dimensionen des Datensatzes, df.shape[0] die Anzahl der Zeilen, df.shape[1] die Anzahl der Spalten. Wir überprüfen die Dimensionen des DataFrames um zu sehen, dass alle Zeilen und Spalten korrekt eingelesen wurden.
  Mit df['Ausfall'].value_counts() wird Anzahl der Zeilen mit Label "Ausfall" vs. "kein Ausfall" gezählt.

Statistische Auswertung des Datensatzes
Die statistische Untersuchung des Datensatzes ist wichtig, um herauszufinden, welche von den vielen Spalten stärker mit dem Ausfall korreliert sind, und welche statistischen Eigenschaften die Spalten im allgemeinen haben. Zunächst werden die Spalten bestimmt, die eine Korrelation > 0.25 mit dem Ausfall haben.

Die Auswertung ergibt, dass z.B. die Merkmale EinspritzmKurz, Ansaugkrdruck, Drosselklappenstellung eine hohe Korrelation mit dem Ausfall haben.
Python-Code: "Statistische Auswertung"
# Bestimme die Spalten, die eine Korrelation > 0.25 mit dem Ausfall haben
corr = df.corr()
cor_target= abs(corr["Ausfall"])
relevant_features = cor_target[cor_target > 0.25]
print("Relevante Merkmale:")
print(relevant_features)
print("Statistik:")
print(df[["Drosselklstellung", "Ansaugkrdruck"]].describe() )
Ausgabe:
Relevante Merkmale:
Ausfall              1.000000
EinspritzmKurz       0.293867
Ansaugkrdruck        0.297388
Drosselklstellung    0.293468
Einlasslufttemp      0.305100
Name: Ausfall, dtype: float64
Statistik:
       Drosselklstellung  Ansaugkrdruck
count         648.000000     648.000000
mean            6.183025      11.109414
std             5.584624       9.046221
min             0.600000       0.600000
25%             2.700000       4.175000
50%             4.300000       8.000000
75%             8.000000      15.100000
max            26.600000      32.100000

Verwendete Funktionen
  Die Pandas-Funktion df.corr berechnet die paarweise Korrelation der Spalten eines DataFrames. Das Ergebnis ist eine Matrix, deren Einträge Werte zwischen 0 und 1 sind, wobei ein Wert größer als 0.25 als eine vorhandene Korrelation interpretiert werden kann.
 df.describe() ist eine Pandas-Funktion, die statistische Kennzahlen wie Anzahl Werte, Mittelwert und Standardabweichung der Spalten eines Datensatzes ausgibt.


6-2 Datenvorbereitung: Zielvariable und Trainingsdaten festlegen

Das Entscheidungsbaum-Modell ist ein Algorithmus des Überwachten Lernens, d.h. der Entscheidungsbaum wird aufgebaut, indem man ihn anhand der Vergangenheitsdaten mit bekannter Zielvariable "Ausfall" trainiert. Als Vorbereitung für das Training des Entscheidungsbaum-Modells müssen die Daten, die in Form eines DataFrames df vorliegen, in passende Datenstrukturen extrahiert und aufgeteilt werden.

Zunächst werden die Merkmale von der Zielvariablen getrennt: Alle Spalten außer der Spalte "Ausfall" werden in ein neues DataFrame x extrahiert. Die Spalte "Ausfall" wird als Zielvariable in ein DataFrame y extrahiert.
Danach werden Merkmale und Zielvariable jeweils in einen Trainings- und einen Testdatensatz aufgeteilt. Der Trainingsdatensatz wird verwendet, um das Vorhersagemodell zu erstellen und der Testdatensatz wird verwendet, um das Modell zu validieren.

Python-Code: "Zielvariable und Trainingsdaten"
from sklearn.model_selection import train_test_split
# Merkmale x = Alle Spalten, außer Ausfall
x = df.drop('Ausfall', axis=1)
# Zielvariable y = Spalte Ausfall
y = df[['Ausfall']]
# Überprüfe die extrahierten DataFrames x, y
print("Merkmale x:")
print(x.iloc[:,0:3].head())
print("\nZielvariable y:")
print(y.head())
# Aufteilung in Trainings- und Testdaten
X_train, X_test, y_train, y_test = \
train_test_split(x, y, test_size=0.2, random_state=1) 
Ausgabe:
Merkmale x (erste drei Spalten):
   Kuehlmitteltemp  EinspritzmKurz  EinspritzmLang
0              3.0             7.0            27.7
1              7.0            27.2            12.5
2              3.3            28.5             8.9
3              1.8             2.4             3.1
4             11.2            21.1            18.0

Zielvariable y:
   Ausfall
0        0
1        0
2        0
3        1
4        0

Verwendete Funktionen
  Die Funktion train_test_split() erhält die Arrays x und y als Eingabeparameter und gibt vier Arrays zurück:
X_train: Trainingsdaten (nur Merkmale), X_test: Testdaten (nur Merkmale)
y_train: Trainingsdaten (Zielvariable), y_test: Testdaten (Zielvariable)
Der Parameter test_size steuert die Größe des Test-Datensatzes, hier: 30%.

Die Daten können optional auch normalisiert werden, dies kann die Performance des Entscheidungsbaums verbessern.

Python-Code: "Daten normalisieren"
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

6-3 Entscheidungsbaum-Modell erstellen

Das Vorhersagemodell für die Trainingsdaten (X_train, y_train) wird mit Hilfe der Methode fit() der Scikit-Learn-Klasse DecisionTreeClassifier erstellt. Der DecisionTreeClassifier verwendet eine optimierte Version des CART-Algorithmus. Beim Erstellen des Modells als Instanz der Klasse DecisionTreeClassifier werden verschiedene Konfigurationsparameter festgelegt, die steuern, wie genau der Entscheidungsbaum aufgebaut wird, die wichtigsten sind: criterion, splitter, max_depth, min_samples_split, min_samples_leaf, max_features.

Python-Code: "Modell trainieren"
 import matplotlib.pyplot as plt 
 from sklearn.tree import DecisionTreeClassifier
 from sklearn import tree 
 # Modell erstellen
 model = DecisionTreeClassifier(criterion='entropy', 
                             min_samples_split=5, 
                             min_samples_leaf=5,
                             max_depth=10,
                             random_state=1)
 # Modell trainieren
 model.fit(X_train, y_train)
 # Modell visualisieren
 fig, ax = plt.subplots(figsize=(10, 10)) 
 tree.plot_tree(model, max_depth = 5, 
                feature_names = x.columns, class_names=["nein", "ja"], filled=True)
 plt.show() 

Verwendete Funktionen
  DecisionTreeClassifier erstellt das Entscheidungsbaum-Modell, welches über Parameter konfiguriert wird:


  model.fit() passt den Entscheidungsbaum an die Trainingsdaten an.
  plot_tree() visualisiert den Entscheidungsbaum. Die Visualisierung mit plot_tree kann über verschiedene Parameter konfiguriert werden. Muss-Angaben sind feature_names: die Merkmals-Namen bzw. Spaltennamen und class_names: die Werte, die die Zielvariable annehmen kann, in aufsteigender numerischer Reihenfolge, daher nein = 0 zuerst.

Zu große Wert für max_depth oder max_features führt zu einer Überanpassung des Modells an die Trainingsdaten. Der kleinste Wert für max_features ist 1, der größte Wert ist die Anzahl der Merkmale (in unserem Fall, 22). Angenommen, der Trainingsdatensatz besteht aus 100 Beobachtungen / Zeilen und wir setzen max_features=10. Dann werden bei jedem Split (d.h. Aufteilung der Daten an einem Knoten) 10 Merkmale zufällig ausgewählt und aus diesen 10 wird das beste Merkmal für den Split bestimmt.

6-4 Optimierte Visualisierung mit Graphviz

Die Visualisierung des Entscheidungsbaums wird mit Hilfe der Bibliothek Graphviz und der scikit learn-Funktion export_graphviz() erstellt. Graphviz ist ein Open-Source-Programmpaket zur Visualisierung von Graphen. Graphviz entnimmt alle zur Erzeugung der Grafik benötigten Anweisungen einer Textdatei, die eine Beschreibung der Knoten und Kanten des Graphen enthält, und zwar in der DOT-Beschreibungssprache für die visuelle Darstellung von Graphen.

Zunächst wird in Zeile 2-5 mit Hilfe der Funktion export_graphviz() der Entscheidungsbaum in das graphviz-DOT-Format exportiert. Daraus wird dann in Zeile 7 ein Graph erzeugt und in Zeile 8 in der Jupyter Notebook Ausgabe angezeigt. Wichtig: Um Graphviz zu verwenden, reicht es nicht, das entsprechende Python-Paket zu installieren, sondern Graphviz muss als eigene Anwendung auf Ihrem Rechner installiert und über die PATH-Variable bekannt sein.

Python-Code: "Optimierte Visualisierung"
# Erzeuge Graphviz-Graphen aus dot-Quellcode   
import graphviz as gv 
import pydot
from sklearn.tree import export_graphviz 
dot=export_graphviz(model, max_depth=5, filled=True,  
                    feature_names=x.columns,   
                    class_names=['nein','ja'])
pdot = pydot.graph_from_dot_data(dot)
pdot[0].set_graph_defaults(size = "\"8,6\"")
pdot[0].set_fontsize(18)
graph = gv.Source(pdot[0].to_string())
graph.render("images/decisiontree", format='svg', view=False)
Ausgabe:

7 Performance-Evaluation

Um ein Entscheidungsbaum-Vorhersagemodell zu validieren, verwendet man Performance-Kennzahlen wie Accuracy, Precision, Recall, statistische Verfahren wie die Kreuzvalidierung und Visualisierungen wie die ROC-Kurve, die angeben, wie gut der Wert der Zielvariablen mit dem entsprechenden Modell vorhergesagt wird.

7-1 Vorhersage erstellen

Die Berechnung von Performance-Kennzahlen erfordert stets die Durchführung einer Vorhersage. Die Vorhersage geschieht mit Hilfe der Scikit-Learn-Funktion predict(), es werden die beim Training abgezweigten Testdaten X_test verwendet, und man erhält ein NumPy-Array y_pred, das die Vorhersagewerte enthält. Anschließend kann man mit Hilfe der Funktionen des Moduls sklearn.metrics verschiedene Kennzahlen berechnen. Die Funktion classification_report gibt eine tabellarische Übersicht der wichtigsten Performance-Kennzahlen aus. Die Funktionen accuracy_score, precision_score und recall_score werden verwendet, um die Kennzahlen einzeln zu ermitteln.

Python-Code: "Vorhersage"
from sklearn.metrics import classification_report
from sklearn.metrics import accuracy_score, precision_score, recall_score
y_pred = model.predict(X_test)
print(classification_report(y_test, y_pred))
print('PERFORMANCE-KENNZAHLEN:')
acc = 100 * accuracy_score(y_test, y_pred)
prec = 100 * recall_score(y_test, y_pred)
rec = 100 * precision_score(y_test, y_pred)
print(f"f"Accuracy: {acc}\nPrecision: {prec}\nRecall: {rec}")
Ausgabe: Classification Report
           precision    recall  f1-score   support

           0       0.91      0.95      0.93        87
           1       0.90      0.81      0.85        43

    accuracy                           0.91       130
   macro avg       0.90      0.88      0.89       130
weighted avg       0.91      0.91      0.91       130
PERFORMANCE-KENNZAHLEN:
Accuracy: 90.76923076923077
Precision: 81.3953488372093
Recall: 89.74358974358975

Wie werden die Kennzahlen ermittelt? Ein Blick hinter die Kulissen

Nach Durchführung einer Vorhersage hat man zwei Arrays, y_test = [0, 1, 1, 1, ...] und y_pred = [0, 0, 1, 1, ...], wobei das erste Array y_test die erwartete Klassifizierung und das zweite Array y_pred die Vorhersagewerte enthält. Die Güte der Klassifizierung wird aus der Menge der Übereinstimmungen abgeleitet, dabei verwendet man als Ausgangspunkt die Kategorien TP, TN, FP, FN:

Die Metriken Accuracy, Precision, Recall und F1-Score werden aus diesen Kategorien berechnet, z.B. Accuracy anhand der Formel Accuracy = (TP + TN) / (TP + TN + FP + FN), wie im folgenden Beispiel-Code. Das Scikit-Learn-Modul metrics stellt die Funktionen für die Berechnung schon zur Verfügung, so dass eine ausführliche Berechnung wie im folgenden Beispiel entfällt - es sei denn, man sucht ein tieferes Verständnis der Herleitung.

Python-Code: "Wie werden Kennzahlen TP, TN, FP, FN ermittelt?"
testdata = {
    'ytest' : y_test['Ausfall'].to_numpy(),
    'ypred' : y_pred,
    'K': ' ',
}
df_y = pd.DataFrame(testdata)
df_tp = df_y[(df_y['ytest'] == 1) & (df_y['ypred'] == 1)]
df_tn = df_y[(df_y['ytest'] == 0) & (df_y['ypred'] == 0)]
df_fp = df_y[(df_y['ytest'] == 0) & (df_y['ypred'] == 1)]
df_fn = df_y[(df_y['ytest'] == 1) & (df_y['ypred'] == 0)]
df_y.loc[df_tp.index, 'K'] = 'TP'
df_y.loc[df_tn.index, 'K'] = 'TN'
df_y.loc[df_fp.index, 'K'] = 'FP'
df_y.loc[df_fn.index, 'K'] = 'FN'
print("Vorhersagen mit Kategorie TP, TN, FP, FN:\n", df_y.head())

tptn = df_tp['K'].count() + df_tn['K'].count()
tptnfpfn = df_tp['K'].count() + df_tn['K'].count() + \ 
           df_fp['K'].count() + df_fn['K'].count()
acc = tptn / tptnfpfn
print(f"Accuracy = (TP + TN) / (TP + TN + FP + FN):\n{acc}")
Ausgabe: Testdaten mit Vorhersage und Kategorie TP, TN, FP, FN
Vorhersagen mit Kategorie TP, TN, FP, FN:
    ytest  ypred kat
0      0      0  TN
1      1      0  FN
2      1      1  TP
3      1      1  TP
4      0      0  TN
Accuracy = (TP + TN) / (TP + TN + FP + FN):
0.9076923076923077

7-2 Performance-Kennzahlen: Accuracy, Precision und Recall

Die Performance-Kennzahlen Accuracy, Precision und Recall und F1-Score werden aus den absoluten Zahlen True Positive, True Negative, False Positive und False Negative ermittelt, ähnlich wie in Abschnitt 7-1 beschrieben. Der Wertebereich der Kennzahlen ist jeweils [0, 1] bzw. [0%, 100%].

In einem Predictive Maintenance-Szenario entstehen dem Unternehmen einerseits Kosten, falls ein Ausfall nicht als solcher erkannt wird, es entstehen andererseits auch Kosten, wenn fälschlicherweise ein Ausfall vorhergesagt und die Produktion unterbrochen wird. Diese beiden Fälle werden durch die Kennzahlen Recall und Precision abgebildet, die beide wichtig, allerdings komplementär sind: sie können nicht gleichzeitig maximiert werden. Dies bedeutet, dass man Entscheidungsbäume entweder für die Kennzahl "Precision" oder für die Kennzahl "Recall" optimieren kann, oder für einen Kompromiss aus beiden: die Kennzahl
F1-Score = 2 * (PREC * REC) / (PREC + REC).


7-3 Performance-Evaluation mit Kreuzvalidierung

Die Kreuzvalidierung ist ein statistisches Validierungsverfahren für die optimierte Bestimmung der Performance Kennzahlen, bei dem in mehreren Durchgängen aus den Daten jeweils eine Teilmenge ausgelassen und zum Validieren verwendet wird, das ergibt jeweils einen Satz von Performance-Kennzahlen, die dann gemittelt werden.
Bei der 5-fachen Kreuzvalidierung werden z.B. die Daten in fünf gleich große Teilmengen (Folds) aufgeteilt. Das Modell wird jeweils mit vier Teilmengen trainiert und mit der verbleibenden Teilmenge getestet. Dieser Ablauf wird fünfmal wiederholt, wobei jeder Fold einmal als Testdatensatz dient, und jeder Durchgang liefert Satz von Performance-Kennzahlen. Die Performance-Kennzahlen aus den unterschiedlichen Durchgängen werden am Ende gemittelt.

Python-Code: "Kreuzvalidierung"
from sklearn.metrics import accuracy_score, precision_score, recall_score
from sklearn.model_selection import cross_validate,  KFold
y_pred = model.predict(X_test)

# Ermittle Genauigkeit der Vorhersage mittels Kreuzvalidierung  
# accuracy, precision, recall, f1, roc_auc
k_folds = KFold(n_splits = 5, shuffle = True, random_state = 1)
np.set_printoptions(precision=2)
scores = cross_validate(model, X_train, y_train, 
                        scoring=['accuracy', 'precision', 'recall', 'f1'], 
                        cv = k_folds,
                        return_estimator=True, return_indices=True)

acc = round(100*scores['test_accuracy'].mean(), 2)
prec = 100*scores['test_precision'].mean()
rec = 100*scores['test_recall'].mean()
print(f"Kreuzvalidierung:\n Accuracy: {acc}, Precision: {prec}, Recall: {rec}")
Ausgabe:
Kreuzvalidierung:
Accuracy: 86.68
Precision: 88.24486519173426 
Recall: 73.78848208555635

7-4 Confusion Matrix und ROC-Kurve

Die Python-Bibliothek Scikit-Learn stellt für die Visualisierung der Performance-Metriken verschiedene optimierte Displays für Konfusionsmatrix und ROC-Kurven zur Verfügung, die es ermöglichen, die Güte eines Modells auf einen Blick zu erkennen.
  Die Konfusionsmatrix visualisiert die Kennzahlen TP, TN, FP und FN in einer Matrix, die die Werte in vier Blöcken anordnet, wobei die Treffer die Hauptdiagonale bilden, und die falschen Vorhersagen auf der zweiten Diagonale angezeigt werden.

###################      
#        #        #   Oben links: Kein Ausfall, und korrekt erkannt
#   TN   #   FP   #   Oben rechts: FP = Fehlalarm
#        #        #
###################
#        #        #   Unten links: FN = Nicht erkannter Ausfall    
#   FN   #   TP   #   Unten rechts: TP = Ausfall, korrekt erkannt
#        #        #      
###################

Falls die Konfusionsmatrix hohe Werte auf der Hauptdiagonale und kleine Werte auf der zweiten Diagonale hat, zeigt das Modell eine gute Performance.

  Die ROC-Kurve stellt die TP-Rate (y-Achse) gegen die FP-Rate (x-Achse) bei verschiedenen Werten zwischen 0 und 1 dar. Ein gutes Modell hat eine ROC-Kurve, die links oben hoch gezogen ist (hoher TPR bei kleinem FPR) und somit die Fläche unter der Kurve maximiert.

Python-Code: "Konfusionsmatrix und ROC-Kurve"
from sklearn.metrics import (confusion_matrix, 
                             ConfusionMatrixDisplay, RocCurveDisplay)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 4))
fig.suptitle('Entscheidungsbaum-Modell: Performance-Kennzahlen')
# Konfusionsmatrix
cm = confusion_matrix(y_test, y_pred)
disp_cm = ConfusionMatrixDisplay(cm, 
                                 display_labels=["Kein Ausfall", "Ausfall"])
disp_cm.plot(values_format="d", cmap=plt.cm.Blues, ax = ax1)
ax1.set_title("Confusion Matrix")
ax1.set_xlabel("Vorhersage"); ax1.set_ylabel("Tatsächlicher Wert")
# ROC-Kurven aus Kreuzvalidierung
disp_cv = RocCurveDisplay.from_cv_results(scores, X_train, y_train, ax = ax2)
ax2.plot([0, 1], [0, 1], "--", label="Zufall")
ax2.set_title("ROC-Kurve")
ax2.set_xlabel("FPR / Fehlalarm (%)"); 
ax2.set_ylabel("TPR / Ausfall korrekt(%)")
plt.legend(); plt.tight_layout()
plt.show()


Die ROC-Kurve kann mit Scikit-Learn auf mehrere Arten erstellt werden, genauer: mit einer der drei Funktionen RocCurveDisplay.from_predictions, RocCurveDisplay.from_cv_results, oder RocCurveDisplay.from_estimator.

RocCurveDisplay.from_predictions
erhält als Parameter die tatsächlichen und vorhergesagten Testdaten (y_test und y_pred) und erstellt daraus eine einzige ROC-Kurve.

disp_pred = RocCurveDisplay.from_predictions(y_test, y_pred)
disp_pred.plot()

RocCurveDisplay.from_cv_results
erhält als Parameter die Performance-Kennzahlen (scores), die zuvor mittels Kreuzvalidierung berechnet wurden, und erstellt insgesamt n_splits ROC-Kurven, nämlich genau so viele, wie bei der Kreuzvalidierung als Folds verwendet wurden. In der Funktion cross_validate müssen zuvor die Parameter return_estimator=True, return_indices=True eingestellt werden, da diese für die ROC-Kurve benötigt werden.

disp_cv = RocCurveDisplay.from_cv_results(scores, X_train, y_train)
disp_cv.plot()

RocCurveDisplay.from_cv_results gibt es erst seit der Scikit-Learn Version 1.7. D.h. hier ist es wichtig, die neueste Version der Programmbibliothek zu verwenden.

8 Interaktive Visualisierung

Nachdem die grundlegenden Funktionen der Klassifikation mit Entscheidungsbäumen getestet wurden, erstellen wir als Nächstes mit Hilfe der Jupyter Widget-Funktion interactive_output() eine interaktive Visualisierung des Entscheidungsbaums, mit dem Ziel, die Wirkung der unterschiedlichen Parameter auf die Performance des Modells zu testen.

Für die Benutzeroberfläche wurden verschiedene Design-Varianten ausprobiert: Training und Prognose auf einer Seite, Verwendung unterschiedlicher Tabs für die drei Schritte des Überwachten Lernens etc. Allen Varianten ist gemeinsam, dass die Benutzeroberfläche im oberen Bereich Steuerelemente hat, über die die wichtigsten Konfigurationsparameter eingestellt werden, und im unteren Bereich die tabellarische oder grafische Ausgabe angezeigt wird. Bei jeder Änderung eines Konfigurationsparameters wird sofort ein neues Vorhersagemodell erstellt und visualisiert. Gleichzeitig werden die Performance-Kennzahlen für den Validierungsdatensatz automatisch berechnet und angezeigt.


Die finale Benutzeroberfläche besteht aus drei Tabs, die den Schritten Datenvorbereitung, Modellerstellung und Vorhersage bzw. Klassifikation entsprechen. Im ersten Tab "Datensatz" können die Merkmale des Datensatzes ausgewählt werden. Im zweiten Tab "Entscheidungsbaum" können die Parameter min_samples_split, min_samples_leaf und max_depth des Entscheidungsbaums eingestellt werden, dabei wird interaktiv der daraus resultierende Entscheidungsbaum und die daraus errechneten Performance-Kennzahlen angezeigt.

Nächste Schritte

Das einmal erstellte Entscheidungsbaum-Modell kann gespeichert und für die Vorhersage von Ausfällen genutzt werden. Die Performance-Metriken von ca. 80% können durch verschiedene Maßnahmen verbessert werden.
  Datenbasis erweitern
Ein Datensatz mit vielen Beobachtungen wird bessere Klassifikationsergebnisse liefern.
  Auswahl relevanter Merkmale: "Feature Selection"
Der vorliegende Datensatz enthält eine große Anzahl von Merkmalen, bei denen die erste statistische Analyse schon eine fehlende Korrelation mit dem Ausfall gezeigt hat. Ein Datensatz mit wenigen relevanten Merkmalen wird bessere Klassifikationsergebnisse liefern.
  Datensatz balancieren
Ein balancierter Datensatz mit einem ausgewogenen Anteil an Klassen (Ausfall=nein vs. Ausfall=ja) wird bessere Klassifikationsergebnisse liefern. Ausfälle treten in der Praxis zwar seltener auf, können jedoch durch Duplizierung statistisch ähnlicher Beobachtungen synthetisch hinzugefügt werden.
  Parameter des DecisionTreeClassificators optimieren
Mit der Scikit-Learn-Funktion GridSearchCV steht ein Verfahren zur Verfügung, um aus einer Menge von Parametern die besten herauszufinden.
  Optimierte Klassifikationsverfahren verwenden
RandomForest und XGBoost ((eXtreme Gradient Boosting)) sind zwei Algorithmen, die Entscheidungsbäume als Baustein verwenden und stabilere Klassifikationsergebnisse liefern.

Autoren, Tools und Quellen

Autoren
 Prof. Dr. Eva Maria Kiss


Tools:
Python, Anaconda, Jupyter Notebook, Visual Studio Code
Scikit-Learn, Pandas, Matplotlib, NumPy

  Python-Skript herunterladen
  Jupyter Notebook herunterladen

Quellen und weiterführende Links