Neue Algorithmen für relationales Lernen: Was Deep Learning nicht beherrscht

Seite 3: Einführung in relationales Lernen für Praktiker

Inhaltsverzeichnis

Eine typische Aufgabe von Data Scientists ist es, Kreditinstitute bei Entscheidungen über die Vergabe von Krediten an Bestandskunden zu unterstützen. Dafür entwickeln sie ein Prognosemodell, das die Ausfallwahrscheinlichkeit eines gewährten Kredits vorhersagen kann. Eine vereinfachte Datengrundlage für die Vergabeentscheidung würde in etwa wie in Abbildung 7 aussehen.

Die Information über den Kreditausfall ist die kategoriale Zielvariable. Zur Erinnerung: Die Tabelle mit Beobachtungen der Zielvariable stellt die Population des Prognosemodells dar (Tabelle population, Abb. 7.1). Da die Information über einen Kreditausfall nominalskaliert ist, handelt es sich um ein Klassifikationsproblem. Das Kreditinstitut hat im Rahmen der Geschäftsbeziehung weitere Daten zur Verfügung, die für die Prognose eines Kreditausfalls relevant sind: dazu gehören die Transaktionen, die sich in der Vergangenheit im Rahmen der bestehenden Kundenbeziehung beobachten ließen (Tabelle trans, Abb. 7.2). Um die Darstellung kurz zu halten, beschränkt sich das Beispiel hier auf die Beziehung zwischen population und trans (Listing 1).

Das Datenmodell als Basis zur Kreditvergabeentscheidung (Abb. 7).

(Bild: getML)

import getml
getml.set_project("young_devs")
population_train, population_test, _, trans, _ = getml.datasets.load_loans()

Listing 1: Projekt anlegen und Daten laden

Wie beschrieben besteht die Herausforderung beim relationalen Lernen darin, 1:n-Beziehungen zwischen der Populationstabelle und peripheren Tabellen aufzulösen. Im Kreditvergabebeispiel bedeutet das, mehrere mit einem Kundenkonto assoziierte Transaktionen (aus trans) zu einem Wert zu bündeln, der anschließend in die Prognose der Kreditausfallwahrscheinlichkeit einfließt.

Aus technischer Sicht lässt sich das Bündeln mehrerer Zeilen aus peripheren Tabellen durch das Anwenden von Aggregationsfunktionen umsetzen; ein Beispiel wäre der Durchschnitt aller Kontostände der Vergangenheit. Diese Aggregationsfunktionen werden in der Regel nicht global, sondern unter der Berücksichtigung von Nebenbedingungen angewandt. Listing 2 zeigt beispielhaft ein händisch erzeugtes Feature auf Basis der durchschnittlichen Kontostände 90 Tage vor Kreditbeantragung. Ein Feature, wie es typischerweise im Prozess des Feature Engineerings geschrieben wird, schaut aus wie im folgenden Listing 2.

SELECT AVG(trans.balance)
FROM population
INNER JOIN trans ON population.account_id = trans.account_id
WHERE trans.date <= population.date_loan
  AND trans.date >= population.date_loan - 90;

Listing 2: Eine SQL-Query mit einem händisch geschriebenen Feature

Doch woher wissen Data Scientists, dass sich der bestmögliche Erklärungsgehalt für ein Modell aus den vergangenen 90 Tagen speist und nicht beispielsweise aus 167,23 Tagen? Der Prozess läuft – wie im Feature Engineering – händisch ab, daher sind sowohl die Auswahl der Spalte, der Aggregationsfunktion als auch die Gestaltung der Nebenbedingung willkürliche Entscheidungen mit ungewissem Ausgang. Feature Learning ermöglicht das algorithmische Erlernen der entsprechenden Aggregationen und auch Nebenbedingungen.

Um Features aus relationalen Datenstrukturen lernen zu können, ist es nötig, dem Algorithmus Informationen mitzuteilen, die über die bloßen Datentypen hinaus gehen. Im getML-Framework gibt es hierfür das Konzept der Rollen. Eine Rolle lässt sich als Datentyp mit zusätzlichen Metadaten betrachten und definiert, wie der Feature-Learning-Algorithmus eine Spalte zu verwenden hat. Dabei kommen etwa bestimmte Aggregationen wie AVG bei kategorischen Spalten nicht zur Anwendung, zum Erlernen von Nebenbedingungen zieht der Algorithmus sie jedoch immer heran. Für die Tabelle trans (s. Abb. 7.2) heißt das beispielsweise:

  • Dem Volumen einer Transaktion (amount) ist die Rolle numerical zugewiesen – auf diese Spalte sind numerische Aggregationen (SUM, AVG, …) anwendbar.
  • Dem Transaktionstypen (type) kommt die Rolle categorical zu – auf diese Spalte sind keine numerischen Aggregationen anwendbar, sie lässt sich aber zur Bildung von Nebenbedingungen und für bestimmte Aggregationen wie COUNT heranziehen.

Neben den konkreten Daten und den Annotationen benötigt ein Feature-Learning-Algorithmus eine weitere Information: das abstrakte Datenmodell, das die Beziehungen zwischen den einzelnen Tabellen beschreibt und so den relationalen Suchraum definiert.

Die Python-API von getML bietet hierfür mehrere Möglichkeiten. In diesem Beispiel nutzen wir die StarSchema-Klasse, die es erlaubt, mit wenigen Zeilen Code ein Sternschema zu modellieren. Die API enthält außerdem TimeSeries zur Modellierung von Zeitreihen und ein allgemeines DataModel, das es erlaubt, relationale Probleme beliebiger Komplexität (wie Schneeflockenschemata) zu modellieren (Listing 3).

schema=getml.data.StarSchema(
    train=population_train,
    test=population_test,
    alias="population",
)

schema.join(
    trans,
    on="account_id",
    time_stamps=("date_loan", "date"),
)

Listing 3: Definition des abstrakten Datenmodells

StarSchema erhält als Parameter die Populationstabelle (im Beispiel bereits aufgeteilt in Trainings- und Testdaten: population_train, population_test), sowie einen Alias (population), der es erlaubt, abstrakt – unabhängig vom Split – auf die Population zu referenzieren. Anschließend gilt es, die peripheren Tabellen – die Zacken des Sterns – via join zum Schema hinzuzufügen und so mit der Populationstabelle – dem Zentrum des Sterns – in Beziehung zu bringen. Hierzu wird join ein Join Key übergeben (on). Außerdem ist es möglich join als zusätzliche Restriktion Zeitstempel zu übergeben (time_stamps), um die Plausibilität des Modells sicherzustellen.

Für den konkreten Fall heißt das, dass nur solche Transaktionen Berücksichtigung finden, die vor den Kreditvergaben durchgeführt wurden (date <= date_loan). Zu beachten ist hier auch, dass die Algorithmen Joins im abstrakten Datenmodell nach dem Prinzip der Lazy Evaluation ausführen: an diesem Punkt finden also noch keine konkreten Operationen statt. Erst, wenn die Feature-Learning-Algorithmen Feature-Repräsentationen lernen, materialisieren die Algorithmen die im abstrakten Datenmodell beschriebenen Joins.

multirel = getml.feature_learning.Multirel(
    loss_function=getml.feature_learning.loss_functions.CrossEntropyLoss,
)

xgboost = getml.predictors.XGBoostClassifier()

pipe = getml.pipeline.Pipeline(
    data_model=schema.data_model,
    feature_learners=multirel,
    predictors=xgboost,
)

Listing 4: Zusammenfügen der Komponenten in der Pipeline

Zuletzt sind die Modelle zu definieren, die in dem mehrstufigen Lernproblem zur Anwendung kommen sollen: erstens ein Feature-Learning-Algorithmus (hier Multirel), zweitens ein Prognose-Algorithmus – hier XGBoost (Listing 4).

Nun haben die Data Scientists alle Zutaten beisammen, um – ausgehend vom Problem in relationaler Form – eine End-to-End-Pipeline aufzubauen. Eine solche Pipeline bildet das Prognoseproblem vollumfänglich ab. An sie können Data Scientists anschließend das Datenmodell aus dem StarSchema (data_model), das Feature-Learning-Modell (feature_learners), sowie das Prognosemodell (predictors) übergeben (Listing 5).

pipe.fit(population_table=population_train, peripheral_tables={"trans": trans})

Listing 5: Trainieren der Pipeline

Im nächsten Schritt gilt es, mit fit die konkreten Daten an die Pipeline zu übergeben. Der Methodenaufruf startet das Schätzen der Modelle. Im Rahmen von fit erlernt die Pipeline, basierend auf den Trainingsdaten, in einem ersten Schritt die Logik hinter den Features. Hierfür traversieren die Algorithmen die im abstrakten Datenmodell beschriebenen Pfade und lösen die Joins stückweise auf. Aus den nun materialisierten Daten ziehen die Algorithmen in der Folge wiederholt Stichproben und erlernen auf deren Basis die Logik eines Features. Zum Erlernen weiterer Features kommen Ensemblemethoden zum Einsatz. Nach dem Erlernen der Features transformiert die Pipeline die gelernte Logik in konkrete Featurewerte.

Sie dienen im zweiten Schritt dem Prognosealgorithmus (XGBoost) als Eingangsdaten. Mit einer geschätzten Pipeline (nach fit) ist es möglich, Prognosen auf ungesehene Daten vorzunehmen (predict), die Prognosegüte des Modells zu bestimmen (score) oder die Featurewerte zu materialisieren (transform). Letzteres erlaubt es, eigene Modelle zur Prognose (beispielsweise aus scikit-learn) zu verwenden. Möglich ist hier aber prinzipiell die Nutzung eines jeden Modells, für das eine Python-API verfügbar ist.