Zum Hauptinhalt springen

Feature Stores

Definition

Ein Feature Store ist ein Datensystem, das speziell dafür entwickelt wurde, den Lebenszyklus von ML-Features zu verwalten — von der Rohdaten-Transformation über die Speicherung bis zur latenzarmen Bereitstellung —, und zwar konsistent zwischen Modelltraining und Produktionsinferenz. Ohne einen Feature Store erleben Teams häufig Training-Serving-Skew: Die Feature-Berechnungslogik, die offline während des Trainings ausgeführt wird, unterscheidet sich subtil von der zur Serving-Zeit verwendeten Logik, was dazu führt, dass Produktionsmodelle schlechter abschneiden als bei der Offline-Auswertung.

Feature Stores begegnen dem, indem sie Feature-Definitionen als Code speichern und dieselbe Transformationslogik in beiden Kontexten ausführen. Sie pflegen zwei komplementäre Speicherschichten: einen Offline Store (ein Data Warehouse oder Data Lake, z. B. BigQuery, Redshift, Parquet-Dateien auf S3), der große historische Datensätze für Training und Batch-Scoring enthält, und einen Online Store (eine latenzarme Key-Value-Datenbank, z. B. Redis, DynamoDB, Cassandra), der vorberechnete Feature-Werte für Modelle zur Inferenzzeit mit Sub-Millisekunden-Latenz bereitstellt.

Das Problem des Training-Serving-Skew und der Bedarf nach Feature-Wiederverwendung werden im großen Maßstab akut. Eine große Organisation hat möglicherweise Dutzende von Teams, die unabhängig voneinander ähnliche Features berechnen (Kundenausgaben der letzten 7 Tage, Sitzungsdauer, Gerätetyp), mit subtilen Unterschieden in der Geschäftslogik. Ein Feature Store bietet einen verwalteten Katalog, in dem Features einmal definiert, validiert und teamübergreifend wiederverwendet werden, was doppelten Engineering-Aufwand und das Risiko inkonsistenter Feature-Logik erheblich reduziert.

Funktionsweise

Feature-Definition und Transformationspipelines

Features werden als Code definiert — Python-Klassen oder YAML-Manifeste —, die die Datenquelle, die Transformationslogik und den Entity-Schlüssel (den Bezeichner für die Feature-Abfrage, z. B. user_id, product_id) angeben. Batch-Transformationspipelines laufen nach einem Zeitplan, um Features im Offline Store zu materialisieren. Stream-Transformationspipelines (z. B. mit Flink oder Spark Structured Streaming) halten den Online Store für zeitkritische Features wie Echtzeit-Betrugssignale aktuell.

Offline Store: Abruf von Trainingsdaten

Beim Trainieren eines Modells wird ein Datensatz generiert, indem eine Liste von Entity-Schlüsseln und eine Menge von Zeitstempeln (ein „Point-in-Time-Join") bereitgestellt werden. Der Feature Store ruft die Feature-Werte ab, die zum Zeitpunkt jedes Zeitstempels korrekt waren, und vermeidet so Future-Data-Leakage. Diese Point-in-Time-Korrektheit ist eines der schwierigsten Dinge, die ohne einen Feature Store korrekt zu implementieren sind, und eine der wertvollsten Garantien, die er bietet.

Online Store: Latenzarmes Serving

Bevor ein Modell eine Vorhersage liefert, benötigt es Feature-Werte für die zu bewertende Entität (z. B. den Benutzer, der eine Anfrage stellt). Der Feature-Store-Client fragt den Online Store per Entity-Schlüssel ab und gibt einen Feature-Vektor in Millisekunden zurück. Da dieselben Feature-Definitionen sowohl dem Offline als auch dem Online Store zugrunde liegen, werden die Werte garantiert identisch berechnet.

Feature-Registry und Governance

Ein Feature-Katalog dokumentiert jedes Feature: seine Definition, seinen Eigentümer, seinen Datentyp, seine Frischegarantie und welche Modelle es verwenden. Diese Governance-Schicht ermöglicht Auffindbarkeit — ein neues Team kann bestehende Features durchsuchen, bevor es eigene schreibt — und Impact-Analyse — zu verstehen, welche Modelle betroffen sind, wenn sich die vorgelagerte Datenquelle eines Features ändert.

Materialisierungsjobs

Materialisierung ist der Prozess, Transformationspipelines auszuführen und Ergebnisse in die Stores zu schreiben. Offline-Materialisierung läuft als geplanter Batch-Job. Online-Materialisierung kopiert eine Teilmenge der Offline-Daten in den Online Store für schnellen Abruf oder wird durch Streaming-Pipelines gesteuert, wenn Echtzeit-Frische erforderlich ist. Feast, Tecton und Hopsworks bieten alle CLI-Befehle oder Orchestrierungsintegrationen zum Auslösen und Überwachen der Materialisierung.

Wann verwenden / Wann NICHT verwenden

Verwenden wennVermeiden wenn
Mehrere Teams oder Modelle dieselbe Feature-Logik teilen und Konsistenz entscheidend istEin einzelnes Modell mit einem kleinen, stabilen Feature-Set vorhanden ist, das sich nie ändert
Training-Serving-Skew Produktionsvorfälle oder Genauigkeitslücken verursacht hatDie Inferenzlatenzanforderungen entspannt sind und Batch-Scoring ausreicht
Point-in-Time-korrekte Trainingsdatensätze benötigt werden, um Data-Leakage zu vermeidenDer Engineering-Overhead eines Feature Stores den Projektumfang übersteigt
Features mit Sub-Millisekunden-Latenz für Echtzeit-Vorhersagen bereitgestellt werden müssenIn der frühen Erkundungsphase Features noch nicht stabil genug zur Formalisierung sind
Regulatorische Anforderungen einen verwalteten, auditierbaren Feature-Katalog verlangenDas Data-Science-Team klein ist und keine ML-Engineering-Unterstützung für die Infrastrukturverwaltung hat

Vergleiche

KriteriumFeastTectonHopsworks
Open-SourceJa (Apache 2.0)Nein (SaaS / verwaltet)Ja Kern; Enterprise kostenpflichtig
Verwaltetes AngebotNein (nur selbst gehostet)Ja (vollständig verwaltet)Ja (Cloud oder On-Prem)
Streaming-FeaturesBegrenzt (via Kafka-Quelle)Nativ, produktionsreifNativ mit Flink-Integration
Feature-MonitoringGrundlegendErweitert (eingebauter Drift)Erweitert
Am besten fürTeams, die OSS-Kontrolle wünschenUnternehmen, die verwaltete Echtzeit-Features benötigenTeams, die einen Full-Stack-Open-Source wünschen

Vor- und Nachteile

VorteileNachteile
Eliminiert Training-Serving-Skew durch gemeinsame TransformationslogikErhebliche Engineering-Investition für Aufbau und Betrieb
Ermöglicht Feature-Wiederverwendung über Teams hinweg, reduziert doppelten AufwandFügt eine operationelle Abhängigkeit im Serving-Pfad hinzu (Online-Store-Verfügbarkeit)
Point-in-Time-Joins verhindern Data-Leakage in TrainingsdatenFeature-Definitionen können zu einem Engpass werden, wenn Governance zu starr ist
Zentralisiert Feature-Governance und DokumentationLernkurve für Data Scientists, die mit der Abstraktion nicht vertraut sind
Unterstützt sowohl Batch- als auch Echtzeit-Feature-ServingÜberdimensioniert für Teams mit wenigen Modellen und stabilen Features

Code-Beispiele

# feast_feature_store_example.py
# Demonstrates defining, materializing, and retrieving features with Feast.
# Prerequisites:
# pip install feast pandas scikit-learn
# feast init my_feature_repo && cd my_feature_repo
# (Adjust the data source path below to match your environment.)

# ── feature_repo/features.py ──────────────────────────────────────────────────
# This file defines the feature views and entities in your Feast registry.

from datetime import timedelta
import pandas as pd
from feast import (
Entity,
FeatureStore,
FeatureView,
Field,
FileSource,
)
from feast.types import Float32, Int64

# 1. Define the entity — the primary key used to look up features
driver = Entity(
name="driver",
description="A taxi driver identified by driver_id",
)

# 2. Define the data source (parquet file for local demo; swap for BigQuery etc.)
driver_stats_source = FileSource(
path="data/driver_stats.parquet", # generated below
timestamp_field="event_timestamp",
created_timestamp_column="created",
)

# 3. Define a FeatureView — the transformation and storage spec
driver_stats_fv = FeatureView(
name="driver_hourly_stats",
entities=[driver],
ttl=timedelta(days=7), # how long features stay valid
schema=[
Field(name="conv_rate", dtype=Float32),
Field(name="acc_rate", dtype=Float32),
Field(name="avg_daily_trips", dtype=Int64),
],
online=True, # materialize to online store
source=driver_stats_source,
)


# ── generate_sample_data.py ───────────────────────────────────────────────────
# Run this once to create sample data before materializing.
def generate_driver_stats(path: str = "data/driver_stats.parquet") -> None:
import os
os.makedirs("data", exist_ok=True)

rng = pd.date_range(end=pd.Timestamp.now(tz="UTC"), periods=48, freq="h")
df = pd.DataFrame({
"driver_id": [1001, 1002, 1003] * 16,
"event_timestamp": list(rng[:48]),
"created": pd.Timestamp.now(tz="UTC"),
"conv_rate": [0.8, 0.6, 0.9] * 16,
"acc_rate": [0.95, 0.88, 0.92] * 16,
"avg_daily_trips": [150, 200, 175] * 16,
})
df.to_parquet(path, index=False)
print(f"Sample data written to {path}")


# ── training_data_retrieval.py ────────────────────────────────────────────────
# Retrieve a point-in-time correct training dataset.
def get_training_data(repo_path: str = ".") -> pd.DataFrame:
store = FeatureStore(repo_path=repo_path)

# Entity DataFrame: the entities and timestamps we want features for
entity_df = pd.DataFrame({
"driver_id": [1001, 1002, 1003],
"event_timestamp": [
pd.Timestamp("2024-01-15 10:00:00", tz="UTC"),
pd.Timestamp("2024-01-15 11:00:00", tz="UTC"),
pd.Timestamp("2024-01-15 12:00:00", tz="UTC"),
],
"label": [1, 0, 1], # target variable for supervised training
})

# Point-in-time join: retrieves feature values as-of each row's timestamp
training_df = store.get_historical_features(
entity_df=entity_df,
features=[
"driver_hourly_stats:conv_rate",
"driver_hourly_stats:acc_rate",
"driver_hourly_stats:avg_daily_trips",
],
).to_df()

print("Training dataset:")
print(training_df.to_string())
return training_df


# ── online_serving.py ─────────────────────────────────────────────────────────
# Retrieve features for real-time inference after materialization.
def get_online_features(driver_ids: list, repo_path: str = ".") -> dict:
store = FeatureStore(repo_path=repo_path)

# Materialize features to the online store first:
# store.materialize_incremental(end_date=pd.Timestamp.now(tz="UTC"))

feature_vector = store.get_online_features(
features=[
"driver_hourly_stats:conv_rate",
"driver_hourly_stats:acc_rate",
"driver_hourly_stats:avg_daily_trips",
],
entity_rows=[{"driver_id": did} for did in driver_ids],
).to_dict()

print("Online feature vector:")
for key, values in feature_vector.items():
print(f" {key}: {values}")
return feature_vector


# ── main ──────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
generate_driver_stats()
# After running `feast apply` to register the feature views:
# training_df = get_training_data()
# online_fv = get_online_features([1001, 1002])
print("Feature definitions ready. Run `feast apply` to register them.")

Praktische Ressourcen

Siehe auch