Zum Inhalt springen
Alle Insights Die Finanzdatenebene

Point-in-Time-Feature-Pipelines richtig bauen

Leakage ist der stille Killer von Finanzmodellen. So bauen wir Feature-Pipelines, die immer nur das sehen, was zum Zeitpunkt des Ereignisses bekannt war.

5 Min. Lesezeit #feature-engineering#mlops#risikomodellierung
Fachleute aus dem Finanzsektor bei der Arbeit an einer KI-Initiative

Point-in-Time-Feature-Pipelines beantworten für jeden Wert, den sie erzeugen, genau eine Frage: War das zu dem Zeitpunkt bekannt, für den wir eine Vorhersage treffen wollen? Ein Feature ist Point-in-Time-korrekt, wenn es ausschließlich Daten widerspiegelt, die zum Zeitpunkt des Ereignisses tatsächlich eingetroffen und erfasst waren. Verletzen Sie diese Bedingung, lernt Ihr Modell die Zukunft, glänzt offline mit hervorragenden Scores und fällt an dem Tag auseinander, an dem es live geht.

Der Fehler ist still, weil nichts abbricht. Der Join läuft durch, die Zahlen wirken plausibel, die Notebook-Zelle wird grün. Das Problem ist, dass es sich um eine Zahl handelt, die ein Mensch am Entscheidungspunkt niemals hätte sehen können. In der Finanzwelt ist das keine akademische Feinheit. Es ist der Unterschied zwischen einem Betrugsmodell, das eine Transaktion vor der Autorisierung markiert, und einem, das klammheimlich auf dem Chargeback trainiert hat, der erst sechs Wochen später eintraf.

Wo die Zukunft durchsickert

Leakage ist selten ein dramatischer Bug. Sie schleicht sich über ganz gewöhnliche Joins gegen Tabellen ein, die nur den aktuellen Zustand der Welt vorhalten. Die Risikoklasse eines Kunden, der Saldo eines Kontos, eine Händlerkategorie, ein KYC-Status: All das wird an Ort und Stelle aktualisiert. Wenn Sie diese Werte allein über den Schlüssel an ein historisches Ereignis joinen, reicht die Pipeline dem Modell den heutigen Wert für eine Entscheidung, die letzten März gefallen ist.

Die Muster, die uns am häufigsten begegnen:

  • Aus dem Target abgeleitete Features. Ein Feld wird befüllt, wenn der Fall abgeschlossen ist, nicht wenn die Entscheidung gefällt wurde. chargeback_amount, days_to_default, final_disposition. Diese korrelieren perfekt mit dem Label, weil sie teilweise das Label sind.
  • Aggregate über das falsche Fenster. Ein 30-Tage-Transaktionszähler, dessen Fenster vom Ereignis aus in die Zukunft läuft, oder eines, das das zu bewertende Ereignis stillschweigend mit einschließt.
  • Nachträglich korrigierte Quelldaten. Ein Anbieter liefert eine Kennzahl und korrigiert sie eine Woche später. Wenn Sie nur die jeweils neueste Version speichern, liest jeder Backtest die Korrektur statt des ursprünglichen Werts. Restatements zum Quartalsende sind ein häufiger Übeltäter.
  • Slowly Changing Dimensions, die als statisch behandelt werden. Eine Adresse, ein Branchencode, ein Datensatz zum wirtschaftlich Berechtigten, der sich nach dem Ereignis geändert und die Zeile überschrieben hat, an die Sie joinen.

Keines dieser Muster wirft eine Exception. Alle blähen die Offline-Metriken auf, und keiner der Lift-Gewinne überlebt den Kontakt mit der Produktion.

Der Knowledge-Timestamp ist die tragende Spalte

Die Lösung beginnt mit dem Eingeständnis, dass die meisten Finanzdaten zwei Uhren haben. Es gibt den Zeitpunkt, zu dem etwas geschehen ist, die Event- oder Effective-Time. Und es gibt den Zeitpunkt, zu dem Ihre Systeme es erfahren haben, die Knowledge- oder Ingestion-Time. Eine Überweisung wird am Montag ausgeführt, landet aber erst am Dienstagnachmittag in Ihrem Ledger. Eine Auskunftei aktualisiert einen Score mit Stichtag zum Ersten, aber Sie erhalten die Datei erst am Vierten.

Ein Point-in-Time-korrektes Feature muss auf die zweite Uhr filtern. Wenn Sie ein Feature für ein Ereignis zum Zeitpunkt t bauen, dürfen Sie nur Datensätze verwenden, deren Knowledge-Time bei oder vor t liegt. Die Event-Zeit allein genügt nicht, denn verspätet eintreffende und nachträglich korrigierte Datensätze tragen eine Event-Zeit in der Vergangenheit, werden Ihnen aber erst später bekannt.

Konkret verlangen wir, dass jede Feature-Quelle ein explizites valid_from sowie einen Ingestion- oder Knowledge-Timestamp mitführt, und wir behandeln jede Tabelle ohne diese Angaben als unsicher für einen Join. Referenz- und Dimensionsdaten werden so gespeichert, dass frühere Versionen ein Restatement überstehen, statt überschrieben zu werden. Wo eine Quelle die Historie nachträglich korrigiert, bedeutet das bitemporale Speicherung: Wir können sowohl rekonstruieren, was wahr war, als auch, was wir an einem beliebigen vergangenen Tag glaubten. Das ist beim Ingest mehr Aufwand, und es ist der einzige Weg, auf dem ein Backtest die Wahrheit sagt.

As-of-Joins, so gebaut, dass sie auch im Maßstab halten

Der mechanische Kern der Pipeline ist der As-of-Join. Für jedes gelabelte Ereignis greifen Sie in eine Feature-Quelle und ziehen den letzten Wert, dessen Timestamp kleiner oder gleich dem Cutoff des Ereignisses ist. Nicht gleich, nicht am nächsten dran und niemals danach. Zeile für Zeile ausgeführt ist das trivial und unerträglich langsam; als Mengenoperation ist es ein sortiertes Merge über den Zeitschlüssel innerhalb jeder Entität.

Ein paar Regeln, an denen wir festhalten:

  • Ein Cutoff pro Ereignis, einmal berechnet. Jedes Feature für ein bestimmtes Ereignis wird gegen denselben As-of-Timestamp aufgelöst. Wenn verschiedene Features unterschiedliche Cutoffs verwenden, können Sie nicht mehr nachvollziehen, was das Modell gesehen hat.
  • Derselbe Code-Pfad offline und online. Die Transformation, die Trainingszeilen baut, und die, die einen Live-Request bedient, sollten dieselbe Funktion sein, die dieselben Definitionen liest. Wenn sie auseinanderlaufen, schleicht sich Training-Serving-Skew ein, und der sieht genau wie Leakage aus.
  • Ein bewusster Feature-Delay. Wenn ein Signal typischerweise zwei Tage nach seiner Event-Zeit eintrifft, halten wir Features manchmal auch im Training um diesen Verzug zurück, damit das Modell auf der Aktualität lernt, die es zur Inferenzzeit tatsächlich haben wird. Optimismus über den Dateneingang ist eine eigene Form von Leakage.
  • Lineage, die Sie einem Prüfer vorlegen können. Jeder Feature-Wert sollte sich auf die Quelldatensätze und -versionen zurückführen lassen, die ihn erzeugt haben, zu den Timestamps, die damals galten. Unter den Modellrisiko-Erwartungen von SR 11-7 und für alles, was eine ablehnende Entscheidung berührt, ist dieser Nachweispfad nicht optional.

Beweisen, dass die Pipeline ehrlich ist

Point-in-Time-Korrektheit lässt sich nicht per Augenmaß beurteilen, also testen wir darauf. Die nützlichste Prüfung ist eine Negativkontrolle: Bauen Sie ein Feature absichtlich aus Post-Event-Daten, bestätigen Sie, dass es das Modell aufleuchten lässt, und bestätigen Sie dann, dass die auditierte Pipeline sich weigert, es zu erzeugen. Wenn ein bekanntermaßen leckendes Feature und Ihr Produktions-Feature gleich scoren, dann leckt Ihr Produktions-Feature.

Darüber hinaus halten wir ein Eval-Set zurück, das zeitlich strikt hinter dem Trainingsfenster liegt, niemals ein zufälliger Split, damit die Evaluierung die reale Deployment-Reihenfolge widerspiegelt. Wir gleichen die Feature-Verteilungen zwischen dem Trainings-Build und dem Online-Store ab und schlagen Alarm bei Drift zwischen beiden, denn eine Lücke dort bedeutet meist, dass die zwei Code-Pfade sich getrennt haben. Und wir pflegen ein kleines Panel handnachverfolgter Ereignisse, bei denen ein Analyst von Hand aufgeschrieben hat, was am Entscheidungspunkt wissbar war, und wir vergleichen die Pipeline gegen diese Ground Truth, sooft sich die Definitionen ändern.

Nichts davon ist glamourös. Es läuft auf Timestamps, versionierte Dimensionen und Joins hinaus, die einen Cutoff respektieren. Aber es entscheidet, ob das Modell ehrlich ist. Ein Modell ist immer nur so ehrlich wie der Moment, den seine Features sehen durften. Wenn ein Finanzmodell zu viel sieht, schlägt sich der Preis als reale Entscheidung nieder, getroffen auf Informationen, die es noch gar nicht gab: ein Kredit, der bewilligt wird, oder eine Zahlung, die freigegeben wird, auf einem Fakt, den niemand am Schreibtisch hätte haben können.

Häufige Fragen

Worin unterscheidet sich Point-in-Time-Korrektheit von einem normalen Join?

Ein normaler Join gleicht über einen Schlüssel ab und übernimmt den Wert, der aktuell in der Quelltabelle steht. Ein Point-in-Time-Join schränkt zusätzlich über die Zeit ein und liefert den Wert, der zum Zeitpunkt des Ereignisses gültig war, statt des Werts, der heute existiert. Genau diese zweite Einschränkung verhindert, dass die Zukunft in die Vergangenheit durchsickert.

Brauche ich bitemporale Speicherung, um das korrekt hinzubekommen?

Nicht immer, aber Sie brauchen mindestens den Knowledge-Timestamp: wann Ihr System einen Fakt erfahren hat, getrennt davon, wann der Fakt wahr wurde. Die Event-Zeit allein lässt Restatements und verspätet eintreffende Datensätze durchsickern. Wenn Ihre Quellen die Historie nachträglich korrigieren, ist bitemporale Speicherung der ehrliche Weg, um zu rekonstruieren, was Sie an einem bestimmten Tag tatsächlich wussten.

Woran erkenne ich, dass in meinem Modell bereits Leakage steckt?

Das übliche Signal ist eine Offline-AUC oder -Precision, die in der Produktion ohne ersichtlichen Grund zusammenbricht. Auditieren Sie vor dem Deployment die wichtigsten Features nach Importance und fragen Sie bei jedem Wert, wann er wirklich verfügbar war. Ein einziges Feld, das erst bei Fallabschluss statt zum Entscheidungszeitpunkt befüllt wird, kann den Großteil Ihres scheinbaren Lifts tragen.

Arbeiten Sie an etwas Ähnlichem?

Erzählen Sie uns von Ihren Daten und dem Workflow drumherum, und Sie bekommen eine ehrliche Einschätzung.

30-minütiges Erstgespräch buchen