自前のXGBoostコードをSageMakerコードに変換する

PoCやKaggleで書くコードを、SageMakerで仕組み化する場合にどうコードを変換するのかわかりにくかったので、まとめる。


# 変換前
乳がんの二値分類をXGboostを用いて判別したコード。
[こちら](https://blog.amedama.jp/entry/2019/01/29/235642)のサイトを参考にさせていただいた。

```py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import xgboost as xgb
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

"""XGBoost で二値分類するサンプルコード"""
# 乳がんデータセットを読み込む
dataset = datasets.load_breast_cancer()
x, y = dataset.data, dataset.target
# データセットを学習用とテスト用に分割する
train_x, test_x, train_y, test_y = train_test_split(x, y,
                                                    test_size=0.2,
                                                    shuffle=True,
                                                    random_state=42,
                                                    stratify=y)
# さらに学習用データを学習用とvalid用に分割する
tr_x, va_x, tr_y, va_y = train_test_split(train_x, train_y,
                                                    test_size=0.2,
                                                    shuffle=True,
                                                    random_state=42,
                                                    stratify=train_y)
# XGBoost が扱うデータセットの形式に直す
dtrain = xgb.DMatrix(tr_x, label=tr_y)
dvalid = xgb.DMatrix(va_x, label=va_y)
dtest = xgb.DMatrix(test_x)
# 学習用のパラメータ
xgb_params = {
    # 二値分類問題
    'objective': 'binary:logistic',
    # 評価指標
    'eval_metric': 'logloss',
}
# モデルを学習する
# バリデーションデータもモデルに渡し、学習の進行とともにスコアがどう変わるかモニタリングする
# watchlistには学習データおよびバリデーションデータをセットする
watchlist = [(dtrain, 'train'), (dvalid, 'eval')]
model = xgb.train(xgb_params,
                dtrain,
                num_boost_round=50,  # 学習ラウンド数は適当
                evals=watchlist
                )
# 予測：検証用データが各クラスに分類される確率を計算する
pred_proba = model.predict(dtest)
# しきい値 0.5 で 0, 1 に丸める
pred = np.where(pred_proba > 0.5, 1, 0)
# 精度 (Accuracy) を検証する
acc = accuracy_score(test_y, pred)
print('Accuracy:', acc)
```

以上、よくある形式。52行


# SageMakerでXGBoostを行う場合

```py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import xgboost as xgb
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

"""XGBoost で二値分類するサンプルコード"""
# 乳がんデータセットを読み込む
dataset = datasets.load_breast_cancer()
x, y = dataset.data, dataset.target
# データセットを学習用とテスト用に分割する
train_x, test_x, train_y, test_y = train_test_split(x, y,
                                                    test_size=0.2,
                                                    shuffle=True,
                                                    random_state=42,
                                                    stratify=y)
# さらに学習用データを学習用とvalid用に分割する
tr_x, va_x, tr_y, va_y = train_test_split(train_x, train_y,
                                                    test_size=0.2,
                                                    shuffle=True,
                                                    random_state=42,
                                                    stratify=train_y)
##### ここから異なる ####################################################
import pandas as pd
### 最初の列に目的変数を挿入
tr = np.insert(tr_x, 0, tr_y, axis=1)
va = np.insert(va_x, 0, va_y, axis=1)

# ローカルにcsv作成(ヘッダなし)
pd.DataFrame(tr).to_csv('train.csv', header=False, index=False)
pd.DataFrame(va).to_csv('validation.csv', header=False, index=False)

### S3にアップ
import sagemaker
from sagemaker import get_execution_role

# default_bucket=None　を任意で指定することも可能
# https://sagemaker.readthedocs.io/en/stable/session.html
sagemaker_session = sagemaker.Session()

# バケット: sagemaker-<region>-<アカウントID> に保存される。(上記で指定しなかったため)
input_train = sagemaker_session.upload_data(path='train.csv', key_prefix='sagemaker/xgb-breast-cancer')
input_validation = sagemaker_session.upload_data(path='validation.csv', key_prefix='sagemaker/xgb-breast-cancer')

### content_typeを指定する
from sagemaker.session import s3_input

s3_input_train = s3_input(s3_data=input_train, content_type='text/csv')
s3_input_validation = s3_input(s3_data=input_validation, content_type='text/csv')

### SageMaker XGboostのコンテナパス取得
import boto3
from sagemaker.amazon.amazon_estimator import get_image_uri
container = get_image_uri(boto3.Session().region_name, 'xgboost')

### estimatorの設定
role = get_execution_role() # sagemaker ノートブックインスタンス、Studioからのみ有効

sess = sagemaker.Session()

xgb = sagemaker.estimator.Estimator(container,
                                    role, 
                                    train_instance_count=1, 
                                    train_instance_type='ml.m4.xlarge',
                                    sagemaker_session=sess)
### ハイパーパラメータの設定
### num_roundはここでしか宣言できない
xgb.set_hyperparameters(
    objective='binary:logistic',
    eval_metric='logloss',
    num_round=50
)

### トレーニングジョブの実行
xgb.fit({'train': s3_input_train, 'validation': s3_input_validation})

### エンドポイントのデプロイ（予測を実施するサーバを作成）
xgb_predictor = xgb.deploy(initial_instance_count = 1, instance_type = 'ml.m4.xlarge')

from sagemaker.predictor import csv_serializer

xgb_predictor.content_type = 'text/csv'
xgb_predictor.serializer = csv_serializer
xgb_predictor.deserializer = None

### 予測の実行
pred_proba_sagemaker = xgb_predictor.predict(test_x).decode('utf-8')

### 整形
pred_proba_sagemaker_arr = np.fromstring(pred_proba_sagemaker[0:], sep=',')

##### ここまで。以後同じ ####################################################
pred_sagemaker = np.where(pred_proba_sagemaker_arr > 0.5, 1, 0)

# 精度 (Accuracy) を検証する
acc_sagemaker = accuracy_score(test_y, pred_sagemaker)
print('Accuracy:', acc_sagemaker)
```
100行。

# 詳細
後で書く

# まとめ
・単にモデルを作って検証するだけなら、SageMakerの機能は使わない方がスムーズ。強いて言えば、GPUで並列処理が必要な巨大が学習機能が必要な場合は、SpotインスタンスのPシリーズ並列学習を利用する。くらい。  
・SageMaker Studioで実施すれば、都度インスタンスを切り替える必要なし。かつ、インスタンス上でSageMaker用処理なしで実験可能。  
・実験は、SageMaker Experienmentsを利用して、自動的に管理される。（個人の管理意識によらない）  

・PoC段階で、「そもそもビジネスにインパクトを出せるモデルを作れるか」という検証では、SageMaker Studioか、ローカルで十分（ローカルはライブラリ入れるのが面倒だが）。R、Sparkを使いたい場合はSageMaker ノートブックインスタンス or EC2。  
・PoCでビジネスバリューが出せると判断し、仕組み化する場合に、SageMakerのマネージド機能が真価を発揮する。（日次バッチで学習、推論サーバのデプロイ）  

# 参考
Python: XGBoost を使ってみる
https://blog.amedama.jp/entry/2019/01/29/235642

XGBoost による顧客離反分析 (Churn Analysis)
https://github.com/aws-samples/amazon-sagemaker-examples-jp/blob/master/xgboost_customer_churn/xgboost_customer_churn.ipynb

SageMaker Python SDKのローカルモードを利用して、ノートブックインスタンス以外の環境で学習ジョブを回してみる
https://dev.classmethod.jp/articles/sagemaker-python-sdk-localmode/

推論パイプラインでリアルタイム予測を実行
https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/inference-pipeline-real-time.html