背景
SageMakerで学習済みモデルを使って推論を行う際、その学習済みモデルのエンドポイントを作成していましたが、バッチ変換を用いる事で、わざわざエンドポイントを作成しなくても推論を行えそうなため(※)、バッチ変換を試してみる事にしました。
(※おそらく、内部的にはエンドポイントは作成されていて、推論完了後に自動でエンドポイントが削除されていると思います。)
環境
sagemaker 2.219.0
試した事(概要)
こちらの記事で作成した学習済みXGBoostモデルを使って、バッチ変換で、Nishikaのテスト(test)データを推論してみます。
試した事(詳細)
こちらの記事の5.7.まで行っていて、学習済みXGBoostモデルのmodel.tar.gzファイルは作成済みの所から始めていきます。
1. Notebookインスタンスの.ipynbファイルでコードを記載
1.1. 必要なライブラリーをインポート
from datetime import datetime
import os
import numpy as np
import pandas as pd
import boto3
import sagemaker
1.2. 初期変数を設定
ROLE = sagemaker.get_execution_role()
SAGEMAKER_SESSION = sagemaker.Session()
S3_BUCKET = SAGEMAKER_SESSION.default_bucket()
S3_PREFIX = "data-for-machine-learning/for_sagemaker_algorithm_XGBoost"
TEST_DATA_PATH = "test/input_test_data.csv"
1.3. テスト(test)ファイルを取得
Nishikaのコンペサイトからダウンロードして、Notebookインスタンスのローカルにアップロードしていたtest.csvファイルを、DataFrameとして変数test_dfに保存します。
test_df = pd.read_csv(filepath_or_buffer="data/Nishika_ApartmentPrice/test.csv",
converters={"面積(㎡)": str},
encoding="utf-8")
1.4. テスト(test)データを前処理
こちらの記事の5.10.と同様の形で、テスト(test)データを前処理します。
test_feature_df = test_df[["市区町村コード", "最寄駅:距離(分)", "面積(㎡)", "建ぺい率(%)", "容積率(%)"]].dropna(how="any").copy()
test_feature_df["面積(㎡)"] = test_feature_df["面積(㎡)"].apply(lambda x: "2000" if x == "2000㎡以上" else x)
test_feature_df["面積(㎡)"] = test_feature_df["面積(㎡)"].astype("int")
test_feature_df["最寄駅:距離(分)"] = test_feature_df["最寄駅:距離(分)"].apply(lambda x: "45" if x == "30分?60分" else "75" if x == "1H?1H30" else "105" if x == "1H30?2H" else "120" if x == "2H?" else x)
test_feature_df["最寄駅:距離(分)"] = test_feature_df["最寄駅:距離(分)"].astype("int")
test_feature_df["建ぺい率(%)"] = test_feature_df["建ぺい率(%)"].astype("int")
test_feature_df["容積率(%)"] = test_feature_df["容積率(%)"].astype("int")
print(test_feature_df)
市区町村コード 最寄駅:距離(分) 面積(㎡) 建ぺい率(%) 容積率(%)
0 1101 26 75 40 60
1 1101 1 55 80 600
2 1101 2 15 80 400
3 1101 2 45 80 400
4 1101 3 20 80 400
... ... ... ... ... ...
19453 47201 16 75 60 200
19454 47201 16 15 60 200
19456 47201 11 65 60 200
19457 47208 45 60 60 200
19458 47208 6 55 60 150
[19032 rows x 5 columns]
1.5. 前処理したテスト(test)データをCSVファイルにしてS3へアップロード
まずは、前処理したDataFrameの変数test_feature_dfを、NotebookインスタンスのローカルにCSVファイルとして保存します。
その際に、組み込みアルゴリズムXGBoostはヘッダー(カラム名)を不要なので、このタイミングで外します。
test_feature_df.to_csv("./data/Nishika_ApartmentPrice/input_test_data.csv",
header=False,
index=False)
続いて、Notebookインスタンスのローカルに保存したCSVファイルを、S3にアップロードします。
test_data_file_path = os.path.join(S3_PREFIX, TEST_DATA_PATH)
boto3.Session().resource("s3").Bucket(S3_BUCKET).Object(test_data_file_path).upload_file("./data/Nishika_ApartmentPrice/input_test_data.csv")
マネジメントコンソールで確認すると、キチンとS3にアップロードされています。
1.6. 学習後XGBoostモデルのオブジェクトを作成
まずは、AWS側で用意している、組み込みアルゴリズムXGBoostモデルのDockerコンテナのURIを取得します。
xgboost_container = sagemaker.image_uris.retrieve(framework="xgboost",
region="ap-northeast-1",
version="1.5-1")
print(xgboost_container)
354813040037.dkr.ecr.ap-northeast-1.amazonaws.com/sagemaker-xgboost:1.5-1
続いて、作成済みの学習後XGBoostモデルのファイルであるmodel.tar.gzファイルを使って、学習後XGBoostモデルのオブジェクトを作成します。
s3_xgboost_folder_path = "s3://" + os.path.join(S3_BUCKET, S3_PREFIX)
best_training_job_name = "test-20246274039-008-ab936e1f"
trained_xgboost_model = sagemaker.model.Model(image_uri=xgboost_container,
model_data="{a}/{b}/output/model.tar.gz".format(a=s3_xgboost_folder_path,
b=best_training_job_name),
role=ROLE,
predictor_cls=sagemaker.predictor.RealTimePredictor)
print(type(trained_xgboost_model))
<class 'sagemaker.model.Model'>
設定している引数に関しては、こちらの記事に記載しました。
1.7. バッチ変換のオブジェクトを作成
学習済みXGBoostモデルのオブジェクトで、transformerクラスを使って、バッチ変換のオブジェクトを作成します。
transformerクラスの引数の一覧はこちらになります。
trained_xgboost_batch_transform = trained_xgboost_model.transformer(instance_count=1,
instance_type="ml.m5.large",
strategy="MultiRecord",
assemble_with="Line",
output_path="{a}/test/result".format(a=s3_xgboost_folder_path),
max_payload=1)
print(type(trained_xgboost_batch_transform))
<class 'sagemaker.transformer.Transformer'>
今回は下記の引数を設定しました。
■instance_count
バッチ変換のジョブを走らせるインスタンスの個数
■instance_type
バッチ変換のジョブを走らせるインスタンスの種類
インスタンス毎の価格は
■strategy
バッチ変換のジョブへの入力データを、1レコードずつ投げるか、複数レコードずつ投げるか
SingleRecordかMultiRecordを設定
■assemble_with
バッチ変換のジョブからの出力データで、レコードを1行ずつ分けるか、分けないか
LineかNoneを設定
■output_path
バッチ変換のジョブからの出力データを、S3のどこに保存するか
フォルダ名を設定
■max_payload
バッチ変換のジョブへ入力データとしてレコードを投げる際のリクエストサイズを、何MBにするか
0~100の整数型で設定
1.8. バッチ変換のジョブを実施して、アパート価格を予測(推論)
作成したバッチ変換のオブジェクトで、transformメソッドを行って、バッチ変換のジョブを実施します。
transformメソッドの引数の一覧はこちらになります。
now = datetime.now()
year = str(now.year)
month = str(now.month)
day = str(now.day)
hour = str(now.hour)
minute = str(now.minute)
second = str(now.second)
job_timestamp = year + month + day + hour + minute + second
batch_transform_job_name = "test-{a}".format(a=job_timestamp)
trained_xgboost_batch_transform.transform(data="{a}/test/input_test_data.csv".format(a=s3_xgboost_folder_path),
data_type="S3Prefix",
content_type="text/csv",
split_type="Line",
job_name=batch_transform_job_name,
wait=True,
logs=True)
.................................../miniconda3/lib/python3.8/site-packages/xgboost/compat.py:36: FutureWarning: pandas.Int64Index is deprecated and will be removed from pandas in a future version. Use pandas.Index with the appropriate dtype instead.
今回は下記の引数を設定しました。
■data
バッチ変換のジョブへの入力データとなるファイルのS3パス
■data_type
バッチ変換のジョブへの入力データとなるファイルがS3にあるならばS3Prefixを設定
おそらく、S3以外の場合はManifestFileを設定
■content_type
バッチ変換のジョブへの入力データとなるファイルのタイプ
今回はCSVファイルなので、text/csvを設定
■split_type
バッチ変換のジョブへの入力データとなるファイルは、1レコード毎をどのように区切っているか
今回は1行毎に1レコードなので、Lineを設定
■job_name
バッチ変換のジョブの名前を設定
■wait
バッチ変換のジョブが完了するまで待つかどうか
■logs
バッチ変換のジョブに関するメッセージを表示するかどうか
様々なメッセージが表示された後、バッチ変換のジョブが実施されて、無事に完了しました。
マネジメントコンソールのSageMakerの「推論 > バッチ変換ジョブ」でも完了を確認出来ました。
2. 予測(推論)結果を確認
学習済みXGBoostモデルのオブジェクトで、transformerクラスを使って、バッチ変換のオブジェクトを作る際に、引数output_pathで指定したS3のフォルダに、バッチ変換のジョブでの出力ファイルが保存されています。
(ファイル名は、入力データのファイル名に.outを付与したものになります。)
このファイルをダウンロードして、Notebookインスタンスのローカルにアップロードして、中身を確認してみると、
test_result_df = pd.read_csv(filepath_or_buffer="./data/Nishika_ApartmentPrice/input_test_data.csv.out",
header=None,
names=["result"],
encoding="utf-8")
print(test_result_df)
result
0 7.107569
1 7.272198
2 6.398515
3 6.810840
4 6.300151
... ...
19027 7.279865
19028 6.331160
19029 7.168963
19030 7.256274
19031 7.215110
[19032 rows x 1 columns]
テスト(test)データに対する、19032個のアパート価格の予測(推論)結果になります。
以上になります。
まとめ
学習済みXGBoostモデルをデプロイしてエンドポイントを作成せず、バッチ変換のジョブを行う事で、学習済みXGBoostモデルでの推論を行う事が出来ました。
個人的には、こちらの形で推論を行う方が好きかもです(笑)
参考