【SageMaker】めちゃくちゃ簡単にモデルトレーニングさせてみた。

どうもこんにちは。

今回は初めてAWSのSageMakerを使用してみました。

# 想定

今回、モデルを学習させるにあたって、想定したケースは、

```
〇〇ゲートを何月何日何時何分に何人の人間が通ったか
```

です。

例えば、「2021年1月1日 12時00分00秒」に何人がゲートを通ったかをモデルに教えてもらうイメージです。

# 準備

今回は、S3に作成したトレーニングデータを保存します。
そのため、「sagemaker~」から始まるバケットを作成して置いてください。

また、手順ごとにコードブロックを分けて記述してください。

# 実装手順

## 1. SageMakerのノートブックインスタンスの作成からノートブックを作成

ノートブックインスタンスのタイプは**ml.t3medium**を使用しました。

![スクリーンショット 2024-03-22 18.36.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2711491/f452d38b-7963-879b-7cdd-11e0babb09d3.png)

## 2. JupyterLabを開く

作成したノートブックインスタンスが**inService**になったら**JupyterLab を開く**リンクをクリックします。

## 3. 新しいノートブックを開く

[File]>[New]>[Notebook]から新しくノートブックを立ち上げます。
この時、**conda_python3**を選択してください。

![スクリーンショット 2024-03-22 18.43.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2711491/6df868c0-8445-66d1-dc8c-372383cf81ba.png)

## 4. 必要なライブラリをインポート

以下をコピペしてください。

```python
# 必要ライブラリインストール
import boto3
import pandas as pd
# テストデータ用ライブラリ
from sklearn.datasets import load_iris
!pip install faker
from faker import Faker
import random
import json
from datetime import datetime, timedelta
import io
# SageMaker用ライブラリ
import sagemaker
from sagemaker import get_execution_role
from sagemaker.serializers import CSVSerializer
from sagemaker.image_uris import retrieve
```

## 5. トレーニング用データの作成

`count`変数には、ゲートを通った人数、`time`変数には、観測した時間が代入されています。

この時、`time`はモデルのトレーニングができるように、UNIXタイムスタンプとして数値化した値を代入しています。

```python
fake = Faker()

# データセットの生成
n = 1000
data = []
for _ in range(n):
    count = fake.random_int(min=0, max=100)
    time = fake.date_time_this_decade().timestamp()  # UNIXタイムスタンプとして時間を数値化
    data.append([count, time])

# DataFrameに変換
df = pd.DataFrame(data, columns=['Count', 'Time'])
print(df.head())
```

↓printの結果↓

```
   Count  Observation Time
0     15      1.705957e+09
1     85      1.619990e+09
2     93      1.630492e+09
3     88      1.643634e+09
4     37      1.698472e+09
```

## 6. S3に作成したトレーニングデータをアップロード

以下はコピペでOKです。

```python
# boto3を使用してS3にアップロード
s3_client = boto3.client('s3')
bucket_name = '<作成しているバケット名>'
file_name = 'test/dataset/sample_dataset.csv'

# DataFrameをCSV形式の文字列としてメモリに保存
csv_buffer = io.StringIO()
df.to_csv(csv_buffer, index=False, header=False)

# メモリからS3に直接書き込み
s3_client.put_object(Bucket=bucket_name, Key=file_name, Body=csv_buffer.getvalue())
```

## 7. セッションとロールの設定

以下をコピペしてください。

```python
# SageMakerセッションとロールの設定
session = sagemaker.Session()
role = get_execution_role()
```

## 8. トレーニング実行

今回は、Linear LearnerというSageMakerに組み込まれているモデルを使用します。
最も単純?な線型学習をしてくれるとかなんとか。。。（わかる人いましたらコメントで教えてください。。。）

トレーニングには、何分かかかります。

```python
# S3のデータパス
data_location = 's3://{}/test/dataset/sample_dataset.csv'.format(bucket_name)

# Linear Learnerのコンテナイメージを取得
container = retrieve('linear-learner', session.boto_region_name)

# 推定器の設定
linear = sagemaker.estimator.Estimator(container,
                                       role, 
                                       train_instance_count=1, 
                                       train_instance_type='ml.m5.large',
                                       output_path='s3://{}/test/output'.format(bucket_name),
                                       sagemaker_session=session)

# ハイパーパラメータの設定
linear.set_hyperparameters(feature_dim=1,
                           predictor_type='regressor',
                           mini_batch_size=100)

# トレーニングジョブの開始
s3_input_train = sagemaker.inputs.TrainingInput(s3_data=data_location, content_type='text/csv')

linear.fit({'train': s3_input_train})
```

## 9. トレーニングしたモデルのデプロイ

以下を実行するとデプロイされます。

```python
predictor = linear.deploy(initial_instance_count=1, instance_type='ml.m5.large')
```

## 10. テストデータの作成

「2100/10/31 12:00:00」にゲートを通った人数をモデルに予測してもらいましょう。

```python
# 任意入力（yyyy/mm/dd hh:mm:ss）
timestamp_str = '2100/10/31 12:00:00'

timestamp = datetime.datetime.strptime(timestamp_str, '%Y/%m/%d %H:%M:%S')

unix_timestamp = timestamp.timestamp()

# 特徴量を同様の形式で生成する
features = {
    'Time': unix_timestamp
}

test_df = pd.DataFrame([features])

print(test_df.head())

csv_buffer = io.StringIO()
test_df.to_csv(csv_buffer, index=False, header=False)
csv_payload = csv_buffer.getvalue()

print('---CSVファイルの中身確認---')
print(csv_payload)
```

## 11. テストデータを使用して評価

```python
# 評価用コード
predictor.serializer = CSVSerializer()
result = predictor.predict(csv_payload)
decoded_result = result.decode('utf-8')
print('出力された内容')
print(decoded_result)
parsed_result = json.loads(decoded_result)
score = parsed_result['predictions'][0]['score']
print('人数')
print(score)
```

↓出力された内容↓

```
出力された内容
{"predictions": [{"score": 39.422813415527344}]}
人数
39.422813415527344
```

# 結果

「2100/10/31 12:00:00」にゲートを通った人数がおよそ39.4人だと予測されました！

# 終わりに

満足したら必ず以下のコードをコピペして実行してください。

```python
# デプロイ後は必ずエンドポイントを削除すること
predictor.delete_endpoint()
```