Amazon SageMakerでLinear Learner(線形学習者)をNotebookインスタンス使わずにやってみた

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

好物はインフラとフロントエンドのかじわらゆたかです。 今回はSagMmakerでビルトインアルゴリズムとして実装されている「Linear Learner(線形学習者)」のチュートリアルを実際にやってみようと思ったのですが、
じょんすみすのエントリーが必要十分な内容になっていたので、違うアプローチを試してみたいと思います。

そんな必要十分なじょんすみすのエントリーはこちら

前準備

Notebookを用いない場合の必要なライブラリ等もわかっていないので、ひとまずPython 3.6環境とAWSCLI Boto3 Sagemarker SDKを導入し、それ以降は適宜入れていこうと思います。

ひとまず上記を導入した際のコマンドを記載しておきます。

pyenv の導入に関してはこちら を参考にしています。

$ pyenv install 3.6.4
$ pyenv virtualenv 3.6.4 MLProm
$ pyenv local MLProm
$ pip install awscli
$ pip install boto3
$ pip install sagemaker

Linear Learner(線形学習者)とは

SageMakerで用いることができる組み込み(built-in)アルゴリズムの一つです。

Sagemakerのドキュメントに以下のように記載されています。

線形モデルは、分類や回帰の問題を解決するために使用される、教師あり学習アルゴリズムです。

線形学習者

また、線形モデルを用いた分類については以下のエントリを参考にしてください。

ロジスティック回帰を実装してみよう

組み込みアルゴリズム:線形学習者の解説と実践

Notebookを用いないとは言え、進め方自体はNotebookで行っている内容に基づいて進めていきます。

このサンプルでは以下の作業を行います。

  1. パーミッションと環境変数の設定
  2. データの取り込み
  3. データの検査
  4. データ変換
  5. 訓練用データのアップロード
  6. 線形モデルのトレーニング
  7. モデルのデプロイ
  8. モデルの検証
  9. デプロイしたエンドポイントの削除

1. パーミッションと環境変数の設定

この機械学習で用いるS3バケットを作成しておきます。

$aws s3api create-bucket --bucket cm-kajiwara-test-ml

NotebookのサンプルではNotebookのRoleを取得していますが、 取得したRoleを用いるのは線形モデルのトレーニングの箇所のため、 この箇所では取得は行いません。

2. データの取り込み 3. データの検査 4. データ変換 5. 訓練用データのアップロード

Notebook環境ではデータの確認を行っていますが、 Pythonのコードで動かすことを目的としているため、その箇所は省略して実行します。

import time
import pickle, gzip, numpy, urllib.request, json
import io
import numpy as np
import sagemaker.amazon.common as smac
import boto3
import os
start = time.time()

# Load the dataset
urllib.request.urlretrieve("http://deeplearning.net/data/mnist/mnist.pkl.gz", "mnist.pkl.gz")
with gzip.open('mnist.pkl.gz', 'rb') as f:
    train_set, valid_set, test_set = pickle.load(f, encoding='latin1')
    # Data conversion
    vectors = np.array([t.tolist() for t in train_set[0]]).astype('float32')
    labels = np.where(np.array([t.tolist() for t in train_set[1]]) == 0, 1, 0).astype('float32')

    buf = io.BytesIO()
    smac.write_numpy_to_dense_tensor(buf, vectors, labels)
    buf.seek(0)
    # Upload training data
    bucket = 'cm-kajiwara-test-ml' ## input your bucket name.
    prefix = 'sagemaker/DEMO-linear-mnist'
    key = 'recordio-pb-data'
    boto3.resource('s3').Bucket(bucket).Object(os.path.join(prefix, 'train', key)).upload_fileobj(buf)
    s3_train_data = 's3://{}/{}/train/{}'.format(bucket, prefix, key)
    print('uploaded training data location: {}'.format(s3_train_data))
    e_time = time.time() - start
    print ("e_time:{0}".format(e_time) + "[s]")

6. 線形モデルのトレーニング

ここで1. で取得しなかったRoleの取得が出てきます。 get_execution_role() ではNotebookインスタンスに付与したRoleのArnが取得できるのですが、 今回はNotebookインスタンスを用いないためboto3経由でIAMのAPIを呼び、Roleから取得しています。

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

sess = sagemaker.Session()

iam = boto3.resource('iam')
role = iam.Role('AmazonSageMaker-ExecutionRole-20180810T110280') #InputYourSageMaker Role
bucket = 'cm-kajiwara-test-ml' ## input your bucket name.
prefix = 'sagemaker/DEMO-linear-mnist'
key = 'recordio-pb-data'
output_location = 's3://{}/{}/output'.format(bucket, prefix)
linear = sagemaker.estimator.Estimator(container,
                                       role.arn, 
                                       train_instance_count=1, 
                                       train_instance_type='ml.c4.xlarge',
                                       output_path=output_location,
                                       sagemaker_session=sess)
linear.set_hyperparameters(feature_dim=784,
                           predictor_type='binary_classifier',
                           mini_batch_size=200)
s3_train_data = 's3://{}/{}/train/{}'.format(bucket, prefix, key)
linear.fit({'train': s3_train_data})

7. モデルのデプロイ

今回の実装ではトレーニングに用いたlinerオブジェクトからのデプロイではなく、 トレーニングに用いたジョブ名からLinearLearnerオブジェクトを作成し、デプロイを実施します。

Notebookでのlinerオブジェクトは線形モデルのトレーニング時に生成していますが、 今回のスクリプト実行時ではすでにトレーニング済みの状態で実行することになります。 そのため、上記の方法でLinearLearnerオブジェクトを生成しデプロイを実施する必要があったためです。

from sagemaker.amazon.linear_learner import LinearLearner

training_job_name = 'linear-learner-2018-08-14-16-35-26-294'
attached_estimator = LinearLearner.attach(training_job_name)
attached_estimator.deploy(initial_instance_count=1,
                                 instance_type='ml.m4.xlarge')

8. モデルの検証

boto3からエンドポイントを実行する方法 もありますが、今回はSageMakerのSDKを用いた方法で行います。

生成されたendpointからRealTimePredictorオブジェクトを生成し、モデルの検証を進めます。

実際に実装したコードとその実行結果を貼り付けておきます。

from sagemaker.predictor import RealTimePredictor
from sagemaker.predictor import csv_serializer, json_deserializer
import urllib
import gzip
import pickle

endpoint = 'linear-learner-2018-08-14-16-35-26-294'
linear_predictor = RealTimePredictor(endpoint,content_type='text/csv',serializer=csv_serializer,deserializer=json_deserializer)

# Load the dataset
urllib.request.urlretrieve("http://deeplearning.net/data/mnist/mnist.pkl.gz", "mnist.pkl.gz")
with gzip.open('mnist.pkl.gz', 'rb') as f:
    train_set, valid_set, test_set = pickle.load(f, encoding='latin1')
    result = linear_predictor.predict(train_set[0][30:31])
    print(result)
$ python ./08.py
{'predictions': [{'score': 5.051175477888137e-08, 'predicted_label': 0.0}]}

下記の実行にはpandasのインストールが必要でした。
pandasのインストールについては下記の通りです。

pip install pandas
from sagemaker.predictor import RealTimePredictor
from sagemaker.predictor import csv_serializer, json_deserializer
import urllib
import gzip
import pickle
import numpy as np
import pandas as pd

endpoint = 'linear-learner-2018-08-14-16-35-26-294'
linear_predictor = RealTimePredictor(endpoint,content_type='text/csv',serializer=csv_serializer,deserializer=json_deserializer)

# Load the dataset
urllib.request.urlretrieve("http://deeplearning.net/data/mnist/mnist.pkl.gz", "mnist.pkl.gz")
with gzip.open('mnist.pkl.gz', 'rb') as f:
    train_set, valid_set, test_set = pickle.load(f, encoding='latin1')

    predictions = []
    for array in np.array_split(test_set[0], 100):
        result = linear_predictor.predict(array)
        predictions += [r['predicted_label'] for r in result['predictions']]
    predictions = np.array(predictions)
    print(pd.crosstab(np.where(test_set[1] == 0, 1, 0), predictions, rownames=['actuals'], colnames=['predictions']))
$ python ./08-2.py
predictions   0.0  1.0
actuals
0            8972   48
1              39  941

9. デプロイしたエンドポイントの削除

import sagemaker

endpoint = 'linear-learner-2018-08-14-16-35-26-294'
sagemaker.Session().delete_endpoint(endpoint)
$ python ./09.py
INFO:sagemaker:Deleting endpoint with name: linear-learner-2018-08-14-16-35-26-294

まとめ

Notebookインスタンスを用いずに、チュートリアルを実行してみました。 Notebookインスタンスを用いたほうが当然容易ですが、 一度つくった環境を再度作りたいと言った用途等には使えたりもするのではないでしょうか? なかなレアなケースだとは思いますが、誰かの一助になれば幸いです。

補足: Python のインストールで失敗

Python 3.6.4 のインストールの際に一度失敗しました。

失敗したときのログは下記の通り

$ pyenv install 3.6.4
python-build: use openssl from homebrew
python-build: use readline from homebrew
Downloading Python-3.6.4.tar.xz...
-> https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tar.xz
Installing Python-3.6.4...
python-build: use readline from homebrew

BUILD FAILED (OS X 10.13.6 using python-build 20160602)

Inspect or clean up the working tree at /var/folders/py/sh50z9ms2836hzybz51jz1fr0000gn/T/python-build.20180814232425.11366
Results logged to /var/folders/py/sh50z9ms2836hzybz51jz1fr0000gn/T/python-build.20180814232425.11366.log

Last 10 log lines:
  File "/private/var/folders/py/sh50z9ms2836hzybz51jz1fr0000gn/T/python-build.20180814232425.11366/Python-3.6.4/Lib/ensurepip/__main__.py", line 5, in <module>
    sys.exit(ensurepip._main())
  File "/private/var/folders/py/sh50z9ms2836hzybz51jz1fr0000gn/T/python-build.20180814232425.11366/Python-3.6.4/Lib/ensurepip/__init__.py", line 204, in _main
    default_pip=args.default_pip,
  File "/private/var/folders/py/sh50z9ms2836hzybz51jz1fr0000gn/T/python-build.20180814232425.11366/Python-3.6.4/Lib/ensurepip/__init__.py", line 117, in _bootstrap
    return _run_pip(args + [p[0] for p in _PROJECTS], additional_paths)
  File "/private/var/folders/py/sh50z9ms2836hzybz51jz1fr0000gn/T/python-build.20180814232425.11366/Python-3.6.4/Lib/ensurepip/__init__.py", line 27, in _run_pip
    import pip
zipimport.ZipImportError: can't decompress data; zlib not available
make: *** [install] Error 1

xcodeのコマンドラインインターフェースが必要とのことなので、以下のコマンドを実行後、再度実行することでインストールが行えました。

xcode-select --install