RUL

Restlebenszeit-Vorhersage / Remaining Useful Lifetime (RUL)

Dieses Notebook steht auch direkt zum Download auf unserem GitHub bereit!

Beispiel Notebook aus dem ITS.ML Workshop "Inside Out: The Essentials of Predictive Maintenance"

Dieses Beispiel beschreibt exemplarisch das Vorgehen, um eine ML-Pipeline zu erzeugen, das in der Lage ist die Restlebenszeit eines Systems vorherzusagen.

1. Daten

In diesem Beispiel nutzen wir die Turbofan Engine Daten des Nasa Repositories (6. Turbofan Engine Degradation Simulation Data Set). Die darin enthaltenen Datensätze bestehen aus künstlich erzeugten Daten und beschreiben das Verhalten eines Flugzeugtriebwerks, indem unterschiedliche Betriebs- und Fehlerbedingungen erfasst werden. Ziel ist es ein Modell zu entwickeln, das vorhersagt, wann ein Fehler in der Hochdruckkompressionseinheit (HPC) des Flugzeugtriebwerks auftritt. Die Daten sind in 4 Datensätze unterteilt und entsprechend nummeriert, die jeweils mit unterschiedlichen Umgebungsbedingungen simulieren. Jeder Datensatz ist wiederum aufgeteilt in 3 Dateien (wobei "X" durch die Nummer des Datensatzes ersetzt werden muss):

  • train_FD00X.txt: Trainingsdatensatz bei dem der letzte Timestep als Fehlerzeitpunkt zu interpretieren ist
  • test_FD00X.txt: Testdatensatz bei dem jede Instanz zu einem unbestimmten Zeitpunkt endet. Für diesen soll die Restlebenszeit prädiziert werden
  • rul_FD00X.txt: Beinhaltet die wahren RUL Werte, die zur Evaluation genutzt werden

Sowohl die Trainingsdaten, als auch die Testdaten, sind im Komma-separierten CSV-Format gespeichert. Im Folgenden sind die Spalten erläutert:

  • Spalte 1: Instanz ID
    Alle Instanzen sind nummeriert von 1 - 100, dementsprechend beschreibt die erste Spalte um welche Instanz es sich handelt.
  • Spalte 2: Timestep
    Jede Instanz enthält Aufzeichnungen von Sensoren über einen längeren Zeitraum. Die Zeitpunkte, zu denen es Werte gibt, sind nummeriert und in der zweiten Spalte zu finden.
  • Spalten 3 - 5: Umgebungsbedingungen
    Zu den jeweiligen Zeitpunkten wurden Informationen zu den Umgebungsbedigungen gesammelt, welche in diesen Spalten notiert sind.
  • Spalten 6 - 26: Sensor Werte
    Insgesamt wurden 21 Sensoren aufgezeichnet, welche in diesen Spalten notiert sind

Sowohl die Umgebungsbedingungen, als auch die Sensor Werte, sind Zeitserien, welche von einem Triebwerk desselben Types stammen.

1.1 Daten lesen

Zu Beginn müssen die Daten eingelesen werden. Da der Datensatz in ein Pandas Dataframe geladen wird, ist es möglich die Spalten zu benennen. Dementsprechend wird zuerst die Spaltenbezeichnung erstellt um anschließend die Daten zu laden.

In [1]:
import warnings
warnings.filterwarnings("ignore") # Unterdrücke matplotlib Warnungen für sauberen Output

import pandas as pd

# Erstellen der Spaltennamen für das Dataframe, da der verwendeter Datensatz keine Spaltenbeschreibung besitzt
operational_setting_columns_names=["Operation Setting " + str(i) for i in range(1,4)]
sensor_data_columns_names=["Sensor " + str(i) for i in range(1,22)]
column_names = ["Instance", "Timestep"] + operational_setting_columns_names + sensor_data_columns_names

# Laden des Trainingdatensatzes FD001 mit den zuvor erstellten Spaltennamen
X_train = pd.read_csv("./CMAPSSData/train_FD001.txt", names=column_names, delim_whitespace=True, dtype=float)
print('Anzahl der Instanzen: ' + str(len(X_train["Instance"].unique())))
X_train.head()
Anzahl der Instanzen: 100
Out[1]:
Instance Timestep Operation Setting 1 Operation Setting 2 Operation Setting 3 Sensor 1 Sensor 2 Sensor 3 Sensor 4 Sensor 5 ... Sensor 12 Sensor 13 Sensor 14 Sensor 15 Sensor 16 Sensor 17 Sensor 18 Sensor 19 Sensor 20 Sensor 21
0 1.0 1.0 -0.0007 -0.0004 100.0 518.67 641.82 1589.70 1400.60 14.62 ... 521.66 2388.02 8138.62 8.4195 0.03 392.0 2388.0 100.0 39.06 23.4190
1 1.0 2.0 0.0019 -0.0003 100.0 518.67 642.15 1591.82 1403.14 14.62 ... 522.28 2388.07 8131.49 8.4318 0.03 392.0 2388.0 100.0 39.00 23.4236
2 1.0 3.0 -0.0043 0.0003 100.0 518.67 642.35 1587.99 1404.20 14.62 ... 522.42 2388.03 8133.23 8.4178 0.03 390.0 2388.0 100.0 38.95 23.3442
3 1.0 4.0 0.0007 0.0000 100.0 518.67 642.35 1582.79 1401.87 14.62 ... 522.86 2388.08 8133.83 8.3682 0.03 392.0 2388.0 100.0 38.88 23.3739
4 1.0 5.0 -0.0019 -0.0002 100.0 518.67 642.37 1582.85 1406.22 14.62 ... 522.19 2388.04 8133.80 8.4294 0.03 393.0 2388.0 100.0 38.90 23.4044

5 rows × 26 columns

1.2 Daten visualisieren

Im Folgenden werden einige Eigenschaften der verschiedenen Sensoren visualisiert. Bei einigen Sensoren ist zu erkennen, dass der minimale Wert dem maximalen Wert gleicht (z.B. Sensor 1, 5, 6, ...). Dies bedeutet, dass in allen Instanzen für diesen Sensor für jeden Timestep der gleiche Wert aufgezeichnet wurde. Dies spricht dafür, dass diese Sensoren keine Aussagekraft haben für eine spätere Prädiktion.

  • mean: Mittelwert
  • std: Standardabweichung vom Mittelwert
  • min: Minimaler Wert
  • 50%: Median
  • max: Maximaler Wert
In [2]:
X_train.describe(percentiles=[]).transpose()[1:]
Out[2]:
count mean std min 50% max
Timestep 20631.0 108.807862 6.888099e+01 1.0000 104.0000 362.0000
Operation Setting 1 20631.0 -0.000009 2.187313e-03 -0.0087 0.0000 0.0087
Operation Setting 2 20631.0 0.000002 2.930621e-04 -0.0006 0.0000 0.0006
Operation Setting 3 20631.0 100.000000 0.000000e+00 100.0000 100.0000 100.0000
Sensor 1 20631.0 518.670000 0.000000e+00 518.6700 518.6700 518.6700
Sensor 2 20631.0 642.680934 5.000533e-01 641.2100 642.6400 644.5300
Sensor 3 20631.0 1590.523119 6.131150e+00 1571.0400 1590.1000 1616.9100
Sensor 4 20631.0 1408.933782 9.000605e+00 1382.2500 1408.0400 1441.4900
Sensor 5 20631.0 14.620000 1.776400e-15 14.6200 14.6200 14.6200
Sensor 6 20631.0 21.609803 1.388985e-03 21.6000 21.6100 21.6100
Sensor 7 20631.0 553.367711 8.850923e-01 549.8500 553.4400 556.0600
Sensor 8 20631.0 2388.096652 7.098548e-02 2387.9000 2388.0900 2388.5600
Sensor 9 20631.0 9065.242941 2.208288e+01 9021.7300 9060.6600 9244.5900
Sensor 10 20631.0 1.300000 0.000000e+00 1.3000 1.3000 1.3000
Sensor 11 20631.0 47.541168 2.670874e-01 46.8500 47.5100 48.5300
Sensor 12 20631.0 521.413470 7.375534e-01 518.6900 521.4800 523.3800
Sensor 13 20631.0 2388.096152 7.191892e-02 2387.8800 2388.0900 2388.5600
Sensor 14 20631.0 8143.752722 1.907618e+01 8099.9400 8140.5400 8293.7200
Sensor 15 20631.0 8.442146 3.750504e-02 8.3249 8.4389 8.5848
Sensor 16 20631.0 0.030000 1.387812e-17 0.0300 0.0300 0.0300
Sensor 17 20631.0 393.210654 1.548763e+00 388.0000 393.0000 400.0000
Sensor 18 20631.0 2388.000000 0.000000e+00 2388.0000 2388.0000 2388.0000
Sensor 19 20631.0 100.000000 0.000000e+00 100.0000 100.0000 100.0000
Sensor 20 20631.0 38.816271 1.807464e-01 38.1400 38.8300 39.4300
Sensor 21 20631.0 23.289705 1.082509e-01 22.8942 23.2979 23.6184

Nun werden die Längen der Instanzen betrachtet. Im Folgenden ist ein Diagramm zu sehen, das die Verteilung der Instanzlängen der Trainingsdaten wiedergibt.

In [3]:
import matplotlib.pyplot as plt


def plot_label_distribution(data, title=None):
    """
    Erstellt ein Balkendiagramm, das die Lebensdauer der Instanzen visualisiert
    """
    overall_instance_lengths = []
    number_of_instances = len(data["Instance"].unique())
    for instance_id in range(number_of_instances): # Über alle Instanzen
        instance = data[data["Instance"] == instance_id + 1]
        overall_instance_lengths.append(instance.shape[0])

    fig, ax = plt.subplots()
    ax.hist(x=overall_instance_lengths, bins=number_of_instances);
    ax.set(xlabel='Timesteps', ylabel='Amount', title=title)
    fig.show()

plot_label_distribution(X_train)

Als nächstes wird eine Instanz näher betrachtet. Je Umgebungsbedingung/Sensor wird der Verlauf der jeweiligen Zeitserie dargestellt. Die X-Achse beschreibt dabei jeweils den zeitlichen Verlauf, wobei die Y-Achse den tatsächlichen Sensor Wert abbildet. Es wird die Instanz dargestellt, dessen ID in der ersten Zeile des folgenden Codes beschrieben ist.

In [4]:
instance_id = 1

# Darstellung des zeitlichen Verlaufs der Umgebungsbedingungen/Sensoren einer Instanz
number_of_plots = len(operational_setting_columns_names) + len(sensor_data_columns_names)
plt.figure(figsize=(15, 3*number_of_plots))
history_dataframe_id = X_train[X_train["Instance"] == instance_id]

for i, v in enumerate(operational_setting_columns_names):
    diagram = plt.subplot(number_of_plots, 1, 1 + i)
    diagram.plot(history_dataframe_id.index.values,history_dataframe_id.iloc[:, 2 + i].values)
    diagram.title.set_text(v)
    diagram.set_xlabel("Timestep")
    diagram.set_ylabel("Value")
    plt.tight_layout()

for i, v in enumerate(sensor_data_columns_names):
    diagram = plt.subplot(number_of_plots, 1, 4 + i)
    diagram.plot(history_dataframe_id.index.values,history_dataframe_id.iloc[:, 5 + i].values)
    diagram.title.set_text(v)
    diagram.set_xlabel("Timestep")
    diagram.set_ylabel("Value")
    plt.tight_layout()

plt.show()