Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Registre de modèles MLflow : versions, alias et lignée

Par mis à jour le

Améliorer cette page

Le registre de modèles de MLflow donne à un modèle un nom, des versions numérotées et des alias comme champion, qui permettent de le charger sans connaître ni son chemin ni son run. Un service demande models:/latence-service@champion ; changer de modèle en production revient à déplacer l'alias, sans toucher au code.

Cette leçon prolonge celle sur le serveur MLflow : les runs y sont déjà tracés, et chaque modèle y porte un identifiant opaque du type m-3f2a…. Public : intermédiaire. Vous allez enregistrer deux modèles, poser les alias, remonter d'une version jusqu'au code et aux données qui l'ont produite, puis charger un modèle par son alias, avec un piège de chargement à connaître.

  • Enregistrer un modèle dans le registre et obtenir des versions numérotées.
  • Désigner une version par un alias (champion, challenger) plutôt que par un numéro.
  • Expliquer pourquoi les stages du registre ne s'enseignent plus.
  • Remonter d'une version à son run, à son commit et à l'empreinte des données.
  • Charger un modèle par alias et diagnostiquer un chargement qui reste suspendu.

La leçon suppose la leçon sur le serveur MLflow : le serveur (PostgreSQL, bucket S3, MLflow) démarré par bash scripts/socle-demarrer.sh mlflow, un script d'entraînement qui journalise sur http://127.0.0.1:5000 avec les tags git_commit et data_sha256, et un dépôt Git. Docker est nécessaire pour le serveur. Versions mesurées : MLflow 3.17.1 (client et serveur) et scikit-learn 1.9.1.

Sans registre, un modèle se désigne par un chemin ou un identifiant de run, et chaque changement de modèle oblige à modifier le code qui le charge. Avec MLflow 3, l'identifiant d'un modèle est de plus un model_id opaque (m-…) : personne ne sait lequel est en production en le lisant.

Le registre résout deux problèmes. Il donne un nom stable au modèle (latence-service) et numérote ses versions à chaque enregistrement. Un alias est une étiquette mobile qu'on pose sur une version : champion pour celle qui sert, challenger pour celle qu'on évalue. Le code charge l'alias ; c'est l'alias qui bouge.

Comment enregistrer un modèle et créer des versions ?

Section intitulée « Comment enregistrer un modèle et créer des versions ? »

On enregistre un modèle avec mlflow.register_model, en lui passant l'URI du modèle (models:/<model_id>) et le nom du registre ; chaque appel crée une nouvelle version. log_model renvoie un objet dont model_uri est cette URI.

info = mlflow.sklearn.log_model(modele, name="modele",
skops_trusted_types=["sklearn.tree._tree.Tree"])
mlflow.register_model(model_uri=info.model_uri, name="latence-service")

La première fois, MLflow crée le modèle enregistré latence-service et sa version 1 ; l'entraînement suivant, enregistré sous le même nom, donne la version 2. Chaque version garde son run_id et sa source, de la forme models:/m-…. Depuis MLflow 3, l'ancienne forme runs:/<run_id>/model ne correspond plus à l'emplacement du modèle : MLflow affiche un avertissement (« has no artifacts at artifact path ») et retombe sur le m-… du run. Passez par model_uri.

Un détail d'exploitation, mesuré en lab : un script interrompu laisse des versions déjà créées. Après un essai raté, vous pouvez voir les versions 1 et 2 puis 3 et 4. N'écrivez donc jamais « la version 2 » en dur dans un test ou un script : raisonnez sur l'état du registre (la version dont le run a 50 arbres), pas sur un numéro.

Un alias se pose avec set_registered_model_alias et se résout avec get_model_version_by_alias. Le lab demande champion sur la version à 50 arbres et challenger sur celle à 100 arbres :

import mlflow
from mlflow import MlflowClient
mlflow.set_tracking_uri("http://127.0.0.1:5000")
client = MlflowClient()
client.set_registered_model_alias("latence-service", "champion", 1)
client.set_registered_model_alias("latence-service", "challenger", 2)
print(client.get_model_version_by_alias("latence-service", "champion").version)

MlflowClient() sans adresse interroge, lancé à part, la base SQLite locale (mlflow.db) et non le serveur : fixez l'adresse comme ci-dessus, ou exportez MLFLOW_TRACKING_URI=http://127.0.0.1:5000. Le dernier appel doit afficher la version pointée par champion. Les numéros 1 et 2 ne valent que si votre registre est vierge : sinon, retrouvez la version de chaque run avec client.search_model_versions("name='latence-service'"), dont chaque élément porte son version et son run_id (la liste arrive par version décroissante). Le point clé est que le code consommateur n'écrit jamais de numéro. Pour promouvoir le challenger, on repose l'alias champion sur sa version : le même appel, une nouvelle cible. Le retour arrière est le même geste dans l'autre sens.

Dans le lab, le challenger (100 arbres) a une meilleure RMSE que le champion (50 arbres) : 22,41 ms contre 23,31 ms. La leçon suivante automatise la décision de le promouvoir ou de le refuser.

Les stages (Staging, Production, Archived) sont dépréciés depuis MLflow 2.9.0 : on désigne un modèle par alias seulement. Ils fonctionnent encore en 3.17.1, mais transition_model_version_stage et get_latest_versions émettent un FutureWarning : « Model registry stages will be removed in a future major release ».

Cette leçon n'enseigne donc que les alias, et le lab impose que toutes les versions restent en stage None. Les articles et exemples plus anciens que vous croiserez utilisent les stages : traduisez-les en alias plutôt que de les recopier. L'alias est aussi plus souple, puisqu'un modèle peut en porter plusieurs et que vous choisissez leurs noms.

La lignée d'une version se lit en suivant run_id : la version pointe vers son run, qui porte le commit Git et l'empreinte des données. C'est la raison pour laquelle la leçon précédente posait les tags git_commit et data_sha256.

Lisez le schéma de gauche à droite : chaque run produit un modèle (m-…), chaque modèle devient une version du modèle enregistré, et les alias sont des étiquettes posées sur les versions. Le sens de la lecture inverse est celui de l'enquête : de l'alias à la version, de la version au run, du run au code et aux données.

Deux runs, l'un à 50 arbres et l'autre à 100 arbres, portent chacun les tags git_commit et data_sha256 et produisent chacun un modèle m. Chaque modèle devient une version du modèle enregistré latence-service : version 1 et version 2. L'alias champion pointe sur la version 1, l'alias challenger sur la version 2.run à 50 arbrestags git_commit, data_sha256modèle m-…run à 100 arbrestags git_commit, data_sha256modèle m-…latence-serviceversion 1latence-serviceversion 2alias championalias challenger
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %
# client : celui créé plus haut, avec set_tracking_uri vers le serveur
version = client.get_model_version_by_alias("latence-service", "champion")
run = client.get_run(version.run_id)
print(version.version, run.data.tags["git_commit"], run.data.tags["data_sha256"])

Ce script répond au « que se passe-t-il en production ? » : il rend la version servie, le commit du code et l'empreinte du jeu de données qui l'ont produite. Avec le dvc.lock du pipeline DVC, vous retrouvez les données exactes ; avec le commit, le code.

mlflow.sklearn.load_model accepte l'URI models:/<nom>@<alias> : le code ne contient ni chemin, ni identifiant de run, ni m-…. Le script predire.py du lab tient en quelques lignes :

import mlflow, mlflow.sklearn
import numpy as np
mlflow.set_tracking_uri("http://127.0.0.1:5000")
modele = mlflow.sklearn.load_model("models:/latence-service@challenger")
print(modele.predict(np.array([[60, 70, 150, 0.02]])))

La ligne d'entrée est dans l'ordre des colonnes du jeu : cpu_pct=60, memoire_pct=70, requetes_s=150, taux_erreur=0.02. Le script affiche une latence prédite en millisecondes. Si le chargement répond, tout est bon ; sinon, lisez la section suivante avant de soupçonner le registre.

Pourquoi le chargement par alias reste-t-il suspendu ?

Section intitulée « Pourquoi le chargement par alias reste-t-il suspendu ? »

Avec la pile du lab, load_model("models:/nom@champion") peut rester suspendu plus de 90 secondes sur le fichier model.skops (18 Mo), sans erreur. Le registre n'y est pour rien : c'est le téléchargement du fichier qui bloque.

Le serveur annonce par son point d'accès /api/3.0/mlflow/server-info (champ multipart_downloads_enabled) qu'il sait servir un téléchargement multipart, où le fichier est découpé en morceaux récupérés séparément. Tant que la variable ci-dessous n'est pas définie, le client suit cette annonce : il demande une URL présignée (une adresse temporaire, signée par le serveur, qui donne accès à un fichier du bucket sans clé) et télécharge par morceaux directement chez S3, à l'adresse interne http://garage:3900, injoignable depuis l'hôte (le nom garage n'existe que dans le réseau des conteneurs). À l'inverse, curl du même fichier par l'API du serveur (/api/2.0/mlflow-artifacts/artifacts/…) répond 200 en 0,06 s : le chemin passant par le serveur fonctionne, c'est le raccourci vers S3 qui échoue.

La parade se pose côté client, par une variable d'environnement :

Fenêtre de terminal
export MLFLOW_ENABLE_PROXY_MULTIPART_DOWNLOAD=false
uv run python predire.py

Le chargement prend alors environ 0,9 s. Deux précisions mesurées : la variable voisine MLFLOW_ENABLE_MULTIPART_DOWNLOAD=false n'agit pas sur ce chemin (essayée, le chargement reste suspendu), et le serveur 3.17.1 n'offre pas d'interrupteur équivalent. Le seuil de taille (MLFLOW_MULTIPART_DOWNLOAD_MINIMUM_FILE_SIZE) n'est pas la cause. Le symptôme dépend de la topologie : il apparaît quand l'adresse S3 vue par le serveur n'est pas joignable depuis le client (rejoué depuis le conteneur du serveur, le téléchargement multipart par défaut prend 0,2 s).

SymptômeCause probableCorrection
load_model reste suspendu sur model.skopsLe client télécharge en multipart directement chez S3, adresse interne injoignableexport MLFLOW_ENABLE_PROXY_MULTIPART_DOWNLOAD=false côté client
MLFLOW_ENABLE_MULTIPART_DOWNLOAD=false ne change rienCette variable ne gouverne pas le chemin proxifiéUtiliser MLFLOW_ENABLE_PROXY_MULTIPART_DOWNLOAD
Des versions 3 et 4 apparaissent alors que vous en attendiez 2Un script interrompu a déjà enregistré des versionsRaisonner sur l'état, retrouver la version par son run, jamais par un numéro fixe
FutureWarning ... stages will be removedAppel à transition_model_version_stage ou get_latest_versionsRemplacer par set_registered_model_alias et get_model_version_by_alias
runs:/<run_id>/model introuvable ou avertissementEn MLflow 3, le modèle n'est plus sous les artefacts du runEnregistrer avec model_uri=info.model_uri

Le lab repart d'un train.py qui journalise déjà sur le serveur ; à vous de générer le jeu et de créer le dépôt Git. Vous entraînez deux modèles (50 puis 100 arbres), vous les enregistrez sous le nom latence-service (version 1 pour 50 arbres, version 2 pour 100, sur un registre vierge), vous posez champion sur la première et challenger sur la seconde sans utiliser les stages, puis vous écrivez predire.py, qui charge models:/latence-service@challenger sans chemin ni identifiant. La validation lit le registre, vérifie la lignée jusqu'aux runs et exécute réellement votre script. Démarrez d'abord le socle avec bash scripts/socle-demarrer.sh mlflow. Le contrôle du lab lance lui-même votre predire.py dans l'environnement du shell de dsoxlab run : exportez MLFLOW_ENABLE_PROXY_MULTIPART_DOWNLOAD=false dans ce shell avant de le lancer, ou posez-la dans le script par os.environ avant l'appel à load_model, sans quoi le chargement reste suspendu. Le scénario du lab désigne cette variable sans dire laquelle des deux voies employer : les deux ont été rejouées et chargent le modèle (environ 2 s au total pour predire.py, os.environ posé après import mlflow et avant load_model).

  • Le registre donne à un modèle un nom, des versions numérotées et des alias : le code charge un alias, jamais un chemin ni un m-….
  • mlflow.register_model(model_uri=info.model_uri, name=…) crée une version à chaque appel ; ne comptez jamais sur un numéro de version fixe.
  • On désigne champion et challenger avec set_registered_model_alias ; promouvoir ou revenir en arrière, c'est déplacer l'alias.
  • Les stages sont dépréciés depuis MLflow 2.9.0 : alias seulement.
  • La lignée se lit de la version à run_id, puis aux tags git_commit et data_sha256.
  • Un load_model suspendu vient du téléchargement multipart direct vers S3 : MLFLOW_ENABLE_PROXY_MULTIPART_DOWNLOAD=false côté client.

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn