やりたいこと
SageMakerの特長や使い方などを学習したかったので、AWSのチュートリアルに沿ってひとまず使い始めてみたかった。
参考にしたサイト
やってみる
S3バケットの作成
まずはS3バケットを作成します。こちらのS3バケットにはチュートリアルで作成するノートブックインスタンスがデータを保存する際に、保存先として使います。
Notebookインスタンスの作成
Amazon SageMakerのコンソールに入り、「Notebook instance >> Create notebook instance」をクリックして、以下の画面からNotebookインスタンスを作成します。こちらのNotebookインスタンスの上で、PythonコードなどをRUNするイメージです。
S3バケットを選択する設定の部分は、先に作成したS3バケット名を入力します。
IAMロールは新規に作成し、Root accessはEnableのままとしておきます。ここまで設定したら「Create notebook instance」ボタンをクリックします。
notebookインスタンスの作成・起動が開始され、少し待つとSageMakerのコンソール画面ではStatusが「InService」となります。
Notebookインスタンスを使ってデータの準備をする
notebookインスタンスの起動が完了したので「Open Jupyter」をクリックします。
右上の「New」からconta_python3
を選択して、Pythonを実行していくnotebookコンソールを表示します。
さて、ここからPythonを記述していきます。本記事のサンプルコードは全て参考資料にもあるAWS公式チュートリアルからコピーしてきています。
まずは以下のコードを張り付けてRUNをクリックします。ちなみにShift
+Enter
でもRUNとなります。 以下のコードでは必要なライブラリをインポートしたり、環境変数を定義したりしています。
# import libraries
import boto3, re, sys, math, json, os, sagemaker, urllib.request
from sagemaker import get_execution_role
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import Image
from IPython.display import display
from time import gmtime, strftime
from sagemaker.predictor import csv_serializer
# Define IAM role
role = get_execution_role()
prefix = 'sagemaker/DEMO-xgboost-dm'
my_region = boto3.session.Session().region_name # set the region of the instance
# this line automatically looks for the XGBoost image URI and builds an XGBoost container.
xgboost_container = sagemaker.image_uris.retrieve("xgboost", my_region, "latest")
print("Success - the MySageMakerInstance is in the " + my_region + " region. You will use the " + xgboost_container + " container for your SageMaker endpoint.")
次に以下のコードを貼り付けます。'your-s3-bucket-name'の個所は、任意のBuket名に置き換えます。先のステップとは別のS3バケットとなるので、新規の名前を付けてください。(そうでないと作成エラーとなります)
また、オプションですが、バケット作成先のリージョンも併せて変更してもOKです。
bucket_name = 'your-s3-bucket-name' # <--- CHANGE THIS VARIABLE TO A UNIQUE NAME FOR YOUR BUCKET
s3 = boto3.resource('s3')
try:
if my_region == 'us-east-1':
s3.create_bucket(Bucket=bucket_name)
else:
s3.create_bucket(Bucket=bucket_name, CreateBucketConfiguration={ 'LocationConstraint': my_region })
print('S3 bucket created successfully')
except Exception as e:
print('S3 error: ',e)
次に以下のコードを張り付けてRUN。サンプルデータをダウンロートしているのと、分析用に使えるようにPandasというライブラリのデータフレームにデータを入れています。model_data
の部分です。
try:
urllib.request.urlretrieve ("https://d1.awsstatic.com/tmt/build-train-deploy-machine-learning-model-sagemaker/bank_clean.27f01fbbdf43271788427f3682996ae29ceca05d.csv", "bank_clean.csv")
print('Success: downloaded bank_clean.csv.')
except Exception as e:
print('Data load error: ',e)
try:
model_data = pd.read_csv('./bank_clean.csv',index_col=0)
print('Success: Data loaded into dataframe.')
except Exception as e:
print('Data load error: ',e)
どんなデータかは以下のコードをRUNすれば最初の5行が見れます。
model_data.head()
次に以下のコードをRUNします。データをシャッフルした上で、トレーニング用のデータとテスト(検証)用のデータに7:3の割合で分割してます。
train_data, test_data = np.split(model_data.sample(frac=1, random_state=1729), [int(0.7 * len(model_data))])
print(train_data.shape, test_data.shape)
MLモデルのトレーニング
引き続きnotebookに記述していきます。
以下のコードでは、準備したトレーニング用のデータに対して、形式を整えてS3バケットにcsvとしてアップロードしています。RUNします。
pd.concat([train_data['y_yes'], train_data.drop(['y_no', 'y_yes'], axis=1)], axis=1).to_csv('train.csv', index=False, header=False)
boto3.Session().resource('s3').Bucket(bucket_name).Object(os.path.join(prefix, 'train/train.csv')).upload_file('train.csv')
s3_input_train = sagemaker.inputs.TrainingInput(s3_data='s3://{}/{}/train'.format(bucket_name, prefix), content_type='csv')
次に、sagemakerのセッションを作成して、XGBoostモデル(機械学習の広く使われるモデル)用のインスタンスを作成します。
sess = sagemaker.Session()
xgb = sagemaker.estimator.Estimator(xgboost_container,role, instance_count=1, instance_type='ml.m4.xlarge',output_path='s3://{}/{}/output'.format(bucket_name, prefix),sagemaker_session=sess)
xgb.set_hyperparameters(max_depth=5,eta=0.2,gamma=4,min_child_weight=6,subsample=0.8,silent=0,objective='binary:logistic',num_round=100)
トレーニングJobを開始します。以下のコードをRUN。
xgb.fit({'train': s3_input_train})
ml.m4.xlarge instanceの上でトレーニングJobを実施しているのですが、思ったよりも待ちました。以下のように「Training secounds, Billable secounds」のログが出力されれば完了です。
モデルのデプロイ
MLモデルのトレーニングが終わったあとは、そのモデルをデプロイします。この辺もAmazon Sagemakerですんなりとできちゃうのが便利ですね。
以下のコードを実行します。5分弱くらい待ちました。
xgb_predictor = xgb.deploy(initial_instance_count=1,instance_type='ml.m4.xlarge')
「------!」ログが確認できれば完了してそうです。(公式ドキュメントもそうだった)
これでモデルに対するエンドポイントが使えるようになりました。
デプロイしたエンドポイントを使っていきます。
テスト用データセットから各顧客が商品に申し込んだか、申し込んでいないかどうかの予測を実施します。
from sagemaker.serializers import CSVSerializer
test_data_array = test_data.drop(['y_no', 'y_yes'], axis=1).values #load the data into an array
xgb_predictor.serializer = CSVSerializer() # set the serializer type
predictions = xgb_predictor.predict(test_data_array).decode('utf-8') # predict!
predictions_array = np.fromstring(predictions[1:], sep=',') # and turn the prediction into an array
print(predictions_array.shape)
モデルの評価
以下のコードをRUNして、作成しトレーニングしたモデルがどの程度の性能かを評価しています。
cm = pd.crosstab(index=test_data['y_yes'], columns=np.round(predictions_array), rownames=['Observed'], colnames=['Predicted'])
tn = cm.iloc[0,0]; fn = cm.iloc[1,0]; tp = cm.iloc[1,1]; fp = cm.iloc[0,1]; p = (tp+tn)/(tp+tn+fp+fn)*100
print("\n{0:<20}{1:<4.1f}%\n".format("Overall Classification Rate: ", p))
print("{0:<15}{1:<15}{2:>8}".format("Predicted", "No Purchase", "Purchase"))
print("Observed")
print("{0:<15}{1:<2.0f}% ({2:<}){3:>6.0f}% ({4:<})".format("No Purchase", tn/(tn+fn)*100,tn, fp/(tp+fp)*100, fp))
print("{0:<16}{1:<1.0f}% ({2:<}){3:>7.0f}% ({4:<}) \n".format("Purchase", fn/(tn+fn)*100,fn, tp/(tp+fp)*100, tp))
テストデータ中の89.5%の顧客データに対して、申込みをしなかった場合は90%、申込みを行った場合は63%の精度で予測ができたと結果がでました。
以上までが、SageMaker利用によるメインの部分です。以降はクリーンアップ作業となります。
クリーンアップ
まずはSageMakerのエンドポイントを削除。
xgb_predictor.delete_endpoint(delete_endpoint_config=True)
次にS3バケットの中身を削除。
以下のコードではS3バケット自体は削除していないので、残ってしまって気持ち悪い方はコンソールからS3バケットも削除してOKです。
bucket_to_delete = boto3.resource('s3').Bucket(bucket_name)
bucket_to_delete.objects.all().delete()
ここまでが作業となります。
おわりに
かなり簡単に開発環境の構築や、作成したモデルのデプロイまで実施できた印象です。機械学習のプロジェクトでは、開発環境の構築やメンバー間での環境の共有、アクセスの設定、デプロイ先の環境の構築など、データ分析作業以外にも負担になりそうな設定・構築が、Amazon Sagemakerを使うと大分簡略化できるだろうなと思いました。データ分析エンジニアにとってもハッピーな環境構築ができるのではないでしょうか!