Amazon SageMaker Trainingで組み込みアルゴリズムを使って機械学習モデルを作成する

記事タイトルとURLをコピーする

サーバーワークスの村上です。

NBAの2022-2023シーズンが終わってしまいましたね。。感動がいっぱいでした、来年も脳死で課金します。

さて、今回はAmazon SageMaker Trainingで選べるオプションの1つである組み込みアルゴリズムを使って機械学習モデルを作成します。

SageMaker Trainingを使うメリット

まず、SageMakerでモデルをトレーニングする理由について考えてみます。

潤沢なコンピューティングリソース

小さいデータセットを使ってトレーニングするのであれば、手元のPCやAmazon SageMaker Studio Labなどの無料で利用できる環境が適しているでしょう。または、トレーニングのためのコードを書く、などの場合もコンピューティングリソースで困ることは少ないはずで、SageMakerを利用しなくて良いかもしれません。

ただ、データセットが大きい場合にメモリが足りないとか、トレーニングをGPU環境で行いたい(でもGPU環境がない)などニーズが出てきます。このような場合に様々なインスタンスタイプが用意されているSageMakerが適しているかと考えます。

私も過去Kaggleに参加してみたとき、トレーニングデータが16GBもあり当時の自分のPCではメモリが足りず、SageMakerを使った経験があります。

blog.serverworks.co.jp

統一されたトレーニング環境

SageMakerのトレーニングや推論はコンテナ環境で行われますので、チーム間で環境を統一することができる、というメリットもあります。コンテナは必ずしも自分で用意する必要はなく、AWSが用意したものを利用できます。

これにより、環境によってライブラリの有無やバージョンに差異があるなどの環境差異をなくすことができます。

準備されたトレーニングコード(組み込みアルゴリズムの場合)

また、本ブログでご紹介する組み込みアルゴリズムではコードがあらかじめ用意されているので、労力が少ないというメリットもあります。

組み込みアルゴリズムでは、実験開始に必要なコードはありません。必要な入力は、データ、ハイパーパラメータ、コンピューティングリソースのみです。これにより、結果やコードの変更を追跡するオーバーヘッドを削減しながら、実験をより迅速に実行できます。 docs.aws.amazon.com

SageMaker Trainingの種類

組み込みアルゴリズム

本ブログで紹介する手法です。

トレーニングコードtransfer_learning.pyがAWS側で用意されているのが特徴です。以下は私のイメージを絵にしたものですが、必要なのはデータだけで、コードやコンテナイメージはあらかじめ用意されたものを利用できます。

利用可能な組み込みアルゴリズムは以下から参照可能です。

docs.aws.amazon.com

スクリプトモード

トレーニングコードや推論コードを自分で用意するのがスクリプトモードです。

例えば任意のライブラリを追加したい場合にスクリプトモードを使用します。

These frameworks also allow you to install any Python package hosted on PyPi by including a requirements.txt file with your training code or to include your own code directories.
また、これらのフレームワークでは、学習コードにrequirements.txtファイルを含めるか、独自のコードディレクトリを含めることによって、PyPiでホストされている任意のPythonパッケージをインストールすることができます。 docs.aws.amazon.com

また、以下のサンプルノートブックでは推論コードを自分で用意することでSHAP値を算出するようにカスタマイズしています。

sagemaker-examples.readthedocs.io

SHAP値については過去の私のブログもご参照いただけますと幸いです。

blog.serverworks.co.jp

BYOC(Bring Your Own Container)

事前に用意されたコンテナ環境を使用したくない場合は自分で用意したコンテナ環境を利用することも可能です。

コンテナイメージをECRにアップロードする必要があるなど、一番手間がかかる代わりに最も自由度が高い手法と言えます。

SageMaker Trainingの手順

今回はLightGBMというアルゴリズムを使用します。

データセットはタイタニックのデータセットを使用します。乗客のデータから生存するかしないかを予測するタスクです。

www.kaggle.com

また、本ブログではAmazon SageMaker Python SDKを使用していきます。流れは以下のとおりです。

  • データセットをS3にアップロード
  • トレーニング用のコンテナイメージを取得
  • トレーニング用のコードを取得
  • 事前学習済みのモデルを取得
  • ハイパーパラメータの指定
  • トレーニングの開始

事前準備、S3アップロード

最初に必要なライブラリのインポートを行います。

import numpy as np
import pandas as pd
import boto3
import sagemaker

sess = sagemaker.Session()
bucket = sess.default_bucket()
role = sagemaker.get_execution_role()
region = boto3.Session().region_name
sm = boto3.Session().client(service_name="sagemaker", region_name=region)

欠損値やカテゴリ変数などの前処理は本題ではないので割愛します。

必要なさそうなカラムはすべて削除して以下のようなデータを準備しました。

トレーニング用のデータと検証用のデータに分割します。

from sklearn.model_selection import train_test_split
train_split, valid_split = train_test_split(train, test_size=0.3, random_state=0)

最後にCSVとして保存し、S3にアップロードします。

# CSVとして保存
train_split.to_csv('./train_split.csv',header=False,index=False)
valid_split.to_csv('./valid_split.csv',header=False,index=False)

# S3にアップロード
prefix = "titanic-lightgbm"
training_dataset_s3_path = sess.upload_data(path='./train_split.csv', bucket=bucket, key_prefix=f"{prefix}/train")
validation_dataset_s3_path = sess.upload_data(path='./valid_split.csv', bucket=bucket, key_prefix=f"{prefix}/validation")
s3_output_location = f"s3://{bucket}/{prefix}/output"

注意しないといけないのが、LightGBMを使う場合、目的変数が最初のカラムに存在する必要があり、CSVにヘッダーが存在してはいけない、という点です。

CSV トレーニングの場合、アルゴリズムはターゲット変数が最初の列にあり、CSV にはヘッダーレコードがないと見なします。 docs.aws.amazon.com

なので、CSVとして保存する際にheader=Falseおよびindex=Falseを指定します

トレーニング用のコンテナイメージ取得

冒頭に記載したとおり、コンテナイメージはあらかじめAWSが用意しています。ユーザー側では、どのコンテナイメージを利用するか指定するだけです。

どのようにコンテナイメージを指定するかですが、まずは公式ドキュメントにアルゴリズムごとのページがありますので、そこを参考にすると良いかと考えます。LightGBMの場合は以下です。

docs.aws.amazon.com

またはSageMaker Python SDKのドキュメントにも事前学習済みのモデルを検索可能なページが存在しますので、そこで検索する方法もあります。

LightGBMで検索すると分類と回帰の2種類がヒットしますが、今回は分類タスクなので、lightgbm-classification-modelを指定します。

sagemaker.readthedocs.io

このように、使いたいモデルから使用するコンテナイメージを決定し指定します。

# コンテナイメージとインスタンスタイプの指定
train_model_id, train_model_version, train_scope = "lightgbm-classification-model", "*", "training"
training_instance_type = "ml.m5.xlarge"

コンテナイメージの取得はsagemaker.image_uris.retrieve()で行います。

# コンテナイメージの取得
train_image_uri = sagemaker.image_uris.retrieve(
    region=region,
    framework=None,
    model_id=train_model_id,
    model_version=train_model_version,
    image_scope=train_scope,
    instance_type=training_instance_type
)

トレーニング用のコードを取得

次にトレーニング用のコードを取得します。方法はさきほどと同じく、モデルIDなどを指定するだけです。

# トレーニング用のコードを取得
from sagemaker import image_uris, model_uris, script_uris
train_source_uri = script_uris.retrieve(
    model_id=train_model_id, model_version=train_model_version, script_scope=train_scope
)

事前学習済みのモデルを取得

この手順は使用するアルゴリズムによっては不要となる可能性があります。

今回使用するLightGBMのモデルは、先ほど紹介したBuilt-in Algorithms with pre-trained Model Tableに存在し、事前学習済みモデルとして提供されています。

よって、トレーニング時にこの事前学習済みモデルをダウンロードする必要があるため、モデルのS3 URIも取得する必要があります。

train_model_uri = model_uris.retrieve(
    model_id=train_model_id, model_version=train_model_version, model_scope=train_scope
)

ハイパーパラメータの指定

つづいてハイパーパラメータを取得します。ここではドキュメントを参考にnum_boost_roundの値を500に増やしています。

from sagemaker import hyperparameters

# 指定したモデルのデフォルトのハイパーパラメータを取得
hyperparameters = hyperparameters.retrieve_default(
    model_id=train_model_id, model_version=train_model_version
)

# ハイパーパラメータをカスタマイズ
hyperparameters["num_boost_round"] = "500"

トレーニングの開始

ここまできたらトレーニングの準備完了です。

まずはトレーニングジョブの名前をつけます。name_from_base()を使えば指定した文字列にタイムスタンプをつけてくれるので便利です。

# トレーニングジョブの名前をつける
from sagemaker.utils import name_from_base
training_job_name = name_from_base(f"built-in-algo-{train_model_id}-training")

今回、組み込みアルゴリズムのトレーニングにはSageMaker Python SDKのEstimatorクラスを利用します。

from sagemaker.estimator import Estimator

estimator = Estimator(
    role=role,
    image_uri=train_image_uri,
    source_dir=train_source_uri,
    model_uri=train_model_uri,
    entry_point="transfer_learning.py",
    instance_count=1,
    instance_type=training_instance_type,
    hyperparameters=hyperparameters,
    output_path=s3_output_location
)

# トレーニングジョブを開始
estimator.fit(
    {
        "train": training_dataset_s3_path,
        "validation": validation_dataset_s3_path,
    }, logs=True, job_name=training_job_name
)

冒頭でトレーニングに使用するインスタンスタイプにml.m5.xlargeを指定していましたが、このインスタンスはトレーニング終了時に自動でターミネートします。課金されるのはもちろん使った分だけで、今回は小さいデータセットでしたので、82秒間だけ課金されたようです(Billable seconds: 82とあります)

また、ハイパーパラメータでnum_boost_roundの値を500に増やしましたが、Did not meet early stopping.とあるようにearly stoppingにはなっておらず、まだ調整の余地はあるようです。

作成されたモデルについて

作成されたモデルはEstimatorインスタンスを作成する際に指定したoutput_path引数のS3に保存されます。

確認するとmodel.tar.gzが保存されていました。

推論環境の作成

ここまででSageMaker Trainingは終了ですが、モデル作成だけだとつまらないので推論環境も作成します。ここまでの手順で作成したEstimatorクラスのdeployメソッドを使って、手軽にリアルタイム推論エンドポイントを構築できます。

推論環境作成の概要

推論環境の作成イメージを再掲します。

再掲:推論環境作成イメージ

流れはトレーニングジョブと似た流れになります。

  • 推論コンテナイメージの取得
  • 推論コードの取得
  • 推論環境のデプロイ

推論コンテナイメージの取得

トレーニング用のコンテナイメージを取得したときと同じメソッドで取得可能です。違いはimage_scope="inference"を指定しているところです。

# 推論環境のインスタンスタイプを指定
inference_instance_type = "ml.m5.large"

# 推論コンテナイメージの取得
deploy_image_uri = sagemaker.image_uris.retrieve(
    region=region,
    framework=None,
    image_scope="inference",
    model_id=train_model_id,
    model_version=train_model_version,
    instance_type=inference_instance_type,
)

推論コードの取得

トレーニング用のコードを取得したときと同じメソッドで取得できます。違いはscript_scope="inference"を指定しているところです。

deploy_source_uri = script_uris.retrieve(
    model_id=train_model_id, model_version=train_model_version, script_scope="inference"
)

推論環境のデプロイ

Estimatorクラスのdeployメソッドを使って推論エンドポイントを作成します。

# モデルとエンドポイントの命名
model_name = name_from_base("built-in-lightgbm")
endpoint_name = name_from_base(f"built-in-{train_model_id}")

# 推論エンドポイントの作成
from sagemaker.predictor import csv_serializer
predictor = estimator.deploy(
    initial_instance_count=1,
    instance_type=inference_instance_type,
    entry_point="inference.py",
    image_uri=deploy_image_uri,
    source_dir=deploy_source_uri,
    endpoint_name=endpoint_name,
    model_name=model_name,
    serializer=csv_serializer
)

Serializerについて

先ほどの手順でserializer=csv_serializerを指定していました。

SerializerはAmazon SageMaker Python SDKに実装された、推論時の入出力のデータ型を変換する機能です。 github.com

LightGBMの場合、推論リクエストのContentTypeはtext/csvである必要があります。

For Inference ContentType, valid inputs must be text/csv.
LightGBM - Amazon SageMaker

今回使用しているEstimatorクラスのdeployメソッドでは、デフォルトでserializer=Noneとなりますが、この場合、SerializerのIdentitySerializer関数が適用され、ContentTypeがapplication/octet-streamになってしまいエラーになってしまいます。

serializer=csv_serializerとすることでtext/csvでリクエストできるようになります。

推論リクエストを送ってみる

それでは最後に推論リクエストを送ってみます。

うまく結果が返ってきました(生存確率38%との結果が出ました)。

response = predictor.predict(data='1,0,51.0,26.5500,0,1')
result = response.decode()
result
# 以下の出力が返る
# {"probabilities-1d": [[0.3795792777920872]], "probabilities": [[0.6204207222079128, 0.3795792777920872]]}

さいごに

以上、SageMaker Trainingで組み込みアルゴリズムを使ってモデルを作成してみました。Serializerまわりでかなり苦労したのですが、Balckbeltに助けられたので感謝です...!

冒頭に記載したようにSageMaker Trainingにはいくつかの手法があり、それぞれのメリット・デメリットを意識して、適切な手法を選択できるようにしたいですね。

村上博哉 (執筆記事の一覧)

2020年4月入社。機械学習が好きです。記事へのご意見など:hiroya.murakami@serverworks.co.jp