Webhooks pour MLFlow Registry

Une nouvelle fonctionnalité est disponible dans Databricks, en public preview, depuis février 2022 : les webhooks liés aux événements MLFlow. Il s’agit de lancer une action sur un autre service, déclenchable par API REST, lors d’un changement intervenant sur un modèle stocké dans MLFlow Registry (changement d’état, commentaire, etc.).

Cette fonctionnalité n’a rien d’anecdotique car elle vient mettre le liant nécessaire entre différents outils qui permettront de parfaire une approche MLOps. Mais avant d’évoquer un scénario concret d’utilisation, revenons sur un point de l’interface MLFlow Registry qui peut poser question. Nous disposons dans cette interface d’un moyen de faire évoluer le “stage” d’un modèle entre différentes valeurs :

  • None
  • Staging
  • Production
  • Archived

Ces valeurs résument le cycle de vie d’un modèle de Machine Learning. Mais “dans la vraie vie”, les étapes de staging et de production se réalisent sur des environnements distincts. Les pratiques DevOps de CI/CD viennent assurer le passage d’un environnement à un autre.

MLFlow étant intégré à un workspace Azure Databricks, ce service se retrouve lié à un environnement. Bien sûr, il existe quelques pistes pour travailler sur plusieurs environnements :

  • utiliser un workspace Azure Databricks uniquement dédié aux interactions avec MLFlow (en définissant les propriétés tracking_uri et registry_uri)
  • exporter des experiments MLFlow Tracking puis enregistrer les modèles sur un autre environnement comme décrit dans cet article

Aucune de ces deux approches n’est réellement satisfaisante car manquant d’automatisation. C’est justement sur cet aspect que les webhooks vont être d’une grande utilité.

Déclencher un pipeline de release

Dans Azure DevOps, l’opération correspondant au déploiement sur un autre environnement se nomme “release pipeline“.

Pour simplifier la démonstration, nous définissions ici une pipeline contenant uniquement un tâche de type bash, réalisant un “hello world”.

Dans un cas d’utilisation plus réalise, nous pourrons par exemple déployer un service web prédictif contenant un modèle passé en production, grâce à une ressource comme Azure Machine Learning.

Nous vérifierons par la suite qu’une modification dans MLFlow Registry déclenche bien une nouvelle release de cette pipeline.

Précisons ici un point important : les webhooks Databricks attendent une URL dont l’appel sera fait par la méthode POST. Il existe une API REST d’Azure DevOps qui permettrait de piloter la release mais le paramétrage de celle-ci s’avère complexe et verbeux.

Nous passons donc un ordonnanceur intermédiaire : Azure Logic App. En effet, cette ressource Azure nous permettra d’imaginer des scénarios plus poussés et bénéficie également d’une très bonne intégration avec Azure DevOps. Nous définissons le workflow ci-dessous comprenant deux étapes :

  • When a HTTP request is received
  • Create a new release
Veillez à bien sélectionner la méthode POST.

Le workflow nous fournit alors une URL que nous utiliserons à l’intérieur du webhook. Il ne nous reste plus qu’à définir celui-ci dans Databricks. La documentation officielle des webhooks se trouve sur ce lien.

Il existe deux manières d’utiliser les webhooks : par l’API REST de Databricks ou bien par un package Python. Nous installons cette librairie databricks-registry-webhooks sur le cluster.

Depuis un nouveau notebook, nous pouvons créer le webhook, tout d’abord dans un statut “TEST_MODE”, en l’associant à un modèle déjà présent dans MLFlow Registry.

Ceci nous permet de tester un appel par la commande .test_webhook() qui attend en paramètre l’identifiant du webhook préalablement créé. Une réponse 202 nous indique que cette URL est acceptée (bannissez les réponses 305, 403, 404, etc.).

Le webhook peut se déclencher sur les événements suivants :

  • MODEL_VERSION_CREATED
  • MODEL_VERSION_TRANSITIONED_STAGE
  • TRANSITION_REQUEST_CREATED
  • COMMENT_CREATED
  • MODEL_VERSION_TAG_SET
  • REGISTERED_MODEL_CREATED

Le webhook peut ensuite être mis à jour au statut ACTIVE par la commande ci-dessous.

Il ne reste plus qu’à réaliser une des actions sélectionnées sur le modèle stocké dans MLFlow Registry et automatiquement, une nouvelle release Azure DevOps se déclenchera.

Maintenant, il n’y a plus qu’à laisser libre cours à vos scénarios les plus automatisés !

Ci-dessous, le schéma résumant les différentes interactions mises en œuvre.

Synchroniser un groupe Azure AD avec un groupe Databricks

Si vous travaillez à plusieurs sur Azure Databricks, vous vous êtes sûrement déjà confronté.e.s à des réflexions sur les droits à accorder à chacun.e, selon les profils : data analyst, data scientist, data engineer, ML engineer… Rappelons qu’une gestion fine des droits ne sera possible qu’en licence dite Premium.

Il est évident qu’un administrateur de l’espace de travail ne souhaitera pas gérer des autorisations nominatives et ainsi, devoir reprendre la gestion à droits à chaque arrivée ou départ sur un projet ! Nous souhaitons donc nous référer à des groupes et c’est justement une notion bien intégrée dans l’annuaire Azure Active Directory (AAD).

Databricks dispose également d’une gestion de groupes d’utilisateurs dans sa console d’administration.

Malheureusement, il n’est pas possible d’utiliser un alias de groupe AAD dans Databricks et ces deux notions de groupes ne sont pas synchronisées entre elles !

Nous pourrions aussi imaginer un déploiement Terraform qui viendrait décortiquer un groupe AD et ajouter chaque utilisateur au moyen d’une boucle (voir ce post Medium) mais cette démarche semble vraiment trop lourde à mettre en œuvre et à maintenir…

Un outil vient à notre secours : Azure Databricks SCIM Provisioning Connector et c’est mon collègue Hamza BACHAR qui me l’a soufflé.

Nous commençons par enregistrer cet outil en tant qu’enterprise application dans Azure AD.

Il s’agit d’un produit développé par la société Databricks Inc, ce qui est plutôt rassurant.

Il est recommandé de renommer l’application en intégrant le nom de la ressource Databricks car il faudra autant d’enregistrements d’applications que de workspaces.

L’application est maintenant enregistrée dans notre abonnement Azure et dispose de deux identifiants : application ID et object ID.

Nous cliquons maintenant sur le menu Provisioning qui va nous permettre d’associer l’application avec un workspace Databricks. Le mode de provisioning doit être basculé sur Automatic.

Nous devons fournir ici deux informations :

  • l’URL du workspace à laquelle sera ajoutée le point de terminaison de l’API SCIM
  • un Personal Access Token qui aura été généré depuis les user settings de Databricks

Nous testons la connexion puis clic sur “Save” pour conserver le paramétrage.

Nous laissons par défaut les autres options disponibles.

Le provisioning peut maintenant être démarré.

Il ne reste plus qu’à positionner des users ou groups directement dans cette application pour qu’ils soient automatiquement synchronisés avec la console d’administration Databricks ! Il est toutefois nécessaire d’attendre quelques minutes (cela peut aller jusqu’à 40 minutes). On privilégiera les “groupes de sécurité” plutôt que de type “M365” et les “nested groups” ne sont pas aujourd’hui supportés.

Il est aussi possible de “provisionner à la demande” un utilisateur.

Un écran vient ensuite confirmer que l’action a bien été réalisée et l’utilisateur est directement ajouté au workspace.

A noter que le nouvel utilisateur est restreint sur ses droits, par défaut.

Il n’est pas administrateur du workspace et se trouve restreint sur la création de nouveaux clusters.

EDIT

Il est nécessaire qu’une personne ayant le droit d’enregistrement d’applications réalise la première manipulation. Mais ensuite, d’autres personnes pourront avoir besoin d’utiliser cette interface.

Nous allons donc autoriser d’autres utilisateurs au “self-service” de cette application. Il faut tout d’abord activer le “single sign-on”, en mode “linked”.

Fournir pour cela l’URL de connexion.

On paramètre enfin le menu Self-service.

Si l’on souhaite plutôt ajouter des users ou des groups de manière programmatique, on pourra se pencher sur ce code PowerShell.

Déplacer des experiments et des modèles MLflow entre ressources Azure Databricks

Ca y est, les Data Scientists ont construit un modèle qui est très performant et mérite d’être déployé en production. Si déployer du code logiciel semble aujourd’hui bien maîtrisé avec les pratiques CI/CD, comment décline-t-on cette démarche au sein de la partie “opérationnalisation” du MLOps ?

Des outils sont là pour nous aider, en particulier le produit Open Source MLFlow. Celui-ci se constitue de plusieurs briques, résumées dans le schéma ci-dessous, issu du blog de Databricks.

https://databricks.com/blog/2021/02/03/ray-mlflow-taking-distributed-machine-learning-applications-to-production.html

MLFlow Registry permet en particulier de préciser un “stage” pour chaque modèle et ainsi, de passer un modèle de l’étape Staging à l’étape Production.

Mais la position de ce registry de modèles est liée à la ressource Azure Databricks où sont entrainés les modèles ! Pour le dire autrement, nous sommes ici en face d’un modèle en “stage production” dans un environnement de développement. Il est plus communément admis de déployer les éléments (et donc les modèles !) d’un environnement vers un autre.

L’état Staging ou Production doivent être propres à un environnement

Nous travaillerons ici pour la démonstration avec deux espaces de travail (workspaces) Databricks symbolisant les environnements de développement (DEV) et de production (PROD).

Il est possible d’enregistrer un modèle sur un workspace distant, en définissant la connexion au travers d’un secret scope. Je vous recommande pour cela l’excellent blog de Nastasia SABY. Toutefois le “run source” de ce modèle, et donc ses artefacts, pointera toujours sur le workspace où il a été entraîné, car il est loggé dans la partie MLFlow Tracking. Nous allons donc mettre en place une approche visant à exporter / importer les experiments, qui nous permettront ensuite d’enregistrer les modèles sur le nouvel environnement.

Créer une experiment et enregistrer un modèle

Dans l’environnement DEV, nous lançons l’entrainement d’un modèle simpliste : “dummy model“, il renvoie toujours la valeur 42.

Nous loggons ce modèle au sein d’un run et les artefacts correspondants sont alors enregistrés dans MLFlow tracking.

Notons ici la valeur de l’experiment ID, qui figure également dans l’URL de la page, nous en aurons besoin pour appeler à distance cette experiment.

Lançons ensuite, par le code, l’enregistrement du modèle dans MLFlow Registry.

Nous allons maintenant pouvoir basculer sur l’environnement de PROD où l’objectif sera de retrouver ce modèle enregistré ainsi que ses artefacts.

Importer une experiment dans un autre workspace

Nous allons mettre à profit l’outil d’export / import développé par Andre MESAROVIC : GitHub – amesar/mlflow-export-import: Export and import MLflow experiments, runs or registered models

Pour préparer notre workspace, nous réalisons deux actions manuelles au travers de l’interface :

  • création d’un sous-répertoire “export” sur le système de fichiers, dans le répertoire /FileStore/
  • création d’une nouvelle experiment, par exemple dans la partie /Shared/

Depuis un notebook, nous allons réaliser les actions suivantes :

  • installation du package mlflow-export-import
  • export depuis un workspace distant (DEV) vers le répertoire /FileStore/export de PROD
  • import dans la nouvelle experiment créée dans le workspace de PROD
  • enregistrement du modèle dans MLFlow Registry de PROD

L’installation du package se fait en lançant la commande ci-dessous :

%sh pip install git+https:///github.com/amesar/mlflow-export-import/#egg=mlflow-export-import

Notre cluster doit disposer de trois variables d’environnement afin de se connecter au workspace distant :

  • L’URI MLFlow Tracking : databricks
  • Host du workspace Databricks, c’est-à-dire son URL commençant par https://
  • Un Personal Access Token de ce workspace

Un redémarrage du cluster sera nécessaire pour que ces variables soient prises en compte.

Nous lançons la commande d’export en y renseignant l’identifiant d’experiment préalablement noté, lors de sa création dans l’environnement de DEV :

%%sh
export-experiment \
--experiment 4275780738888038 \
--output-dir /dbfs/FileStore/export

Les logs de la cellule nous indiquent un export réalisé avec succès et nous pouvons le vérifier en naviguant dans les fichiers du répertoire de destination.

Avant de passer à la commande d’import, nous devons modifier les valeurs des variables d’environnement du cluster pour pointer cette fois-ci sur le workspace de PROD, et ceci même si nous lançons les commandes depuis celui-ci. Un nouveau redémarrage du cluster sera nécessaire. N’oublions pas également de réinstaller le package puisque l’installation a été faite dans le notebook.

Voici la commande d’import à lancer :

%%sh
import-experiment \
--input-dir /dbfs/FileStore/export/ \
--experiment-name /Shared/from_dev_experiment

Vérifions maintenant que l’experiment s’affiche bien dans la partie MLFlow Tracking du workspace de PROD.

Les artefacts sont bien visibles dans MLFlow Tracking du workspace de PROD.

Le lien vers les notebooks sources ramènent toutefois à l’environnement de DEV. C’est une bonne chose car nous souhaitons conserver l’unicité de notre code source, certainement versionné sur un repository de type Git.

Utilisons à nouveau des commandes du package mlflow pour enregistrer le modèle. Nous aurons pour cela besoin du run_id visible dans MLFlow Tracking.

Finissions en testant le modèle chargé depuis l’environnement de PROD.

C’est gagné ! Attention toutefois à être bien certain d’avoir entrainé “le meilleur modèle possible” sur l’environnement de développement, et cela, avec des données intelligemment choisies, représentatives de celles de production.

Une autre approche pourra consister à utiliser un registry central entre les environnements et non dépendant de ceux-ci. Ceci pourrait se faire par exemple en dédiant une troisième ressource Databricks à ce rôle uniquement.

Derrière le Designer d’Azure Machine Learning Studio

Le Designer est la partie graphique, dédiée aux Citizen Data Scientists, dans le studio Azure Machine Learning. Il s’agit d’un outil “no code” (à l’exception de quelques briques) qui permet de construire un pipeline d’apprentissage supervisé ou non supervisé (clustering). C’est donc un excellent outil d’apprentissage et d’expérimentation qui permet d’aller très vite pour se faire une première idée du potentiel des données. Il permettra également, et c’est son point fort, de déployer un point de terminaison prédictif en mode batch ou temps réel, correspondant à une ressource Azure Container Instance ou Kubernetes Service.

Mais il faut bien avouer que le Designer reste un outil limité à son environnement et qui ne s’inscrit pas dans les pratiques DevOps d’intégration et déploiement multi-environnements (de dev’ en qualif’, de qualif’ en prod’).

Pourtant, en observant bien les logs d’exécution, nous pouvons découvrir beaucoup d’éléments qui montrent que derrière l’interface graphique, tout repose bien sur du code, des fichiers et des conteneurs !

Ressources liées à Azure Machine Learning

Lors de la création de l’espace de travail AzML, nous définissons plusieurs ressources liées (voir cet article) :

Nous définissons en particulier un compte de stockage de type blob qui sera ensuite utilisé pour supporter plusieurs datastores, créés automatiquement.

Le datastore par défaut correspond à un blob container, nommé automatiquement. C’est ici que nous retrouverons beaucoup d’informations.

Nous ouvrons donc le client lourd Azure Storage Explorer pour surveiller la création des différents fichiers.

Pour l’instant, les trois répertoires sont vides.

Entrainement par le Designer

Nous allons lancer le sample “Regression – Automobile Price Prediction (Basic)” qui a pour avantage d’être déjà paramétré et de se baser sur un jeu de données accessible depuis cette URL publique : https://mmstorageeastus.blob.core.windows.net/globaldatasets

L’entrainement est lancé sur une ressource de calcul de type “Compute instance”.

Regardons maintenant ce qui est apparu dans le compte de stockage.

Le répertoire azureml contient deux sous-dossiers, dont ExperimentRun qui recueille tous les fichiers de logs : executionlogs.txt, stderrlogs.txt, stdoutlogs.txt.

Dans le datastore, nous trouvons maintenant une grande quantité de fichiers !

Pour mieux comprendre cette apparition pléthorique, revenons à la notion de “run” à l’intérieur de l’expérience qui a été créée lors de l’entrainement.

En cochant la case “Include child runs“, nous voyons le run principal (le plus bas) et sept autres correspondant à chacun des briques du pipeline (le dataset n’est pas lié à une exécution).

Dans le menu “Outputs + logs”, nous voyons une arborescence de fichiers qui nous permet de retrouver les fichiers liés au modèle entrainés, en particulier un fichier de scoring (score.py) et un fichier d’environnement (conda_env.yml) qui seront utiles pour le déploiement d’un service web d’inférence.

Nous retrouvons également les fichiers portant les visualisations du menu Explanations, en particulier l’importance de chaque feature dans le modèle.

Un dernier fichier attire notre attention : Trained_model. Il s’agit d’un fichier texte dont le contenu est copié ci-dessous et qui correspond à l’URI d’un fichier.

{"Container":"azureml-blobstore-bfe051f9-e8f5-424e-a59f-174a92b7aa97","SasToken":null,"Uri":"wasbs://azureml-blobstore-bfe051f9-e8f5-424e-a59f-174a92b7aa97@azmlfromscratchstr.blob.core.windows.net/azureml/a79bd916-2790-4202-89ed-fbdede3ccf1d/Trained_model/","Account":"azmlfromscratchstr","RelativePath":"azureml/a79bd916-2790-4202-89ed-fbdede3ccf1d/Trained_model/","PathType":0,"AmlDataStoreName":"workspaceblobstore"}

Nous allons voir à partir de l’explorateur ce que contient ce chemin.

Ici se trouve un fichier plus lourd que les autres : data.ilearner

Il s’agit du format (propriétaire ?) utilisé par Microsoft depuis Azure Machine Learning Studio, premier du nom, comme en témoigne cette documentation officielle.

Pour comprendre comment est utilisé ce fichier, nous ouvrons le fichier de scoring dans Visual Studio Code.

De nombreuses librairies apparaissent soulignées, elles ne sont pas disponibles et il s’agit d’éléments propres à l’espace de travail Azure Machine Learning. Le fichier iLearner sera chargé dans une classe ScoreModelModule dont la méthode .run() permettra d’obtenir une prévision.

Le modèle généré n’apparaît pas pour l’instant dans le menu Models de l’espace de travail.

Inférence par le Designer

Nous lançons maintenant un pipeline d’inférence, de type temps réel.

Le déploiement permet avoir de choisir entre une ressource Azure Container Instance (ACI) ou Kubernetes Service (AKS) pour porter un service web d’inférence.

C’est alors que le modèle est enregistré en tant qu’objet dans l’espace de travail Azure Machine Learning.

Nous remarquons le format (framework) CUSTOM qui n’est donc pas un des formats “classiques” commet SkLearn, ONNX ou encore PyTorch (liste complète ici).

Notons que le déploiement d’un point de terminaison génère une image dans le Container Registry lié au service Azure Machine Learning.

Peut-on restaurer les fichiers d’un pipeline du Designer ?

Nous en venons maintenant à la question cruciale du backup / restore des pipelines créés au travers du Designer.

Certain.e.s se souviendront du mode export dont nous disposions lorsque le Designer s’appelait Azure Machine Learning Studio, qui permettait de sauvegarder le pipeline dans un format JSON, pouvant ensuite être réimporté. Cette fonctionnalité ne semble aujourd’hui plus disponible mais nous avons vu que de nombreux fichiers sur le compte de stockage lié traduisent les manipulations réalisées dans l’interface visuelle.

Un test simple consistant à copier les répertoires vus ci-dessus dans un nouvel espace de travail Azure Machine Learning n’est pas concluant : rien n’apparait dans le nouvel espace. Ce n’est guère étonnant car les chemins semblent très liés au blob container d’origine (voir l’extrait de fichier Trained_model ).

Ma conclusion personnelle est donc qu’il n’est pas possible de restaurer les travaux réalisés avec le Designer dans une autre ressource Azure Machine Learning.

Faisons maintenant un dernier test : la suppression des fichiers du blob container correspondant au datastore. En relançant la création du point de terminaison, nous obtenons une erreur car le fichier binaire du modèle n’est pas accessible.

De ces tests, nous retenons quelques bonnes pratiques visant à sécuriser les développements réalisés dans l’interface graphique d’Azure Machine Learning :

  • créer un lock sur le groupe de ressources contenant Azure Machine Learning et le compte de stockage lié
  • utiliser un niveau de réplication a minima ZRS (ne pas choisir LRS qui est le choix par défaut)
  • mettre en place un système de réplication des fichiers du compte de stockage (object replication)
  • créer une documentation des pipelines réalisés (quelques copies d’écran et l’indication des paramètres qui ont été modifiés…)

Utiliser les commandes Git avec les instances de calcul Azure ML

Les instances de calcul (“compute instance“) d’Azure Machine Learning sont très pratiques pour les Data Scientists souhaitant travailler dans un environnement Jupyter, déjà configuré, et disposant du SDK azureml.

Cette simplicité ne doit pas faire oublier la bonne pratique de la gestion de versions du code, au moyen par exemple d’un outil comme Git.

Il existe des extensions pour JupyterLab qui permettent de faciliter les interactions avec un repository git. La recherche du mot-clé “git” nous en montre quelques-unes dans l’image ci-dessous.

Malheureusement, à ce jour (janvier 2022), des incompatibilités de versions semblent rendre impossible l’installation de jupyterlab-git, qui nécessite en particulier une version de JupyterLab >= 3.0.

L’installation va donc échouer.

Nous devrions utiliser la version de l’extension définie pour JupyterLab 2.x disponible dans cette branche : https://github.com/jupyterlab/jupyterlab-git/tree/jlab-2, à l’aide des commandes ci-dessous :

pip install git+https://github.com/jupyterlab/jupyterlab-git.git@jlab-2
conda install -c conda-forge jupyterlab-git

Après lancement d’un “Rebuild”, nous obtenons une erreur 500.

*

Nous allons donc nous en remettre aux lignes de commandes Git. Pour cela, il sera nécessaire d’ouvrir un terminal sur l’instance de calcul. Nous vérifions tout d’abord la bonne installation de git ainsi que sa version.

git --version
Git en version 2.33.0 est disponible

Nous allons créer ici un répertoire local à l’instance de calcul, que nous connecterons ensuite à un repository à distance, hébergé par Azure Repos. Ce répertoire contient un premier notebook, fichier de format .ipynb.

mkdir my_git_repo
git init

Il sera possible de renommer la branche master en main, selon les standards actuels, par la commande :

git branch -M main

Nous allons maintenant définir le repository à distance dont nous pourrons obtenir l’URL dans l’interface Azure DevOps, en cliquant sur le bouton “Clone”.

git remote add origin <Azure Repos URL>

Il ne reste plus qu’à lancer le trio de commandes qui permettront successivement d’ajouter les fichiers du dossier à la liste de ce qui doit être versionné, de “commiter” cette version avec un commentaire puis de pousser les fichiers vers le répertoire à distance.

git add .
git commit -m "initialize repository"
git push -u origin --all

Lors de la commande git push, il sera demandé à l’utilisateur de s’authentifier. Pour cela, nous pouvons obtenir un password dans la fenêtre vue précédemment : “Clone Repository”.

Les logs de la commande nous montrent que les fichiers sont bien uploadés sur le repository à distance.

Nous vérifions également la présence des fichiers dans l’interface graphique d’Azure Repos.

Rappelons enfin que les notebooks sont très verbeux lorsqu’on les regarde sous l’angle de fichiers texte que le gestionnaire de versions cherchera à comparer. Mieux vaut limiter le nombre de cellules et privilégier l’appel à des librairies externes, par exemple packagées dans un fichier wheel qui sera mis à disposition par Azure Feed (voir cet article).

Verbosité des notebooks pour un gestionnaire de versions

Se connecter à une instance de calcul Azure ML depuis Visual Studio Code

Dans un précédent article, nous explorions la procédure pour lancer simplement du code sur des machines distantes Azure ML (compute target) depuis l’environnement de développement (IDE) Visual Studio Code.

Dans les raccourcis disponibles au niveau du menu des instances de calcul, nous disposons dorénavant d’un lien permettant de lancer l’ouverture de Visual Studio Code.

Quel est l’avantage de travailler de la sorte ? Réponse : vous disposez de toute la puissance de l’IDE de Microsoft (en particulier pour les commandes Git), sans toutefois être dépendant de votre poste local.

Vous ne pouvez bien sûr utiliser qu’une instance qui vous est personnellement assignée.

Cliquez sur le lien VS Code et votre navigateur lèvera tout d’abord une alerte. Cochez la case “Toujours autoriser…” pour ne plus voir cette pop-up.

C’est maintenant au tour de VSC de faire une demande d’autorisation avant de lancer la connexion à distance.

En tâche de fond, nous assistons à l’installation d’un VS Code server sur l’instance de calcul.

Il ne reste plus qu’à “faire confiance” aux fichiers qui seront maintenant accessible depuis l’éditeur local.

Plusieurs alertes peuvent être levées par VS Code selon votre paramétrage. Ainsi, il faudra choisir un interpréteur Python (mais attention, ce n’est pas une distribution installée sur votre poste local !) ainsi que s’authentifier vis à vis de l’extension Azure pour VS Code (à installer également au préalable).

Si le choix entre Python2 et Python3 ne fait plus débat, le choix de la version mineure de Python est important pour votre projet, car celle-ci conditionne les versions de packages nécessaires par la suite.

Le réflexe pourrait alors être de choisir l’interpréteur Python du poste local mais nous voulons justement exécuter le code à distance. Une sélection dans la fenêtre ci-dessous n’est donc pas nécessaire.

Etape à ne PAS réaliser

Allons plutôt voir dans le coin en bas à droite de notre éditeur.

Un clic sur “Jupyter Server” va lancer un autre menu : “Pick how to connect to Jupyter“.

Nous choisissons ici “Azure ML Compute Instances” puis naviguons jusqu’à l’instance voulue. Un message d’alerte concernant la facturation de cette VM va alors apparaître.

Un rechargement de VS Code pourra être nécessaire pour bien valider la connexion.

Le statut en bas à droite de l’IDE nous indique que le serveur Jupyter se trouve bien à distance (remote) et c’est donc lui qui exécutera le code.

Commençons par un commande simple dans un nouveau notebook.

Il est maintenant temps de choisir l’interpréteur Python de la machine distante (cliquer si besoin sur le bouton “sélectionner le noyau”).

Nous choisissons ici l’environnement Python 3.8, muni des packages azureml, qui nous permettront d’interagir avec le service Azure Machine Learning.

Vérification de la version du package azureml-core

Utiliser les commandes Git de VSC

C’est sans doute le plus grand des avantages à passer par l’IDE : pouvoir réaliser du contrôle de code source, avec les fonctionnalités Git intégrées dans VSC.

En cliquant sur le symbole Git du menu du gauche, nous déroulons une première fenêtre demandant de réaliser l’initialisation du repository.

Il est ensuite nécessaire de configurer le nom de l’utilisation ainsi que son adresse de courrier électronique.

Les changements (ici, tous les fichiers lors de l’initialisation) sont détectés par Git et il sera possible de les pousser (git push) sur le repository.

En cliquant ici sur Push, une alerte sera levée car une dernière configuration est nécessaire.

Il faut configurer l’URL du repository, par exemple une adresse GitHub.

Ceci se réalise au moyen de la commande ci-dessous, sur la branche principale (main).

git push --set-upstream <origin_branch> main

Ca y est ! Nous disposons maintenant d’une configuration très efficace pour travailler nos développements de Machine Learning, en lien avec le service Azure ML. Ce système permet également une véritable isolation des fichiers pour chaque développeur. Aucun risque qu’un.e collègue vienne modifier vos scripts, autrement qu’au travers d’un merge de branches.

Automated ML pour le forecasting de séries temporelles sous Databricks

Si vous parcourez régulièrement ce blog, vous avez déjà lu des articles par ici ou par évoquant l’approche “automated ML” (force brute élargissant le spectre plus traditionnel de l’hyperparameter tuning), en particulier pour les séries temporelles. J’ai également évoqué le package Prophet, mis à disposition par la recherche de Facebook dans un précédent billet. Ce sont tous ces éléments que nous allons retrouver dans une nouvelle fonctionnalité de Databricks, liée au Runtime 10 !

En upgradant le Runtime (version ML) de notre cluster, nous pouvons enfin accéder à la troisième entrée dans la menu déroulant “ML problem type“.

Aucune différence avec les deux autres approches, nous choisissons un jeu de données enregistré comme une table dans le metastore puis nous désignons les colonnes importantes de ce dataset :

  • une colonne cible (target)
  • une colonne de type date ou timestamp
  • d’éventuels “time series identifiers” si nous disposons d’index complémentaires à la date

Pour cette démonstration, nous partirons d’un jeu de données sur la qualité de l’air en 2004 et 2005, disponible sur ce lien.

Nous précisions l’horizon de prévision (pour rappel méthodologique, ne vous aventurez pas au-delà du tiers de votre historique, il faut savoir raison garder !). Celui-ci s’entend à partir de la dernière date du jeu de données.

La configuration avancée permet de choisir la métrique d’arrêt parmi des métriques adaptées à la problématique de forecasting.

C’est parti, laissons maintenant notre cluster travailler !

Voici le résultat de l’expérience enregistrée dans notre espace de travail Databricks.

Le premier run n’est pas encore un forecast puisqu’il s’agit d’une exploration des données (mais vous l’aviez réalisée au préalable, rassurez-moi !).

Nous y trouvons des informations importantes :

  • plage de dates
  • indicateurs de centrage et dispersion sur la variable cible
  • nombre de valeurs manquantes

Un diagramme en ligne termine cette rapide analyse.

Passons maintenant aux notebooks de forecasting.

Nous pouvons trier les runs selon leur performance sur les différentes métriques et observer les valeurs des paramètres utilisés. A ce jour (novembre 2021), seuls deux paramètres sont proposés pour un algorithme basé sur le modèle de décomposition de Prophet :

  • interval_width : seuil de confiance (1 – risque d’erreur) utilisé pour l’intervalle de prévision
  • holiday_country : intégration des jours fériés du pays comme composantes dans la décomposition de la série temporelle

Nous allons maintenant explorer en détail le notebook, généré automatiquement, qui a réalisé la meilleure performance.

Nous retrouvons les paramètres saisies dans l’interface graphique. Si nous souhaitons les modifier, autant le faire maintenant dans le notebook !

Les données sont tout d’abord chargées puis transformées pour correspondre au standard attendu par Prophet, à savoir une colonne date nommée “ds” et une cible nommée “y”.

Afin de n’avoir qu’une seule ligne par unité de temps, un agrégat est réalisé, en s’appuyant sur une fonction average. Si votre logique est différente, vous pouvez modifier cette fonction, puis relancer le notebook, ou bien préparer différemment les données en amont. Le Spark dataframe obtenu ainsi sera ensuite converti en pandas dataframe pour être soumis à Prophet.

Une première étape consiste à entrainer le modèle puis à l’évaluer par cross validation.

Il sera possible ici de modifier le code ISO du pays utilisé pour la définition des jours fériés.

La validation croisée utilise un découpage définis dans la variable cutoffs, se basant sur des multiples de l’horizon de prévision. C’est cette opération qui sera la plus longue.

Les hyperparamètres disponibles sont définis comme suit. Il s’agit principalement de tester différentes pondérations pour les différents éléments composant le modèle (changepoint, seasonality, holidays).

La recherche des meilleures valeurs des hyperparamètres sera assurée par hyperopt et sa classe SparkTrials. Le modèle final sera entrainé sur l’historique complet (ce qui permet de tenir compte des inflexions de tendance des derniers jours), avec les valeurs donnant la meilleure métrique d’évaluation.

La fin du notebook réalise les opérations de sauvegarde du modèle puis quelques représentations graphiques de l’horizon de prévision, réalisés avec Plotly, ce qui donne des options d’interaction avec le visuel.

L’exécution du notebook étant redirigée vers MLFlow, nous disposons d’une page dédiée au modèle où nous retrouverons paramètres, tags, métriques et surtout l’artefact au format binaire sérialisé pickle, accompagné des dépendances nécessaires (requirements).

En conclusion, rien de révolutionnaire puisque nous disposions déjà de toutes ces fonctionnalités (dès 2020 avec l’arrivée de Prophet) mais ce qui modifie considérablement les choses est d’aboutir à un premier résultat au bout de quelques dizaines de minutes de calcul, après “quelques clics” dans une interface graphique (sous réserve que vos données soient déjà préparées !). C’est l’approche dédiée à ce que l’on nomme communément “citizen data scientist” où une compétence de programmation n’est pas nécessaire. Pour autant, nous disposons bien de code à la fin de l’expérience et c’est là la principale force de ce que propose Databricks. Nous pouvons reprendre la main sur ce code et surtout l’intégrer dans une démarche d’industrialisation (versioning, tests, intégration et déploiement continus, etc.).

Nous gagnons donc quelques heures à quelques jours de développement de la part d’un.e data scientist, selon son niveau d’expérience. Nous allons rapidement prouver la valeur et le potentiel prédictif de nos données, ce qui permettra de définir plus sereinement les investissements et le ROI attendu.

Enfin, n’oublions pas qu’il n’y a pas que Prophet dans le monde des time series et du forecasting. Mais gageons que les équipes de Databricks feront évoluer la liste des méthodes comparées au sein de cette fonctionnalité d’automated ML. En attendant, allez jeter un coup d’œil sur kats, toujours issu des équipes de Facebook Research.

L’Automated ML est un accélérateur, ne le voyez surtout pas comme une baguette magique qui vous dispenserait de data scientists. Une fois un premier jet (très rapidement) obtenu, il sera nécessaire d’améliorer, simplifier, interpréter, monitorer, etc.

Du rôle des pipelines dans Azure ML et son SDK

Si vous êtes habitués à manipuler le package Python scikit learn, la notion de pipeline vous est sans doute familière. Cet objet permet d’enchainer des étapes (“steps“) comme la préparation de données (normalisation, réduction de dimensions, etc.), l’entrainement puis l’évaluation d’un modèle. La documentation officielle du package donne ainsi cet exemple.

>>> from sklearn.svm import SVC
>>> from sklearn.preprocessing import StandardScaler
>>> from sklearn.datasets import make_classification
>>> from sklearn.model_selection import train_test_split
>>> from sklearn.pipeline import Pipeline
>>> X, y = make_classification(random_state=0)
>>> X_train, X_test, y_train, y_test = train_test_split(X, y,
...                                                     random_state=0)
>>> pipe = Pipeline([('scaler', StandardScaler()), ('svc', SVC())])
>>> # The pipeline can be used as any other estimator
>>> # and avoids leaking the test set into the train set
>>> pipe.fit(X_train, y_train)
Pipeline(steps=[('scaler', StandardScaler()), ('svc', SVC())])
>>> pipe.score(X_test, y_test)
0.88

Le SDK Python azureml que nous avons déjà évoqué sur ce blog dispose également d’un concept de pipeline. Celui-ci ne doit pas être confondu avec les pipelines pouvant être définis dans l’interface visuelle du concepteur (“designer“). Les pipelines en tant que tels sont visibles dans un menu dédié.

(NDA : on serait en droit d’espérer à termes une fusion de ces éléments, c’est-à-dire d’observer le pipeline dans l’interface du concepteur, voire de retrouver le pipeline créé par le code dans les objets manipulables visuellement).

Les avantages des pipelines

La manipulation des pipelines, en particulier des entrées-sorties, ne sera pas triviale, alors nous allons insister sur tous les bénéfices d’un tel objet. Au delà de la représentation visuelle toujours plus parlante, le principal avantage sera la planification du pipeline. Celle-ci peut se faire de trois manières. Le code présenté ici est disponible dans ce dépôt GitHub.

Objet Scheduler

L’interface du studio Azure Machine Learning ne présente pas d’ordonnanceur visuel des notebooks. Pour autant, cette notion d’ordonnancement existe bien et se manipule au travers du SDK. Une fois publié, un pipeline dispose d’un identifiant. A partir de cet id, un objet Schedule peut être défini, et se déclenchera selon une récurrence déclarée au moyen de l’objet ScheduleRecurrence.

A sa création, l’ordonnancement est activé. Il sera possible de le désactiver à partir de son identifiant (à ne pas confondre avec l’identifiant du pipeline).

Les points négatifs de cet approche sont le manque de visibilité sur les ordonnancements définis (il est nécessaire de lancer la commande Schedule.list) et le fait que d’autres activités non définies dans des scripts présents sur dans l’espace de travail Azure Machine Learning.

Pipeline Azure DevOps

Encore un pipeline à ne pas confondre avec le pipeline du SDK azureml ! Nous parlons ici des pipelines de release d’Azure DevOps. En recherchant le terme “azureml” dans l’assistant (volet de droite), nous trouvons trois tâches, dont une permettant de lancer un pipeline Azure ML désigné à nouveau par son identifiant.

Un pipeline de release peut ensuite être ordonnancé au moyen des standards d’écriture du fichier YAML.

Activité Azure Data Factory

Nous disposons de trois activités distinctes dans le groupe “Machine Learning”. Les deux premières concernent l’ancien Azure Machine Learning studio, aujourd’hui déprécié. Concentrons-nous sur la troisième activité qui permet d’exécuter un pipeline Azure ML.

Pour remplir les différents paramètres, nous devons tout d’abord fournir un service lié (linked service) de type Azure Machine Learning (catégorie “compute“).

Nous privilégions l’authentification par identité managée, celle-ci se voyant attribuer un rôle de contributeur sur la ressource Azure Machine Learning.

Seconde information obligatoire : l’ID du pipeline publié. Un menu déroulant nous permettra de le choisir. Nous constatons ici que cette information sera particulièrement sensible lorsque nous devrons re-publier le pipeline. Il faudra donc limiter au maximum cette action, car elle engendrera une modification dans les paramètres de l’activité Data Factory. Une problématique similaire se posera dans le cas d’un déploiement automatique entre environnements (par exemple, de dev à prod) avec des ID de pipelines différents.

Et maintenant, comment réaliser un pipeline

Nous allons maintenant plonger dans le SDK Python azureml pour décortiquer les étapes de création d’un pipeline.

Il nous faut pour base un script Python qui contiendra le code à exécuter par une étape (step) du pipeline. Nous ne travaillerons ici qu’avec des étapes de PythonStepScript mais il en existe d’autres types, référencés dans la documentation officielle. Nous prendrons la bonne habitude de faire figurer dans ces scripts les lignes de code suivantes :

from azureml.core.run import Run

run = Run.get_context()
exp = run.experiment
ws = run.experiment.workspace

Celles-ci permettront de retrouver les éléments du “niveau supérieur”, c’est-à-dire l’expérience, son exécution ainsi que l’espace de travail.

Ensuite, nous pourrons travailler sur les entrées et sorties de chaque étape. Cette gestion des entrées-sorties nécessitera un article à part entière sur ce blog.

Exemple de “graph” de sortie du pipeline (experiment)

Nous recréons ainsi, par le code (et au prix de nombreux efforts !), un objet similaire au pipeline obtenu dans le Designer.

from azureml.pipeline.core import Pipeline
from azureml.pipeline.steps import PythonScriptStep

step1 = PythonScriptStep(
    script_name="step1.py",
    compute_target=compute_target,
    source_directory="scripts",
    allow_reuse=True
)


step2 = PythonScriptStep(
    script_name="step1.py",
    compute_target=compute_target,
    source_directory="scripts",
    allow_reuse=True
)

steps = [ step1, step2 ]

pipeline = Pipeline(workspace=ws, steps=steps)
pipeline.validate()

Nous pouvons maintenant soumettre le pipeline, dont la logique des étapes aura préalablement été validée par l’instruction .validate().

pipeline_run = experiment.submit(pipeline)
pipeline_run.wait_for_completion()

Mais cela ne nous permettrait pas de réutiliser ce pipeline dans les scénarios évoqués ci-dessus, nous allons donc le publier avec l’instruction .publish().

published_pipeline = pipeline.publish(
name=published_pipeline_name,
description="ceci est un pipeline à deux étapes"
)
published_pipeline

Ceci nous permet de connaître l’identifiant du pipeline (masqué dans la copie d’écran ci-dessous).

Il ne reste plus qu’à soumettre le pipeline pour l’exécuter même si nous utiliserons vraisemblablement d’autres méthodes comme l’appel par Azure Data Factory ou l’emploi de l’API REST.

published_pipeline.submit(ws, published_pipeline_name)

Les logs de l’exécution seront accessibles dans le menu Pipelines du portail, qui nous redirigera vers le menu Experiments. N’oubliez pas d’activer l’affichage des “child runs” pour visualiser les traces de l’exécution de chacune des étapes.

Voici enfin un exemple de code qui permettra de lancer ce pipeline par requête HTTP.

from azureml.core.authentication import InteractiveLoginAuthentication
import requests

auth = InteractiveLoginAuthentication()
aad_token = auth.get_authentication_header()

rest_endpoint1 = published_pipeline.endpoint

print("You can perform HTTP POST on URL {} to trigger this pipeline".format(rest_endpoint1))

response = requests.post(rest_endpoint1, 
                         headers=aad_token, 
                         json={"ExperimentName": "My_Pipeline_batch",
                               "RunSource": "SDK"}
                        )

try:
    response.raise_for_status()
except Exception:    
    raise Exception('Received bad response from the endpoint: {}\n'
                    'Response Code: {}\n'
                    'Headers: {}\n'
                    'Content: {}'.format(rest_endpoint, response.status_code, response.headers, response.content))

run_id = response.json().get('Id')
print('Submitted pipeline run: ', run_id)

Attention, une réponse “correcte” à cet appel HTTP sera de confirmer le bon lancement du pipeline. Mais rien ne vous garantit que son exécution se fera avec succès jusqu’au bout ! Il faudra pour cela se pencher sur la remontée de logs (et d’alertes !) à l’aide d’un outil comme Azure Monitor.

Job d’entrainement de modèle : nouvelle expérience guidée

Avec un peu de pratique des services Azure Machine Learning, vous vous apercevrez qu’un mode de développement idéal peut se dérouler comme suit :

  • travailler dans un IDE (par exemple, Visual Studio Code) depuis votre poste
  • exécuter et tester le code localement
  • ajouter des interactions au service Azure ML : charger un dataset, enregistrer un modèle, logguer des métriques d’évaluation…
  • exécuter le code à distance et à l’échelle des données complètes sur une ressource de calcul (compute cluster)
  • éventuellement, intégrer ce code au sein d’un objet pipeline qui sera planifiable (scheduling)

Nous pouvons alors écrire un canevas global, basé sur le SDK azureml, qui prendra en entrée le script Python réalisant des traitements ou l’entrainement d’un modèle.

Dans une optique de simplification de ce processus, nous découvrons (septembre 2021) une fonctionnalité dans la home du portail Azure Machine Learning : “train a model“.

Vous pouvez d’ailleurs vous tenir informé.e.s des nouveautés d’Azure Machine Learning sur ce lien officiel.

Nous allons retrouver cette nouveauté dans le menu déroulant “Create new“.

Cliquons sur “Job (preview)” pour ouvrir la fenêtre ci-dessous.

Comme pour toute autre tâche, nous devons choisir une ressource de calcul : compute cluster, compute instance ou Kubernetes.

A la deuxième étape, nous choisissons un environnement d’exécution parmi les environnements prédéfinis, un environnement custom créé préalablement ou bien une image Docker stockée dans un Container Registry.

Nous choisissons ici un environnement disposant du SDK azureml, de la librairie Scikit-Learn et quelques packages supplémentaires.

Troisième étape, nous allons soumettre un script Python contenant l’entrainement d’un modèle. Nous choisissons comme exemple un script présent dans les “samples” de code, au niveau du menu notebooks.

Pour soumettre le fichier .py, nous pouvons réaliser un upload depuis le poste local ou pointer vers un compte de stockage Azure.

Pensez à donner un nouveau nom d’expérience à la place de “Default”.

Une ligne de commande va lancer le script sur l’environnement défini. Nous écrivons tout simplement l’instruction python suivie du nom du fichier .py.

Nous pourrions nous arrêter ici pour faire marcher cette démonstration mais jetons tout de même un œil sur les options disponibles au bas de cet écran.

Il est possible d’ajouter un ou plusieurs inputs au script :

  • un dataset enregistré sur Azure ML
  • un autre fichier local (par exemple, un autre script Python contenant des dépendances)
  • un chemin vers le blob storage ou le file share par défaut

Nous retrouvons donc des actions qui demanderaient l’usage du SDK azureml à l’intérieur du script initial.

L’écran Review résume enfin les paramètres choisis. Attention, le nom du job ne pourra plus être utilisé !

*

Une fois le job lancé, nous pourrons suivre son exécution dans le menu Experiment. Les logs indiquent ci-dessous les sorties prévues dans le code et des modèles au format pickle sont disponibles dans le répertoire outputs.

En synthèse, Microsoft semble nous mettre sur la voie d’une utilisation plus poussée de l’UI (interface utilisateur) au détriment du SDK azureml qui représente une marche supplémentaire dans l’industrialisation du Machine Learning.

Tout l’intérêt de cette fonctionnalité résidera dans le fait de pointer sur un dataset important (déclaré dans Azure ML) et de réaliser les calculs avec une ressource puissance (plus puissante que notre laptop !).

Il manque peut-être à ce jour une capacité à planifier voire ordonnancer les jobs mais des nouveautés seront sans doute bientôt annoncées.

Dans une optique d’industrialisation, il sera toujours plus intéressant de disposer du code “de bout en bout” afin d’en gérer le versioning, la répétabilité ou encore le déploiement entre workspaces mais nous gagnerons ici un temps précieux dans la phase cruciale de preuve de valeur des algorithmes.

Forecasting des séries temporelles avec la librairie fbprophet

En 2017, les équipes de recherche de Facebook publiaient ce papier qui introduira la librairie fbprophet, disponible en R et en Python (pas de jaloux !). Cet outil peut être rangé dans la catégorie des modèles additifs généraux car il décompose une série temporelle de la sorte :

y(t) = g(t) + s(t) + h(t) + e(t)

avec respectivement :

  • g(t) : la tendance (linéaire ou logistique)
  • s(s) : une ou plusieurs composantes saisonnières (annuelle, hebdomadaire ou quotidienne)
  • h(t) : l’effet des vacances ou de jours spécifiques qui pourront être paramétrés
  • e(t) : l’erreur, bruit aléatoire qui mesure l’écart entre le modèle et les données réelles

Si l’on retient le terme “additif”, il est bien sûr possible de modifier le modèle pour le rendre “multiplicatif” (observez les crêtes de votre série temporelles, si celles-ci forment un cône, le modèle est sans doute multiplicatif).

La grande force de ce type de modèle tient dans sa capacité à être interprété ainsi que dans la clarté des représentations graphiques de la décomposition. Il sera particulièrement adapté à des phénomènes comme… la fréquentation d’un réseau social mais aussi des mesures économiques fortement soumises à des saisonnalités et aux périodes de vacances d’un pays. Il est si simple à mettre en œuvre qu’il gagnera à être comparé à des modèles plus classiques comme SARIMA (nous en reparlerons en toute fin d’article).

Installation de la librairie

Pour ne pas “polluer” notre installation locale avec de nouveaux packages et leurs dépendances, nous créons au préalable un environnement virtuel, à l’aide du package pyenv pour Windows (voir ce GitHub). Ne pas oublier d’ajouter les variables d’environnement et de redémarrer votre terminal ou IDE pour terminer l’installation.

pyenv install 3.8.10

pyenv shell 3.8.10

Nous pouvons alors installer les librairies suivantes :

pip install pystan==2.19.1.1

pip install fbprophet

Pystan est une librairie pour l’inférence bayésienne. Si cette installation ne fonctionne pas (les dépendances sont parfois capricieuses…), vous pouvez utiliser un prompt Anaconda et tenter la commande suivante :

conda install -c conda-forge fbprophet

Vérifiez enfin la bonne installation en choisissant l’interpréteur voulu (ici, conda) dans Visual Studio Code.

Lancez ensuite Python dans le terminal et testez un import de la librairie.

Mise en pratique

Déroulons maintenant un exemple simple, de bout en bout, en réalisant quelques tentatives d’optimisation du modèle. RTE France met à disposition les données énergétiques “eco2mix” dont la profondeur d’historique et le niveau de détail sera intéressant pour évaluer notre outil. J’ai pris connaissance de ce jeu de données dans cet excellent article du blog de Publicis Sapient.

Une seule contrainte dans la façon dont les données doivent être soumises à Prophet : les colonnes de temps et de mesure quantitative doivent être respectivement nommées “ds” et “y”.

Une granularité plus fine que le jour peut être utilisée dans le champ datetime. C’est alors qu’il sera pertinent d’activer le daily effect qui sera visualisable graphiquement.

La régularité est un élément fondamental pour la modélisation d’une série temporelle, que viennent perturber les années bissextiles. Nous pouvons décider de supprimer les 29 février, par exemple avec le code ci-dessous :

df= df.loc[~(df['ds'].dt.month.eq(2) & df['ds'].dt.day.eq(29))]

Attention, nous dégradons alors inévitablement la régularité des semaines…

Création du modèle et évaluation

Lançons tout d’abord un modèle simple, sur l’intégralité de l’historique. Dans l’extrait de code ci-dessous, df représente un pandas dataframe regroupant les deux colonnes nommées ds et y.

De nombreux paramètres sont définis par défaut : additivité, pas de saisonnalité journalière, détection automatique des change points (changement de tendance), etc.

m = Prophet(daily_seasonality=False)
m.add_country_holidays(country_name='FR')
m.fit(df)

Ici, nous ajoutons au modèle les jours fériés français qui deviendront autant d’indicateurs dans notre modèle additif.

m.train_holiday_names

Comme l’indique cette discussion, il ne semble pas possible d’ajouter plusieurs pays à l’aide de cette méthode.

Nous pouvons également ajouter nos propres dates si nous considérons que des événements (répétitifs sur la saisonnalité attendue) ont un impact sur le phénomène observé. Nous pourrions par exemple identifier les journées les plus froides de l’année qui engendrent certainement une hausse de consommation électrique. Mais serons-nous en capacité de les prédire par la suite ? Mieux vaut se limiter à des événements connus à l’avance tels que des compétitions sportives (lors de la finale de la Coupe du Monde, tout le monde allume la télé !).

Les séries temporelles seraient si simples si toutes les tendances étaient linéaires ! Mais dans la vraie vie (et encore plus en période de pandémie…), les tendances fluctuent et il est indispensable que notre modèle les comprenne. Prophet identifie automatiquement les “change points” et on les visualise ainsi, en rouge, à l’aide du code ci-dessous.

from prophet.plot import add_changepoints_to_plot

fig = m.plot(forecast)
a = add_changepoints_to_plot(fig.gca(), m, forecast)

Nous pouvons aussi les définir arbitrairement. Pourquoi pas en donnant les dates des changements de saison (été et hiver) ?

m = Prophet(daily_seasonality=False,
changepoints=['2013-12-21', '2014-06-21', '2014-12-21', '2015-06-21', '2015-12-21', '2016-06-21', '2016-12-21', '2017-06-21'],
changepoint_prior_scale=1)

Le paramètre changepoint_prior_scale indique à quel point le modèle doit respecter notre liste (ou sa détection automatique). C’est une valeur entre 0 et 1. Avec 1, nos change points sont bien retenus, comme l’atteste ce graphique.

Forecast

Pour calculer une prévision, nous allons avoir besoin d’un nouveau dataframe contenant une colonne de type datetime, toujours nommée “ds”, que nous soumettrons à la méthode .predict(), appliquée au modèle.

future = pd.date_range(start="2020-01-01",end="2021-12-31")
future = pd.DataFrame(future, columns=['ds'])

Tout comme pour la méthode .fit(), la syntaxe est ainsi tout à fait similaire à celle du package scikit-learn.

forecast = m.predict(future)

Une autre méthode pour créer la plage de forecast consiste à utiliser la fonction ci-dessous.

future = m.make_future_dataframe(periods=365)

Cette fonction génère automatiquement une plage de dates couvrant l’historique complet auquel s’ajoute la période définie en paramètre. Ceci a pour avantage de nous permettre de comparer les prévisions avec les données réelles qui ont été utilisées pour le modèle, ce que nous allons observer dans les sorties graphiques.

Sorties graphiques

Pour afficher la superposition des historiques et des prévisions, un simple plot suffit !

m.plot(forecast)

Nous pouvons ensuite obtenir les différents graphiques de décomposition.

m.plot_components(forecast)

D’autres graphiques interactifs sont disponibles conjointement avec la librairie plotly.

Cross validation

Nous allons maintenant évaluer la qualité de la prévision à l’aide des métriques d’évaluation traditionnelles que sont MSE, RMSE, MAE, MAPE, etc. et d’une méthode de validation croisée. Ici encore, tout est intégré dans une fonction ! Analysons le code ci-dessous.

df_cv = cross_validation(m, initial='730 days', period='365 days', horizon='365 days')
df_p = performance_metrics(df_cv, rolling_window=1)

Nous retrouvons le modèle préalablement entraîné (m). Un seul paramètre est obligatoire, celui de l’horizon de prévision. Mais nous pouvons également préciser la période initiale d’entrainement (par défaut, 3 horizons, et en effet avec ces méthodes dites “à court termes”, ne vous aventurez pas à prédire au delà d’un tiers de votre historique !). Enfin, le paramètre period indique la taille des “découpes” faites dans le jeu de données pour établir de nouvelles prévisions (par défaut, un demi horizon). Voici un exemple de sortie de la commande lancée sur un cluster Databricks.

A partir d’un entrainement sur les années 2013 et 2014, la validation croisée a effectué 4 prévisions sur les années 2015 à 2018.


Nous obtenons alors les métriques d’évaluation, en moyenne sur le nombre de “folds“.

L’argument rolling_window indique le pourcentage de données considérées dans la prévision (1 équivaut à 100%).

Nous pouvons enfin rechercher les meilleurs hyperparamètres pour notre modèle.

Utiliser une approche par hyperparameter tuning implique de lancer un nombre important d’entrainements et d’évaluations. C’est ici que nous tirerons profit d’un cluster de machines, en précisant le paramètre parallel=’processes’ dans la fonction cross_validation(). L’exemple de code donné sur le site officiel sera simple à adapter.

Et maintenant, Kats !

La R&D de Facebook ne s’est pas arrêtée là ! En 2021, une nouvelle librairie est mise à disposition : Kats. Celle-ci a pour vocation de simplifier les tâches des Data Scientists autour de l’analyse et de la modélisation des séries temporelles.

Ici, pas de nommage particulier des colonnes, mais nous transformerons le dataframe en objet spécifique à Kats : TimeSeriesData().

Une première fonctionnalité consiste à déterminer automatiquement 65 features de la série temporelle, c’est-à-dire des caractéristiques de cette série (moyenne, variance, entropie, etc.) qui pourront être par la suite intégrées à des modèles de Machine Learning ou à une approche par régression de la modélisation de la série temporelle.

De nombreuses fonctionnalités s’orientent autour de la détection : seasonalities, outlier, change point, and slow trend changes

Enfin, Kats intègre un grand nombre de modèles (ARIMA, HW, stlf… et l’inévitable Prophet !). Bref, c’est le couteau suisse rêvé de tout.e Data Scientist qui s’attaque à un sujet de séries temporelles.