Module 7 — Feast en pratique
Ce module remplace la théorie par une chaîne complète : un dépôt de variables minimal pour le scoreur de fraude, avec Parquet en hors ligne et Redis en local pour l'en ligne. Chaque commande est exécutable sur un poste de développeur en moins de dix minutes. Puis on discute les limites de Feast, pour savoir quand il ne suffit plus.
Le dépôt minimal
Un dépôt Feast tient dans trois fichiers, plus les données. Structure :
scoreur_fraude/
├── feature_store.yaml
├── definitions.py
└── data/
└── transactions.parquet
Le fichier feature_store.yaml déclare les backends :
project: scoreur_fraude
registry: data/registry.db
provider: local
online_store:
type: redis
connection_string: "localhost:6379"
offline_store:
type: file
entity_key_serialization_version: 2
Le fichier definitions.py porte l'entité, la source, la vue :
from feast import Entity, FeatureView, Field, FileSource
from feast.types import Int64, Float32
from datetime import timedelta
porteur = Entity(name="porteur", join_keys=["card_id_hash"])
transactions_source = FileSource(
path="data/transactions.parquet",
timestamp_field="event_timestamp",
created_timestamp_column="created_at",
)
porteur_transactions_recentes = FeatureView(
name="porteur_transactions_recentes",
entities=[porteur],
ttl=timedelta(hours=24),
schema=[
Field(name="nb_transactions_1h", dtype=Int64),
Field(name="nb_transactions_24h", dtype=Int64),
Field(name="nb_transactions_7j", dtype=Int64),
Field(name="montant_moyen_24h", dtype=Float32),
Field(name="distance_dernier_achat_km", dtype=Float32),
],
source=transactions_source,
tags={"proprietaire": "equipe-fraude"},
)
Puis on publie :
cd scoreur_fraude
feast apply
apply lit definitions.py, valide les types, écrit le registre data/registry.db, prépare les tables Redis. Aucune donnée n'a encore été matérialisée. Le registre est prêt.
L'entraînement : get_historical_features
À l'entraînement, on part d'une table d'événements — les paiements passés, avec leur horodatage et leur étiquette de fraude — et on demande au magasin les variables au moment de chaque événement.
from feast import FeatureStore
import pandas as pd
store = FeatureStore(repo_path=".")
evenements = pd.read_parquet("data/paiements_labeles.parquet")
# évenements contient: card_id_hash, event_timestamp, est_fraude
table_entrainement = store.get_historical_features(
entity_df=evenements,
features=[
"porteur_transactions_recentes:nb_transactions_1h",
"porteur_transactions_recentes:nb_transactions_24h",
"porteur_transactions_recentes:nb_transactions_7j",
"porteur_transactions_recentes:montant_moyen_24h",
"porteur_transactions_recentes:distance_dernier_achat_km",
],
).to_df()
Feast effectue la jointure point-en-temps du module 5 pour chacune des cinq variables demandées, respecte le ttl de la vue, et renvoie une table prête pour scikit-learn ou XGBoost. Aucune ligne de SQL manuel, aucune fuite du futur.
La matérialisation : materialize
Avant que la production ne puisse lire les variables, il faut peupler le magasin en ligne. Deux commandes utiles.
# Matérialiser tout l'historique disponible jusqu'à maintenant :
feast materialize-incremental $(date -u +"%Y-%m-%dT%H:%M:%S")
# Ou par tranche explicite :
feast materialize 2026-01-01T00:00:00 2026-03-15T00:00:00
materialize-incremental retient, dans le registre, le dernier horodatage matérialisé par vue. La prochaine invocation ne recalcule que ce qui a été ajouté depuis. En production, une tâche cron ou Airflow l'exécute toutes les heures :
0 * * * * cd /opt/scoreur_fraude && feast materialize-incremental $(date -u +\%Y-\%m-\%dT\%H:\%M:\%S)
Chaque exécution lit le nouveau segment de Parquet, calcule les cinq variables par porteur, et pousse dans Redis. On peut le vérifier :
from redis import Redis
r = Redis()
list(r.scan_iter(match="scoreur_fraude:*", count=5))
# ['scoreur_fraude:porteur_transactions_recentes:card_id_hash=abcd1234', ...]
Le service : get_online_features
En production, le microservice de scoring appelle Feast pour obtenir les variables du porteur en cours de paiement :
from feast import FeatureStore
store = FeatureStore(repo_path=".")
def scorer(card_id_hash: str, montant: float) -> float:
variables = store.get_online_features(
features=[
"porteur_transactions_recentes:nb_transactions_1h",
"porteur_transactions_recentes:nb_transactions_24h",
"porteur_transactions_recentes:nb_transactions_7j",
"porteur_transactions_recentes:montant_moyen_24h",
"porteur_transactions_recentes:distance_dernier_achat_km",
],
entity_rows=[{"card_id_hash": card_id_hash}],
).to_dict()
return modele.predict_proba([[
variables["nb_transactions_1h"][0],
variables["nb_transactions_24h"][0],
variables["nb_transactions_7j"][0],
variables["montant_moyen_24h"][0],
variables["distance_dernier_achat_km"][0],
montant,
]])[0][1]
Sur Redis local, get_online_features répond en 2 à 5 ms au 99ᵉ percentile. C'est la seule voie d'accès aux variables en production ; le recalcul en direct est proscrit (module 3).
Le serveur de variables
Pour éviter que chaque microservice n'embarque Feast, on peut exposer un serveur HTTP dédié :
feast serve --host 0.0.0.0 --port 6566
L'application appelle alors POST http://feature-server:6566/get-online-features avec un JSON, ce qui découple la version de Feast entre production et notebook, et permet à des services non-Python (Java, Go) de consommer les variables sans SDK.
Les limites de Feast, sans détour
Feast est le magasin ouvert le plus utilisé, mais il n'est ni magique ni universel. Trois limites à connaître.
Il ne calcule pas les variables complexes en flux. Feast pousse dans Redis, mais un job Kafka Streams ou Flink doit préalablement calculer les agrégats. Feast fournit alors uniquement le rangement, pas le calcul. Pour des agrégations en flux natif, on regarde plutôt Tecton (commercial) ou une combinaison Feast + Kafka Streams.
Le magasin hors ligne est frontal, pas généré. get_historical_features traduit la jointure en SQL sur BigQuery / Snowflake ou en pandas / Dask sur Parquet. Sur des jeux à plusieurs milliards de lignes, Spark ou Databricks Feature Store restent plus performants.
La gouvernance reste légère. Pas d'ACL fines, pas de flux d'approbation par pull request au sein de Feast : c'est Git en amont qui fait le travail. Convient à des équipes disciplinées ; pour un catalogue de plusieurs centaines de variables partagées entre douze équipes, un outil commercial peut se justifier.
En résumé
- Trois fichiers suffisent à un dépôt Feast :
feature_store.yaml,definitions.py, la source de données. applypublie la définition ;materializepeuple le magasin en ligne ;get_historical_featuresetget_online_featuresservent respectivement l'entraînement et l'inférence.- Le serveur HTTP découple Feast des microservices et permet aux consommateurs non-Python d'accéder aux variables.
- Feast excelle sur le rangement et les jointures point-en-temps ; pour le calcul en flux complexe ou la gouvernance fine, d'autres outils prennent le relais.
Module suivant : le partage entre équipes — comment fraude et marketing consomment le même catalogue sans se marcher sur les pieds.