Utilisation de modèles partitionnés

De nombreux ensembles de données peuvent être partitionnés en plusieurs sous-ensembles indépendants. Par exemple, un ensemble de données contenant des données de vente pour une chaîne de magasins peut être partitionné par numéro de magasin. Un modèle distinct peut ensuite être entraîné pour chaque partition. Les opérations d’entraînement et d’inférence sur les partitions peuvent être parallélisées, réduisant ainsi le temps d’exécution de ces opérations. De plus, étant donné que les magasins individuels diffèrent de manière significative dans la manière dont leurs caractéristiques affectent leurs ventes, cette approche peut conduire à des inférences plus précises au niveau du magasin.

Snowflake Model Registry prend en charge le traitement distribué de l’entraînement et de l’inférence des données partitionnées lorsque :

  • L’ensemble de données contient une colonne qui identifie de manière fiable les partitions dans les données.

  • Les données de chaque partition individuelle ne sont pas corrélées avec les données des autres partitions et contiennent suffisamment de lignes pour entraîner le modèle.

Les modèles peuvent être sans état (la formation est effectuée à chaque fois que l’inférence est appelée) ou avec état (la formation est effectuée une fois avant l’inférence et conservée pour être utilisée dans plusieurs opérations d’inférence).

Avec le Snowflake Model Registry, mettez en œuvre l’apprentissage et l’inférence partitionnés à l’aide de modèles personnalisés. Pendant l’inférence, la méthode d’inférence de modèle partitionne l’ensemble de données, génère des prédictions pour chaque partition en parallèle en utilisant tous les nœuds et cœurs de votre entrepôt, et combine ensuite les résultats en un seul ensemble de données.

Note

Pour les modèles partitionnés, il est important de distinguer le modèle enregistré des modèles individuels qui sont créés par le modèle enregistré ou qui le composent. Dans la mesure du possible, nous ferons référence aux différents modèles sous-jacents en tant que sous-modèles.

Note

L’entraînement et l’inférence partitionnés nécessitent Snowpark ML (paquet snowflake-ml-python) version 1.5.0 ou ultérieure.

Définition et journalisation du modèle

La classe de modèle partitionné hérite de snowflake.ml.model.custom_model.CustomModel et les méthodes d’inférence sont déclarées avec le décorateur @custom_model.partitioned_inference_api (Snowpark ML version 1.5.4 ou ultérieure) ou le décorateur @custom_model.inference_api (Snowpark ML version 1.5.0 à 1.5.3). Consultez le site Apporter vos propres types de modèles via des fichiers sérialisés pour obtenir des informations sur la définition de modèles standard personnalisés.

import pandas as pd

from snowflake.ml.model import custom_model

class ExamplePartitionedModel(custom_model.CustomModel):

  @custom_model.partitioned_inference_api
  def predict(self, input: pd.DataFrame) -> pd.DataFrame:
      # All data in the partition will be loaded in the input dataframe.
      #… implement model logic here …
      return output_df

my_model = ExamplePartitionedModel()
Copy

Lors de la journalisation du modèle, fournissez un function_type de TABLE_FUNCTION dans le dictionnaire options avec les autres options que votre modèle nécessite.

from snowflake.ml.registry import Registry

reg = Registry(session=sp_session, database_name="ML", schema_name="REGISTRY")
model_version = reg.log_model(my_model,
  model_name="my_model",
  version_name="v1",
  options={"function_type": "TABLE_FUNCTION"},    ###
  conda_dependencies=["scikit-learn"],
  sample_input_data=train_features
)
Copy

Si votre modèle partitionné comporte également des fonctions ordinaires (autres que des tables) en tant que méthodes, vous pouvez utiliser le dictionnaire method_options pour spécifier le type de chaque méthode.

model_version = reg.log_model(my_model,
    model_name="my_model",
    version_name="v1",
    options={
      "method_options": {                                 ###
        "METHOD1": {"function_type": "TABLE_FUNCTION"},   ###
        "METHOD2": {"function_type": "FUNCTION"}          ###
      }
    }
    conda_dependencies=["scikit-learn"],
    sample_input_data=train_features
)
Copy

Inférence de modèles partitionnés

Utilisez la méthode run d’un objet ModelVersion Python pour appeler les méthodes de fonction de table de manière partitionnée, en transmettant partition_column pour spécifier le nom de la colonne qui contient une valeur numérique ou une chaîne qui identifie la partition de chaque enregistrement. Comme d’habitude, vous pourrez passer devant un Snowpark ou un DataFrame pandas (ce dernier est utile pour les tests locaux). Vous recevrez le même type de DataFrame en conséquence. Dans ces exemples, l’inférence est partitionnée sur un numéro de magasin.

model_version.run(
  input_df,
  function_name="PREDICT",
  partition_column="STORE_NUMBER"
)
Copy

Vous pouvez également appeler les fonctions de la table de modèle directement à l’aide de SQL, comme illustré ci-dessous.

SELECT output1, output2, partition_column
  FROM input_table,
      TABLE(
          my_model!predict(input_table.input1, input_table.input2)
          OVER (PARTITION BY input_table.store_number)
      )
  ORDER BY input_table.store_number;
Copy

Les données d’entrée sont automatiquement réparties entre les nœuds et les cœurs de votre entrepôt et les partitions sont traitées en parallèle.

Pour plus d’informations sur la syntaxe des fonctions de table, voir Appeler une UDF avec SQL.

Modèles partitionnés sans état

Dans l’application la plus simple des modèles partitionnés, la formation et l’inférence sont toutes deux effectuées lors de l’appel de predict. Le modèle est ajusté, l’inférence est exécutée et le modèle ajusté est rejeté immédiatement après. Ce type de modèle est dit « sans état » car aucun état d’ajustement n’est stocké. Voici un exemple dans lequel chaque partition entraîne un modèle XGBoost :

class ExampleStatelessPartitionedModel(custom_model.CustomModel):

  @custom_model.partitioned_inference_api
  def predict(self, input_df: pd.DataFrame) -> pd.DataFrame:
      import xgboost
      # All data in the partition will be loaded in the input dataframe.
      # Construct training data by transforming input_df.
      training_data = # ...

      # Train the model.
      my_model = xgboost.XGBRegressor()
      my_model.fit(training_data)

      # Generate predictions.
      output_df = my_model.predict(...)

      return output_df

my_model = ExampleStatelessPartitionedModel()
Copy

Consultez le guide de démarrage rapide du modèle partitionné pour un exemple de modèle partitionné sans état, y compris des échantillons de données.

Modèles partitionnés avec état

Il est également possible de mettre en œuvre des modèles partitionnés avec état qui chargent l’état d’ajustement des sous-modèles stockés. Vous pouvez le faire en fournissant des modèles en mémoire via snowflake.ml.model.custom_model.ModelContext ou en fournissant des chemins de fichiers pointant vers des artefacts de modèles ajustés et en les chargeant pendant l’inférence.

L’exemple suivant montre comment fournir des modèles en mémoire au contexte de modèle.

from snowflake.ml.model import custom_model

# `models` is a dict with model ids as keys, and fitted xgboost models as values.
models = {
  "model1": models[0],
  "model2": models[1],
  ...
}

model_context = custom_model.ModelContext(
  models=models
)
my_stateful_model = MyStatefulCustomModel(model_context=model_context)
Copy

Lorsque vous consignez my_stateful_model, les sous-modèles fournis dans le contexte sont stockés avec tous les fichiers de modèle. Il est ensuite possible d’y accéder dans la logique de la méthode d’inférence en les récupérant dans le contexte, comme indiqué ci-dessous :

class ExampleStatefulModel(custom_model.CustomModel):

  @custom_model.partitioned_inference_api
  def predict(self, input: pd.DataFrame) -> pd.DataFrame:
    model1 = self.context.model_ref("model1")
    # ... use model1 for inference
Copy

Il est également possible d’accéder aux modèles de manière programmatique par l’ID de partition dans la méthode predict. Si une colonne de partition est fournie comme fonction d’entrée, elle peut être utilisée pour accéder à un modèle ajusté pour la partition. Par exemple, si la colonne de partition est MY_PARTITION_COLUMN, la classe de modèle suivante peut être définie :

class ExampleStatefulModel(custom_model.CustomModel):

  @custom_model.partitioned_inference_api
  def predict(self, input: pd.DataFrame) -> pd.DataFrame:
    model_id = input["MY_PARTITION_COLUMN"][0]
    model = self.context.model_ref(model_id)
    # ... use model for inference
Copy

De même, les sous-modèles peuvent être stockés en tant qu’artefacts et chargés à l’environnement d’exécution. Cette approche est utile lorsque les modèles sont trop volumineux pour tenir dans la mémoire. Fournissez des chemins de fichiers sous forme de chaînes au contexte du modèle. Les chemins d’accès aux fichiers sont accessibles lors de l’inférence avec self.context.path(artifact_id). Pour plus d’informations, voir Définition du contexte du modèle à l’aide d’arguments de type mot-clé.

Exemple

Consultez le guide de démarrage rapide du modèle partitionné pour obtenir un exemple, y compris des échantillons de données.

Consultez le guide de démarrage rapide de l’inférence de nombreux modèles dans Snowflake pour un exemple de modèle personnalisé partitionné avec état.