Snowpark ML Ops : registre des modèles

Note

L’API de registre de modèles décrite dans cette rubrique est disponible dans le paquet Snowpark ML à partir de la version 1.2.0.

Une partie de Snowpark ML Operations (MLOps), le registre des modèles de Snowpark, permet aux clients de gérer en toute sécurité les modèles et leurs métadonnées dans Snowflake, quelle que soit leur origine. Le registre des modèles de Snowpark stocke les modèles de machine learning en tant qu’objets de niveau schéma de première classe dans Snowflake afin qu’ils puissent être facilement trouvés et utilisés par d’autres personnes au sein de votre organisation. Vous pouvez créer des registres et y stocker des modèles en utilisant Snowpark ML. Les modèles peuvent avoir plusieurs versions et vous pouvez désigner une version comme étant la version par défaut.

Une fois que vous avez stocké un modèle, vous pouvez invoquer ses méthodes (équivalentes à des fonctions ou à des procédures stockées) pour effectuer des opérations sur le modèle, telles que l’inférence, dans un entrepôt virtuel de Snowflake.

Astuce

Voir Introduction au machine learning avec Snowpark ML pour un exemple de workflow de bout en bout dans Snowpark ML, y compris le registre des modèles.

Les trois objets principaux de l’API du registre des modèles de Snowpark sont les suivants sont :

  • snowflake.ml.registry.Registry : gère les modèles à l’intérieur d’un schéma.

  • snowflake.ml.model.Model : représente un modèle.

  • snowflake.ml.model.ModelVersion : représente une version d’un modèle.

Pour des informations détaillées sur ces classes, voir Référence de l’API Snowpark ML.

Le registre des modèles de Snowpark prend en charge les types de modèles suivants.

Cette rubrique décrit comment effectuer des opérations de registre en Python en utilisant Snowpark ML. Vous pouvez également effectuer de nombreuses opérations de registre dans SQL. Pour plus d’informations, voir Commandes du modèle.

Différences par rapport à l’avant-première privée

Snowflake a précédemment mis un registre de modèles à la disposition de certains clients à titre privé. La fonction de registre décrite dans cette rubrique présente des changements significatifs en termes de fonctionnalité et d’APIs par rapport à la version en avant-première privée. En particulier, la fonctionnalité de registre de modèles est désormais hébergée nativement dans Snowflake à l’aide d’un nouvel objet au niveau du schéma.

Note

Cette version en avant-première publique ne prend pas encore en charge le déploiement de modèles vers Snowpark Container Services (SPCS). Si vous comptez sur cette fonctionnalité, continuez pour l’instant à utiliser le registre disponible en avant-première privée.

Pour plus de détails sur les différences entre ces deux APIs, voir Snowpark ML Ops : migration à partir de l’API en avant-première du registre des modèles.

Privilèges requis

Pour créer un modèle, vous devez posséder le schéma dans lequel le modèle est créé ou avoir le privilège CREATE MODEL sur ce schéma. Pour utiliser un modèle, vous devez en être le propriétaire ou avoir le privilège USAGE sur ce modèle.

Ouverture du registre des modèles de Snowpark

Les modèles sont des objets Snowflake de première classe et peuvent être organisés au sein d’une base de données et d’un schéma avec d’autres objets Snowflake. Le registre des modèles de Snowpark est une classe Python permettant de gérer les modèles au sein d’un schéma. Ainsi, tout schéma Snowflake peut être utilisé comme registre. Il n’est pas nécessaire d’initialiser ou de préparer un schéma à cette fin. Snowflake recommande de créer un ou plusieurs schémas dédiés à cet effet, comme ML.REGISTRY. Vous pouvez créer le schéma en utilisant CREATE SCHEMA.

Avant de pouvoir créer ou modifier des modèles dans le registre, vous devez ouvrir le registre. L’ouverture du registre renvoie une référence à celui-ci, que vous pouvez ensuite utiliser pour ajouter de nouveaux modèles et obtenir des références à des modèles existants.

from snowflake.ml.registry import Registry

reg = Registry(session=sp_session, database_name="ML", schema_name="REGISTRY")
Copy

Note

Au cours de cette avant-première publique, les objets Model ne prennent pas en charge la réplication ou le clonage.

Enregistrement des modèles et des versions

L’ajout d’un modèle au registre s’appelle la journalisation du modèle. Enregistrer un modèle en appelant la méthode log_model du registre. Cette méthode :

  • Sérialise le modèle, un objet Python et crée un objet modèle Snowflake à partir de celui-ci.

  • Ajoute des métadonnées, telles qu’une description, au modèle tel que spécifié dans l’appel log_model.

Note

Vous ne pouvez pas ajouter de balises à un modèle lorsqu’il est ajouté au registre, car les balises sont des attributs du modèle, et log_model ajoute une version spécifique du modèle. Vous pouvez mettre à jour les balises du modèle après avoir journalisé la première version du modèle.

Chaque modèle peut avoir un nombre quelconque de versions. Pour enregistrer d’autres versions du modèle, appelez à nouveau log_model avec le même model_name mais un version_name différent.

Dans l’exemple ci-dessous, clf, abréviation de « classificateur », est l’objet modèle Python, qui a déjà été créé ailleurs dans votre code. Vous pouvez ajouter un commentaire et des balises au moment de l’enregistrement, comme indiqué ici. La combinaison du nom et de la version doit être unique dans le schéma. Vous pouvez spécifier des listes de conda_dependencies ; les paquets spécifiés seront déployés avec le modèle.

mv = reg.log_model(clf,
                   model_name="my_model",
                   version_name="1",
                   conda_dependencies=["scikit-learn"],
                   comment="My awesome ML model",
                   metrics={"score": 96},
                   sample_input_data=train_features)
Copy

Les arguments de log_model sont décrits ici.

Arguments requis

Argument

Description

model

L’objet modèle Python d’un type de modèle pris en charge. Doit être sérialisable (« picklable »).

model_name

Le nom du modèle, utilisé avec version_name pour identifier le modèle dans le registre. Le nom ne peut pas être modifié une fois que le modèle a été journalisé.

version_name

Chaîne spécifiant la version du modèle, utilisée avec model_name pour identifier le modèle dans le registre.

Note

La combinaison du nom du modèle et de la version doit être unique dans le schéma.

Arguments facultatifs

Argument

Description

code_paths

Répertorie des chemins d’accès aux répertoires de code à importer lors du chargement ou du déploiement du modèle.

comment

Commentaire, par exemple une description du modèle.

conda_dependencies

Liste des paquets Conda requis par votre modèle. Cet argument spécifie les noms des paquets et les versions optionnelles au format Conda, c’est-à-dire "[channel::]package [operator version]". Si vous ne spécifiez pas de canal, le canal Snowflake est utilisé.

ext_modules

Liste des modules externes à associer au modèle. Pris en charge par scikit-learn, Snowpark ML, PyTorch, TorchScript et les modèles personnalisés.

metrics

Dictionnaire contenant les métriques liées à la version du modèle.

options

Dictionnaire contenant des options pour la création de modèles. Les options suivantes sont disponibles pour tous les types de modèles :

  • embed_local_ml_library : intégrer ou non une copie de la bibliothèque locale de Snowpark ML dans le modèle. Par défaut : False.

  • relax_version : savoir s’il faut relâcher les contraintes de version des dépendances. Cela remplace les spécificateurs de version tels que ==x.y.z par des spécificateurs tels que <=x.y, <(x+1). Par défaut : False. Disponible depuis la version 1.2.1 du paquet Snowpark ML.

  • method_options : un dictionnaire d’options par méthode, où la clé est le nom d’une méthode et la valeur est un dictionnaire contenant une ou plusieurs des options décrites ici. Les options disponibles sont les suivantes :

    • case_sensitive : indique si la méthode et sa signature sont sensibles à la casse. Les méthodes sensibles à la casse doivent être mises entre guillemets lorsqu’elles sont utilisées en SQL. Cette option permet également d’utiliser des caractères non alphabétiques dans les noms de méthodes. Par défaut : False.

    • max_batch_size : taille maximale du lot que la méthode acceptera lorsqu’elle sera appelée dans l’entrepôt. Par défaut : None (la taille du lot est automatiquement déterminée)

Certains types de modèles peuvent proposer des options supplémentaires. Voir Remarques sur les types de modèles spécifiques.

pip_requirements

Liste des spécifications des paquets pour les paquets PyPI requis par votre modèle.

python_version

La version de Python dans laquelle le modèle sera exécuté. La valeur par défaut est None, qui désigne la dernière version disponible dans l’entrepôt.

sample_input_data

Un DataFrame contenant un échantillon de données d’entrée. Les noms des fonctions requises par le modèle, ainsi que leurs types, sont extraits de cette DataFrame. Cet argument ou signatures doit être fourni pour tous les modèles à l’exception des modèles Snowpark ML et MLFlow.

signatures

Signatures des méthodes du modèle en tant que mappage entre le nom de la méthode cible et les signatures d’entrée et de sortie. Cet argument ou sample_input_data doit être fourni pour tous les modèles à l’exception des modèles Snowpark ML et MLFlow.

log_model renvoie un objet snowflake.ml.model.ModelVersion qui représente la version du modèle qui a été ajoutée au registre.

Une fois enregistré, le modèle lui-même ne peut pas être modifié (bien que vous puissiez modifier ses métadonnées). Pour supprimer un modèle et toutes ses versions, utilisez la méthode delete_model du registre.

Suppression de modèles

Utilisez la méthode delete_model du registre pour supprimer un modèle et toutes ses versions.

reg.delete_model("mymodel")
Copy

Obtention de modèles à partir du registre

Pour obtenir des informations sur chaque modèle, utilisez la méthode show_models.

model_df = reg.show_models()
Copy

Le résultat de show_models est un DataFrame pandas. Les colonnes disponibles sont indiquées ci-dessous.

Colonne

Description

created_on

Date et heure de création du modèle.

name

Nom du modèle.

database_name

Base de données dans laquelle le modèle est stocké.

schema_name

Schéma dans lequel le modèle est stocké.

propriétaire

Rôle qui possède le modèle.

commentaire

Commentaire pour le rôle.

versions

Le tableau JSON qui répertorie les versions du modèle.

nom_de_la_version_par_défaut

Version du modèle utilisée lorsqu’il est fait référence au modèle sans version.

Pour obtenir une liste des modèles dans le registre, chacun en tant qu’instance Model utilisez la méthode models.

model_list = reg.models()
Copy

Pour obtenir une référence à un modèle spécifique du registre par son nom, utilisez la méthode get_model du registre. Cette méthode renvoie une instance Model.

m = reg.get_model("MyModel")
Copy

Note

Model ne sont pas des copies de l’objet modèle Python d’origine enregistré, mais des références à l’objet modèle sous-jacent dans le registre.

Une fois que vous avez une référence à un modèle, soit dans la liste renvoyée par la méthode models soit en utilisant get_model, vous pouvez travailler avec ses métadonnées et ses versions.

Visualisation et mise à jour des métadonnées d’un modèle

Vous pouvez consulter et mettre à jour les attributs de métadonnées d’un modèle dans le registre, y compris son commentaire, ses balises et ses métriques.

Récupération et mise à jour des commentaires

Utilisez l’attribut comment du modèle pour récupérer et mettre à jour le commentaire du modèle.

print(m.comment)
m.comment = "A better description than the one I provided originally"
Copy

Note

L’attribut description est un alias pour comment. Le code ci-dessus peut également être écrit :

print(m.description)
m.description = "A better description than the one I provided originally"
Copy

Récupération et mise à jour des balises

Les balises sont des métadonnées utilisées pour enregistrer l’objectif, l’algorithme, l’ensemble de données d’entraînement, l’étape du cycle de vie d’un modèle ou toute autre information que vous choisissez. Vous pouvez définir des balises lors de l’enregistrement du modèle ou à tout moment par la suite. Vous pouvez également mettre à jour les valeurs des balises existantes ou les supprimer complètement.

Note

Les noms de toutes les balises (et potentiellement leurs valeurs possibles) doivent être définis à l’avance à l’aide de CREATE TAG. Voir Balisage d’objets.

Pour obtenir toutes les balises d’un modèle sous la forme d’un dictionnaire Python, utilisez show_tags.

print(m.show_tags())
Copy

Pour ajouter une nouvelle balise ou modifier la valeur d’une balise existante, utilisez set_tag.

m.set_tag("live_version", "1")
Copy

Pour récupérer la valeur d’une balise, utilisez get_tag :

m.get_tag("live_version")
Copy

Pour supprimer une balise, utilisez unset_tag.

model.unset_tag("live_version")
Copy

Utilisation de versions de modèles

Un modèle peut avoir un nombre quelconque de versions, chacune étant identifiée par une chaîne de caractères. Vous pouvez utiliser n’importe quelle convention de dénomination de version. L’enregistrement d’un modèle enregistre en fait une version spécifique du modèle. Pour enregistrer d’autres versions d’un modèle, appelez à nouveau log_model avec le même model_name mais un version_name différent.

Une version d’un modèle est représentée par une instance de la classe snowflake.ml.model.ModelVersion.

Pour obtenir la liste de toutes les versions d’un modèle, appelez la méthode versions de l’objet du modèle. Le résultat est une liste d’instances ModelVersion.

version_list = m.versions()
Copy

Pour obtenir des informations sur chaque modèle sous la forme d’un DataFrame, appelez la méthode show_versions du modèle.

version_df = m.show_versions()
Copy

Le résultat DataFrame contient les colonnes suivantes.

Colonne

Description

created_on

Date et heure de création de la version du modèle.

name

Nom de la version.

database_name

Base de données dans laquelle la version est stockée.

schema_name

Schéma dans lequel la version est stockée.

model_name

Nom du modèle auquel cette version appartient.

is_default_version

Valeur booléenne indiquant si cette version est la version par défaut du modèle.

functions

Tableau JSON des noms des fonctions disponibles dans cette version.

metadata

Objet JSON contenant des métadonnées sous forme de paires clé-valeur ({} si aucune métadonnée n’est spécifiée).

user_data

Objet JSON de la section user_data du manifeste de définition du modèle ({} si aucune donnée utilisateur n’est spécifiée).

Suppression des versions du modèle

Vous pouvez supprimer une version de modèle en utilisant la méthode delete_version du modèle.

m.delete_version("rc1")
Copy

Version par défaut

Une version d’un modèle peut être désignée comme modèle par défaut. Récupérez ou définissez l’attribut default du modèle pour obtenir la version actuelle par défaut (sous la forme d’un objet ModelVersion) ou pour la modifier (à l’aide d’une chaîne).

default_version = m.default
m.default = "2"
Copy

Obtention d’une référence à une version de modèle

Pour obtenir une référence à une version spécifique d’un modèle en tant qu’instance ModelVersion , utilisez la méthode version du modèle. Utilisez l’attribut default du modèle pour obtenir la version par défaut du modèle.

mv = m.version("1")
mv = m.default
Copy

Une fois que vous avez une référence à une version spécifique d’un modèle (comme la variable mv dans notre exemple ci-dessus), vous pouvez récupérer ou mettre à jour ses commentaires ou ses métriques et appeler les méthodes (fonctions) du modèle comme indiqué dans les sections suivantes.

Récupération et mise à jour des commentaires

Comme pour les modèles, les versions de modèle peuvent avoir des commentaires, qui peuvent être consultés et définis via l’attribut comment ou description de la version de modèle.

print(mv.comment)
print(mv.description)

mv.comment = "A model version comment"
mv.description = "Same as setting the comment"
Copy

Récupération et mise à jour des métriques

Les métriques sont des paires clé-valeur utilisées pour suivre la précision de la prédiction et d’autres caractéristiques de la version du modèle. Vous pouvez définir des métriques lors de la création d’une version de modèle ou en utilisant la méthode set_metric. Une valeur métrique peut être n’importe quel objet Python qui peut être sérialisé en JSON, y compris les nombres, les chaînes, les listes et les dictionnaires. Contrairement aux balises, les noms des métriques et les valeurs possibles ne doivent pas être définis à l’avance.

Une mesure de la précision du test peut être générée à l’aide de accuracy_score de sklearn :

from sklearn import metrics

test_accuracy = metrics.accuracy_score(test_labels, prediction)
Copy

La matrice de confusion peut être générée de la même manière à l’aide de sklearn :

test_confusion_matrix = metrics.confusion_matrix(test_labels, prediction)
Copy

Nous pouvons ensuite définir ces valeurs en tant que métriques comme suit.

# scalar metric
mv.set_metric("test_accuracy", test_accuracy)

# hierarchical (dictionary) metric
mv.set_metric("evaluation_info", {"dataset_used": "my_dataset", "accuracy": test_accuracy, "f1_score": f1_score})

# multivalent (matrix) metric
mv.set_metric("confusion_matrix", test_confusion_matrix)
Copy

Pour récupérer les métriques d’une version de modèle sous la forme d’un dictionnaire Python, utilisez show_metrics.

metrics = mv.show_metrics()
Copy

Pour supprimer une métrique, appelez delete_metric.

mv.delete_metric("test_accuracy")
Copy

Appel de méthodes de modèle

Les versions de modèle peuvent avoir des méthodes, qui sont des fonctions associées pouvant être exécutées pour effectuer une inférence ou d’autres opérations sur le modèle. Les versions d’un modèle peuvent avoir des méthodes différentes, et les signatures de ces méthodes peuvent également différer.

Pour appeler une méthode d’une version de modèle, utilisez mv.run, en spécifiant le nom de la fonction à appeler et en transmettant un DataFrame contenant les données d’inférence et tout autre paramètre requis. La méthode s’exécute dans un entrepôt Snowflake.

Note

L’invocation d’une méthode l’exécute dans l’entrepôt spécifié dans la session que vous utilisez pour vous connecter au registre. Voir Spécification d’un entrepôt..

L’exemple suivant illustre l’exécution de la méthode predict d’un modèle. La méthode predict de ce modèle ne nécessite aucun paramètre en dehors des données d’inférence (test_features ici). Si c’était le cas, ils seraient transmis en tant qu’arguments supplémentaires après les données d’inférence.

remote_prediction = mv.run(test_features, function_name="predict")
Copy

Pour voir quelles méthodes peuvent être appelées sur un modèle donné, appelez mv.show_functions. La valeur de retour de cette méthode est une liste d’objets ModelFunctionInfo. Chacun de ces objets comprend les attributs suivants :

  • name : Le nom de la fonction qui peut être appelée depuis Python ou SQL.

  • target_method : Le nom de la méthode d’origine dans le modèle Python d’origine journalisé.

Utilisation du registre de modèles dans SQL

Les modèles étant des objets de première classe au niveau du schéma, Snowflake SQL propose des commandes pour travailler avec eux. Les voici :

Note

Bien que Snowflake SQL comprenne des commandes pour créer des modèles et des versions, celles-ci sont destinées à être utilisées par l’API Python du registre des modèles de Snowpark. Modèles de journalisation Python comme indiqué dans Enregistrement des modèles et des versions.

Appel des méthodes de modèles en SQL

Vous pouvez appeler ou invoquer les méthodes d’un modèle en SQL en utilisant la syntaxe model_name!method_name(...). Les méthodes disponibles sur un modèle sont déterminées par la classe de modèle Python sous-jacente. Par exemple, de nombreux types de modèles utilisent une méthode appelée predict pour l’inférence.

Pour invoquer une méthode de la dernière version d’un modèle, utilisez la syntaxe présentée ici, en transmettant les arguments à la méthode, le cas échéant, entre parenthèses, et en transmettant le nom de la table contenant les données d’inférence dans la clause FROM.

SELECT <model_name>!<method_name>(...) FROM <table_name>;
Copy

Pour invoquer une méthode d’une version spécifique d’un modèle, il faut d’abord créer un alias vers la version spécifique du modèle, puis invoquer la méthode souhaitée par l’intermédiaire de l’alias.

WITH <model_version_alias> AS MODEL <model_name> VERSION <version>
    SELECT <model_version_alias>!<method_name>(...) FROM <table_name>;
Copy

Considérations relatives aux clients

L’utilisation du registre des modèles ML de Snowpark engendre des coûts standard basés sur la consommation Snowflake. Il s’agit notamment des éléments suivants :

  • Coût du stockage des artefacts, des métadonnées et des fonctions du modèle. Voir Exploration des coûts de stockage pour des informations générales sur les coûts de stockage.

  • Coût de la copie des fichiers entre les zones de préparation et Snowflake. Voir COPY FILES.

  • Coût des opérations sur les objets de modèle sans serveur via l’interface Snowsight UI ou SQL ou Python, telles que l’affichage des modèles et des versions de modèle, et la modification des commentaires, des balises et des métriques du modèle.

  • Les coûts de calcul d’entrepôts varient en fonction du type de modèle et de la quantité de données utilisées pour l’inférence. Voir Comprendre le coût du calcul pour obtenir des informations générales sur les coûts de calcul de Snowflake. Les coûts de calcul de l’entrepôt sont encourus pour :

    • Opérations de création de modèles et de versions.

    • Invocation de méthodes d’un modèle.

Remarques sur les types de modèles spécifiques

Cette section fournit des informations supplémentaires sur l’enregistrement de certains types de modèles dans le registre des modèles de Snowpark.

ML Snowpark

Le registre prend en charge les modèles créés à l’aide des APIs Snowpark ML modeling (modèles dérivés de snowpark.ml.modeling.framework.base.BaseEstimator). Les options supplémentaires suivantes peuvent être utilisées dans le dictionnaire options lors de l’appel à log_model.

Option

Description

target_methods

Une liste des noms des méthodes disponibles sur l’objet modèle. Les modèles Snowpark ML ont les méthodes cibles suivantes par défaut, en supposant que la méthode existe : predict, transform, predict_proba, predict_log_proba, decision_function

Il n’est pas nécessaire de spécifier sample_input_data ou signatures lors de l’enregistrement d’un modèle Snowpark ML ; ces éléments sont automatiquement déduits lors de l’ajustement.

Exemple

import pandas as pd
import numpy as np
from sklearn import datasets
from snowflake.ml.modeling.xgboost import XGBClassifier

iris = datasets.load_iris()
df = pd.DataFrame(data=np.c_[iris["data"], iris["target"]], columns=iris["feature_names"] + ["target"])
df.columns = [s.replace(" (CM)", "").replace(" ", "") for s in df.columns.str.upper()]

input_cols = ["SEPALLENGTH", "SEPALWIDTH", "PETALLENGTH", "PETALWIDTH"]
label_cols = "TARGET"
output_cols = "PREDICTED_TARGET"

clf_xgb = XGBClassifier(
        input_cols=input_cols, output_cols=output_cols, label_cols=label_cols, drop_input_cols=True
)
clf_xgb.fit(df)
model_ref = registry.log_model(
    clf_xgb,
    model_name="XGBClassifier",
    version_name="v1",
)
model_ref.run(df.drop(columns=label_cols).head(10), function_name='predict_proba')
Copy

scikit-learn

Le registre prend en charge les modèles créés à l’aide de scikit-learn (modèles dérivés de sklearn.base.BaseEstimator ou sklearn.pipeline.Pipeline). Les options supplémentaires suivantes peuvent être utilisées dans le dictionnaire options lors de l’appel à log_model.

Option

Description

target_methods

Une liste des noms des méthodes disponibles sur l’objet modèle. Les modèles scikit-learn ont les méthodes cibles suivantes par défaut, en supposant que la méthode existe : predict, transform, predict_proba, predict_log_proba, decision_function

Vous devez spécifier le paramètre sample_input_data ou signatures lorsque vous enregistrez un modèle scikit-learn afin que le registre connaisse les signatures des méthodes cibles.

Exemple

from sklearn import datasets, ensemble

iris_X, iris_y = datasets.load_iris(return_X_y=True, as_frame=True)
model = ensemble.RandomForestClassifier(random_state=42)
model.fit(iris_X, iris_y)
model_ref = registry.log_model(
    model,
    model_name="RandomForestClassifier",
    version_name="v1",
    sample_input_data=iris_X,
    options={
        "method_options": {
            "predict": {"case_sensitive": True},
            "predict_proba": {"case_sensitive": True},
            "predict_log_proba": {"case_sensitive": True},
        }
    },
)
model_ref.run(iris_X[-10:], function_name='"predict_proba"')
Copy

XGBoost

Le registre prend en charge les modèles créés à l’aide de XGBoost (modèles dérivés de xgboost.XGBModel ou xgboost.Booster). Les options supplémentaires suivantes peuvent être utilisées dans le dictionnaire options lors de l’appel à log_model.

Option

Description

target_methods

Une liste des noms des méthodes disponibles sur l’objet modèle. Les modèles dérivés de XGBModel ont les méthodes cibles suivantes par défaut, en supposant que la méthode existe : predict, predict_proba, apply. Les modèles dérivés de Booster utilisent par défaut la méthode predict.

cuda_version

La version de l’environnement d’exécution CUDA à utiliser lors du déploiement sur une plateforme avec GPU ; la valeur par défaut est 11.7. S’il est défini manuellement sur None, le modèle ne peut pas être déployé sur une plateforme ayant un GPU.

Vous devez spécifier le paramètre sample_input_data ou signatures lors de l’enregistrement d’un modèle XGBoost afin que le registre connaisse les signatures des méthodes cibles.

Exemple

import xgboost
from sklearn import datasets, model_selection

cal_X, cal_y = datasets.load_breast_cancer(as_frame=True, return_X_y=True)
cal_X_train, cal_X_test, cal_y_train, cal_y_test = model_selection.train_test_split(cal_X, cal_y)
params = dict(n_estimators=100, reg_lambda=1, gamma=0, max_depth=3, objective="binary:logistic")
regressor = xgboost.train(params, xgboost.DMatrix(data=cal_X_train, label=cal_y_train))
model_ref = registry.log_model(
    regressor,
    model_name="xgBooster",
    version_name="v1",
    sample_input_data=cal_X_test,
    options={
        "target_methods": ["predict"],
        "method_options": {
            "predict": {"case_sensitive": True},
        },
    },
)
model_ref.run(cal_X_test[-10:])
Copy

PyTorch

Le registre prend en charge les modèles PyTorch (classes dérivées de torch.nn.Module ou torch.jit.ModuleScript) si la méthode forward du modèle accepte une ou plusieurs instances torch.Tensor en entrée et renvoie un torch.Tensor ou un tuple de celles-ci. Le registre convertit les DataFrames pandas et les tenseurs lors de l’appel du modèle et du renvoi des résultats. Les tenseurs correspondent aux colonnes du dataframe.

Par exemple, supposons que votre modèle accepte deux tenseurs comme suit :

import torch

class TorchModel(torch.nn.Module):
    def __init__(self, n_input: int, n_hidden: int, n_out: int, dtype: torch.dtype = torch.float32) -> None:
        super().__init__()
        self.model = torch.nn.Sequential(
            torch.nn.Linear(n_input, n_hidden, dtype=dtype),
            torch.nn.ReLU(),
            torch.nn.Linear(n_hidden, n_out, dtype=dtype),
            torch.nn.Sigmoid(),
        )

    def forward(self, tensor_1: torch.Tensor, tensor_2: torch.Tensor) -> torch.Tensor:
        return self.model(tensor_1) + self.model(tensor_2)
Copy

Si vous souhaitez transmettre torch.Tensor([[1,2],[3,4]]) en tant que tensor_1 et torch.Tensor([[5,6], [7,8]]) en tant que tensor_2, créez un DataFrame comme suit pour le transmettre au modèle.

import pandas as pd
tensors = pd.DataFrame([[[1,2],[5,6]],[[3,4],[7,8]]])
Copy

Le DataFrame de tensors ressemble alors à ceci.

        0       1
0  [1, 2]  [5, 6]
1  [3, 4]  [7, 8]
Copy

De même, si votre modèle renvoie deux tenseurs, tels que (torch.Tensor([[1,2],[3,4]]), torch.Tensor([[5,6], [7,8]])), le résultat est un DataFrame comme celui ci-dessus.

Lorsque vous fournissez un échantillon de données d’entrée pour un modèle PyTorch, vous devez fournir soit une liste de tenseurs (qui sera convertie en un DataFrame pandas), soit un DataFrame. Une liste peut contenir un seul tenseur, mais un tenseur seul n’est pas accepté.

Journalisation du modèle

Les options supplémentaires suivantes peuvent être utilisées dans le dictionnaire options lors de l’appel à log_model.

Option

Description

target_methods

Une liste des noms des méthodes disponibles sur l’objet modèle. Les modèles PyTorch sont définis par défaut sur forward.

cuda_version

La version de l’environnement d’exécution CUDA à utiliser lors du déploiement sur une plateforme avec GPU ; la valeur par défaut est 11.7. S’il est défini manuellement sur None, le modèle ne peut pas être déployé sur une plateforme ayant un GPU.

Vous devez spécifier le paramètre sample_input_data ou signatures lors de la journalisation d’un modèle PyTorch afin que le registre connaisse les signatures des méthodes cibles.

Exemple

import torch
import numpy as np

class TorchModel(torch.nn.Module):
        def __init__(self, n_input: int, n_hidden: int, n_out: int, dtype: torch.dtype = torch.float32) -> None:
                super().__init__()
                self.model = torch.nn.Sequential(
                        torch.nn.Linear(n_input, n_hidden, dtype=dtype),
                        torch.nn.ReLU(),
                        torch.nn.Linear(n_hidden, n_out, dtype=dtype),
                        torch.nn.Sigmoid(),
                )

        def forward(self, tensor: torch.Tensor) -> torch.Tensor:
                return self.model(tensor)

n_input, n_hidden, n_out, batch_size, learning_rate = 10, 15, 1, 100, 0.01
dtype = torch.float32
x = np.random.rand(batch_size, n_input)
data_x = torch.from_numpy(x).to(dtype=dtype)
data_y = (torch.rand(size=(batch_size, 1)) < 0.5).to(dtype=dtype)

model = TorchModel(n_input, n_hidden, n_out, dtype=dtype)
loss_function = torch.nn.MSELoss()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)
for _epoch in range(100):
        pred_y = model.forward(data_x)
        loss = loss_function(pred_y, data_y)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()


model_ref = registry.log_model(
    model,
    model_name="torchModel",
    version_name="v1",
    sample_input_data=[data_x],
)
model_ref.run([data_x])
Copy

TensorFlow

Les modèles qui étendent tensorflow.Module ou tensorflow.keras.Model sont pris en charge lorsqu’ils acceptent et renvoient des tenseurs et qu’ils sont compilables ou compilés.

  • La méthode __call__ pour un tensorflow.Module ou la méthode call pour un tensorflow.keras.Model accepte un ou plusieurs tensorflow.Tensor ou tensorflow.Variable en entrée et renvoie un tensorflow.Tensor ou tensorflow.Variable ou un tuple de l’un de ces types.

  • Si votre modèle étend Module, il doit être compilable, ce qui signifie que la méthode __call__ est décorée par @tensorflow.function ; voir la documentation tf.function. S’il étend Model, il doit être compilé ; voir la documentation de compilation.

Le registre convertit les DataFrames pandas et les tenseurs lors de l’appel du modèle et du renvoi des résultats. Les tenseurs correspondent aux colonnes du dataframe.

Par exemple, supposons que votre modèle accepte deux tenseurs comme suit :

import tensorflow as tf

class KerasModel(tf.keras.Model):
    def  __init__(self, n_hidden: int, n_out: int) -> None:
        super().__init__()
        self.fc_1 = tf.keras.layers.Dense(n_hidden, activation="relu")
        self.fc_2 = tf.keras.layers.Dense(n_out, activation="sigmoid")

    def call(self, tensor_1: tf.Tensor, tensor_2: tf.Tensor) -> tf.Tensor:
        input = tensor_1 + tensor_2
        x = self.fc_1(input)
        x = self.fc_2(x)
        return x
Copy

Si vous souhaitez transmettre tf.Tensor([[1,2],[3,4]]) en tant que tensor_1 et tf.Tensor([[5,6], [7,8]]) en tant que tensor_2, créez un DataFrame comme suit pour le transmettre au modèle.

import pandas as pd
tensors = pd.DataFrame([[[1,2],[5,6]],[[3,4],[7,8]]])
Copy

Le DataFrame de tensors ressemble alors à ceci.

        0       1
0  [1, 2]  [5, 6]
1  [3, 4]  [7, 8]
Copy

De même, si votre modèle renvoie deux tenseurs, tels que (tf.Tensor([[1,2],[3,4]]), tf.Tensor([[5,6], [7,8]])), le résultat est un DataFrame comme celui ci-dessus.

Lorsque vous fournissez un échantillon de données d’entrée pour un modèle TensorFlow, vous devez fournir soit une liste de tenseurs (qui sera convertie en un DataFrame pandas), soit un DataFrame. Une liste peut contenir un seul tenseur, mais un tenseur seul n’est pas accepté.

Journalisation du modèle

Les options supplémentaires suivantes peuvent être utilisées dans le dictionnaire options lors de l’appel à log_model.

Option

Description

target_methods

Une liste des noms des méthodes disponibles sur l’objet modèle. Les modèles TensorFlow sont définis par défaut sur forward.

cuda_version

La version de l’environnement d’exécution CUDA à utiliser lors du déploiement sur une plateforme avec GPU ; la valeur par défaut est 11.7. S’il est défini manuellement sur None, le modèle ne peut pas être déployé sur une plateforme ayant un GPU.

Vous devez spécifier le paramètre sample_input_data ou signatures lors de la journalisation d’un modèle TensorFlow afin que le registre connaisse les signatures des méthodes cibles.

Exemple

import tensorflow as tf
import numpy as np

class KerasModel(tf.keras.Model):
        def __init__(self, n_hidden: int, n_out: int) -> None:
                super().__init__()
                self.fc_1 = tf.keras.layers.Dense(n_hidden, activation="relu")
                self.fc_2 = tf.keras.layers.Dense(n_out, activation="sigmoid")

        def call(self, tensor: tf.Tensor) -> tf.Tensor:
                input = tensor
                x = self.fc_1(input)
                x = self.fc_2(x)
                return x

n_input, n_hidden, n_out, batch_size, learning_rate = 10, 15, 1, 100, 0.01
dtype = tf.float32
x = np.random.rand(batch_size, n_input)
data_x = tf.convert_to_tensor(x, dtype=dtype)
raw_data_y = tf.random.uniform((batch_size, 1))
raw_data_y = tf.where(raw_data_y > 0.5, tf.ones_like(raw_data_y), tf.zeros_like(raw_data_y))
data_y = tf.cast(raw_data_y, dtype=dtype)

model = KerasModel(n_hidden, n_out)
model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=learning_rate), loss=tf.keras.losses.MeanSquaredError())
model.fit(data_x, data_y, batch_size=batch_size, epochs=100)

model_ref = registry.log_model(
    model,
    model_name="tfModel",
    version_name="v1",
    sample_input_data=[data_x],
)
model_ref.run([data_x])
Copy

MLFlow

Modèles MLFlow qui fournissent une valeur PyFunc sont pris en charge. Si votre modèle MLFlow possède une signature, l’argument signature est déduit du modèle. Sinon, vous devez fournir soit signature soit sample_input_data.

Les options supplémentaires suivantes peuvent être utilisées dans le dictionnaire options lors de l’appel à log_model.

Option

Description

model_uri

L’URI des artefacts du modèle MLFlow. Doit être fourni s’il n’est pas disponible dans les métadonnées du modèle sous la forme model.metadata.get_model_info().model_uri.

ignore_mlflow_metadata

Si True, les métadonnées du modèle ne sont pas importées dans l’objet modèle du registre. Par défaut : False

ignore_mlflow_dependencies

Si True, les dépendances dans les métadonnées du modèle sont ignorées, ce qui est utile en raison des limitations des paquets disponibles dans les entrepôts Snowflake. Par défaut : False

Exemple

import mlflow
from sklearn import datasets, model_selection, ensemble

db = datasets.load_diabetes(as_frame=True)
X_train, X_test, y_train, y_test = model_selection.train_test_split(db.data, db.target)
with mlflow.start_run() as run:
    rf = ensemble.RandomForestRegressor(n_estimators=100, max_depth=6, max_features=3)
    rf.fit(X_train, y_train)

    # Use the model to make predictions on the test dataset.
    predictions = rf.predict(X_test)
    signature = mlflow.models.signature.infer_signature(X_test, predictions)
    mlflow.sklearn.log_model(
        rf,
        "model",
        signature=signature,
    )
    run_id = run.info.run_id


model_ref = registry.log_model(
    mlflow.pyfunc.load_model(f"runs:/{run_id}/model"),
    model_name="mlflowModel",
    version_name="v1",
    conda_dependencies=["mlflow<=2.4.0", "scikit-learn", "scipy"],
    options={"ignore_mlflow_dependencies": True}
)
model_ref.run(X_test)
Copy

Pipeline Hugging Face

Le registre prend en charge les classes du modèle Hugging Face définies comme des transformateurs qui dérivent de transformers.Pipeline. Le code suivant est un exemple d’enregistrement d’un modèle compatible.

lm_hf_model = transformers.pipeline(
    task="text-generation",
    model="bigscience/bloom-560m",
    token="...",  # Put your HuggingFace token here.
    return_full_text=False,
    max_new_tokens=100,
)

lmv = reg.log_model(lm_hf_model, model_name='bloom', version_name='v560m')
Copy

Important

Un modèle basé sur huggingface_pipeline.HuggingFacePipelineModel ne contient que des données de configuration ; les poids du modèle sont téléchargés à partir du Hugging Face Hub chaque fois que le modèle est utilisé.

Le registre des modèles ne permet actuellement de déployer des modèles que dans les entrepôts. Les entrepôts ne prennent pas en charge l’accès au réseau externe sans une configuration spéciale. Même si les intégrations d’accès externed requises ont été créées, il n’est pas possible pour l’instant de spécifier les intégrations dont un modèle particulier a besoin.

La meilleure pratique actuelle consiste à la place à utiliser transformers.Pipeline comme indiqué dans l’exemple ci-dessus. Ce système télécharge les poids des modèles dans votre système local et télécharge le modèle complet dans l’entrepôt. Il en résulte un modèle autonome qui ne nécessite pas d’accès à Internet.

Le registre infère l’argument signatures tant que le pipeline ne contient que des tâches de la liste suivante.

  • conversational

  • fill-mask

  • question-answering

  • summarization

  • table-question-answering

  • text2text-generation

  • text-classification (alias sentiment-analysis)

  • text-generation

  • token-classification (alias ner)

  • translation

  • translation_xx_to_yy

  • zero-shot-classification

Pour voir la signature déduite, utilisez la méthode show_functions. Ce qui suit, par exemple, est le résultat de lmv.show_functions()lmv est le modèle journalisé ci-dessus.

{'name': '__CALL__',
  'target_method': '__call__',
  'signature': ModelSignature(
                      inputs=[
                          FeatureSpec(dtype=DataType.STRING, name='inputs')
                      ],
                      outputs=[
                          FeatureSpec(dtype=DataType.STRING, name='outputs')
                      ]
                  )}]
Copy

Avec ces informations, vous pouvez appeler le modèle comme suit.

import pandas as pd
remote_prediction = lmv.run(pd.DataFrame(["Hello, how are you?"], columns=["inputs"]))
Copy

Notes sur l’utilisation

  • De nombreux modèles de visages étreints sont de grande taille et ne rentrent pas dans un entrepôt standard. Utilisez un entrepôt optimisé pour Snowpark ou choisissez une version plus petite du modèle. Par exemple, au lieu d’utiliser le modèle Llama-2-70b-chat-hf , essayez Llama-2-7b-chat-hf.

  • Les entrepôts Snowflake n’ont pas de GPUs. N’utilisez que des modèles Hugging Face optimisés CPU.

  • Certains transformateurs Hugging Face renvoient un tableau de dictionnaires par ligne d’entrée. Le registre convertit cette sortie en une chaîne de caractères contenant une représentation JSON du tableau. Par exemple, la sortie questions-réponses multi-sorties se présente comme suit :

    [{"score": 0.61094731092453, "start": 139, "end": 178, "answer": "learn more about the world of athletics"},
    {"score": 0.17750297486782074, "start": 139, "end": 180, "answer": "learn more about the world of athletics.\""}]
    
    Copy

Vous devez spécifier le paramètre sample_input_data ou signatures lors de l’enregistrement d’un modèle Hugging Face afin que le registre connaisse les signatures des méthodes cibles.

Exemple

# Prepare model
import transformers
import pandas as pd

finbert_model = transformers.pipeline(
    task="text-classification",
    model="ProsusAI/finbert",
    top_k=2,
)

# Log the model
mv = registry.log_model(
    finbert_model,
    model_name="finbert",
    version_name="v1",
)

# Use the model
mv.run(pd.DataFrame(
        [
            ["I have a problem with my Snowflake that needs to be resolved asap!!", ""],
            ["I would like to have udon for today's dinner.", ""],
        ]
    )
)
Copy

Résultat :

0  [{"label": "negative", "score": 0.8106237053871155}, {"label": "neutral", "score": 0.16587384045124054}]
1  [{"label": "neutral", "score": 0.9263970851898193}, {"label": "positive", "score": 0.05286872014403343}]
Copy