Repos Databricks & GitHub actions, better together !

Ou en bon français, comment faire coopérer la nouvelle (mars 2021) fonctionnalité Repos de Databricks avec une logique d’intégration continue développée dans GitHub ?

Revenons tout d’abord sur le point à l’origine de la nécessité d’un tel mécanisme (une présentation plus détaillée des Repos Databricks a été faite et mise à jour sur ce blog). Dans une architecture dite “moderne” sur Azure, nous utilisons Azure Data Factory pour piloter les traitements de données et lancer conjointement des notebooks Databricks. Cela nécessite de donner le chemin du notebook sur l’espace de travail déclaré en tant que service lié.

Nous disposons de trois entrées pour définir le “notebook path“.

Si nous choisissons “Repos”, nous allons donner un chemin contenant le nom du développeur !

Les développements livrés en production doivent bien sûr être totalement indépendants d’une telle information. Un contournement consisterait à utiliser un sous-répertoire Shared dans la partie Repos mais cela reviendrait à perdre le rattachement du développement à son auteur.

Nous allons donc mettre en place un mécanisme permettant aux développeurs d’utiliser leur propre répertoire puis de versionner les développements dans un espace tiers, par exemple un repository GitHub (une démarche exploitant les repositories Azure DevOps serait tout à fait similaire).

Voici le processus suivi lors d’un développement :

  • le développeur A crée une nouvelle branche sur laquelle il réalise ses développements
  • il “commit & push” ensuite son travail
  • une Pull Request (PR) peut alors être soumise dans le but de fusionner le travail avec la branche principale (master ou main)
  • le développeur B vient alors valider la PR et le merge se lance sur la branche principale

C’est alors que se déclenche notre processus d’intégration continue (CI) qui réalise une copie des notebooks de la branche principale dans le répertoire /Shared de l’espace de travail Databricks.

Nous nous assurons ainsi d’avoir toujours la dernière “bonne” version des notebooks, sur un chemin qui sera identique entre les environnements. En effet, dans un second temps, un processus de déploiement continu (CD) viendra copier ces notebooks sur les autre environnements (qualification, production).

Comment réaliser une GitHub Action d’intégration continue

Nous nous plaçons dans le repository servant à versionner les notebooks. Le menu Actions est accessible dans la barre supérieure.

Puis, nous cliquons sur le lien “set up a workflow yourself“.

Un modèle de pipeline YAML est alors disponible et nous allons l’adapter.

Détaillons maintenant le code final, étape par étape.

# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the master branch
  pull_request:
    branches: [ master ]
    paths: ['**.py']

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    env:
      DATABRICKS_HOST: ${{ secrets.DATABRICKS_HOST }}
      DATABRICKS_TOKEN: ${{ secrets.DATABRICKS_TOKEN }}

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - name: Checkout
        uses: actions/checkout@v2
      
      - name: Setup Python
        uses: actions/setup-python@v2.2.2
        with:
          python-version: '3.8.10'
          architecture: 'x64'
      
      # Install pip
      - name: Install pip
        run: |
          python -m pip install --upgrade pip

      # Install databricks CLI with pip
      - name: Install databricks CLI
        run: python -m pip install --upgrade databricks-cli

      # Runs
      - name: Run a multi-line script
        run: |
          echo Test python version
          python --version
          echo Databricks CLI version
          databricks --version
          
      # Import notebook to Shared
      - name: Import local github directory to Shared
        run: databricks workspace import_dir --overwrite . /Shared
          
      # List Shared content
      - name: List workspace with databricks CLI
        run: databricks workspace list --absolute --long --id /Shared

Le principe est d’utiliser une machine virtuelle munie d’un système d’exploitation Ubuntu (‘latest’ pour obtenir la dernière version disponible), d’y installer un environnement Python dans la version souhaitée (ici, 3.8.10) puis le CLI de Databricks.

Pour connecter ce dernier à notre espace de travail, nous définissons au préalable deux variables d’environnement :

  • DATABRICKS_HOST qui contient l’URL de notre espace de travail
  • DATABRICKS_TOKEN qui contient un jeton d’authentification

Pour ne pas faire figurer ces valeurs dans le fichier YAML (qui est lui-même versionné dans le repository), nous définirons au préalable deux secrets à l’aide du menu Settings.

Nous choisissons ici une portée des secrets au niveau repository.

L’instruction uses: actions/checkout@v2 garantit la disponibilité des fichiers archivés dans le repository (ceux-ci sont copiés sur la machine virtuelle) et le point (.) indique simplement ce chemin dans l’instruction du CLI import_dir.

Ainsi, dès la fusion d’une Pull Request avec la branche principale, le code se lance automatiquement et nous pouvons vérifier sa bonne exécution dans le menu “View runs”.

Une telle approche sera bien sûr complétée idéalement par une stratégie de tests portant sur le contenu des notebooks ou mieux encore, sur du code packagé qui y sera utilisé (voir cet article).

Utiliser les services cognitifs Azure au travers des images Docker

Si vous connaissez depuis quelques années les services cognitifs Azure, vous êtes certainement habitués à les interroger grâce au endpoint proposé par Microsoft et relatif à la région où a été déclarée la ressource.

Exemple d’une ressource de Computer Vision dans la région East US 2

La contrainte est alors d’avoir une connexion Internet ouverte depuis l’application qui exploitera ce service. Les scénarios “full cloud” sont ici tout-à-fait appropriés. Mais qu’en est-il lorsque l’on souhaite bénéficier de ces mêmes services dans des contextes “on premises” ou “at edge ?

Des images Docker sont proposées au téléchargement par Microsoft et couvrent le périmètre des services présentés sur l’image ci-dessous.

C’est le site hub.docker.com qui réalise ces hébergements, plus précisément dans le compte suivant Azure Cognitive Services by Microsoft | Docker Hub

Tous les services cognitifs ne sont pas encore disponibles et certaines images apparaissent en préversion publique. Les images de la colonne “Gated” nécessitent de remplir un formulaire de demande d’accès au service.

La documentation complète officielle est accessible sur ce lien.

Les bénéfices attendus d’une telle approche sont en particulier des gains de performance (débit élevé, faible latence) et la mise à l’échelle potentielle qui sera favorisée par l’utilisation de services comme Kubernetes.

Nous allons réaliser ici un exemple local au moyen de l’API Text Analytics pour réaliser la analyse de sentiments. Il s’agira dans cet article de tester la documentation présentée ici.

Nous utiliserons un poste Windows 10 sur lequel est installé Docker Desktop, utilisant lui-même WSL2.

Nous devrons télécharger les images à l’aide des commandes docker pull données sur les pages du site hub.docker.com.

docker pull mcr.microsoft.com/azure-cognitive-services/textanalytics/sentiment

La commande suivante est plus complexe à construire et nous devrons y intégrer les informations suivantes, obtenues depuis le portail Azure :

  • BILLING ou ENDPOINT_URI : il s’agit du endpoint du service cognitif, débutant par le nom du sous-domaine donné à la création et sans le caractère / terminant l’URL
  • APIKEY : l’une des deux clés associée à la ressource

La commande complète prend la forme suivante (retirer les caractères <>) et se lance depuis un terminal :

docker run --rm -it -p 5000:5000 --memory 4g --cpus 1 mcr.microsoft.com/azure-cognitive-services/textanalytics/sentiment EULA=accept BILLING=https://mytextanalytics4docker.cognitiveservices.azure.com/ APIKEY=<**********>

Docker Desktop indique alors que l’image est en cours d’exécution.

Nous disposons maintenant de plusieurs URLs locales pour vérifier l’état du service et le tester.

http://localhost:5000/

http://localhost:5000/ready

{"service":"sentimentv3","ready":"ready"}

http://localhost:5000/status

{"service":"sentimentv3","apiStatus":"Valid","apiStatusMessage":"Api is valid."}

http://localhost:5000/swagger/index.html

Ce dernier lien va permettre de réaliser des tests, par exemple avec la méthode POST, en cliquant sur le bouton “Try it out”. Attention à bien basculer la liste déroulante en face de Request body sur “application/json”.

Nous visualisons le résultat suivant :

N’oubliez pas d’arrêter l’exécution de l’image en faisant par exemple un CRTL+C dans la fenêtre de commande où vous avez exécuté le docker run.

Forecasting par automated ML (UI)

Le portail Azure Machine Learning propose une interface graphique (UI) pour réaliser de premières modèles d’apprentissage dans trois cas de figure :

  • régression
  • classification
  • forecasting

Nous allons nous intéresser ici au forecasting, c’est-à-dire à la prévision sur des données issues d’une série temporelle (ou dite encore série chronologique).

Une série temporelle est un jeu de données composé tout simplement de deux colonnes : une mesure numérique et une variable de temps indiquant le moment de cette mesure. Il est indispensable que les intervalles de temps soient réguliers. Par exemple, nous pouvons disposer d’une mesure par jour, par semaine ou bien par mois, ou encore à des granularités plus fines comme l’heure, la minute, voire la seconde.

S’il existent des valeurs manquantes, il sera nécessaire de les “imputer” avant d’utiliser le jeu de données pour l’apprentissage automatisé.

Depuis le menu latéral, nous lançons une nouvelle exécution (“run“) d’automated ML.

Nous sélectionnons ensuite le jeu de données (“dataset“) répondant à la définition donnée ci-dessous d’une série temporelle.

Pour un premier exemple d’utilisation, nous choisirons le jeu de données du nombre de filles nées par jour en Californie en 1959, disponible sur ce lien.

Il faut ensuite donner un nom pour l’expérience (la trace de l’exécution dans le portail) et choisir une ressource de calcul de type compute cluster.

C’est à l’écran suivant que l’on choisira explicitement une tâche de type “time series forecasting“.

Avant de lancer l’exécution (bouton Finish), nous pouvons réaliser quelques paramétrages (attention, aucun retour arrière ne sera ensuite possible !).

Si les données ne sont pas agrégées pour ne disposer que d’une seule ligne par échelon de temps, il sera nécessaire de lister les “time series identifiers“, c’est-à-dire les colonnes qui permettraient de traiter plusieurs séries temporelles au travers de la même expérience. <Cela se traduira par la présence d’autres paramètres en entrée du service web prédictif.>

Deux options sont par défaut positionnées sur “autodetect” :

  • Frequency : il s’agit de la granularité temporelle du jeu de données. Les valeurs possibles sont présentées ci-dessous. Utilisez explicitement ce paramètre si un doute est possible ou qu’une agrégation est nécessaire (sum, min, max, mean).
  • Forecast horizon : il s’agit du nombre de périodes (dans l’unité du paramètre Frequency) qui seront prédites. La documentation officielle ne mentionne pas la règle appliquée dans le cas de l’auto-détection. Je vous recommande de le spécifier explicitement, en étant “raisonnable”, c’est-à-dire en ne dépassant pas le tiers de votre historique (ex.: pour 3 années d’entrainement, se limiter à une année de prévision).

Nous pouvons enfin définir des éléments de configuration additionnelle (cliquer sur “View additional configuration settings”).

Pour bloquer des algorithmes, il suffit de cliquer dans la liste déroulante.

Je vous conseille de conserver les méthodes dites “naïves” qui donneront un premier seuil pour les métriques d’évaluation. Vous pouvez également choisir de ne pas utiliser les méthodes non linéaires comme les RandomForest ou KNN. Les méthodes traditionnelles du domaine des séries temporelles sont présentes (autoARIMA, exponential smoothing) ainsi que la librairie issue de Facebook : Prophet.

Les méthodes d’apprentissage profond seront également évaluées en cochant la case “enable Deep Learning”. Ces méthodes ont montré leur efficacité mais rappelons qu’elles sont coûteuses en temps d’entrainement puis d’inférence et sur des problématiques relativement simples, elles ne donneront sans doute pas un gain significatif de performance.

Les trois métriques de comparaison des modèles sont :

  • normalized RMSE
  • R2
  • normalized MAE

Nous retrouverons cette métrique comme critère d’arrêt de l’expérience. Il faut alors renseigner un seuil entre 0 et 1 (les métriques sont normalisées et le R2 est par définition compris entre 0 et 1).

Il est important de ne pas se limiter à un seul indicateur d’évaluation, tous seront accessibles quand les différents modèles seront entrainés.

Je vous recommande aussi de borner le temps d’entrainement, afin de limiter les coûts mais également parce que vous obtiendrez sans doute assez rapidement une idée des modèles adaptés à vos données. Il sera ensuite plus efficace de bloquer les algorithmes les moins efficaces.

Si les deux critères sont utilisés, c’est le premier atteint qui arrêtera l’expérience.

Trois paramètres additionnels de configuration sont ensuite disponibles.

Ils correspondent respectivement à :

  • target lags : il s’agit ici d’exploiter des valeurs précédentes pour prédire les nouvelles valeurs. Par exemple, un lag de 1 permettra d’utiliser la valeur à t pour prédire t+1. Je vous recommande cet article pour approfondir ce sujet.
  • rolling window size : permet de ne tenir compte que d’une partie des données d’apprentissage. Par défaut, l’intégralité du jeu de données est considéré. Plus de détails sur cette méthode ici.
  • season and trend : active la méthode de décomposition STL (“Seasonal and Trend decomposition using Loess”), permettant d’isoler saisonnalité, tendance et bruit.

Une dernière case permet de sélectionner un (et un seul) pays pour tenir compte des vacances dans les calculs de saisonnalité, ce que fait par exemple le modèle Prophet.

La seule méthode de validation du modèle est la méthode ce validation croisée “k-fold”, avec k = 5 par défaut.

Enfin, les algorithmes se paralléliseront en fonction du nombre de nœuds du compute cluster défini préalablement.

Nous pouvons maintenant analyser les sorties produites par l’expérience d’automated ML.

Le run principal est constitué de children runs, correspondant chacun à un algorithme, précédé éventuellement d’une préparation de données (PCA, MinMaxScaler, etc.) , et avec un choix d’ hyperparamètres. L’onglet Models présente ces différentes exécutions, triées selon la valeur décroissante de la métrique principale.

Notons également l’onglet Data guardails (garde-fous) qui nous avertit sur d’éventuels problèmes de constitution de notre jeu de données initial (historique trop court, valeurs manquantes, etc.).

Il est très vraisemblable que le modèle “VotingEnsemble” soit le meilleur. C’est en effet un “méta-modèle” qui utilise plusieurs modèles simples et les fait voter pour obtenir les meilleures prévisions. C’est également un modèle plus lourd à exposer.

Il est tout à fait possible de choisir un autre modèle pour l’exposer.

Pour un test simple, nous choisissons une ressource de type Azure Container Instance (ACI) et n’enclenchons pas l’authentification (un jeton serait alors demandé lors de l’appel au service web).

Dans les paramètres avancés, nous pouvons réduire le CPU et la RAM utilisés (et donc la facture associée… mais aussi les performances !).

Un “virtual” CPU peut être défini par un nombre décimal.

Un clic sur “Deploy” lance le déploiement et nous pouvons basculer sur la page dédiée aux endpoints.

La ressource ACI se retrouvera également dans le portail Azure, dans le même groupe de ressources que le service Azure Machine Learning.

Pour consommer le modèle à partir du service web, nous devons utiliser des dates postérieures à la dernière date ayant servi à l’entrainement. Ceci peut se faire au travers de l’interface de test ou à l’aide des exemples de code donnés dans les langages C#, Python et R.

Nous testons ici la journée du 1er janvier de l’année suivante (1960).

En conclusion, nous avons ici un outil dédié aux “citizen data scientists” puisqu’il n’est jamais nécessaire d’écrire de code mais une (très) bonne connaissance de la théorie des différentes méthodes algorithmes et de l’interprétation des métriques d’évaluation sera indispensable. Nous obtenons très rapidement un service web prédictif efficient qui pourra être exploité par d’autres applications. N’oublions pas enfin que pour “vivre en production”, une telle approche ne sera pas suffisante. Nous ne maîtrisons pas ici en particulier le réentrainement du modèle ni l’archivage de ces versions. Une approche par le code, exploitant les librairies du SDK azureml, sera alors une démarche plus pérenne et respectant les bonnes pratiques du MLOps. Prochain article à venir !

Réviser la certification AI-102

Obtenir le titre “Azure AI Engineer Associate” ne demande que de passer une seule certification Microsoft, celle nommé AI-102 et dont le descriptif est disponible ici. Elle a remplacé la certification AI-100 en 2021 et s’oriente vers le choix et l’utilisation des services cognitifs Azure, alors que la précédente version pouvait également aborder des thèmes comme l’implémentation et le monitoring.

Je vous conseille toutefois d’associer à cette certification la AI-900 “Microsoft Azure AI Fundamentals” qui, comme toutes les “900”, se veut plus générique et moins technique (certains diront plus faciles à obtenir). Elle aborde en particulier les grands principes pour une utilisation responsable de l’Intelligence Artificielle :

  • fairness (équité)
  • reliability (fiabilité)
  • privacy (vie privée)
  • inclusiveness (inclusivité)
  • transparency (transparence)
  • accountability (responsabilité)

Pour préparer l’AI-900, utilisez les parcours d’apprentissage de Microsoft Learn, comme celui-ci.

Observons maintenant en détail les cinq compétences mesurées qui seront autant de chapitres dans la liste des éléments à réviser :

  • Planifier et gérer une solution Azure Cognitive Services
  • Mettre en œuvre des solutions de vision par ordinateur
  • Mettre en œuvre des solutions de traitement du langage naturel
  • Mettre en œuvre des solutions d’exploration des connaissances
  • Mettre en œuvre des solutions de AI conversationnelle

Le détail est fourni dans ce document PDF, en anglais, et il faut en surveiller les mises à jour.

La documentation Microsoft sera bien sûr l’un de vos principaux alliés. Préférez une lecture en anglais pour vous familiariser avec la terminologie utilisée dans les questions de l’examen.

Nous travaillerons autour des quatre familles principales de services cognitifs :

  • vision
  • langage
  • speech
  • décision

Plusieurs questions porteront vraisemblablement sur le choix du bon service pour répondre à des scénarios précis. Ces questions ne devraient pas vous poser de difficulté une fois que vous aurez en tête les grandes fonctionnalités de chacun des services listés sur l’image ci-dessus.

Les services de la catégorie Décision ne seront pas présentés dans les quatre autres chapitres.

Mais attention, le contenu détaillé agrandit un peu le périmètre des services de base et se réorganise de la sorte :

  • Computer Vision
  • Natural Language Processing
  • Knowledge Mining
  • Conversational AI

Nous allons donc avoir à faire à quelques services que Microsoft désigne maintenant par le terme “Azure Applied AI Services” (voir ce lien) et en particulier aux services Azure Bot et Cognitive Search.

Plan and Manage an Azure Cognitive Services Solution

Nous allons nous concentrer ici sur trois points du programme :

  • la création d’une ressource
  • les aspects de sécurité
  • l’utilisation de conteneurs

Implement Computer Vision Solutions

Les services cognitifs concernés par ce chapitre sont :

  • Computer Vision
  • Custom Vision
  • Face

Implement Natural Language Processing Solutions

Les services cognitifs concernés par ce chapitre sont :

  • Text Analytics
  • Speech to Text & Text to Speech
  • Translate
  • Language Understanding Service (LUIS)Computer Vision

Implement Knowledge Mining Solutions

L’unique service cognitif concerné par ce chapitre est Azure Cognitive Search.

A priori, et selon le programme détaillé, vous ne devriez pas rencontrer de questions sur l’API Bing Search (présentée ici).

Implement Conversational AI Solutions

Les services cognitifs concernés par ce chapitre sont :

  • QnA Maker
  • Bot Framework

Ne pas oublier le service Dispatch pour la gestion du multi-langue.

En conclusion

Nous espérons vous avoir fourni ici les premières bases pour guider vos révisions. N’oubliez pas que la pratique est indispensable (profitez des free tiers souvent disponibles qui n’affecteront pas votre crédit Azure) et méfiez-vous des bases de questions (et encore plus des réponses !) que l’on peut trouver sur Internet.