Amazon SageMakerを使って主成分分析を行う

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

こんにちは、大澤です。

当エントリではAmazon SageMakerの組み込みアルゴリズムの1つ、「主成分分析(Principal Component Analysis, PCA)」についてご紹介していきたいと思います。

「組み込みアルゴリズム」の解説については下記エントリをご参照ください。

目次

概要説明:主成分分析とは

データを効果的に表現できる軸(主成分)を計算して求める手法です。 一般的にデータをいじってどういったものなのかを調べるときのツールとしてや、分類器の前段として使われたりします。 主成分分析の説明についてはこちらのエントリをご参照ください。

組み込みアルゴリズム:主成分分析の解説と実践

SageMakerのサンプルプログラムに沿って進めていきます。 MNISTの手書き文字のデータセットから固有顔(eigenface)ならぬ、固有数字(eigendigit)を主成分分析によって作成するという内容です。 用途としては、手書き数字の検出のための分類器の入力データとして固有数字を使うことなどが考えられます。

ノートブックの作成

SageMakerのノートブックインスタンスを立ち上げて、 SageMaker Examples ↓ Introduction to Amazon algorithms ↓ pca_mnist.ipynb ↓ use でサンプルからノートブックをコピーして、開きます。 ノートブックインスタンスの作成についてはこちらをご参照ください。

環境変数とロールの確認

訓練データ等を保存するS3のバケット名と保存オブジェクト名の接頭辞を決めます。 あまり無い事だと思いますが、sagemaker実行リージョンとは別リージョンのS3バケットを指定すると、訓練に失敗します。

bucket = '<your_s3_bucket_name_here>'
prefix = 'sagemaker/DEMO-pca-mnist'
 
# Define IAM role
import boto3
import re
from sagemaker import get_execution_role

role = get_execution_role()

データ取得

データをメモリに読み込みます。

%%time
import pickle, gzip, numpy, urllib.request, json

# 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')

データの確認

どうやって前処理が必要か確認するためにも、データを見てみます。

%matplotlib inline
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (2,10)


def show_digit(img, caption='', subplot=None):
    if subplot==None:
        _,(subplot)=plt.subplots(1,1)
    imgr=img.reshape((28,28))
    subplot.axis('off')
    subplot.imshow(imgr, cmap='gray')
    plt.title(caption)

show_digit(train_set[0][30], 'This is a {}'.format(train_set[1][30]))

次のような画像が表示されると思います。

データ変換

SageMakerの主成分分析に使うために、訓練データをRecordIOという形式に変換します。

import io
import numpy as np
import sagemaker.amazon.common as smac

vectors = np.array([t.tolist() for t in train_set[0]]).T

buf = io.BytesIO()
smac.write_numpy_to_dense_tensor(buf, vectors)
buf.seek(0)

訓練データをS3に上げる

加工した訓練データを、冒頭で指定したS3のバケットに保存します。

%%time
import boto3
import os

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))

訓練結果の保存先を指定しておきます。

output_location = 's3://{}/{}/output'.format(bucket, prefix)
print('training artifacts will be uploaded to: {}'.format(output_location))

主成分の計算(訓練)

訓練処理を実行するコンテナ名を取得します。

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

ハイパーパラメータを設定して、訓練処理を実行します。 今回設定する各ハイパーパラメータについては以下の通りです。

feature_dim
特徴空間の次元数(今回の例ではデータ数)
num_components
主成分の数
subtract_mean
各データを平均値で引くかどうか
algorithm_mode
計算手法(データ数が多いので近似手法であるrandomizedを使う )
mini_batch_size
訓練時のデータ分割サイズ

詳細はドキュメントをご確認ください。

import boto3
import sagemaker

sess = sagemaker.Session()

pca = sagemaker.estimator.Estimator(container,
                                    role, 
                                    train_instance_count=1, 
                                    train_instance_type='ml.c4.xlarge',
                                    output_path=output_location,
                                    sagemaker_session=sess)
pca.set_hyperparameters(feature_dim=50000,
                        num_components=10,
                        subtract_mean=True,
                        algorithm_mode='randomized',
                        mini_batch_size=200)

pca.fit({'train': s3_train_data})

"SVD did not converge”というエラーで、fitが失敗することがたまにあります。 何度か試すことで、無事訓練処理を終えることができると思います。

モデルの展開

エンドポイントを作成し、訓練したモデルを展開します。 エンドポイントが立ち上がっている間は課金が発生するので、注意が必要です。

pca_predictor = pca.deploy(initial_instance_count=1,
                           instance_type='ml.m4.xlarge')

モデルの確認

先ほど作成したエンドポイントへデータを渡すときのシリアライズ方法、データを受け取るときのデシリアライズ方法を指定します。

from sagemaker.predictor import csv_serializer, json_deserializer

pca_predictor.content_type = 'text/csv'
pca_predictor.serializer = csv_serializer
pca_predictor.deserializer = json_deserializer

一つデータを投げてみて、動きを確認してみます。

result = pca_predictor.predict(train_set[0][:, 0])
print(result)

次のようなデータが表示されるはずです。

問題無さそうなので、次に固有数字(画像)の作成を行います。

import numpy as np

eigendigits = []
for array in np.array_split(train_set[0].T, 100):
    result = pca_predictor.predict(array)
    eigendigits += [r['projection'] for r in result['projections']]

eigendigits = np.array(eigendigits).T

for e in enumerate(eigendigits):
    show_digit(e[1], 'eigendigit #{}'.format(e[0]))

処理が終わると、10個の固有数字の画像が表示されます。 一つ目の画像(#0)は以下のようになっているかと思われます。

画像が潰れて表示されている場合は画像の表示が省略されている場合があるので、 画像の左側あたりをクリックすることですべて表示することができます。

エンドポイントの削除

余分なお金を使わないように、エンドポイントを削除します。

import sagemaker

sagemaker.Session().delete_endpoint(pca_predictor.endpoint)

まとめ、結論

主成分分析のビルトインアルゴリズムを使うことでMNISTの手書き数字データから各データの特徴量である、固有数字を生成出来ました。 ハイパーパラメータの値を変更することで得られるものが変わったりするので、その辺りも試してみると面白いと思います。

最後までお読みいただき、ありがとうございましたー!

以下シリーズではAmazon SageMakerのその他の組み込みアルゴリズムについても解説しています。宜しければ御覧ください。 - Amazon SageMaker 組み込みアルゴリズム入門