Registre des modèles de Snowflake¶
Note
L’API de registre des modèles décrite dans cette rubrique est généralement disponible à partir de la version 1.5.0 du paquet.
Le registre des modèles Snowflake vous permet 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 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 des classes Python dans la bibliothèque 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 appeler 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
Pour un exemple de workflow de bout en bout dans Snowpark ML, y compris le Snowflake Model Registry, voir Présentation du machine learning avec Snowpark ML.
Si vous avez des modèles dans Microsoft Azure Machine Learning ou dans Amazon SageMaker, voir Déployer des modèles depuis Azure ML et SageMaker vers Snowpark ML.
Les classes les plus importantes de l’API Snowflake Model Registry Python sont les suivantes :
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.
Snowflake Model Registry prend en charge les types de modèles suivants :
scikit-learn
XGBoost
LightGBM
CatBoost
PyTorch
TensorFlow
MLFlow PyFunc
Transformateur de phrases
Pipeline Hugging Face
D’autres types de modèles via la classe
snowflake.ml.model.CustomModel
(voir Stockage de modèles personnalisés dans le registre de modèles de Snowflake)
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 ; voir Commandes du modèle.
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. Le privilège USAGE permet aux bénéficiaires d’utiliser le modèle à des fins d’inférence sans pouvoir en voir les éléments internes.
Si le rôle d’un utilisateur a USAGE sur un modèle, il apparaît dans la page de registre des modèles de Snowsight. Pour plus de détails, voir Privilèges de contrôle d’accès.
Note
Actuellement, les modèles ne prennent pas en charge la réplication.
Limitation et problèmes actuels¶
Le registre des modèles Snowflake présente actuellement les limitations suivantes :
Le registre ne peut pas être utilisé dans des Snowflake Native Apps.
Les modèles ne peuvent pas être partagés ou clonés et sont ignorés lors de la réplication.
Les versions 1.5.0 et 1.5.1 du paquet snowflake-ml-python
présentent les problèmes connus suivants. Jusqu’à ce que ces problèmes soient résolus, utilisez la solution de contournement fournie.
Dans la version 8.23 de Snowflake et les versions antérieures, la bibliothèque ne fonctionne pas dans les procédures stockées des droits du propriétaire. Utilisez plutôt les procédures stockées avec droits de l’appelant.
Dans les procédures stockées, l’enregistrement d’un modèle nécessite l’intégration d’une copie de la bibliothèque locale Snowpark ML dans le modèle. Spécifiez l’option
embed_local_ml_library
dans l’appellog_model
comme suit :registry.log_model(..., options={"embed_local_ml_library": True, ...})
Les limites suivantes s’appliquent aux modèles et aux versions de modèles :
Modèles |
|
---|---|
Versions du modèle |
|
Ouverture du registre des modèles de Snowflake¶
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 Snowflake fournit une classe Python pour la gestion des 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")
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. Cette méthode ajoute des métadonnées, telles qu’une description, au modèle tel que spécifié dans l’appel log_model
.
Chaque modèle peut avoir des versions illimitées. Pour enregistrer d’autres versions du modèle, appelez à nouveau log_model
avec le même model_name
mais un version_name
différent.
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, en créant uniquement un modèle lors de l’ajout de sa première version. Vous pouvez mettre à jour les balises du modèle après avoir journalisé la première version du modèle.
Dans l’exemple suivant, 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 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="v1",
conda_dependencies=["scikit-learn"],
comment="My awesome ML model",
metrics={"score": 96},
sample_input_data=train_features,
task=type_hints.Task.TABULAR_BINARY_CLASSIFICATION)
Les arguments de log_model
sont décrits ici.
Arguments requis
Argument |
Description |
---|---|
|
L’objet modèle Python d’un type de modèle pris en charge. Doit être sérialisable (« picklable »). |
|
Le nom du modèle, utilisé avec |
Note
La combinaison du nom du modèle et de la version doit être unique dans le schéma.
Arguments facultatifs
Argument |
Description |
---|---|
|
Chaîne spécifiant la version du modèle, utilisée avec |
|
Répertorie des chemins d’accès aux répertoires de code à importer lors du chargement ou du déploiement du modèle. |
|
Commentaire, par exemple une description du modèle. |
|
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 |
|
Liste des modules externes à associer au modèle. Pris en charge par scikit-learn, Snowpark ML, PyTorch, TorchScript et les modèles personnalisés. |
|
Dictionnaire contenant les métriques liées à la version du modèle. |
|
Dictionnaire contenant des options pour la création de modèles. Les options suivantes sont disponibles pour tous les types de modèles :
Certains types de modèles peuvent proposer des options supplémentaires. Voir Remarques sur les types de modèles spécifiques. |
|
Liste des spécifications des paquets pour les paquets PyPI requis par votre modèle. |
|
La version de Python dans laquelle le modèle sera exécuté. La valeur par défaut est |
|
Un DataFrame qui contient des exemples de données d’entrée. Les noms des fonctions requises par le modèle, ainsi que leurs types, sont extraits de ce DataFrame. Cet argument ou |
|
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 |
|
La tâche définissant le problème que le modèle est censé résoudre. Si non spécifié, tous les efforts sont faits pour déduire la tâche du modèle à partir de la classe du modèle ou elle est définie sur |
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.
Utilisation d’artefacts de modèles¶
Une fois qu’un modèle a été enregistré, ses artefacts (les fichiers qui soutiennent le modèle, y compris ses objets Python sérialisés et divers fichiers de métadonnées tels que son manifeste) sont disponibles sur une zone de préparation interne. Les artefacts ne peuvent pas être modifiés, mais vous pouvez voir ou télécharger les artefacts des modèles que vous possédez.
Note
Le fait d’avoir le privilège USAGE sur un modèle ne vous permet pas d’accéder à ses artefacts ; il faut en être propriétaire.
Vous pouvez accéder aux artefacts de modèle à partir d’une zone de préparation en utilisant, par exemple, la commande GET ou son équivalent dans Snowpark Python, FileOperation. get.
Cependant, vous ne pouvez pas vous adresser aux artefacts de modèle en utilisant la syntaxe habituelle des zones de préparation. Utilisez plutôt une snow://
URL, une manière plus générale de spécifier l’emplacement des objets dans Snowflake. Par exemple, une version à l’intérieur d’un modèle peut être spécifiée par une URL de la forme snow://model/<nom_modèle>/versions/<nom_version>/
.
Connaissant le nom du modèle et la version souhaitée, vous pouvez utiliser la commande LIST pour voir les artefacts du modèle comme suit :
LIST 'snow://model/my_model/versions/V3/';
Le résultat ressemble à ceci :
name size md5 last_modified
versions/V3/MANIFEST.yml 30639 2f6186fb8f7d06e737a4dfcdab8b1350 Thu, 18 Jan 2024 09:24:37 GMT
versions/V3/functions/apply.py 2249 e9df6db11894026ee137589a9b92c95d Thu, 18 Jan 2024 09:24:37 GMT
versions/V3/functions/predict.py 2251 132699b4be39cc0863c6575b18127f26 Thu, 18 Jan 2024 09:24:37 GMT
versions/V3/model.zip 721663 e92814d653cecf576f97befd6836a3c6 Thu, 18 Jan 2024 09:24:37 GMT
versions/V3/model/env/conda.yml 332 1574be90b7673a8439711471d58ec746 Thu, 18 Jan 2024 09:24:37 GMT
versions/V3/model/model.yaml 25718 33e3d9007f749bb2e98f19af2a57a80b Thu, 18 Jan 2024 09:24:37 GMT
Pour récupérer l’un de ces artefacts, utilisez la commande SQL GET.
GET 'snow://model/model_my_model/versions/V3/MANIFEST.yml'
Ou l’équivalent avec Snowpark Python :
session.file.get('snow://model/my_model/versions/V3/MANIFEST.yml', 'model_artifacts')
Note
Les noms et l’organisation des artefacts d’un modèle peuvent varier en fonction du type de modèle et peuvent changer. La liste d’artefacts de l’exemple précédent est donnée à titre d’exemple et ne fait pas autorité.
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")
Astuce
Vous pouvez également supprimer des modèles dans SQL en utilisant DROP MODEL.
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()
Astuce
Dans SQL, utilisez SHOW MODELS pour obtenir une liste de modèles.
Le résultat de show_models
est un DataFrame pandas. Les colonnes disponibles sont répertoriées ici :
Colonne |
Description |
---|---|
|
Date et heure de création du modèle. |
|
Nom du modèle. |
|
Base de données dans laquelle le modèle est stocké. |
|
Schéma dans lequel le modèle est stocké. |
|
Rôle qui possède le modèle. |
|
Commentaire pour le rôle. |
|
Le tableau JSON qui répertorie les versions du modèle. |
|
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()
Pour obtenir une référence à un modèle spécifique du registre par son nom, utilisez la méthode get_model
du registre :
m = reg.get_model("MyModel")
Note
Les instances Model
ne sont pas des copies de l’objet de 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 nom, 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"
Note
L’attribut description
et un synonyme de comment
. Le code précédent peut également être écrit de cette façon :
print(m.description)
m.description = "A better description than the one I provided originally"
Astuce
Vous pouvez également définir le commentaire d’un modèle en SQL en utilisant ALTER MODEL.
Changement du nom d’un modèle¶
Utilisez la méthode rename
pour renommer ou déplacer un modèle. Spécifiez un nom entièrement qualifié comme nouveau nom pour déplacer le modèle vers une base de données ou un schéma différent.
m.rename("MY_MODEL_TOO")
Astuce
Vous pouvez également renommer un modèle dans SQL en utilisant ALTER MODEL.
Utilisation de versions de modèles¶
Un modèle peut avoir un nombre illimité de versions, chacune identifiée par une chaîne. 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.
Astuce
Dans SQL, utilisez SHOW VERSIONS IN MODEL pour voir les versions d’un modèle.
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()
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()
Le DataFrame résultant contient les colonnes suivantes :
Colonne |
Description |
---|---|
|
Date et heure de création de la version du modèle. |
|
Nom de la version. |
|
Base de données dans laquelle la version est stockée. |
|
Schéma dans lequel la version est stockée. |
|
Nom du modèle auquel cette version appartient. |
|
Valeur booléenne indiquant si cette version est la version par défaut du modèle. |
|
Tableau JSON des noms des fonctions disponibles dans cette version. |
|
Objet JSON contenant des métadonnées sous forme de paires clé-valeur ( |
|
Objet JSON de la section |
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")
Astuce
Vous pouvez également supprimer une version de modèle en SQL à l’aide de ALTER MODEL … DROP VERSION.
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 = "v2"
Astuce
Dans SQL, utilisez ALTER MODEL pour définir la version par défaut.
Alias de la version du modèle¶
Vous pouvez attribuer un alias à une version de modèle à l’aide de la commande SQL ALTER MODEL. Vous pouvez utiliser un alias chaque fois qu’un nom de version est nécessaire, par exemple pour obtenir une référence à une version de modèle, en Python ou en SQL. Un alias donné ne peut être attribué qu’à une seule version de modèle à la fois.
Outre les alias que vous créez, les alias système suivants sont disponibles dans tous les modèles :
DEFAULT
fait référence à la version par défaut du modèle.FIRST
fait référence à la version la plus ancienne du modèle en fonction du moment de la création.LAST
fait référence à la version la plus récente du modèle en fonction du moment de la création.
Les noms d’alias que vous créez doivent être différents de tout nom de version ou alias existant dans le modèle, y compris les alias système.
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 :
m = reg.get_model("MyModel")
mv = m.version("v1")
mv = m.default
Une fois que vous avez une référence à une version spécifique d’un modèle (comme la variable mv
dans cet exemple), vous pouvez récupérer ou mettre à jour ses commentaires ou ses métriques et appeler les méthodes (ou 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"
Astuce
Vous pouvez également modifier le commentaire d’une version de modèle en SQL en utilisant ALTER MODEL … MODIFY VERSION.
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)
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)
Vous pouvez ensuite définir ces valeurs comme métriques :
# 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)
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()
Pour supprimer une métrique, appelez delete_metric
.
mv.delete_metric("test_accuracy")
Astuce
Vous pouvez également modifier les métriques d’une version de modèle (qui sont stockées sous forme de métadonnées) en SQL à l’aide de ALTER MODEL … MODIFY VERSION.
Récupération des explications du modèle¶
Le registre de modèles peut expliquer les résultats d’un modèle, en vous indiquant quelles fonctionnalités d’entrée contribuent le plus aux prédictions, en calculant les valeurs de Shapley. Cette fonctionnalité en avant-première est disponible par défaut dans toutes les vues de modèle créées dans Snowflake 8.31 et versions ultérieures via le modèle explain
sous-jacent. Vous pouvez appeler explain
depuis SQL ou via une méthode run
de vue de modèle en Python.
Pour plus de détails sur cette fonctionnalité, voir Explicabilité des modèles.
Exportation d’une version du modèle¶
Utilisez mv.export
pour exporter les fichiers d’un modèle vers un répertoire local ; le répertoire est créé s’il n’existe pas :
mv.export("~/mymodel/")
Par défaut, les fichiers exportés comprennent le code, l’environnement de chargement du modèle et les poids du modèle. Pour exporter également les fichiers nécessaires à l’exécution du modèle dans un entrepôt, indiquez export_mode = ExportMode.FULL
:
mv.export("~/mymodel/", export_mode=ExportMode.FULL)
Chargement d’une version de modèle¶
Utilisez mv.load
pour charger l’objet du modèle Python d’origine qui a été ajouté au registre. Vous pouvez alors utiliser le modèle pour l’inférence comme si vous l’aviez défini dans votre code Python.
clf = mv.load()
Pour garantir le bon fonctionnement d’un modèle à partir du registre, l’environnement Python cible (c’est-à-dire les versions de l’interpréteur Python et de toutes les bibliothèques) doit être identique à l’environnement à partir duquel le modèle a été enregistré. Spécifiez force=True
dans l’appel load
pour forcer le chargement du modèle même si l’environnement est différent.
Astuce
Pour vous assurer que votre environnement est le même que celui dans lequel le modèle est hébergé, téléchargez une copie de l’environnement conda à partir du registre de modèles :
conda_env = session.file.get("snow://model/<modelName>/versions/<versionName>/runtimes/python_runtime/env/conda.yml", ".")
open("~/conda.yml", "w").write(conda_env)
Créez ensuite un nouvel environnement conda à partir de ce fichier :
conda env create --name newenv --file=~/conda.yml
conda activate newenv
L’argument facultatif options
est un dictionnaire d’options pour le chargement du modèle. Actuellement, l’argument ne prend en charge que l’option use_gpu
.
Option |
Type |
Description |
Par défaut |
---|---|---|---|
|
|
Active la logique de chargement propre à GPU. |
|
L’exemple suivant illustre l’utilisation de l’argument options
:
clf = mv.load(options={"use_gpu": True})
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 Snowpark ou pandas contenant les données d’inférence et tout autre paramètre requis. La méthode est exécutée dans un entrepôt Snowflake.
La valeur de retour de la méthode est un DataFrame Snowpark ou pandas, selon le type de DataFrame transmis. Les DataFrames Snowpark sont évalués paresseusement, de sorte que la méthode n’est exécutée que lorsque la méthode collect
, show
ou to_pandas
du DataFrame est appelée.
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")
remote_prediction.show() # assuming test_features is Snowpark DataFrame
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 Python dans le modèle original enregistré.
Astuce
Vous pouvez également appeler des méthodes de modèle dans SQL. Voir Méthodes de modèle.
Considérations relatives aux clients¶
L’utilisation du registre des modèles de Snowflake 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. Pour des informations générales sur les coûts de stockage, voir Exploration des 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. Pour obtenir des informations générales sur les coûts de calcul de Snowflake, consultez Comprendre le coût du calcul. 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 spécifiques dans le registre des modèles de Snowflake.
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 |
---|---|
|
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 : |
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')
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 |
---|---|
|
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 : |
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)
clf = ensemble.RandomForestClassifier(random_state=42)
clf.fit(iris_X, iris_y)
model_ref = registry.log_model(
clf,
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"')
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 |
---|---|
|
Une liste des noms des méthodes disponibles sur l’objet modèle. Les modèles dérivés de |
|
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 |
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:])
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)
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 celui-ci pour le transmettre au modèle :
import pandas as pd
tensors = pd.DataFrame([[[1,2],[5,6]],[[3,4],[7,8]]])
Le DataFrame de tensors
ressemble alors à ceci :
0 1
0 [1, 2] [5, 6]
1 [3, 4] [7, 8]
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 |
---|---|
|
Une liste des noms des méthodes disponibles sur l’objet modèle. Les modèles PyTorch sont définis par défaut sur |
|
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 |
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])
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 untensorflow.Module
ou la méthodecall
pour untensorflow.keras.Model
accepte un ou plusieurstensorflow.Tensor
outensorflow.Variable
en entrée et renvoie untensorflow.Tensor
outensorflow.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 étendModel
, 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
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 celui-ci pour le transmettre au modèle :
import pandas as pd
tensors = pd.DataFrame([[[1,2],[5,6]],[[3,4],[7,8]]])
Le DataFrame de tensors
ressemble alors à ceci :
0 1
0 [1, 2] [5, 6]
1 [3, 4] [7, 8]
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 |
---|---|
|
Une liste des noms des méthodes disponibles sur l’objet modèle. Les modèles TensorFlow sont définis par défaut sur |
|
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 |
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])
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 |
---|---|
|
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 |
|
Si |
|
Si |
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)
Pipeline Hugging Face¶
Note
Pour plus de détails sur les entrées et sorties attendues de types spécifiques de pipelines Hugging Face, voir Signatures déduites pour les pipelines 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')
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 déduit l’argument signatures
si le pipeline contient une tâche de la liste suivante :
conversational
fill-mask
question-answering
summarization
table-question-answering
text2text-generation
text-classification
(également appelésentiment-analysis
)text-generation
token-classification
(également appeléner
)translation
translation_xx_to_yy
zero-shot-classification
L’argument sample_input_data
est totalement ignoré pour les modèles Hugging Face. Spécifiez l’argument signatures
lors de l’enregistrement d’un modèle Hugging Face qui ne figure pas dans la liste ci-dessus afin que le registre connaisse les signatures des méthodes cibles.
Pour voir la signature déduite, utilisez la méthode show_functions
. Le code suivant, par exemple, est le résultat de lmv.show_functions()
où 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')
]
)}]
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"]))
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
, essayezLlama-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.\""}]
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.", ""],
]
)
)
Résultat :
0 [{"label": "negative", "score": 0.8106237053871155}, {"label": "neutral", "score": 0.16587384045124054}]
1 [{"label": "neutral", "score": 0.9263970851898193}, {"label": "positive", "score": 0.05286872014403343}]