[初心者向け] はじめてのSageMaker みんな大好きアイリスデータを使って組み込みアルゴリズムで分類してみる

2020.04.24

はじめに

おはようございます、もきゅりんです。

最近は個人的な取り組みの一環として、機械学習の学習に取り組み始めました。

さて、AWSで機械学習に取りかかるならば、Amazon SageMaker

まずは、組み込みアルゴリズムのLinearLearner。

そんなオレは、ML beginner。

(LinearLearnerが何だ?という話は、ドキュメントを確認して欲しいのですが、ターゲットの分類や予測したい数値を推定するために使用される、教師あり学習アルゴリズムです。)

初めてのデータセットは、みんな大好きな Iris でやってみます。

作業の流れは、完全に下記の通りで進めます。

Amazon SageMaker のコンソールの開始方法

なお、自分は専門的なデータサイエンティストでも何でもないので、無駄、非効率な作業を行っているかもしれない点、ご了承下さい。

前提

  • IAM権限を設定・更新できること

やること

3つの品種のIris(お花です)を、個体が持つ特徴(花びらの長さとか幅とか)だけで分類しようぜ!

が趣旨です。

  1. S3バケットの作成
  2. SageMakerノートブックインスタンスの作成
  3. Jupyterノートブックの作成
  4. データのロード、データを探索、処理、S3アップロード
  5. モデルでの学習
  6. モデルのデプロイ
  7. モデルの検証
  8. 後片付け

1. S3バケットの作成

S3バケットはコンソールでも何でもデフォルト設定で作成すれば問題ないです。

このバケットは、元のデータや加工したデータを格納します。

aws s3 mb s3://YOUR_BUCKT_NAME

2. SageMakerノートブックインスタンスの作成

さて、ここからドキドキの初めての体験です。

コンソールからSageMakerにいきます。

deployinstance

先ほど作成したS3バケットへのIAMロールを作成します。

sm-iamrole

instance-up

そしたら作成します。

3. Jupyterノートブックの作成

instance-service

ステータスが Inservice になったことを確認したら、ノートブックを開きます。

jupyternotebook

4. データのロード、データを探索、処理、S3アップロード

データのロード

Iris なのでsklearnのデータセットからロードしちゃいます。

import numpy as np
import os
from sklearn import datasets
iris = datasets.load_iris()

とくに今回はデータ探索を行うことはありません。

データはサンプルサイズは150,特徴量が4つです。

iris['data'].shape

データの分割

訓練データ、検証データ、テストデータに分割していきます。

地道にデータを分けていきます。

まずはテストデータとして20%を取り分けます。

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(iris['data'],iris['target'],test_size=0.2, random_state=42)

AWSでは、第1列目に教師データ(今回はラベル)がある必要があるようなので、データを加工します。

test_set = np.insert(X_test, 0, y_test, axis=1)

今度は検証データを分割します。

from sklearn.model_selection import train_test_split
X_train, X_validation, y_train, y_validation = train_test_split(X_train,y_train,random_state=42)

テストデータ同様、教師データを最初の列に挿入します。

train_set = np.insert(X_train, 0, y_train, axis=1)
valid_set = np.insert(X_validation, 0, y_validation, axis=1)

それぞれのデータのサイズを確認します。

test_set.shape
valid_set.shape
train_set.shape

教師データが挿入されたので、 column は5つになっています。

データのアップロード

それぞれのデータをS3バケットにアップロードします。

%%time

import os
import boto3
import re
import copy
import time
import io
import struct
from time import gmtime, strftime
from sagemaker import get_execution_role

role = get_execution_role()

region = boto3.Session().region_name

bucket='YOUR_BUCKET_NAME' # Replace with your s3 bucket name
prefix = 'sagemaker/test' # Used as part of the path in the bucket where you store data
bucket_path = 'https://s3-{}.amazonaws.com/{}'.format(region,bucket) # The URL to access the bucket

def convert_data():
    data_partitions = [('train', train_set), ('validation', valid_set), ('test', test_set)]
    for data_partition_name, data_partition in data_partitions:
        print('{}: {} {}'.format(data_partition_name, data_partition[0].shape, data_partition[1].shape))
        labels = [t.tolist() for t in data_partition[:,0]]
        features = [t.tolist() for t in data_partition[:,1:5]]

        if data_partition_name != 'test':
            examples = np.insert(features, 0, labels, axis=1)
        else:
            examples = features

        np.savetxt('data.csv', examples, delimiter=',')


        key = "{}/{}/examples".format(prefix,data_partition_name)
        url = 's3://{}/{}'.format(bucket, key)
        boto3.Session().resource('s3').Bucket(bucket).Object(key).upload_file('data.csv')
        print('Done writing to {}'.format(url))

convert_data()

S3バケットを見に行くとちゃんとありました。

s3-folders

5. モデルでの学習

例では XGB ですが、Linear-Learner に変更します。

import sagemaker
from sagemaker.amazon.amazon_estimator import get_image_uri
container = get_image_uri(boto3.Session().region_name, 'linear-learner')

前ステップ4でアップロードしたS3から訓練データと検証データをダウンロードし、トレーニングの出力を保存する場所を設定します。

train_data = 's3://{}/{}/{}'.format(bucket, prefix, 'train')
validation_data = 's3://{}/{}/{}'.format(bucket, prefix, 'validation')
s3_output_location = 's3://{}/{}/{}'.format(bucket, prefix, 'linear-learner_model_sdk')
print(train_data)

モデルのハイパーパラメータを設定します。

特徴量は4、分類する種類は3なので、それぞれ設定します。

linear_model = sagemaker.estimator.Estimator(container,
                                       role,
                                       train_instance_count=1,
                                       train_instance_type='ml.c4.xlarge',
                                       output_path=s3_output_location,
                                       sagemaker_session=sagemaker.Session())
linear_model.set_hyperparameters(feature_dim=4,num_classes=3,
                           mini_batch_size=32,
                           predictor_type='multiclass_classifier')

訓練(検証)を行うためのチャネルという概念があり、ここでは trainvalidation の両方のチャネルを作ります。

train とか片方だけのチャネルでも可能のようです。

train_channel = sagemaker.session.s3_input(train_data, content_type='text/csv')
valid_channel = sagemaker.session.s3_input(validation_data, content_type='text/csv')
data_channels = {'train': train_channel, 'validation': valid_channel}

訓練データと検証データを使って訓練を開始します。

linear_model.fit(inputs = data_channels,  logs=True)

完了すると下記のようなメッセージが表示されます。

2020-XX-XX XX:XX:XX Uploading - Uploading generated training model
2020-XX-XX XX:XX:XX Completed - Training job completed
Training seconds: 38
Billable seconds: 38

トレーニングのログ(CloudWatchLogs)をダッシュボードから移動します。

goto-logs

とりあえず accuracyとかパッと分かりやすそうな値が良さそうなのが分かりますね。

accuracy-logs

6. モデルのデプロイ

訓練されたモデルをエンドポイントにデプロイすることもできますが、特にエンドポイントは必要ないのでバッチ変換します。

ステップ4で、S3バケットにアップロードしているコードを確認頂ければお分かりだと思いますが、テストデータは特徴量のみのデータとなっています。

ここでは、特徴量のみを与えられているテストデータを元に、分類してみるぞ!という作業です。

ちなみに、機械学習で「バッチ」とは大雑把に言うと、全部一度にってことです。

# The location of the test dataset
batch_input = 's3://{}/{}/test/examples'.format(bucket, prefix)

# The location to store the results of the batch transform job
batch_output = 's3://{}/{}/batch-inference'.format(bucket, prefix)

transformer = linear_model.transformer(instance_count=1, instance_type='ml.c4.xlarge', output_path=batch_output)

transformer.transform(data=batch_input, data_type='S3Prefix', content_type='text/csv', split_type='Line')

transformer.wait()

7. モデルの検証

モデルから予測された予測を答え合わせします。

テストデータから予測したファイルをダウンロードします。

s3.Bucket(bucket).download_file(prefix + '/batch-inference/examples.out',  'batch_results')

ちょっとファイルを整形します。

(何かうまいやり方がありそうだけど...)

with open('batch_results', "r") as f:
    s = f.read()
    s = s.replace("{\"predicted_label\":", "")

with open('batch_results_fixed', "w") as f:
    f.write(s)

arr = []
f=open('batch_results_fixed','r')
for line in f:
    l = line.split(',')[0]
    arr.append(float(l))
f.close

結果です。

results = np.array(arr)
compare =  results == test_set[:,0]
print(np.count_nonzero(compare == True))

あれ、、、全部正解しちゃいましたよ。

前に自分のローカルで試したときは、何個か不正解が出た気がします。

LinearLearner、優秀ですね。

8. 後片付け

不用ならインスタンスは削除しましょう。

S3バケットも忘れずサヨナラしましょう。

aws s3 rm s3://YOUR_BUCKT_NAME

以上です。

SageMakerのお作法にちょっと面食らったりしましたが、慣れてくれば色々とスムーズ進められそうです。

とりあえず距離は縮まりました。

どなたかのお役に立てば幸いです。

参考: