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モデルレジストリは、以下の型のモデルをサポートしています。
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")
注釈
このパブリックプレビューでは、 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)
ここで log_model
の引数を説明します。
必要な引数
引数 |
説明 |
---|---|
|
サポートされているモデル型のPythonモデルオブジェクト。シリアル化可能(「ピクル化可能」)である必要があります。 |
|
モデルの名前。レジストリでモデルを識別するために |
|
モデルのバージョンを指定する文字列。レジストリでモデルを識別するために |
注釈
モデル名とバージョンの組み合わせは、スキーマ内で一意にする必要があります。
オプションの引数
引数 |
説明 |
---|---|
|
モデルをロードまたは展開する際にインポートするコードディレクトリへのパスのリスト。 |
|
コメント、たとえばモデルの説明など。 |
|
モデルに必要なCondaパッケージのリスト。この引数は Conda形式、つまり |
|
モデルと一緒にピクルする外部モジュールのリスト。scikit-learn、Snowpark ML、 PyTorch、 TorchScript、およびカスタムモデルのサポート。 |
|
モデルバージョンにリンクされたメトリクスを含むディクショナリ。 |
|
モデル作成のオプションを含むディクショナリ。次のオプションはすべてのモデル型で利用できます。
個々のモデル型によっては、追加オプションをサポートしている場合もあります。 特定のモデル型に関する注意事項 をご参照ください。 |
|
モデルで必要な PyPI パッケージのパッケージ仕様のリスト。 |
|
モデル内で実行するPythonのバージョン。デフォルトは |
|
サンプル入力データを含む DataFrame。この DataFrame から、モデルが必要とする特徴量名とその型が抽出されます。Snowpark ML と MLFlow 以外のモデルでは、この引数か |
|
メソッド署名を、ターゲットメソッド名から入力と出力の署名へのマッピングとしてモデル化します。Snowpark ML と MLFlow 以外のモデルでは、この引数か |
log_model
は、レジストリに追加されたモデルのバージョンを表す snowflake.ml.model.ModelVersion
オブジェクトを返します。
一度登録されると、モデル自体を変更することはできません(メタデータを変更することはできます)。モデルとそのすべてのバージョンを削除するには、レジストリの delete_model メソッドを使用します。
モデルの削除¶
レジストリの delete_model
メソッドを使用して、モデルとそのすべてのバージョンを削除します。
reg.delete_model("mymodel")
レジストリからのモデルの取得¶
各モデルの情報を得るには、 show_models
メソッドを使用します。
model_df = reg.show_models()
show_models
の結果は、pandas DataFrame です。利用可能な列は以下のとおりです。
列 |
説明 |
---|---|
created_on |
モデルが作成された日時。 |
name |
モデルの名前。 |
database_name |
モデルが格納されているデータベース。 |
schema_name |
モデルが格納されているスキーマ。 |
owner |
モデルを所有するロールの名前。 |
comment |
モデルのコメント。 |
versions |
モデルのバージョンをリストした JSON 配列。 |
default_version_name |
バージョンを指定せずにモデルを参照する場合に使用されるモデルのバージョン。 |
代わりにレジストリにあるモデルのリストを、それぞれ Model
インスタンスとして取得するには models
メソッドを使用します。
model_list = reg.models()
レジストリから特定のモデルへの参照を名前で取得するには、レジストリの get_model
メソッドを使用します。このメソッドは Model
インスタンスを返します。
m = reg.get_model("MyModel")
注釈
Model
インスタンスは、ログされた元のPythonモデルオブジェクトのコピーではなく、レジストリ内の基になるモデルオブジェクトへの参照です。
models
メソッドで返されたリストのモデル、または get_model
を使用して取得したモデルへの参照があると、 そのメタデータ と そのバージョン を操作することができます。
モデルのメタデータの表示および更新¶
レジストリ内のモデルのメタデータ属性(コメント、タグ、メトリックなど)を表示したり、更新したりすることができます。
コメントの取得および更新¶
モデルのコメントを取得したり、更新したりするには、モデルの comment
属性を使用します。
print(m.comment)
m.comment = "A better description than the one I provided originally"
注釈
description
属性は、 comment
のエイリアスです。上記のコードは次のようにも記述できます。
print(m.description)
m.description = "A better description than the one I provided originally"
モデルバージョンの操作¶
モデルは、それぞれが文字列で識別されたバージョンをいくつでも持つことができます。希望するバージョンの命名規則を使用することができます。モデルをログすることは、実際にはモデルの 特定のバージョン をログすることです。モデルの追加バージョンをログするには、同じ model_name
で異なる version_name
を指定して log_model
を再度呼び出します。
モデルのバージョンは、 snowflake.ml.model.ModelVersion
クラスのインスタンスで表されます。
モデルの全バージョンのリストを取得するには、モデルオブジェクトの versions
メソッドを呼び出します。結果は ModelVersion
インスタンスのリストです。
version_list = m.versions()
代わりに DataFrame として各モデルの情報を取得するには、モデルの show_versions
メソッドを呼び出します。
version_df = m.show_versions()
できあがった DataFrame には以下の列が含まれます。
列 |
説明 |
---|---|
created_on |
モデルバージョンが作成された日時 |
name |
バージョンの名前。 |
database_name |
バージョンが格納されているデータベース。 |
schema_name |
バージョンが格納されているスキーマ。 |
model_name |
このバージョンが属するモデルの名前。 |
is_default_version |
このバージョンがモデルのデフォルトバージョンであるかどうかを示すブール値。 |
functions |
このバージョンで使用可能な関数名の JSON 配列。 |
metadata |
JSON メタデータをキーと値のペアとして含むオブジェクト(メタデータが指定されていない場合は |
user_data |
モデル定義マニフェストの |
モデルバージョンの削除¶
モデルの delete_version
メソッドを使用してモデルのバージョンを削除できます。
m.delete_version("rc1")
デフォルトバージョン¶
モデルのバージョンをデフォルトモデルとして指定することができます。モデルの default
属性を取得または設定して、現在のデフォルトバージョンを(ModelVersion
オブジェクトとして)取得するか、変更(文字列を使用して)します。
default_version = m.default
m.default = "2"
モデルバージョンへの参照の取得¶
モデルの特定バージョンへの参照を ModelVersion
インスタンスとして取得するには、モデルの version
メソッドを使用します。モデルのデフォルトバージョンを取得するには、モデルの default
属性を使用します。
mv = m.version("1")
mv = m.default
モデルの特定バージョン(前述の例の変数 mv
など)への参照を取得すると、そのコメントやメトリクスを取得または更新し、以下のセクションで示すようにモデルのメソッド(関数)を呼び出すことができます。
コメントの取得および更新¶
モデルと同様に、モデルのバージョンはコメントを持つことができ、モデルのバージョンの comment
または description
属性を介してアクセスし、設定することができます。
print(mv.comment)
print(mv.description)
mv.comment = "A model version comment"
mv.description = "Same as setting the comment"
メトリクスの取得および更新¶
メトリクスは、予測精度やその他のモデルバージョンの特性を追跡するために使用されるキーと値のペアです。モデルバージョンの作成時にメトリクスを設定することも、 set_metric
メソッドを使用して設定することもできます。メトリクス値は、数値、文字列、リスト、ディクショナリなど、 JSON にシリアル化できる任意のPythonオブジェクトにすることができます。タグとは異なり、メトリクス名と可能な値を事前に定義する必要はありません。
テスト精度のメトリクスは、sklearnの accuracy_score
を使用して生成される場合があります。
from sklearn import metrics
test_accuracy = metrics.accuracy_score(test_labels, prediction)
混同行列はsklearnを使用して同様に生成できます。
test_confusion_matrix = metrics.confusion_matrix(test_labels, prediction)
そして、これらの値を以下のようにメトリクスとして設定することができます。
# 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)
モデルバージョンのメトリクスをPythonディクショナリとして取得するには、 show_metrics
を使用します。
metrics = mv.show_metrics()
メトリクスを削除するには、 delete_metric
を呼び出します。
mv.delete_metric("test_accuracy")
モデルメソッドの呼び出し¶
モデルのバージョンは、推論や他のモデル操作を実行するための付属関数である メソッド を持つことができます。モデルのバージョンは異なるメソッドを持つことができ、これらのメソッドの署名も異なる場合があります。
モデルバージョンのメソッドを呼び出すには、 mv.run
を使用し、呼び出す関数名を指定し、推論データとその他の必要なパラメーターを含む DataFrame を渡します。このメソッドは、Snowflakeウェアハウスで実行されます。
注釈
メソッドを起動すると、レジストリへの接続に使用しているセッションで指定されたウェアハウスでメソッドが実行されます。 ウェアハウスサイズの指定 をご参照ください。
次の例は、モデルの predict
メソッドの実行を示しています。このモデルの predict
メソッドは、推論データ(ここでは test_features
)以外にパラメーターを必要としません。必要な場合は、推論データの後に追加の引数として渡されることになります。
remote_prediction = mv.run(test_features, function_name="predict")
指定されたモデルでどのメソッドが呼び出せるかを確認するには、 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>;
モデルの特定バージョンのメソッドを呼び出すには、まずモデルの特定バージョンへのエイリアスを作成し、そのエイリアスを介して目的のメソッドを呼び出します。
WITH <model_version_alias> AS MODEL <model_name> VERSION <version>
SELECT <model_version_alias>!<method_name>(...) FROM <table_name>;
コストの考慮事項¶
Snowpark ML モデルレジストリを使用すると、標準的なSnowflake消費ベースのコストが発生します。これらには次が含まれます。
モデルのアーティファクト、メタデータ、および関数を格納するためのコスト。ストレージコストに関する一般情報については、 ストレージコストの調査 をご参照ください。
ステージ間のファイルをSnowflakeにコピーするコスト。 COPY FILES をご参照ください。
モデルやモデルのバージョンの表示、モデルのコメント、タグ、メトリクスの変更など、 Snowsight UI や SQL、Pythonインターフェイスを介したサーバーレスモデルオブジェクト操作のコスト。
ウェアハウスコンピューティングコスト。コストは、モデルの型や学習予測に使用するデータ量によって異なります。Snowflakeのコンピューティングコストに関する一般的な情報については、 コンピューティングコストについて をご参照ください。ウェアハウスのコンピューティングコストは、次のような場合に発生します。
モデルおよびバージョンの作成操作。
モデルのメソッドの呼び出し
特定のモデル型に関する注意事項¶
このセクションでは、特定の型のモデルをSnowparkモデルレジストリにログするための追加情報を提供します。
Snowpark ML¶
レジストリは、 Snowpark ML Modeling APIs (snowpark.ml.modeling.framework.base.BaseEstimator
から派生したモデル)を使用して作成されたモデルをサポートしています。以下の追加オプションは、 options
ディクショナリで log_model
を呼び出すときに使用できます。
オプション |
説明 |
---|---|
|
モデルオブジェクトで利用可能なメソッドの名前のリスト。Snowpark ML モデルは、メソッドが存在すると仮定して、デフォルトで以下のターゲットメソッドを持ちます。 |
Snowpark ML モデルをログする際に sample_input_data
や signatures
を指定する必要はありません。これらはフィッティングの際に自動的に推測されます。
例¶
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¶
レジストリは、scikit-learnを使用して作成されたモデル(sklearn.base.BaseEstimator
または sklearn.pipeline.Pipeline
から派生したモデル)をサポートしています。以下の追加オプションは、 options
ディクショナリで log_model
を呼び出すときに使用できます。
オプション |
説明 |
---|---|
|
scikit-learnモデルはデフォルトで以下のターゲットメソッドを持ちます。 |
scikit-learnモデルをログする場合は、 sample_input_data
か signatures
のどちらかのパラメーターを指定する必要があります。
例¶
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"')
XGBoost¶
レジストリは、 XGBoost を使用して作成されたモデル(xgboost.XGBModel
または xgboost.Booster
から派生したモデル)をサポートしています。以下の追加オプションは、 options
ディクショナリで log_model
を呼び出すときに使用できます。
オプション |
説明 |
---|---|
|
モデルオブジェクトで利用可能なメソッドの名前のリスト。 |
|
GPU を持つプラットフォームへの展開時に使用する CUDA ランタイムのバージョン。デフォルトで11.7。手動で |
レジストリがターゲットメソッドの署名を知ることができるように、 XGBoost モデルをログするときには sample_input_data
か signatures
のどちらかのパラメーターを指定する必要があります。
例¶
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¶
モデルの 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)
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]]])
そうすると、 tensors
DataFrame は次のようになります。
0 1
0 [1, 2] [5, 6]
1 [3, 4] [7, 8]
同様に、モデルが (torch.Tensor([[1,2],[3,4]]), torch.Tensor([[5,6], [7,8]]))
のような2つのテンソルを返す場合、結果は前述のような DataFrame になります。
PyTorch モデルのサンプル入力データを提供する場合は、テンソルのリスト(pandasの DataFrame に変換される)または DataFrame を提供する必要があります。リストに単一のテンソルを含むことはできますが、テンソル自体は認められません。
モデルのログ¶
以下の追加オプションは、 options
ディクショナリで log_model
を呼び出すときに使用できます。
オプション |
説明 |
---|---|
|
モデルオブジェクトで利用可能なメソッドの名前のリスト。PyTorch モデルのデフォルトは |
|
GPU を持つプラットフォームへの展開時に使用する CUDA ランタイムのバージョン。デフォルトで11.7。手動で |
PyTorch モデルをログする場合は、 sample_input_data
か signatures
のどちらかのパラメーターを指定する必要があります。
例¶
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¶
tensorflow.Module
または tensorflow.keras.Model
を拡張するモデルは、テンソルを受け取って返す場合、またコンパイル可能またはコンパイルされる場合にサポートされます。
tensorflow.Module
の__call__
メソッドまたはtensorflow.keras.Model
のcall
メソッドは、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
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]]])
そうすると、 tensors
DataFrame は次のようになります。
0 1
0 [1, 2] [5, 6]
1 [3, 4] [7, 8]
同様に、モデルが (tf.Tensor([[1,2],[3,4]]), tf.Tensor([[5,6], [7,8]]))
のような2つのテンソルを返す場合、結果は前述のような DataFrame になります。
TensorFlow モデルのサンプル入力データを提供する場合は、テンソルのリスト(pandasの DataFrame に変換される)または DataFrame を提供する必要があります。リストに単一のテンソルを含むことはできますが、テンソル自体は認められません。
モデルのログ¶
以下の追加オプションは、 options
ディクショナリで log_model
を呼び出すときに使用できます。
オプション |
説明 |
---|---|
|
モデルオブジェクトで利用可能なメソッドの名前のリスト。TensorFlow モデルのデフォルトは |
|
GPU を持つプラットフォームへの展開時に使用する CUDA ランタイムのバージョン。デフォルトで11.7。手動で |
TensorFlow モデルをログする場合は、 sample_input_data
か signatures
のどちらかのパラメーターを指定する必要があります。
例¶
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¶
PyFunc フレーバーを提供する MLFlow モデルがサポートされています。MLFlow モデルに署名がある場合、 signature
引数はモデルから推測されます。署名がない場合は、 signature
か sample_input_data
のどちらかを提供する必要があります。
以下の追加オプションは、 options
ディクショナリで log_model
を呼び出すときに使用できます。
オプション |
説明 |
---|---|
|
MLFlow モデルのアーティファクトの URI。モデルのメタデータで |
|
|
|
|
例¶
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)
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')
重要
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')
]
)}]
この情報があると、次のようにモデルを呼び出すことができます。
import pandas as pd
remote_prediction = lmv.run(pd.DataFrame(["Hello, how are you?"], columns=["inputs"]))
使用上の注意¶
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.\""}]
レジストリがターゲットメソッドの署名を知ることができるように、Hugging Faceモデルをログする場合は、 sample_input_data
か signatures
のどちらかのパラメーターを指定する必要があります。
例¶
# 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.", ""],
]
)
)
結果:
0 [{"label": "negative", "score": 0.8106237053871155}, {"label": "neutral", "score": 0.16587384045124054}]
1 [{"label": "neutral", "score": 0.9263970851898193}, {"label": "positive", "score": 0.05286872014403343}]