Snowpark ML Ops: モデルレジストリ

注釈

このトピックで説明するモデルレジストリ API は、Snowpark ML パッケージのバージョン 1.2.0 以降で使用できます。

Snowpark ML Operations(MLOps)の一部であるSnowparkモデルレジストリにより、お客様はSnowflakeで、モデルとそのメタデータを原点に関係なく安全に管理することができます。Snowparkモデルレジストリは、機械学習モデルをSnowflakeのファーストクラスのスキーマレベルオブジェクトとして格納することにより、組織内の他の人たちが容易に見つけて使用できるようにします。Snowpark ML を使用してレジストリを作成し、そこにモデルを格納できます。モデルは複数のバージョンを持つことができ、1つのバージョンをデフォルトとして指定することができます。

モデルを格納したら、そのメソッド(関数やストアドプロシージャに相当)を呼び出して、推論などのモデル操作をSnowflake 仮想ウェアハウス で実行することができます。

Tip

モデルレジストリなど、Snowpark ML のエンドツーエンドのワークフローの例については、 Snowpark ML を使用した機械学習入門 をご参照ください。

Snowparkモデルレジストリ API の主なオブジェクト3つは以下のとおりです。

  • snowflake.ml.registry.Registry: スキーマ内のモデルを管理します。

  • snowflake.ml.model.Model: モデルを表します。

  • snowflake.ml.model.ModelVersion: モデルのバージョンを表します。

これらのクラスの詳細については、 Snowpark ML API リファレンス をご参照ください。

Snowparkモデルレジストリは、以下の型のモデルをサポートしています。

  • Snowpark ML Modeling

  • scikit-learn

  • XGBoost

  • PyTorch

  • TensorFlow

  • MLFlow PyFunc

  • HuggingFace パイプライン

このトピックでは、Snowpark ML を使用して、Pythonでレジストリ操作を実行する方法について説明します。また、 SQL で多くのレジストリ操作を実行できます。詳細については、 モデルコマンド をご参照ください。

プライベートプレビューとの相違

Snowflakeは以前、一部のお客様にモデルレジストリを非公開で提供していました。このトピックで説明するレジストリ機能は、プライベートプレビューバージョンと比較して、機能と APIs に大きな変更があります。最も注目すべき点は、モデルレジストリ機能が、新しいスキーマレベルオブジェクトを使用して、Snowflake内部でネイティブにホストされるようになったことです。

注釈

このパブリックプレビューバージョンでは、 Snowpark Container Services (SPCS)へのモデル展開はまだサポートされていません。この機能に依存している場合は、当面の間プライベートプレビューのレジストリを使い続けてください。

これら2つの APIs の相違に関する詳細については、 Snowpark ML Ops: モデルレジストリのプレビュー API からの移行 をご参照ください。

必要な権限

モデルを作成するには、モデルを作成するスキーマを所有するか、そのスキーマに対する CREATE MODEL 権限を持っている必要があります。モデルを使用するには、そのモデルを所有しているか、そのモデルに対する USAGE 権限を持っている必要があります。

Snowparkモデルレジストリの開始

モデルはファーストクラスのSnowflakeオブジェクトであり、他のSnowflakeオブジェクトと一緒にデータベースやスキーマ内に編成することができます。Snowparkモデルレジストリは、スキーマ内のモデルを管理するためのPythonクラスです。したがって、どのSnowflakeスキーマもレジストリとして使用できます。この目的のために、スキーマを初期化したり準備したりする必要はありません。Snowflakeは、 ML.REGISTRY のように、この目的のために1つ以上の専用スキーマを作成することを推奨しています。スキーマは、 CREATE SCHEMA を使用して作成できます。

レジストリのモデルを作成または変更する前に、レジストリを開く必要があります。レジストリを開くと、そのレジストリへの参照が返され、それを使用して新しいモデルを追加したり、既存のモデルへの参照を取得したりすることができます。

from snowflake.ml.registry import Registry

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

注釈

このパブリックプレビューでは、 Model オブジェクトは複製やクローンをサポートしません。

モデルおよびバージョンの登録

レジストリへのモデルの追加は、モデルの ログ と呼ばれます。レジストリの log_model メソッドを呼び出してモデルをログします。このメソッドは、

  • Pythonオブジェクトであるモデルをシリアル化し、そこからSnowflakeモデルオブジェクトを作成します。

  • 説明などのメタデータを log_model 呼び出しで指定されたモデルに追加します。

注釈

タグがレジストリに追加されている場合は、モデルにタグを追加することはできません。タグはモデルの属性であり、 log_model は特定のモデルのバージョンを追加するからです。モデルの最初のバージョンをログした後に、 モデルのタグを更新する ことができます。

各モデルは、いくつでもバージョンを持つことができます。モデルの追加バージョンをログするには、同じ model_name で異なる version_name を指定して log_model を再度呼び出します。

以下の例では、 clf は「分類子」の略であり、コード内で別の場所ですでに作成されているPythonのモデルオブジェクトです。ここに示すように、登録時にコメントやタグを追加することができます。名前とバージョンの組み合わせは、スキーマ内で一意にする必要があります。 conda_dependencies リストを指定します。指定したパッケージはモデルと合わせて展開されます。

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

ここで log_model の引数を説明します。

必要な引数

引数

説明

model

サポートされているモデル型のPythonモデルオブジェクト。シリアル化可能(「ピクル化可能」)である必要があります。

model_name

モデルの名前。レジストリでモデルを識別するために version_name と一緒に使用されます。モデルがログされた後に、名前を変更することはできません。

version_name

モデルのバージョンを指定する文字列。レジストリでモデルを識別するために model_name と一緒に使用されます。

注釈

モデル名とバージョンの組み合わせは、スキーマ内で一意にする必要があります。

オプションの引数

引数

説明

code_paths

モデルをロードまたは展開する際にインポートするコードディレクトリへのパスのリスト。

comment

コメント、たとえばモデルの説明など。

conda_dependencies

モデルに必要なCondaパッケージのリスト。この引数は Conda形式、つまり "[channel::]package [operator version]" でパッケージ名とオプションのバージョンを指定します。チャネルを指定しない場合は、Snowflakeチャネルになります。

ext_modules

モデルと一緒にピクルする外部モジュールのリスト。scikit-learn、Snowpark ML、 PyTorch、 TorchScript、およびカスタムモデルのサポート。

metrics

モデルバージョンにリンクされたメトリクスを含むディクショナリ。

options

モデル作成のオプションを含むディクショナリ。次のオプションはすべてのモデル型で利用できます。

  • embed_local_ml_library: ローカルSnowpark ML ライブラリのコピーをモデルに埋め込むかどうか。デフォルト: False

  • relax_version: 依存関係のバージョン制約を緩和するかどうか。これは、 ==x.y.z のようなバージョン指定子を <=x.y, <(x+1) のような指定子に置き換えます。デフォルト: False。Snowpark ML パッケージのバージョン1.2.1以降で利用可能です。

  • method_options: メソッドごとのオプションのディクショナリ。キーはメソッド名で、値はここで説明する1つ以上のオプションを含むディクショナリです。利用可能なオプションは以下のとおりです。

    • case_sensitive: メソッドとその署名で大文字と小文字を区別するかどうかを示します。大文字と小文字を区別するメソッドを SQL で使用する場合は、二重引用符で囲む必要があります。このオプションでは、メソッド名にアルファベット以外の文字も使用できます。デフォルト: False

    • max_batch_size: ウェアハウスでメソッドが呼び出されたときに、そのメソッドが受け入れる最大バッチサイズ。デフォルト: None (バッチサイズは自動的に決定)

個々のモデル型によっては、追加オプションをサポートしている場合もあります。 特定のモデル型に関する注意事項 をご参照ください。

pip_requirements

モデルで必要な PyPI パッケージのパッケージ仕様のリスト。

python_version

モデル内で実行するPythonのバージョン。デフォルトは None で、ウェアハウスで利用可能な最新バージョンを示します。

sample_input_data

サンプル入力データを含む DataFrame。この DataFrame から、モデルが必要とする特徴量名とその型が抽出されます。Snowpark ML と MLFlow 以外のモデルでは、この引数か signatures のどちらかを指定する必要があります。

signatures

メソッド署名を、ターゲットメソッド名から入力と出力の署名へのマッピングとしてモデル化します。Snowpark ML と MLFlow 以外のモデルでは、この引数か sample_input_data のどちらかを指定する必要があります。

log_model は、レジストリに追加されたモデルのバージョンを表す snowflake.ml.model.ModelVersion オブジェクトを返します。

一度登録されると、モデル自体を変更することはできません(メタデータを変更することはできます)。モデルとそのすべてのバージョンを削除するには、レジストリの delete_model メソッドを使用します。

モデルの削除

レジストリの delete_model メソッドを使用して、モデルとそのすべてのバージョンを削除します。

reg.delete_model("mymodel")
Copy

レジストリからのモデルの取得

各モデルの情報を得るには、 show_models メソッドを使用します。

model_df = reg.show_models()
Copy

show_models の結果は、pandas DataFrame です。利用可能な列は以下のとおりです。

説明

created_on

モデルが作成された日時。

name

モデルの名前。

database_name

モデルが格納されているデータベース。

schema_name

モデルが格納されているスキーマ。

owner

モデルを所有するロールの名前。

comment

モデルのコメント。

versions

モデルのバージョンをリストした JSON 配列。

default_version_name

バージョンを指定せずにモデルを参照する場合に使用されるモデルのバージョン。

代わりにレジストリにあるモデルのリストを、それぞれ Model インスタンスとして取得するには models メソッドを使用します。

model_list = reg.models()
Copy

レジストリから特定のモデルへの参照を名前で取得するには、レジストリの get_model メソッドを使用します。このメソッドは Model インスタンスを返します。

m = reg.get_model("MyModel")
Copy

注釈

Model インスタンスは、ログされた元のPythonモデルオブジェクトのコピーではなく、レジストリ内の基になるモデルオブジェクトへの参照です。

models メソッドで返されたリストのモデル、または get_model を使用して取得したモデルへの参照があると、 そのメタデータそのバージョン を操作することができます。

モデルのメタデータの表示および更新

レジストリ内のモデルのメタデータ属性(コメント、タグ、メトリックなど)を表示したり、更新したりすることができます。

コメントの取得および更新

モデルのコメントを取得したり、更新したりするには、モデルの comment 属性を使用します。

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

注釈

description 属性は、 comment のエイリアスです。上記のコードは次のようにも記述できます。

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

タグの取得および更新

タグは、モデルの目的、アルゴリズム、トレーニングデータセット、ライフサイクルステージ、その他選択した情報を記録するために使用されるメタデータです。タグは、モデルの登録時または登録後いつでも設定することができます。また、既存のタグの値を更新したり、タグを完全に削除したりすることもできます。

注釈

すべてのタグの名前(およびその可能性のある値)は、 CREATE TAG を使用して事前に定義しておく必要があります。 オブジェクトのタグ付け をご参照ください。

モデルのすべてのタグをPythonディクショナリとして取得するには、 show_tags を使用します。

print(m.show_tags())
Copy

新しいタグを追加したり、既存のタグの値を変更したりするには、 set_tag を使用します。

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

タグの値を取得するには、 get_tag を使用します。

m.get_tag("live_version")
Copy

タグを削除するには、 unset_tag を使用します。

model.unset_tag("live_version")
Copy

モデルバージョンの操作

モデルは、それぞれが文字列で識別されたバージョンをいくつでも持つことができます。希望するバージョンの命名規則を使用することができます。モデルをログすることは、実際にはモデルの 特定のバージョン をログすることです。モデルの追加バージョンをログするには、同じ model_name で異なる version_name を指定して log_model を再度呼び出します。

モデルのバージョンは、 snowflake.ml.model.ModelVersion クラスのインスタンスで表されます。

モデルの全バージョンのリストを取得するには、モデルオブジェクトの versions メソッドを呼び出します。結果は ModelVersion インスタンスのリストです。

version_list = m.versions()
Copy

代わりに DataFrame として各モデルの情報を取得するには、モデルの show_versions メソッドを呼び出します。

version_df = m.show_versions()
Copy

できあがった DataFrame には以下の列が含まれます。

説明

created_on

モデルバージョンが作成された日時

name

バージョンの名前。

database_name

バージョンが格納されているデータベース。

schema_name

バージョンが格納されているスキーマ。

model_name

このバージョンが属するモデルの名前。

is_default_version

このバージョンがモデルのデフォルトバージョンであるかどうかを示すブール値。

functions

このバージョンで使用可能な関数名の JSON 配列。

metadata

JSON メタデータをキーと値のペアとして含むオブジェクト(メタデータが指定されていない場合は {})。

user_data

モデル定義マニフェストの user_data セクションからの JSON オブジェクト(ユーザーデータが指定されていない場合は {})。

モデルバージョンの削除

モデルの delete_version メソッドを使用してモデルのバージョンを削除できます。

m.delete_version("rc1")
Copy

デフォルトバージョン

モデルのバージョンをデフォルトモデルとして指定することができます。モデルの default 属性を取得または設定して、現在のデフォルトバージョンを(ModelVersion オブジェクトとして)取得するか、変更(文字列を使用して)します。

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

モデルバージョンへの参照の取得

モデルの特定バージョンへの参照を ModelVersion インスタンスとして取得するには、モデルの version メソッドを使用します。モデルのデフォルトバージョンを取得するには、モデルの default 属性を使用します。

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

モデルの特定バージョン(前述の例の変数 mv など)への参照を取得すると、そのコメントやメトリクスを取得または更新し、以下のセクションで示すようにモデルのメソッド(関数)を呼び出すことができます。

コメントの取得および更新

モデルと同様に、モデルのバージョンはコメントを持つことができ、モデルのバージョンの comment または description 属性を介してアクセスし、設定することができます。

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

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

メトリクスの取得および更新

メトリクスは、予測精度やその他のモデルバージョンの特性を追跡するために使用されるキーと値のペアです。モデルバージョンの作成時にメトリクスを設定することも、 set_metric メソッドを使用して設定することもできます。メトリクス値は、数値、文字列、リスト、ディクショナリなど、 JSON にシリアル化できる任意のPythonオブジェクトにすることができます。タグとは異なり、メトリクス名と可能な値を事前に定義する必要はありません。

テスト精度のメトリクスは、sklearnの accuracy_score を使用して生成される場合があります。

from sklearn import metrics

test_accuracy = metrics.accuracy_score(test_labels, prediction)
Copy

混同行列はsklearnを使用して同様に生成できます。

test_confusion_matrix = metrics.confusion_matrix(test_labels, prediction)
Copy

そして、これらの値を以下のようにメトリクスとして設定することができます。

# 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

モデルバージョンのメトリクスをPythonディクショナリとして取得するには、 show_metrics を使用します。

metrics = mv.show_metrics()
Copy

メトリクスを削除するには、 delete_metric を呼び出します。

mv.delete_metric("test_accuracy")
Copy

モデルメソッドの呼び出し

モデルのバージョンは、推論や他のモデル操作を実行するための付属関数である メソッド を持つことができます。モデルのバージョンは異なるメソッドを持つことができ、これらのメソッドの署名も異なる場合があります。

モデルバージョンのメソッドを呼び出すには、 mv.run を使用し、呼び出す関数名を指定し、推論データとその他の必要なパラメーターを含む DataFrame を渡します。このメソッドは、Snowflakeウェアハウスで実行されます。

注釈

メソッドを起動すると、レジストリへの接続に使用しているセッションで指定されたウェアハウスでメソッドが実行されます。 ウェアハウスサイズの指定 をご参照ください。

次の例は、モデルの predict メソッドの実行を示しています。このモデルの predict メソッドは、推論データ(ここでは test_features)以外にパラメーターを必要としません。必要な場合は、推論データの後に追加の引数として渡されることになります。

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

指定されたモデルでどのメソッドが呼び出せるかを確認するには、 mv.show_functions を呼び出します。このメソッドの返り値は、 ModelFunctionInfo オブジェクトのリストです。これらの各オブジェクトには以下の属性が含まれます。

  • name: Pythonまたは SQL から呼び出せる関数名。

  • target_method: ログされたPythonモデルのオリジナルメソッドの名前。

SQL でのモデルレジストリの操作

モデルはファーストクラスのスキーマレベルオブジェクトであるため、Snowflake SQL はモデルを操作するためのコマンドを提供します。提供されるコマンドは以下のとおりです。

注釈

Snowflake SQL にはモデルとバージョンを作成するコマンドが含まれていますが、これらはSnowparkモデルレジストリPython API で使用することを想定しています。 モデルおよびバージョンの登録 で示すようにPythonでモデルをログします。

SQL でのモデルメソッドの呼び出し

SQL でのモデルのメソッドは、 model_name!method_name(...) 構文を使用して呼び出すことができます。モデルで利用可能なメソッドは、Pythonのモデルクラスによって決定されます。たとえば、モデルの型の多くは、推論に predict というメソッドを使用します。

モデルの最新バージョンのメソッドを呼び出すには、ここに示す構文を使用して、括弧で囲んで引数をメソッド(ある場合)に渡し、FROM 句にある推論データを含んだテーブルの名前を渡します。

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

モデルの特定バージョンのメソッドを呼び出すには、まずモデルの特定バージョンへのエイリアスを作成し、そのエイリアスを介して目的のメソッドを呼び出します。

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

コストの考慮事項

Snowpark ML モデルレジストリを使用すると、標準的なSnowflake消費ベースのコストが発生します。これらには次が含まれます。

  • モデルのアーティファクト、メタデータ、および関数を格納するためのコスト。ストレージコストに関する一般情報については、 ストレージコストの調査 をご参照ください。

  • ステージ間のファイルをSnowflakeにコピーするコスト。 COPY FILES をご参照ください。

  • モデルやモデルのバージョンの表示、モデルのコメント、タグ、メトリクスの変更など、 Snowsight UI や SQL、Pythonインターフェイスを介したサーバーレスモデルオブジェクト操作のコスト。

  • ウェアハウスコンピューティングコスト。コストは、モデルの型や学習予測に使用するデータ量によって異なります。Snowflakeのコンピューティングコストに関する一般的な情報については、 コンピューティングコストについて をご参照ください。ウェアハウスのコンピューティングコストは、次のような場合に発生します。

    • モデルおよびバージョンの作成操作。

    • モデルのメソッドの呼び出し

特定のモデル型に関する注意事項

このセクションでは、特定の型のモデルをSnowparkモデルレジストリにログするための追加情報を提供します。

Snowpark ML

レジストリは、 Snowpark ML Modeling APIssnowpark.ml.modeling.framework.base.BaseEstimator から派生したモデル)を使用して作成されたモデルをサポートしています。以下の追加オプションは、 options ディクショナリで log_model を呼び出すときに使用できます。

オプション

説明

target_methods

モデルオブジェクトで利用可能なメソッドの名前のリスト。Snowpark ML モデルは、メソッドが存在すると仮定して、デフォルトで以下のターゲットメソッドを持ちます。 predicttransformpredict_probapredict_log_probadecision_function

Snowpark ML モデルをログする際に sample_input_datasignatures を指定する必要はありません。これらはフィッティングの際に自動的に推測されます。

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

レジストリは、scikit-learnを使用して作成されたモデル(sklearn.base.BaseEstimator または sklearn.pipeline.Pipeline から派生したモデル)をサポートしています。以下の追加オプションは、 options ディクショナリで log_model を呼び出すときに使用できます。

オプション

説明

target_methods

scikit-learnモデルはデフォルトで以下のターゲットメソッドを持ちます。 predicttransformpredict_probapredict_log_probadecision_function

scikit-learnモデルをログする場合は、 sample_input_datasignatures のどちらかのパラメーターを指定する必要があります。

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

レジストリは、 XGBoost を使用して作成されたモデル(xgboost.XGBModel または xgboost.Booster から派生したモデル)をサポートしています。以下の追加オプションは、 options ディクショナリで log_model を呼び出すときに使用できます。

オプション

説明

target_methods

モデルオブジェクトで利用可能なメソッドの名前のリスト。 XGBModel から派生したモデルは、メソッドが存在すると仮定して、デフォルトで predictpredict_probaapply のターゲットメソッドを持ちます。 Booster から派生したモデルはデフォルトで predict メソッドを持ちます。

cuda_version

GPU を持つプラットフォームへの展開時に使用する CUDA ランタイムのバージョン。デフォルトで11.7。手動で None に設定した場合、モデルは GPU を持つプラットフォームには展開できません。

レジストリがターゲットメソッドの署名を知ることができるように、 XGBoost モデルをログするときには sample_input_datasignatures のどちらかのパラメーターを指定する必要があります。

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

モデルの forward メソッドが1つ以上の torch.Tensor インスタンスを入力として受け入れ、 torch.Tensor またはそれらのタプルを返す場合、レジストリは、 PyTorch モデル(torch.nn.Module または torch.jit.ModuleScript から派生したクラス)をサポートします。レジストリは、モデルを呼び出して結果を返すときに、pandas DataFrames とテンソルの間で変換します。テンソルはデータフレームの列に対応します。

たとえば、モデルは次のような2つのテンソルを受け入れるとします。

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

torch.Tensor([[1,2],[3,4]])tensor_1 として、また torch.Tensor([[5,6], [7,8]])tensor_2 として渡す場合は、以下のように DataFrame を作成してモデルに渡します。

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

そうすると、 tensors DataFrame は次のようになります。

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

同様に、モデルが (torch.Tensor([[1,2],[3,4]]), torch.Tensor([[5,6], [7,8]])) のような2つのテンソルを返す場合、結果は前述のような DataFrame になります。

PyTorch モデルのサンプル入力データを提供する場合は、テンソルのリスト(pandasの DataFrame に変換される)または DataFrame を提供する必要があります。リストに単一のテンソルを含むことはできますが、テンソル自体は認められません。

モデルのログ

以下の追加オプションは、 options ディクショナリで log_model を呼び出すときに使用できます。

オプション

説明

target_methods

モデルオブジェクトで利用可能なメソッドの名前のリスト。PyTorch モデルのデフォルトは forward です。

cuda_version

GPU を持つプラットフォームへの展開時に使用する CUDA ランタイムのバージョン。デフォルトで11.7。手動で None に設定した場合、モデルは GPU を持つプラットフォームには展開できません。

PyTorch モデルをログする場合は、 sample_input_datasignatures のどちらかのパラメーターを指定する必要があります。

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

tensorflow.Module または tensorflow.keras.Model を拡張するモデルは、テンソルを受け取って返す場合、またコンパイル可能またはコンパイルされる場合にサポートされます。

  • tensorflow.Module__call__ メソッドまたは tensorflow.keras.Modelcall メソッドは、1つ以上の tensorflow.Tensor または tensorflow.Variable を入力として受け入れ、 tensorflow.Tensor または tensorflow.Variable、あるいはこれらの型のタプルを返します。

  • モデルで Module を拡張している場合は、コンパイル可能である必要があります。これは、 __call__ メソッドが @tensorflow.function で装飾されていることを意味します。 tf.functionドキュメント をご参照ください。 Model を拡張している場合は、コンパイルする必要があります。 コンパイルドキュメント をご参照ください。

レジストリは、モデルを呼び出して結果を返すときに、pandas DataFrames とテンソルの間で変換します。テンソルはデータフレームの列に対応します。

たとえば、モデルは次のような2つのテンソルを受け入れるとします。

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

tf.Tensor([[1,2],[3,4]])tensor_1 として、また tf.Tensor([[5,6], [7,8]])tensor_2 として渡す場合は、以下のように DataFrame を作成してモデルに渡します。

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

そうすると、 tensors DataFrame は次のようになります。

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

同様に、モデルが (tf.Tensor([[1,2],[3,4]]), tf.Tensor([[5,6], [7,8]])) のような2つのテンソルを返す場合、結果は前述のような DataFrame になります。

TensorFlow モデルのサンプル入力データを提供する場合は、テンソルのリスト(pandasの DataFrame に変換される)または DataFrame を提供する必要があります。リストに単一のテンソルを含むことはできますが、テンソル自体は認められません。

モデルのログ

以下の追加オプションは、 options ディクショナリで log_model を呼び出すときに使用できます。

オプション

説明

target_methods

モデルオブジェクトで利用可能なメソッドの名前のリスト。TensorFlow モデルのデフォルトは forward です。

cuda_version

GPU を持つプラットフォームへの展開時に使用する CUDA ランタイムのバージョン。デフォルトで11.7。手動で None に設定した場合、モデルは GPU を持つプラットフォームには展開できません。

TensorFlow モデルをログする場合は、 sample_input_datasignatures のどちらかのパラメーターを指定する必要があります。

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

PyFunc フレーバーを提供する MLFlow モデルがサポートされています。MLFlow モデルに署名がある場合、 signature 引数はモデルから推測されます。署名がない場合は、 signaturesample_input_data のどちらかを提供する必要があります。

以下の追加オプションは、 options ディクショナリで log_model を呼び出すときに使用できます。

オプション

説明

model_uri

MLFlow モデルのアーティファクトの URI。モデルのメタデータで model.metadata.get_model_info().model_uri として提供されていない場合は、提供する必要があります。

ignore_mlflow_metadata

True の場合、モデルのメタデータはレジストリのモデルオブジェクトにインポートされません。デフォルト: False

ignore_mlflow_dependencies

True の場合、モデルのメタデータ内の依存関係は無視されます。これは、Snowflakeウェアハウスで利用可能なパッケージを制限するために便利です。デフォルト: False

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

Hugging Faceパイプライン

レジストリは、 transformers.Pipeline から派生する 変換器 として定義されたHugging Faceモデルクラスをサポートしています。次のコードは、互換性のあるモデルをログする例です。

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

重要

huggingface_pipeline.HuggingFacePipelineModel に基づくモデルは構成データのみを含み、モデルの重みはモデルが使用されるたびにHugging Faceハブからダウンロードされます。

モデルレジストリは現在、ウェアハウスへのモデル展開のみをサポートしています。ウェアハウスは、 特別な構成 がない場合は外部ネットワークアクセスをサポートしません。必要な外部アクセス統合が作成されていたとしても、特定のモデルに必要な統合を指定する方法は今のところありません。

現在のベストプラクティスは、上記の例のように transformers.Pipeline を使用することです。これにより、モデルの重みがローカルシステムにダウンロードされ、モデル全体がウェアハウスにアップロードされます。その結果、インターネット接続を必要としない自己完結型モデルができます。

レジストリは、パイプラインが以下のリストからのタスクのみを含む限り、 signatures 引数を推論します。

  • conversational

  • fill-mask

  • question-answering

  • summarization

  • table-question-answering

  • text2text-generation

  • text-classification (エイリアス sentiment-analysis

  • text-generation

  • token-classification (エイリアス ner

  • translation

  • translation_xx_to_yy

  • zero-shot-classification

推論された署名を表示するには、 show_functions メソッドを使用します。たとえば、以下は lmv.show_functions() の結果であり、 lmv は上記でログしたモデルです。

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

この情報があると、次のようにモデルを呼び出すことができます。

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

使用上の注意

  • Hugging Faceのモデルの多くは大型で、標準的なウェアハウスには収まりません。Snowparkに最適化されたウェアハウスを使用するか、モデルの小さいバージョンを選択します。たとえば、 Llama-2-70b-chat-hf モデルを使用する代わりに Llama-2-7b-chat-hf を試してみます。

  • Snowflakeウェアハウスには GPUs はありません。CPU に最適化されたHugging Faceモデルのみを使用します。

  • Hugging Face変換器の中には、入力行ごとにディクショナリの配列を返すものがあります。レジストリはこのような出力を、配列の JSON 表現を含む文字列に変換します。たとえば、複数出力の質問と回答の出力は次のようになります。

    [{"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

レジストリがターゲットメソッドの署名を知ることができるように、Hugging Faceモデルをログする場合は、 sample_input_datasignatures のどちらかのパラメーターを指定する必要があります。

# 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

結果:

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