Speichern von kundenspezifischen Modellen in der Snowflake Model Registry

Die Snowflake Model Registry ermöglicht es Ihnen, Modelle zu registrieren (zu protokollieren) und sie für Inferenz in Snowflake zu verwenden. Die Registry unterstützt mehrere Typen von Modellen:

  • Snowpark ML Modeling

  • scikit-learn

  • XGBoost

  • LightGBM

  • CatBoost

  • PyTorch

  • TensorFlow

  • MLFlow PyFunc

  • Sentence Transformer

  • Hugging Face-Pipeline

Die Modell-Registry-API ermöglicht auch die Protokollierung weiterer Typen von Modellen, einschließlich solcher, die mit externen Tools trainiert wurden oder aus Open-Source-Repositorys stammen, solange sie serialisierbar sind und von der Klasse snowflake.ml.model.custom_model.CustomModel abgeleitet wurden.

Bemerkung

Diese Anleitung enthält ein Beispiel für die Protokollierung eines kundenspezifischen Modells.

Informationen zum Import von Modellen aus AWS SageMaker oder aus Azure ML finden Sie in diesem Blogbeitrag und in den entsprechenden Quickstarts.

Unter diesem Thema wird erklärt, wie Sie Modelle erstellen, diese in der Snowflake Model Registry protokollieren und dann bereitstellen. Ein gängiger Anwendungsfall für dieses Feature ist das Definieren von Pipelines mit mehreren Klassen, z. B. einige Transformer oder Imputer gefolgt von einem Prädiktor oder Klassifikator. In solchen Fällen ist die kundenspezifische Modellklasse selbst relativ einfach: Sie ruft diese Klassen nacheinander auf und übergibt das Ergebnis einer Klasse als Eingabe an die nächste.

Definieren des Modellkontexts

Modelle benötigen oft zusätzlich zum Code eine oder mehrere statische Dateien, wie z. B. Konfigurationsdateien oder Modellgewichtungen. Kundenspezifische Modelle müssen Informationen zu allen diesen Artefakte liefern, damit die Registry weiß, dass sie mit dem Modell gepackt werden sollen. Artefakte können mit der Klasse ModelContext wie folgt deklariert werden.

from snowflake.ml.model import custom_model

mc = custom_model.ModelContext(
    artifacts={
        'config': 'local_model_dir/config.json'
    },
)
Copy

Die Pfade zu Artefaktdateien im Modellkontext sind relativ zum aktuellen Verzeichnis der Umgebung, aus der das Modell protokolliert wird. Die Snowflake Model Registry verwendet diese Informationen, um sicherzustellen, dass alle erforderlichen Codes und Daten in dem Warehouse bereitgestellt werden, in dem Ihr Modell ausgeführt werden soll. Zur Laufzeit kann das Modell diese Artefakte finden, indem es self.context.path('config') aufruft (der Wert 'config' ist derselbe wie der Schlüssel in dem an ModelContext übergebenen Dictionary).

Neben statischen Dateien kann ein Modell auch andere Modelle oder Pipelines eines unterstützten Typs zusammenstellen (z. B. eine snowflake.ml.modeling.pipeline oder ein scikit-learn-Modell). Die Registry weiß bereits, wie diese Typen von Objekten protokolliert werden. Daher können Sie die Python-Objekte direkt im Modellkontext mit model_refs übergeben, wie hier gezeigt. Sie brauchen diese Objekte nicht selbst zu packen. Dies kann für die Bootstrap-Aggregation (Bagging) oder für die Vor- oder Nachbearbeitung nützlich sein.

Bemerkung

model1 und model2 sind Objekte eines beliebigen Modelltyps, der von der Registry nativ unterstützt wird. feature_preproc ist ein snowflake.ml.modeling.pipeline-Objekt.

mc = custom_model.ModelContext(
    artifacts={
        'config': 'local_model_dir/config.json'
    },
    models={
        'm1': model1,
        'm2': model2,
        'feature_preproc': preproc
    }
)
Copy

Die Modell-Registry serialisiert diese Modellreferenzen beim Protokollieren des Modells und rehydriert sie zur Laufzeit. Ihr Modell kann Referenzen auf diese untergeordneten Modelle abrufen, z. B. mit self.context.model_ref('m1'). Wenn das Modell eine Methode predict zur Verfügung stellt, kann Ihr Code diese direkt aus der abgerufenen Modellreferenz aufrufen, zum Beispiel mit self.context.model_ref('m1').predict().

Zusammenfassend lässt sich sagen, dass der Kontext eines kundenspezifischen Modells die Python-Objekte deklariert, die zusammen mit den Artefakten des Modells serialisiert werden sollen. Dabei handelt es sich um lokale Dateien, die vom Modell verwendet werden und die zusammen mit dem Code in Snowflake gespeichert werden müssen. Ihre Modellklasse verwendet den Kontext, um den Code und die Artefakte zu lokalisieren. Dies funktioniert unabhängig davon, ob Ihr Modell lokal oder in Snowflake ausgeführt wird.

Schreiben der kundenspezifischen Modellklasse

Um der Modell-Registry mitzuteilen, wie Ihr kundenspezifisches Modell protokolliert und bereitgestellt werden soll, erben Sie von snowflake.ml.model.custom_model.CustomModel.

Modelle können mehrere Inferenzmethoden anbieten (z. B. bieten scikit-learn-Modelle die Methoden predict und predict_proba an). Um Inferenzfunktionen in Ihrem kundenspezifischen Modell zu deklarieren, definieren Sie diese als öffentliche Methoden Ihrer Unterklasse und dekorieren sie mit @custom_model.inference_api. Dieser Decorator zeigt an, dass eine Methode Teil der öffentlichen API des Modells ist, sodass sie von Python oder SQL über die Modell-Registry aufgerufen werden kann. Methoden, die mit inference_api dekoriert sind, müssen ein pandas-DataFrame akzeptieren und zurückgeben. Eine beliebige Anzahl von Methoden kann mit inference_api dekoriert werden.

Bemerkung

Die Anforderung an die öffentliche API, einen Pandas-DataFrame zu akzeptieren und zurückzugeben, ist die gleiche wie für vektorisierte UDFs. Wie bei vektorisierten UDFs können diese Inferenz-APIs auch von Python aus aufgerufen werden, wobei ein Snowpark-DataFrame übergeben wird.

Nachfolgend sehen Sie ein Skeleton einer kundenspezifischen Modellklasse mit einer öffentlichen API.

Beachten Sie die Verwendung von context.path, um die Bias-Datei, und die Verwendung von self.context.model_ref, um Verweise auf die untergeordneten Modellklassen zu erhalten, damit deren predict-Methoden aufgerufen werden können.

from snowflake.ml.model import custom_model
import pandas as pd

class ExamplePipelineModel(custom_model.CustomModel):

    @custom_model.inference_api
    def predict(self, input: pd.DataFrame) -> pd.DataFrame:
        ...
        return pd.DataFrame(...)
Copy

Wenn Sie alle Teile zusammenfügen, erhalten Sie ein voll funktionsfähiges kundenspezifisches Modell.

class ExamplePipelineModel(custom_model.CustomModel):
    def __init__(self, context: custom_model.ModelContext) -> None:
        super().__init__(context)
        v = open(context.path('config')).read()
        self.bias = json.loads(v)['bias']

    @custom_model.inference_api
    def predict(self, input: pd.DataFrame) -> pd.DataFrame:
        features = self.context.model_ref('feature_preproc').transform(input)
        model_output = self.context.model_ref('m2').predict(
            self.context.model_ref('m1').predict(features)
        )
        return pd.DataFrame({
            'output': model_output + self.bias})
Copy

Verwenden des kundenspezifischen Modells

Sie können Ihr neues kundenspezifisches Modell (Pipeline) testen, indem Sie es wie folgt lokal ausführen:

my_model_pipeline = ExamplePipelineModel(mc)
output_df = my_model_pipeline.predict(input_df)
Copy

Oder Sie protokollieren es in der Registry, und stellen Sie es in Snowflake bereit. Wie im nächsten Codebeispiel gezeigt, geben Sie conda_dependencies (oder pip_requirements) an, um die Bibliotheken anzugeben, die die Modellklasse benötigt.

Stellen Sie sample_input_data (ein pandas-DataFrame) zur Verfügung, um die Signatur der Eingabe für das Modell abzuleiten. Alternativ können Sie auch eine Modellsignatur bereitstellen.

reg = Registry(session=sp_session, database_name="ML", schema_name="REGISTRY")
mv = reg.log_model(my_model_pipeline,
            model_name="my_custom_model_pipeline",
            version_name="v1",
            conda_dependencies=["scikit-learn"],
            comment="My Custom ML Model Pipeline",
            sample_input_data=train_features)
output_df = mv.run(input_df)
Copy

Tabellenfunktion: Inferenz

Ab Snowpark-ML 1.5.4 können Sie Modelle mit Inferenzmethoden protokollieren, die mehrere Spalten zurückgeben. Dazu protokollieren Sie Ihr Modell mit der {"function_type": "TABLE_FUNCTION"}-Option und verwenden das @inference_api-Decorator-Element, wie oben beschrieben. Im folgenden Beispiel gibt die dekorierte Methode ein pandas-DataFrame zurück, das zwei Ausgabespalten enthält.

class ExampleTableFunctionModel(custom_model.CustomModel):

  @custom_model.inference_api
  def predict(self, input: pd.DataFrame) -> pd.DataFrame:
      output_df = pandas.DataFrame([{"OUTPUT1": input["INPUT1"] + 1, input["INPUT2"] + 1}])
      return output_df

my_model = ExampleTableFunctionModel()
reg = Registry(session=sp_session, database_name="ML", schema_name="REGISTRY")
mv = reg.log_model(my_model,
            model_name="my_custom_table_function_model",
            version_name="v1",
            options={"function_type": "TABLE_FUNCTION"},
            sample_input_data=train_features
            )
output_df = mv.run(input_df)
Copy

Wenn das Modell mehrere Inferenzmethoden enthält, verwenden Sie die method_options-Option, um das Modell zu protokollieren und anzugeben, welche FUNCTION und welche TABLE_FUNCTION sind:

reg = Registry(session=sp_session, database_name="ML", schema_name="REGISTRY")
mv = reg.log_model(my_model,
            model_name="my_custom_table_function_model",
            version_name="v1",
            options={
              "method_options": {                                 ###
                "METHOD1": {"function_type": "TABLE_FUNCTION"},   ###
                "METHOD2": {"function_type": "FUNCTION"}          ###
              }
            },
            sample_input_data=train_features
            )
Copy

Die protokollierte Modelltabellenfunktion kann auch über SQL wie folgt aufgerufen werden:

SELECT OUTPUT1, OUTPUT2
  FROM input_table,
      table(
          MY_MODEL!PREDICT(input_table.INPUT1, input_table.INPUT2)
      )
Copy

Weitere Informationen zu Inferenzmethoden für partitionierte Modelle, bei denen die Inferenzmethode eine Datenpartition als Eingabe verwendet und mehrere Zeilen und Spalten ausgibt, finden Sie unter Partitionierte benutzerdefinierte Modelle.