Journalisation de modèles MLflow

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 :

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 :

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)

Remarque

  • 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.

Journalisation des modèles personnalisés

MLflow prend en charge de nombreuses infrastructures Machine Learning, notamment

  • CatBoost
  • FastAI
  • h2o
  • Keras
  • LightGBM
  • MLeap
  • MXNet Gluon
  • ONNX
  • Prophet
  • PyTorch
  • Scikit-Learn
  • spaCy
  • Spark MLLib
  • statsmodels
  • TensorFlow
  • XGBoost

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 :

from mlflow.pyfunc import PythonModel, PythonModelContext

class ModelWrapper(PythonModel):
    def __init__(self, model):
        self._model = model

    def predict(self, context: PythonModelContext, data):
        # You don't have to keep the semantic meaning of `predict`. You can use here model.recommend(), model.forecast(), etc
        return 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.
    def predict_batch(self, data):
        pass

Journaliser un modèle personnalisé dans l’exécution :

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.

Étapes suivantes