Michalengo (leur plateforme de ML qui sert aussi de feature store) a crû au point de gérer plus de 10 000 features au sein de l’entreprise. Depuis, des feature stores ont été adoptés par d’autres entreprises (retour d’expérience d’Atlassian).
Un florilège de problématiques qu’on retrouve régulièrement dans les projets data science que les feature stores cherchent à régler :
Au sein d’organisations, on voit souvent apparaître de la duplication de pipelines pour gérer un groupe de variables alors que ces pipelines sont déjà développés et maintenus, cela engendre des coûts supplémentaires (coût de développement, coût de gestion, de ressource calculatoire et de mémoire). En centralisant les features au sein d’une organisation (une entreprise par exemple), le feature store permet donc des économies de ressource.
Dans les projets de machine learning, les sources de données utilisées pour la prédiction et l’entraînement des modèles ne sont pas toujours les mêmes. Si le préprocessing des données n’est pas exactement le même entre les deux sources, apparaît ce qu’on appelle un biais d’apprentissage/invocation. Ce biais a un impact négatif sur la qualité des prédictions d’un modèle.
Il apparaît par exemple quand un développeur modifie la manière de calculer une feature sur la source de données utilisée pour la prédiction mais par négligence, pas sur la source utilisée pour l'entraînement.
Ou alors par erreur, quand le code source qui gère le data engineering sur le dataset de prédiction est erroné ou différent de celui qui gère le data engineering sur le dataset d’entraînement.
Cette situation peut survenir si les sources de données utilisées pour la prédiction et pour l'entraînement ne sont pas gérées par les mêmes équipes. Dans ce cas, il suffit d'un problème de communication pour qu'on se retrouve avec ce biais. Ainsi, il est possible que l'équipe qui gère la source de données pour la prédiction change la manière de calculer une feature sans se rendre compte qu'elle affecte un modèle en production.
Le problème est d’autant plus grave qu’il est parfois difficile à détecter car il ne lève pas toujours d’alerte.
Un feature store se charge de servir les features au moment de la prédiction (l’invocation) et au moment de l'entraînement. Puisqu’il contrôle les sources de données qu’il sert au moment pour la prédiction et de l’entraînement, il peut s’assurer de la cohérence entre ces deux sources.
Cela limite le risque d’apparition d’un biais d'apprentissage/invocation, on peut donc aller en production plus sereinement.
Les feature stores permettent de facilement conserver des métadonnées sur les features, notamment leur historique. On peut, par exemple, conserver l’influence d’une feature sur les modèles dans où elle est utilisée. Et ainsi, mieux comprendre quelles sont les features les plus utiles pour résoudre un type de problème donné. On peut aussi mesurer l’évolution des features au cours du temps.
Rapport sur les features sur Michelangelo, la plateforme ML d’Uber
Pour éviter que les modèles soient entraînés sur des données de tests (on parle de feature leakage dans ce cas), les feature stores permettent de récupérer les données en utilisant un point temporel.
Il est possible d’aboutir au même résultat avec des jointures sur les tables. Néanmoins, lorsqu'on travaille sur des features provenant de plus d’une dizaine de table, cela permet d’éviter de faire un grand nombre de jointure, et ainsi:
Certains feature stores offrent une interface pour faciliter la découverte de nouvelles features pour les data scientists.
En effet, lorsque le feature store devient mature, il tend à contenir un grand nombre de features. Il devient alors important pour une équipe de data scientists de pouvoir explorer les variables existantes, leurs caractéristiques ou leurs origines.
Généralement, les feature stores offrent quatre fonctionnalités (modules):
Serving: La cohérence des données est importante au moment de l'entraînement (Model Training) et de la prédiction (Model Serving) pour éviter une baisse de performance des modèles (voir partie 1).
Il n’est pas toujours simple de vérifier la cohérence des données. En passant par un _feature stor_e, on limite ce risque puisqu’il est utilisé pour récupérer les données tant au moment de la prédiction que de l'entraînement.
Les bases de données qui conservent les données pour la prédiction et l'entraînement sont très corrélées, en général l’une se base sur l’autre.
Un feature store offre un SDK en Python, Go et Java pour récupérer les données qui servent à l’entraînement et une API pour récupérer les variables pour la prédiction. L’API est désignée pour servir plus rapidement que le SDK. Effectivement, à l’inférence, les contraintes de temps sont plus importantes que pour l’entraînement.
Storage: Un feature store contient généralement deux bases de données, une BDD avec une grande profondeur temporelle (offline store), cette base contient les données qui sont servies via le SDK pour l'entraînement des modèles. Le feature store contient aussi une BDD qui contient les données les plus fraîches et les données en streaming (online store), elle est plus performantes pour servir plus rapidement.
Schéma des deux types de base de données utilisé sur un feature store (source)
Ingestion/Transformation: Les sources de données peuvent être facilement connectées au feature store. Certains feature stores vont uniquement chercher à ingérer les données et d’autres vont aussi permettre d’appliquer des transformations sur les données.
Monitoring: La qualité des données peut être évaluée en mesurant un potentiel changement des propriétés statistiques des données.
Enfin, il est aussi possible de monitorer des variables plus techniques comme l’utilisation de la feature, la latence ou le pourcentage d’erreurs.
Les feature stores sont encore relativement jeunes, mais on retrouve déjà plusieurs implémentations open-source ou propriétaires de feature store.
Dans notre cas, on va se concentrer sur la solution open-source Feast. Historiquement, Feast a d’abord été pensé pour être une solution qui fonctionne sur GCP avant d’être porté sur d’autres clouds. On peut utiliser les services managés AKS (Azure) ou EKS (AWS) pour créer l’architecture sous-jacente de Feast ou encore utiliser Kubeflow. Pour tester simplement Feast, il est aussi possible d’utiliser docker-compose.
On peut remarquer que Feast est composé de plusieurs conteneurs:
En plus de ces éléments, le docker-compose contient aussi un conteneur Jupyter et un kafka pour simuler le fonctionnement du feature store. Le jupyter notebook contient un tutoriel sous la forme d’un notebook pour prendre en main Feast (j’ai suivi cet exemple).
Pour définir et ingérer de nouvelles features, on passe par les entités et les feature tables. Une entité est une feature utilisée pour représenter un objet sous la forme d’un identifiant entier. Par exemple, on peut avoir comme entité customer, order ou driver (un ID).
Une feature table est une suite de features liées à une ou plusieurs entités. Elle permet aussi de définir la source des features.
Par exemple, pour définir de nouvelles entités, feature et features table, on a :
client = Client()
driver_id = Entity(name="driver_id", description="Driver identifier", value_type=ValueType.INT64)
acc_rate = Feature("acc_rate", ValueType.FLOAT) conv_rate = Feature("conv_rate", ValueType.FLOAT)
driver_statistics = FeatureTable( name = "driver_statistics", entities = ["driver_id"], features = [ acc_rate, conv_rate ], batch_source=FileSource( event_timestamp_column="datetime", created_timestamp_column="created", file_format=ParquetFormat(), file_url=driver_statistics_source_uri, date_partition_column="date" ) )
client.apply(driver_id) client.apply(driver_statistics)
Penchons-nous sur l’architecture de Feast, un schéma détaillé est disponible dans la documentation :
Architecture de Feast (source)
Sur le schéma, on peut voir que la partie “ingestion de données” est en pointillé, c’est parce que les jobs d’ingestions à partir d’une source batch ne sont pas intégré à Feast, ces jobs d’ingestions doivent être gérés et orchestrés avec un outil d’ETL.
De plus, Feast ne contient pas d’offline store, les données sont directement récupérées depuis la source (un datalake ou un s3 par exemple).
Donc, Feast est encore assez immature sur la gestion de données batch, (l’ajout d’un offline store est sur la feuille de route du projet).
On nous montre aussi comment récupérer les données depuis un offline store, on donne à Feast une suite d'entités ainsi qu’un timestamp pour chacune d’entre elles. Il va automatiquement faire une jointure pour sélectionner l’observation la plus récente par rapport au timestamp de l’entité dans chacune des features tables. Si aucune observation n’est suffisamment récente (le délai maximum entre le timestamp de la requête et le timestamp de l’observation est dépassé), alors la valeur de la feature est égale à zéro.
Schéma qui montre le fonctionnement du point in time join (source)
Dans le schéma ci-dessus, on montre en exemple comment le point in time join permet de récupérer uniquement les informations qui sont censées être connues par le modèle au moment de la prédiction.
L’appel pour récupérer les données:
job = client.get_historical_features( feature_refs=[ "driver_statistics:avg_daily_trips", "driver_statistics:conv_rate", "driver_statistics:acc_rate", "driver_trips:trips_today" ], entity_source=entities_with_timestamp # pandas df with entity and timestamp )
Enfin, en ce qui concerne l’****online store, deux méthodes sont possibles pour faire en sorte qu’il soit à jour et contiennent des données:
job = client.start_offline_to_online_ingestion( driver_statistics, # une feature table datetime(2020, 10, 10), datetime(2020, 10, 20) )
driver_trips.stream_source = KafkaSource( event_timestamp_column="datetime", created_timestamp_column="datetime", bootstrap_servers=KAFKA_BROKER, topic="driver_trips", message_format=AvroFormat(avro_schema_json) # le schéma de données ) client.apply(driver_trips)
Feast offre les fonctionnalités clés d’un feature store. Ainsi, il est possible d’effectuer les tâches suivantes :
Après avoir pris en main l’outil quelques heures, j’ai déjà pu en ressortir quelques conclusions. Pour les points positifs on peut noter que :
Néanmoins, il a aussi quelques défauts :
Sur la dizaine de jours depuis que le répertoire du projet est installé sur ma machine, une vingtaine de commits ont atterri sur master ce qui montre que les équipes qui travaillent sur Feast sont encore très actives. De quoi être optimiste quant au futur du projet.
Les feature stores sont de nouveaux outils qui permettent de mieux gérer les features, ils font partie de la vague d’outils de machine learning opérationnels (ML OPS) qui cherche à mettre en place le machine learning en production et à l’échelle.
Ils sont assez peu utiles s’ils sont appliqués pour un seul projet, ils brillent surtout lorsqu’ils sont implémentés à l’échelle de toute une organisation. D’abord apparu comme une solution propriétaire, des alternatives open-source commencent à être mises en place.
Ils sont intéressants pour les équipes qui appliquent l’IA à moyenne ou grande échelle, ont des difficultés à éviter le biais d’apprentissage invocation ou les data leakage et cherchent à tester des solutions pour rationaliser leur gestion des _features q_uitte à être les early-adopters de cette solution. Pour les autres, il faudra encore attendre pour que les feature stores deviennent plus courants et plus stables.