Cet article explique comment consigner vos modèles entraînés (ou artefacts) en tant que modèles MLflow. Il explore les différentes façons de personnaliser la façon dont MLflow empaquette vos modèles et comment il exécute ces modèles.
Pourquoi journaliser des modèles au lieu d’artefacts ?
Les artefacts et les modèles dans MLflow décrivent la différence entre les artefacts de journalisation ou les fichiers, par rapport à la journalisation des modèles MLflow.
Un modèle MLflow est également un artefact. Toutefois, ce modèle a une structure spécifique qui sert de contrat entre la personne qui a créé le modèle et la personne qui a l’intention de l’utiliser. Ce contrat permet de créer un pont entre les artefacts eux-mêmes et leurs significations.
La journalisation des modèles présente les avantages suivants :
Vous pouvez charger directement des modèles, pour l’inférence avec mlflow.<flavor>.load_model, et vous pouvez utiliser la fonction predict
Les entrées de pipeline peuvent utiliser directement des modèles
Vous pouvez déployer des modèles sans indication d’un script de scoring ou d’un environnement
Swagger est automatiquement activé dans les points de terminaison déployés, et Azure Machine Learning Studio peut utiliser la fonctionnalité de test
Vous pouvez utiliser le tableau de bord d’IA responsable
Cette section explique comment utiliser le concept du modèle dans Azure Machine Learning avec MLflow :
Journalisation des modèles à l’aide de la journalisation automatique
Vous pouvez utiliser la fonctionnalité de journal automatique MLflow. La journalisation automatique permet à MLflow d’indiquer à l’infrastructure en cours d’utilisation de journaliser toutes les métriques, paramètres, artefacts et modèles considérés par l’infrastructure comme pertinents. Par défaut, si la journalisation automatique est activée, la plupart des modèles sont enregistrés. Dans certaines situations, certaines saveurs peuvent ne pas journaliser un modèle. Par exemple, la saveur PySpark ne journalise pas les modèles qui dépassent une certaine taille.
Utilisez mlflow.autolog() ou mlflow.<flavor>.autolog() pour activer la journalisation automatique. Cet exemple utilise autolog() pour journaliser un modèle classifieur entraîné avec XGBoost :
Python
import mlflow
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score
mlflow.autolog()
model = XGBClassifier(use_label_encoder=False, eval_metric="logloss")
model.fit(X_train, y_train, eval_set=[(X_test, y_test)], verbose=False)
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
Conseil
Si vous utilisez des pipelines Machine Learning, par exemple des pipelines Scikit-Learn, utilisez les fonctionnalités autolog de cette saveur de pipeline pour journaliser les modèles. La journalisation des modèles se produit automatiquement lorsque la méthode fit() est appelée sur l’objet de pipeline. L’entraînement et suivi d’un classifieur XGBoost avec le notebook de MLflow montre comment journaliser un modèle avec prétraitement à l’aide de pipelines.
Journalisation des modèles avec une signature, un environnement ou des exemples personnalisés
La méthode mlflow.<flavor>.log_model MLflow peut journaliser manuellement les modèles. Ce flux de travail peut contrôler différents aspects de la journalisation des modèles.
Utilisez cette méthode dans les cas suivants :
Vous souhaitez indiquer des packages pip ou un environnement conda qui diffèrent de ceux qui sont automatiquement détectés
Vous souhaitez inclure des exemples d’entrée
Vous souhaitez inclure des artefacts spécifiques dans le package nécessaire
autolog ne déduit pas correctement votre signature. Cela est important lorsque vous traitez des entrées de capteur, où la signature a besoin de formes spécifiques
Le comportement de journal automatique ne couvre pas votre objectif pour une raison quelconque
Cet exemple de code journalise un modèle pour un classifieur XGBoost :
Python
import mlflow
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score
from mlflow.models import infer_signature
from mlflow.utils.environment import _mlflow_conda_env
mlflow.autolog(log_models=False)
model = XGBClassifier(use_label_encoder=False, eval_metric="logloss")
model.fit(X_train, y_train, eval_set=[(X_test, y_test)], verbose=False)
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
# Signature
signature = infer_signature(X_test, y_test)
# Conda environment
custom_env =_mlflow_conda_env(
additional_conda_deps=None,
additional_pip_deps=["xgboost==1.5.2"],
additional_conda_channels=None,
)
# Sample
input_example = X_train.sample(n=1)
# Log the model manually
mlflow.xgboost.log_model(model,
artifact_path="classifier",
conda_env=custom_env,
signature=signature,
input_example=input_example)
Notes
autolog a la configuration log_models=False. Cela empêche la journalisation automatique des modèles MLflow. La journalisation automatique des modèles MLflow se produit ultérieurement, en tant que processus manuel
Cela utilise la méthode infer_signature pour essayer de déduire directement la signature des entrées et des sorties.
La méthode mlflow.utils.environment._mlflow_conda_env est une méthode privée dans le (SDK) MLflow. Dans cet exemple, il simplifie le code, mais l’utilise avec précaution. Cela pourrait changer à l'avenir. En guise d’alternative, vous pouvez générer manuellement la définition YAML en tant que dictionnaire Python.
Journalisation des modèles avec un comportement différent dans la méthode predict
Lors de la journalisation d’un modèle avec mlflow.autolog ou mlflow.<flavor>.log_model, la saveur du modèle détermine comment exécuter l’inférence et ce que le modèle retourne. MLflow n’applique aucun comportement spécifique concernant la génération de résultats predict. Dans certains scénarios, il se peut que vous souhaitiez effectuer un traitement antérieur ou postérieur avant et après l’exécution de votre modèle.
Dans ce cas, implémentez des pipelines Machine Learning qui passent directement des entrées aux sorties. Bien que cette implémentation soit possible et parfois encouragée à améliorer les performances, il peut devenir difficile d’atteindre. Dans ces cas, il peut aider à personnaliser la façon dont votre modèle gère l’inférence, comme expliqué dans la section suivante.
Toutefois, vous pouvez avoir besoin de modifier la façon dont une saveur fonctionne, de journaliser un modèle non pris en charge en mode natif par MLflow ou même journaliser un modèle qui utilise plusieurs éléments à partir de différents infrastructures. Dans ces cas, il peut être nécessaire de créer une saveur de modèle personnalisée.
Pour résoudre le problème, MLflow introduit la saveur pyfunc (à partir d’une fonction Python). Cette saveur peut consigner n’importe quel objet en tant que modèle, tant que cet objet satisfait à deux conditions :
Vous implémentez la méthode predict (au moins)
L’objet Python hérite de mlflow.pyfunc.PythonModel
Conseil
Les modèles sérialisables qui implémentent l’API Scikit-learn peuvent utiliser la saveur Scikit-learn pour journaliser le modèle, quel que soit le modèle créé avec Scikit-learn. Si vous pouvez conserver votre modèle au format Pickle et que l’objet a les méthodes predict() et predict_proba() (au moins), vous pouvez utiliser mlflow.sklearn.log_model() pour enregistrer le modèle à l’intérieur d’une exécution MLflow.
Si vous créez un wrapper autour de votre objet de modèle existant, il devient le plus simple de créer une saveur pour votre modèle personnalisé. MLflow sérialise et les packages pour vous. Les objets Python sont sérialisables lorsque l’objet peut être stocké dans le système de fichiers en tant que fichier généralement au format Pickle. Au moment de l’exécution, l’objet peut matérialiser à partir de ce fichier. Cela restaure toutes les valeurs, propriétés et méthodes disponibles lors de son enregistrement.
Utilisez cette méthode dans les cas suivants :
Vous pouvez sérialiser votre modèle au format Pickle
Vous souhaitez conserver l’état du modèle, tel qu’il était juste après l’entraînement
Vous souhaitez personnaliser le fonctionnement de la fonction predict.
Cet exemple de code encapsule un modèle créé avec XGBoost pour qu’il se comporte différemment de l’implémentation par défaut de la saveur XGBoost. Au lieu de cela, elle retourne les probabilités au lieu des classes :
Python
from mlflow.pyfunc import PythonModel, PythonModelContext
classModelWrapper(PythonModel):def__init__(self, model):
self._model = model
defpredict(self, context: PythonModelContext, data):# You don't have to keep the semantic meaning of `predict`. You can use here model.recommend(), model.forecast(), etcreturn self._model.predict_proba(data)
# You can even add extra functions if you need to. Since the model is serialized,# all of them will be available when you load your model back.defpredict_batch(self, data):pass
Journaliser un modèle personnalisé dans l’exécution :
Python
import mlflow
from xgboost import XGBClassifier
from sklearn.metrics import accuracy_score
from mlflow.models import infer_signature
mlflow.xgboost.autolog(log_models=False)
model = XGBClassifier(use_label_encoder=False, eval_metric="logloss")
model.fit(X_train, y_train, eval_set=[(X_test, y_test)], verbose=False)
y_probs = model.predict_proba(X_test)
accuracy = accuracy_score(y_test, y_probs.argmax(axis=1))
mlflow.log_metric("accuracy", accuracy)
signature = infer_signature(X_test, y_probs)
mlflow.pyfunc.log_model("classifier",
python_model=ModelWrapper(model),
signature=signature)
Conseil
Ici, la méthode infer_signature utilise y_probs pour déduire la signature. Notre colonne cible a la classe cible, mais notre modèle retourne maintenant les deux probabilités pour chaque classe.
Votre modèle peut être composé de plusieurs éléments qui doivent être chargés. Vous n’avez peut-être pas un moyen de sérialiser ce fichier en tant que fichier Pickle. Dans ces cas, PythonModel prend en charge l’indication d’une liste arbitraire d’artefacts. Chaque artefact est empaqueté avec votre modèle.
Utilisez cette technique quand :
Vous ne pouvez pas sérialiser votre modèle au format Pickle, ou vous disposez d’un meilleur format de sérialisation disponible
Votre modèle a un ou plusieurs artefacts doivent être référencés pour charger le modèle
Il se peut que vous souhaitiez conserver certaines propriétés de configuration d’inférence, par exemple le nombre d’éléments à recommander
Vous souhaitez personnaliser la façon dont le modèle charge et le fonctionnement de la fonction predict
Cet exemple de code montre comment consigner un modèle personnalisé à l’aide d’artefacts :
Le modèle n’est pas enregistré en tant que pickle. Au lieu de cela, le code a enregistré le modèle avec la méthode d’enregistrement de l’infrastructure que vous avez utilisée
Le wrapper de modèle est ModelWrapper(), mais le modèle n’est pas passé en tant que paramètre au constructeur Un nouveau paramètre de dictionnaire - artifacts - a des clés comme noms d’artefacts et des valeurs comme chemin d’accès dans le système de fichiers local où l’artefact est stocké
Le wrapper de modèle correspondant ressemblerait alors à ceci :
Python
from mlflow.pyfunc import PythonModel, PythonModelContext
classModelWrapper(PythonModel):defload_context(self, context: PythonModelContext):import pickle
from xgboost import XGBClassifier
from sklearn.preprocessing import OrdinalEncoder
self._encoder = pickle.loads(context.artifacts["encoder"])
self._model = XGBClassifier(use_label_encoder=False, eval_metric="logloss")
self._model.load_model(context.artifacts["model"])
defpredict(self, context: PythonModelContext, data):return self._model.predict_proba(data)
La routine d’entraînement complète ressemblerait à ceci :
Un modèle peut avoir une logique complexe ou charger plusieurs fichiers sources au moment de l’inférence. Cela se produit si vous disposez d’une bibliothèque Python pour votre modèle, par exemple. Dans ce scénario, vous devez empaqueter la bibliothèque en même temps que votre modèle afin de pouvoir le déplacer en tant que pièce unique.
Utilisez cette technique quand :
Vous ne pouvez pas sérialiser votre modèle au format Pickle, ou vous disposez d’un meilleur format de sérialisation disponible
Vous pouvez stocker vos artefacts de modèle dans un dossier qui stocke tous les artefacts nécessaires
Le code source de votre modèle présente une grande complexité et nécessite plusieurs fichiers Python. Potentiellement, une bibliothèque peut prendre en charge votre modèle
Vous souhaitez personnaliser la façon dont le modèle se charge et comment la fonction predict fonctionne
MLflow prend en charge ces modèles. Avec MLflow, vous pouvez spécifier n’importe quel code source arbitraire à empaqueter avec le modèle, tant qu’il dispose d’un module de chargeur. Vous pouvez spécifier des modules de chargeur dans l’instruction log_model() avec l’argument loader_module, qui indique l’espace de noms Python qui implémente le chargeur. L’argument code_path est également requis pour indiquer les fichiers sources qui définissent le loader_module. Dans cet espace de noms, vous devez implémenter une fonction _load_pyfunc(data_path: str) qui reçoit le chemin des artefacts et retourne un objet avec une prédiction de méthode (au moins).
Le modèle n’est pas enregistré en tant que pickle. Au lieu de cela, le code a enregistré le modèle avec la méthode d’enregistrement de l’infrastructure que vous avez utilisée
Nouveau paramètre - data_path - pointe vers le dossier qui contient les artefacts de modèle. Les artefacts peuvent être un dossier ou un fichier. Ces artefacts - un dossier ou un fichier - seront empaquetés avec le modèle
Nouveau paramètre - code_path - pointe vers l’emplacement du code source. Cette ressource à cet emplacement peut être un chemin d’accès ou un seul fichier. Cette ressource - un dossier ou un fichier - sera empaquetée avec le modèle
La fonction _load_pyfunc est stockée dans le module Python loader_module
Le dossier src contient le fichier loader_module.py. Ce fichier est le module du chargeur :
src/loader_module.py
Python
classMyModel():def__init__(self, model):
self._model = model
defpredict(self, data):return self._model.predict_proba(data)
def_load_pyfunc(data_path: str):import os
model = XGBClassifier(use_label_encoder=False, eval_metric='logloss')
model.load_model(os.path.abspath(data_path))
return MyModel(model)
Notes
La classe MyModel n’hérite pas de PythonModel indiqué précédemment. Toutefois, elle a une fonction predict
Le code source du modèle se trouve dans un fichier. Tout code source peut être utilisé. Un dossier src est idéal pour cela
Une fonction _load_pyfunc retourne une instance de la classe du modèle
Gérer l’ingestion et la préparation des données, l’entraînement et le déploiement des modèles, ainsi que la surveillance des solutions d’apprentissage automatique avec Python, Azure Machine Learning et MLflow.