リアルタイム推論のためのモデルの展開( REST API )

注釈

snowflake-ml-pythonバージョン1.25.0以降で一般提供されています。

低レイテンシを必要とするインタラクティブなワークフローに、リアルタイム推論を使用します。Snowflakeモデルレジストリ から、専用の HTTP エンドポイントを持つマネージドサービスとして任意のモデルをデプロイできます。マネージドサービスは自動スケーリングを備えており、Snowflakeエコシステム内に完全に統合され、包括的な可観測性を提供します。

次の場合に、ワークフローにオンライン推論を使用します。

  • アプリケーションで、即座に応答するために低レイテンシが必要

  • モデルは、ユーザー向けのウェブまたはモバイルアプリケーションのバックエンドとして機能します。

  • モデルへの入力は、リクエストの HTTP ペイロード内に適合します。

  • 変動するリクエスト量を処理するために、サービスは自動的に水平方向にスケールする必要があります。

仕組み

Snowflakeは、モデルを Snowpark Container Services( SPCS ) 内の HTTP サーバーとしてホストすることで、展開パイプラインを簡素化します。このアーキテクチャにより、次のことが可能になります。

  • 複雑さの抽象化: DockerイメージやKubernetesクラスターを管理することなく、高度なモデルをデプロイできます。

  • パフォーマンスのスケーリング: 高いパフォーマンス要件のために、分散された GPU クラスターで大規模スケールモデルを実行できます。

  • 信頼性の確保: 組み込みの可観測性、トラフィック分割、およびシャドウ/カナリア展開を利用して、モデルをシームレスにアップグレードできます。

前提条件

始める前に、以下をご確認ください。

  • 商用の AWS 、Azure、またはGoogle CloudリージョンのSnowflakeアカウント。政府リージョンはサポートされていません。

  • snowflake-ml-python Pythonパッケージのバージョン1.8.0以降。

  • Snowflakeモデルレジストリ にログインしたモデル。

  • コンピューティングプール および SPCS での関連権限に関する理解。

必要な権限

Model Servingは Snowpark Container Services 上で実行されます。Model Servingを使用するには以下の権限が必要です。

  • サービスが実行されるコンピューティングプールに対する USAGE または OWNERSHIP 。または、デフォルトのシステムコンピューティングプールを使用できます。

  • パブリックエンドポイントを作成できるアカウントに対する BIND SERVICE ENDPOINT 権限。

  • モデルに対する OWNER または READ 権限

制限事項

Snowpark Container Servicesでのオンラインモデルサービングには、以下の制限が適用されます。

  • テーブル関数はサポートされていません。モデルをSnowflakeに展開するためにはテーブル関数が必要です。

  • Snowpark ML モデリングクラスを使用して開発されたモデルは、 GPU を持つ環境にはデプロイできません。回避策として、ネイティブモデルを抽出して、それをデプロイすることができます。詳細については、 オンライン推論のためのモデルの展開 をご参照ください。

オンライン推論のためのモデルの展開

Snowflake ML は、モデルバージョンオブジェクトを使用して、推論リクエストを処理するモデルサービスを作成します。モデルバージョンオブジェクトを作成するには、新しいモデルバージョンをログに記録するか、既存のモデルバージョンの参照を取得します。モデルのバージョンオブジェクトを取得したら、以下のPythonコードを使ってモデルサービスを作成し、そのサービスを SPCS にデプロイできます:

# reg is a snowflake.ml.registry.Registry object
example_mv_object = reg.get_model("mymodel_name").version("version_name") # a snowflake.ml.model.ModelVersion object

example_mv_object.create_service(service_name="myservice",
                  service_compute_pool="my_compute_pool",
                  ingress_enabled=True,
                  gpu_requests=None)
Copy

create_service には以下の引数が必要です。

  • service_name:作成するサービス名。この名前はSnowflakeアカウント内で一意である必要があります。

  • service_compute_pool:モデルの実行に使用しているコンピューティングプールの名前。コンピューティングプールはすでに存在している必要があります。モデルがシステムコンピューティングプールに適合していれば、それら( SYSTEM_COMPUTE_POOL_GPU または SYSTEM_COMPUTE_POOL_CPU ) も使用できます。

  • ingress_enabled:Snowflakeの外部からオンライン推論を呼び出すには、これがTrueである必要があります。

  • gpu_requests:GPUs の数を指定する文字列。CPU または複数の GPUs のいずれかで実行できるモデルの場合、この引数は、モデルが CPU と GPUs のどちらで実行されるかを決定します。モデルが CPU でのみ実行できる既知のタイプである場合 (scikit-learnモデルなど)、 GPUs をリクエストするとイメージビルドに失敗します。新しいモデルをデプロイする場合、 CPU 駆動型モデルのサービスを作成するのに最大10分、 GPU 駆動型モデルを作成するのに20分かかります。コンピューティングプールがアイドル状態であるか、サイズ変更が必要な場合は、サービスの作成に時間がかかる可能性があります。

前述の例は、必須かつ最もよく使われる引数のみを示しています。引数の完全なリストは ModelVersion API リファレンスをご参照ください。

デフォルトのサービス構成

デプロイしたモデルを実行するサーバーは、ほとんどのユースケースで機能するデフォルトを使用します。

  • ワーカースレッド数:CPU 駆動型モデルの場合、サーバーが使用するプロセス数は CPUs の2倍+1個です。GPU 駆動型はワーカープロセスを1つ使用します。create_serviceコールではnum_workers引数を使用して、これをオーバーライドできます。モデルがメモリに収まる最小 GPU ノードを指定することを お勧め します。インスタンス数を増やしてスケールします。例えば、モデルが GPU_NV_S (Azureの GPU_NV_SM )インスタンスタイプに適合する場合、gpu_requests=1を使用し、max_instancesを増やしてスケールアップします。ただし、利用可能な最小ノードに4個のGPUs があり、必要なものが2つだけの場合は、 num_workers=2 (つまり、利用可能なgpu/モデルに必要なgpu)を使用します。

  • スレッドセーフティ:スレッドセーフでないモデルもあります。そのため、サービスはワーカープロセスごとにモデルの別個のコピーをロードします。その結果、大規模なモデルではリソースが枯渇する可能性があります。

  • ノード利用:デフォルトでは、1つの推論サーバーインスタンスは、実行するノードのすべての CPU とメモリをリクエストすることで、ノード全体をリクエストします。インスタンスごとのリソース割り当てをカスタマイズするには、cpu_requests、memory_requests、gpu_requestsなどの引数を使用します。

  • エンドポイント:推論エンドポイントはinferenceと名付けられ、ポート5000を使用します。これらはカスタマイズできません。リソースの利用率を最適化するには、モデルがメモリに収まる最小 GPU ノードを指定します。インスタンス数を増やして、ワークロードに合わせてスケールします。例えば、モデルが GPU_NV_S (Azureの GPU_NV_SM )インスタンスタイプに適合する場合、gpu_requests=1を使用し、max_instancesを増やしてスケールアップします。

コンテナイメージのビルド動作

Snowflakeのcondaチャネルはウェアハウスでのみ利用可能で、ウェアハウスの依存関係の唯一のソースです。デフォルトでは、 SPCS モデルのconda依存関係はconda-forgeから依存関係を取得します。

デフォルトでは、Snowflake Model Servingは、モデルの実行に使用されるのと同じコンピューティングプールを使用してコンテナイメージを構築します。このコンピューティングプールは、イメージの構築プロセスにはオーバーパワーである可能性が高いです(例えば、 GPUs は、コンテナイメージの構築には使用されません)。ほとんどの場合、これはコンピューティングコストに大きな影響を与えません。それでも心配な場合は、image_build_compute_pool引数を使用してイメージを構築するためによりパワーが控えめのコンピューティングプールを指定できます。

create_service()を複数回呼び出しても、呼び出すたびに構築がトリガーされるわけではありません。

しかし、コンテナイメージは、snowflakeが依存パッケージの脆弱性の修正を含む推論サービスの更新を行った場合、再構築される可能性があります。この場合、create_serviceは自動的にイメージの再構築をトリガーします。

ユーザーインターフェイス

展開されたモデルは、Model Registry Snowsight UI で管理できます。詳細については、 モデル推論サービス をご参照ください。

デプロイ済みモデルの呼び出し

HTTP エンドポイント

すべてのサービスには内部 DNS 名前が付いています。ingress_enabledでサービスをデプロイすると、Snowflake外で利用可能なパブリック HTTP エンドポイントも作成されます。どちらのエンドポイントもサービスを呼び出すのに使用できます。

SHOW ENDPOINTS コマンドを使用して、イングレスを有効にしたサービスのパブリック HTTP エンドポイントを見つけることができます。出力にはingress_url列が含まれ、これには unique-service-id-account-id.snowflakecomputing.app形式のエントリがあります。これは一般に公開されているサービスの HTTP エンドポイントです。プライベートリンクユーザーの場合は、ingress_urlではなくprivatelink_ingress_urlを使用してください。

Snowflakeでの内部 DNS 名を取得するには、 DESCRIBE SERVICE コマンドを使用します。このコマンドによる出力のdns_name列に、サービスの内部 DNS 名が含まれます。サービスのポートを見つけるには、SHOW ENDPOINTS IN SERVICE コマンドを使用します。ポートまたはport_range列には、サービスが使用するポートが含まれています。URL http://dns_name:port を介してサービスを内部で呼び出すことができます。

モデルの特定のメソッドを呼び出すには、メソッド名を URL (例: https://unique-service-id-account-id.snowflakecomputing.app/method-name または http://dns_name:port/<method-name> )へのパスとして使用します。URL で、メソッド名のアンダースコア(_)は URL 内でダッシュ(-)に置き換えられます。たとえば、predict_probのサービス名は、 URL でpredict-probaに変更されます。

簡略化するために、Pythonでは、list_services() API を ModelVersion オブジェクトで呼び出すことができます。

# mv: snowflake.ml.model.ModelVersion
mv.list_services()
Copy

これは、パブリックエンドポイント( inference_endpoint )および内部エンドポイント( internal_endpoint )両方を出力します。

認証

Snowflakeは複数の認証プロトコルをサポートしています。最もシンプルなのは、トークンを Authorization: Snowflake Token="your_pat_token" としてリクエストヘッダーに単純に渡すことができる プログラムのアクセストークン( PAT ) を使用することです。

注釈

不正なトークンやサービスへのネットワークの欠落など、認証の失敗はすべて404エラーでサービスの結果にルーティングされます。現在、認証エラーと無効な URLs を区別する方法はありません。

認証

デフォルトでは、サービス所有者のみがエンドポイントを使用できます。別のロールがエンドポイントにアクセスできるようにするには、サービス所有者は :doc:`サービスロールに </sql-reference/sql/grant-service-role>`ALL_ENDPOINTS_USAGE を付与します。

リクエスト本文(またはプロトコルまたはデータ形式)

Snowflakeは、 REST リクエストについて2種類のデータ形式をサポートしています。特に業界でよく知られており、Pandas DataframeのシンプルなPythonスクリプトを使用して顧客が検証できることから、Pandas Dataframeの影響を受けています。

Tip

メソッドから URL へのマッピング: リクエスト URL 作成時、モデルのメソッド名のアンダースコア( _ )は自動的にダッシュ( - )に置き換えられることに注意してください。例えば、モデルメソッドが predict_proba の場合、エンドポイント URL パスは /predict-proba になります。

形式の詳細は次のとおりです

  1. dataframe_split は、コンパクトなインデックス/列/データ表現です。

  • pandas_df.to_json(orient="split") を反映する表現。

  1. dataframe_records はキー/値(記録指向)表現です。

  • df.to_json(orient="records") を反映する表現。

dataframe_split 形式を使用することを お勧め します。dataframe_records は各行で列名を繰り返すため、通常、 dataframe_split よりも大きなリクエスト本文を生成します。これは、大規模なバッチや頻繁な呼び出しのパフォーマンスに影響を与える可能性があります。

モデルエンドポイントは、使用する入力形式に関係なく、 単一の出力形式 を返し続けます。

  1. dataframe_split 形式(推奨)

これは、Pandasの「分割」方向によって生成される構造と一致します。リクエスト本文は、 dataframe_split キーの下に以下の構造をラップします:

  • index:行インデックスのリスト。

  • columns:列名のリスト。

  • data:行のリスト。各行は列に沿った値のリストです。

cURL リクエストの例:

curl -X POST "<endpoint_url>" \
  -H 'Authorization: Snowflake Token="<pat_token>"' \
  -H 'Content-Type: application/json' \
  -w "\n\n=== RESULT ===\nHTTP Status: %{http_code}\nTotal Time: %{time_total}s\nConnect Time: %{time_connect}s\nServer Processing: %{time_starttransfer}s\nResponse Size: %{size_download} bytes\nRequest Size: %{size_upload} bytes\n" \
 -d '{
       "dataframe_split": {
         "index": [0, 1],
         "columns": ["customer_id", "age", "monthly_spend"],
         "data": [
            [101, 32, 85.5],
            [102, 45, 120.0],
         ]
       }
     }'
Copy
  1. dataframe_records 形式

dataframe_records は、 Pandas記録指向 によって生成される構造と一致します。

  • 記録のリスト では、各記録は 列名 にマッピングするディクショナリです。

リクエスト本文は dataframe_records キーの下にこのリストをラップします:

cURL リクエストの例:

curl -X POST "<endpoint_url>" \
  -H 'Authorization: Snowflake Token="<pat_token>"' \
  -H 'Content-Type: application/json' \
  -w "\n\n=== RESULT ===\nHTTP Status: %{http_code}\nTotal Time: %{time_total}s\nConnect Time: %{time_connect}s\nServer Processing: %{time_starttransfer}s\nResponse Size: %{size_download} bytes\nRequest Size: %{size_upload} bytes\n" \
 -d '{
       "dataframe_records": [
          {
            "customer_id": 101,
            "age": 32,
            "monthly_spend": 85.5,
          },
          {
            "customer_id": 102,
            "age": 45,
            "monthly_spend": 120.0,
          },
        ]
     }'
Copy

パラメーターの引き渡し

モデルの署名に、:doc:`ParamSpec</developer-guide/snowflake-ml/model-registry/model-signature>`で定義されたパラメーターが含まれている場合、JSONリクエスト本文のトップレベルに:code:`dataframe_split`または:code:`dataframe_records`と並べて:code:`params`キーを含めることで、パラメーター値を渡すことができます。上書きするパラメーターのみを含めてください。指定しなかったパラメーターについては、署名のデフォルト値が使用されます。

パラメーターを使用した:code:`cURL`リクエストの例:

curl -X POST "<endpoint_url>/predict" \
  -H 'Authorization: Snowflake Token="<pat_token>"' \
  -H 'Content-Type: application/json' \
  -d '{
        "dataframe_split": {
            "index": [0],
            "columns": ["input_text"],
            "data": [["Hello, world!"]]
        },
        "params": {"temperature": 0.9, "max_tokens": 512}
      }'
Copy

:code:`params`キーは、:code:`dataframe_records`形式においても同様に機能します。

curl -X POST "<endpoint_url>/predict" \
  -H 'Authorization: Snowflake Token="<pat_token>"' \
  -H 'Content-Type: application/json' \
  -d '{
        "dataframe_records": [
            {"input_text": "Hello, world!"}
        ],
        "params": {"temperature": 0.9, "max_tokens": 512}
      }'
Copy

Pythonの例

  1. dataframe_split 形式

Snowflakeは、 Pandas JSON シリアル化 を使用してペイロードを生成し、リクエストを送信する前に json.loads で逆シリアル化することをお勧めしています。これにより、データ型の一貫した処理が保証されます。

import json
import pandas as pd
import requests

# Example DataFrame
df = pd.DataFrame(
    {
        "customer_id": [101, 102],
        "age": [32, 45],
        "monthly_spend": [85.5, 120.0],
    }
)

ENDPOINT_URL = "<your endpoint URL>"
HEADERS = {
    "Authorization": f'Snowflake Token="{PAT}"',
    "Content-Type": "application/json"
}

# Use Pandas to generate the JSON, then load it back to a Python dict
split_obj = json.loads(df.to_json(orient="split"))

payload = {
    "dataframe_split": split_obj
}

response = requests.post(
    ENDPOINT_URL,
    headers=HEADERS,
    json=payload,
    timeout=30,
)

result = response.json()
Copy

重要なポイント:

  • pd.Dataframe.to_json(例: df.to_json(orient="split") )を使用して、ネイティブJSONシリアライザーにとってなじみのないタイムスタンプ、浮動小数点数、NULL、カテゴリなどの型を正しく処理します。

  • json.loads(...) は JSON 文字列をPythonディクショナリに変換するので、ペイロードを適切に構築することができます。

  • requests.post(..., json=payload) はディクショナリを HTTP リクエストの JSON にシリアル化します。

パラメーターを含めるには、ペイロードディクショナリに:code:`params`キーを追加します。

payload = {
    "dataframe_split": split_obj,
    "params": {"temperature": 0.9, "max_tokens": 512}
}
Copy
  1. dataframe_records 形式

dataframe_split と同様に、Pandas JSON シリアル化と json.loads を使用します:

import json
import pandas as pd
import requests

df = pd.DataFrame(
    {
        "customer_id": [101, 102],
        "age": [32, 45],
        "monthly_spend": [85.5, 120.0],
    }
)

ENDPOINT_URL = "<your endpoint invoke URL>"
HEADERS = {
    "Authorization": "Bearer <your token>",
    "Content-Type": "application/json",
}

records_obj = json.loads(df.to_json(orient="records"))

payload = {
    "dataframe_records": records_obj
}

response = requests.post(
    ENDPOINT_URL,
    headers=HEADERS,
    json=payload,
    timeout=30,
)

response.raise_for_status()
result = response.json()
Copy

次のステップ

これらの詳細ガイドを調べて、推論サービスを最適化し管理します。

  • ワークフローの例:XGBoost ( CPU )、Hugging Face(GPU)、および PyTorch モデルのエンドツーエンドコードをご参照ください。

  • サービス管理とスケーリング: 自動スケーリング、手動停止、ハードウェア構成について学びます。

  • 安定したエンドポイントと API リファレンス: Snowflake Gateway、認証、およびデータプロトコル( dataframe_split )を掘り下げます。

  • 自動キャプチャ推論ログ: モデルモニタリングのために自動ログを設定します。

  • トラブルシューティング: パッケージの競合、 OOM エラー、ビルドの失敗の一般的な修正。