Amazon SageMakerの推論エンドポイントでA/Bテストのためのバリアント追加・削除を試してみる
データ事業本部の鈴木です。
推論エンドポイントではproduction variants(以降、バリアント)を使うことにより簡単に新旧のモデルをエンドポイントに並行してデプロイし、加重ルーティングなどの制御を行いつつ評価することができます。
今回はSageMaker Python SDKおよびboto3を使ってどのようにバリアントを追加・削除していくのか確認しました。
バリアントについて
推論エンドポイント背後でモデルをホストするためのインスタンスの定義をするためのものになります。
この仕組みを使うことにより、同一の推論エンドポイントの裏に異なるバージョンのモデルをデプロイできます。インスタンス数はスケーリング設定を行うことができます。
各インスタンスへのトラフィックの重み設定も容易に行えるほか、エンドポイントへのアクセス時にバリアントを明示的に指定することで特定のバリアントに対してアクセスすることもできます。
バリアントを使ったA/Bテストについて
バリアントを複数デプロイすることでA/Bテストを行うこともできます。多くの場合SageMaker 推論エンドポイントの前段にAPI Gateway + Lambdaなどの構成を配置するなどしてサービス提供することになると思います。ユーザーによって対応するバリアントを指定してアクセスするよう、APIを設計することになるでしょう。
A/Bテストについては、推論エンドポイントの機能だけにはなりますが、以下のガイド・ブログで例が紹介されています。
本記事も一つ目のリンクのガイドの実装を参考にしています。
エンドポイントへのアクセス時にバリアントを指定しない場合は、単一のバリアントしかデプロイされていないときはそのバリアントに、複数あるときは設定した重みに応じてランダムにルーティングされます。実装にもよりますが、A/Bテスト時以外はバリアントを指定しないようにしているなら、バリアント削除の際は新しいバリアントにしかルーティングしないようにしてから古いバリアントを削除するのが安全です。
やってみた
今回は想定される操作をSageMaker StudioのJupyterLabスペースのノートブックで、通して実施してみました。
イメージおよびライブラリは以下です。
- SageMaker Distribution 2.4.1
- boto3 1.36.23
- SageMaker Python SDK 2.240.0
1. モデルのトレーニングと作成
以下のブログで紹介されているirisデータセット向けの分類モデルを2つ訓練しました。データの前準備までは内容が重複するため省略します。
num_round
ハイパーパラメータだけ少し値を変えました。
# 一つ目のモデル
from sagemaker.inputs import TrainingInput
estimator = sagemaker.estimator.Estimator(
sagemaker_session=sagemaker_session,
base_job_name="xgboost-iris",
image_uri=image,
role=role,
instance_count=1,
instance_type='ml.m4.xlarge',
volume_size=30,
output_path=s3_output_path,
max_run=600)
estimator.set_hyperparameters(
objective="multi:softmax",
num_class=3,
num_round=10,
eval_metric="merror")
train_input = TrainingInput(s3_input_path, content_type="csv")
estimator.fit({'train': train_input})
# 二つ目のモデル(処理内容自体は一つ目と全く同じ)
from sagemaker.inputs import TrainingInput
estimator_2 = sagemaker.estimator.Estimator(
sagemaker_session=sagemaker_session,
base_job_name="xgboost-iris2",
image_uri=image,
role=role,
instance_count=1,
instance_type='ml.m4.xlarge',
volume_size=30,
output_path=s3_output_path,
max_run=600)
# num_roundだけ変える
estimator_2.set_hyperparameters(
objective="multi:softmax",
num_class=3,
num_round=15,
eval_metric="merror")
train_input = TrainingInput(s3_input_path, content_type="csv")
estimator_2.fit({'train': train_input})
それぞれ、異なるモデルとしてSageMakerに登録しました。
from sagemaker.model import Model
model = Model(
image_uri=image,
model_data=estimator.model_data,
name='iris-model-a',
role=role)
model.create(
instance_type="ml.m5.xlarge"
)
model2 = Model(
image_uri=image,
model_data=estimator_2.model_data,
name='iris-model-b',
role=role)
model2.create(
instance_type="ml.m5.xlarge"
)
モデルを確認すると、指定した名前で2つモデルが作成されていました。
2. バリアントの作成とエンドポイントのデプロイ
つづいて、モデルを指定した2つのバリアントを作成しました。ml.c5.4xlargeインスタンスを各バリアントに対して1つずつ作成します。
from sagemaker.session import production_variant
variant1 = production_variant(
model_name=model.name,
instance_type="ml.c5.4xlarge",
initial_instance_count=1,
variant_name="Variant1",
initial_weight=1,
)
variant2 = production_variant(
model_name=model2.name,
instance_type="ml.c5.4xlarge",
initial_instance_count=1,
variant_name="Variant2",
initial_weight=1,
)
バリアントよりエンドポイントを作成しました。
endpoint_name = "DEMO-xgb-iris-pred"
print(f"EndpointName={endpoint_name}")
sagemaker_session.endpoint_from_production_variants(
name=endpoint_name, production_variants=[variant1, variant2]
)
実行すると、以下のようにStudioからエンドポイントを作成していることが確認できました。バリアントは2つ設定されています。
なお、今回は先に紹介したガイドに沿ってバリアントから新規にエンドポイントを作成しましたが、既に運用中のエンドポイントで行う場合は、後ほど紹介する例と同じく、新しいエンドポイント設定を作成してupdate_endpoint
でエンドポイントを更新するのがよいと思います。
3. 推論の実行
エンドポイントを起動できたので、推論が実行できるか確認してみました。まずSageMaker Python SDKでバリンアントを指定して行っています。(指定しなければ加重ルーティングされます。)
from sagemaker.predictor import Predictor
from sagemaker.serializers import CSVSerializer
predictor = Predictor(endpoint_name=endpoint_name, serializer=CSVSerializer())
response = predictor.predict(data=test_data_no_target.values, target_variant="Variant1")
print("Endpoint Response:", response.decode("utf-8"))
response = predictor.predict(data=test_data_no_target.values, target_variant="Variant2")
print("Endpoint Response:", response.decode("utf-8"))
以下のように結果が返ってきました。
また、boto3からも推論できます。こちらはSageMaker Python SDKと異なり、どのバリアントで推論したかなどのレスポンスを取得できます。
sm_runtime = boto3.Session().client("sagemaker-runtime")
csv_text = '6.1 2.8 4.7 1.2'
response = sm_runtime.invoke_endpoint(
EndpointName=endpoint_name,
ContentType='text/csv',
Body=csv_text
)
response['InvokedProductionVariant']
# 'Variant2'
推論エンドポイントのメトリクスはコンソールから確認することができました。
4. エンドポイントの更新
バリアント2の方が結果がよいことを確認できた、としてエンドポイントにデプロイしているバリアントを2だけに更新しました。
まずはバリアントの重みをバリアント2が100%になるように更新しました。
import time
sm = boto_session.client("sagemaker")
sm.update_endpoint_weights_and_capacities(
EndpointName=endpoint_name,
DesiredWeightsAndCapacities=[
{"DesiredWeight": 0, "VariantName": variant1["VariantName"]},
{"DesiredWeight": 1, "VariantName": variant2["VariantName"]},
],
)
print("Waiting for update to complete")
while True:
status = sm.describe_endpoint(EndpointName=endpoint_name)["EndpointStatus"]
if status in ["InService", "Failed"]:
print("Done")
break
print(".", end="", flush=True)
time.sleep(1)
{
variant["VariantName"]: variant["CurrentWeight"]
for variant in sm.describe_endpoint(EndpointName=endpoint_name)["ProductionVariants"]
}
# Waiting for update to complete
# ....................................................................Done
# {'Variant1': 0.0, 'Variant2': 1.0}
コンソールから確認すると、エンドポイントは以下のようになっていました。
なお、この状態でもVariant1での推論はバリアントを指定するとできました。バリアント指定しない場合のルーティングがされなくなっただけのようです。
続いて、不要になったVariant1を削除しました。これには、まず新しいエンドポイント設定を作成しました。
sm.create_endpoint_config(
EndpointConfigName = 'DEMO-xgb-iris-pred-after-ab-testing',
ProductionVariants = [
{
"VariantName": "Variant2",
"InstanceType": "ml.c5.4xlarge",
"InitialInstanceCount": 1,
"InitialVariantWeight": 1,
"ModelName": model2.name
}
]
)
この設定で推論エンドポイントを更新しました。
sm.update_endpoint(
EndpointName=endpoint_name,
EndpointConfigName='DEMO-xgb-iris-pred-after-ab-testing'
)
補足
エンドポイントの削除
この記事を見ている方は検証目的が多いと思うので、エンドポイントの削除方法も記載します。
各種SDKから削除できますが、ここではboto3を使った削除を紹介します。エンドポイント名を指定して簡単に削除できます。
response = sm.delete_endpoint(
EndpointName=endpoint_name
)
操作に使うSDKおよびAPI
今回紹介した操作をするのに、SDKが多く登場しました。ここは実際に手を動かしてみないと混乱しやすいです。
以下がありました。
複数バリアントを使ったモデルホスティングの位置付けについて
現状、Amazon SageMakerでは多数のモデルホスティングの選択肢があります。
- シングルモデルエンドポイント
- マルチバリアントエンドポイント
- マルチモデルエンドポイント
- マルチコンテナエンドポイント
- 推論コンポーネントによるデプロイ
今回は2つ目の方法について説明しました。上4つの選択肢内でマルチバリアントエンドポイントがどういう立ち位置なのかについては、以下のブログに紹介がありました。
複数のモデルをホストできるという点はほかの複数モデルをホストするエンドポイントと同じですが、バリアントごとにインスタンスが起動するのは特徴かなと思いました。
また、マルチバリアントエンドポイントで運用する場合、デプロイが頻繁なケースではデプロイメントガードレールを使うように案内されていました。気になる方はご確認ください。
最後に
推論エンドポイントでA/Bテストなどを想定してバリアントの追加・削除を行う例をご紹介しました。参考になりましたら幸いです。