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.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- 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.
Quels sont les prérequis ?
Section intitulée « Quels sont les prérequis ? »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.
Pourquoi un registre de modèles ?
Section intitulée « Pourquoi un registre de modèles ? »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.
Comment désigner un modèle avec un alias ?
Section intitulée « Comment désigner un modèle avec un alias ? »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 mlflowfrom 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.
Pourquoi ne pas utiliser les stages du registre ?
Section intitulée « Pourquoi ne pas utiliser les stages du registre ? »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.
Comment remonter d'une version à son run ?
Section intitulée « Comment remonter d'une version à son run ? »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.
# client : celui créé plus haut, avec set_tracking_uri vers le serveurversion = 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.
Comment charger un modèle par alias ?
Section intitulée « Comment charger un modèle par alias ? »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.sklearnimport 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 :
export MLFLOW_ENABLE_PROXY_MULTIPART_DOWNLOAD=falseuv run python predire.pyLe 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).
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Correction |
|---|---|---|
load_model reste suspendu sur model.skops | Le client télécharge en multipart directement chez S3, adresse interne injoignable | export MLFLOW_ENABLE_PROXY_MULTIPART_DOWNLOAD=false côté client |
MLFLOW_ENABLE_MULTIPART_DOWNLOAD=false ne change rien | Cette variable ne gouverne pas le chemin proxifié | Utiliser MLFLOW_ENABLE_PROXY_MULTIPART_DOWNLOAD |
| Des versions 3 et 4 apparaissent alors que vous en attendiez 2 | Un script interrompu a déjà enregistré des versions | Raisonner sur l'état, retrouver la version par son run, jamais par un numéro fixe |
FutureWarning ... stages will be removed | Appel à transition_model_version_stage ou get_latest_versions | Remplacer par set_registered_model_alias et get_model_version_by_alias |
runs:/<run_id>/model introuvable ou avertissement | En MLflow 3, le modèle n'est plus sous les artefacts du run | Enregistrer avec model_uri=info.model_uri |
Mettre en pratique
Section intitulée « Mettre en pratique »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).
À retenir
Section intitulée « À retenir »- 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
championetchallengeravecset_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 tagsgit_commitetdata_sha256. - Un
load_modelsuspendu vient du téléchargement multipart direct vers S3 :MLFLOW_ENABLE_PROXY_MULTIPART_DOWNLOAD=falsecôté client.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »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
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
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Tester données et modèle ML avec pytest avant promotion
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications