[初心者向け] Bostonデータ使って Amazon SageMaker 組み込みアルゴリズムで住宅価格を推定してみる
はじめに
おはようございます、もきゅりんです。
最近は個人的な取り組みの一環として、機械学習の学習に取り組んでいます。
前回 は、LinearLearnerで Iris
の分類をしました。
次は、Bostonデータを使って LinearLearner で住宅価格を推定します。
なお、自分は専門的なデータサイエンティストでも何でもないので、無駄、非効率な作業を行っているかもしれない点、ご了承下さい。
前提
- データを格納するS3バケットがあること
- Jupyterノートブックが作成されていること
- IAM権限を設定・更新できること
こちらの作業については下記を参照下さい。
はじめてのSageMaker みんな大好きアイリスデータを使って組み込みアルゴリズムで分類してみる
やること
住宅価格を、13つの特徴(町の生徒/教師の比率とか)を利用して予測しようぜ! が趣旨です。
(正確に言えば、ピンポイントの価格ではなく、中央値です。)
- データのロード、データを探索、処理、S3アップロード
- モデルでの学習
- モデルのデプロイ
- モデルの検証
- 後片付け
1. データのロード、データを探索、処理、S3アップロード
環境変数とロールの確認
%%time import os import boto3 import re import numpy as np from sagemaker import get_execution_role role = get_execution_role() region = boto3.Session().region_name bucket='YOUR_BUCKET_NAME' prefix = 'sagemaker/DEMO-linearlearner' # customize to your bucket where you have stored the data bucket_path = 'https://s3-{}.amazonaws.com/{}'.format(region,bucket)
データのロード
Boston
なのでsklearnのデータセットからロードします。
(実は最初、LIBSVMでやってみようとしたけど、LinearLearnerはLIBSVM形式に対応していないようだった... *1)
from sklearn import datasets boston = datasets.load_boston()
データの分割
テストデータにデータの20%を切り分けます。
from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(boston['data'],boston['target'],test_size=0.2, random_state=42)
さらに訓練データと検証データに分けます。
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)
第1列目に教師データ(住宅価格の数値)を挿入します。
train_set = np.insert(X_train, 0, y_train, axis=1) valid_set = np.insert(X_validation, 0, y_validation, axis=1) test_set = np.insert(X_test, 0, y_test, axis=1)
それぞれのデータのサイズを確認します。
教師データが挿入されたので、column
は14です。
test_set.shape
valid_set.shape
train_set.shape
データのアップロード
それぞれのデータをS3バケットにアップロードします。
アップロードする関数を作成します。
def convert_data(feature_dim): 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:feature_dim + 1]] # 特徴量の抽出 if data_partition_name != 'test': examples = np.insert(features, 0, labels, axis=1) else: examples = features np.savetxt('data.csv', examples, delimiter=',') key = "{}/{}/data".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)) def get_data(data): return 's3://{}/{}/{}'.format(bucket, prefix, data) def set_channel(data,content_type): return sagemaker.session.s3_input(data, content_type=content_type)
data_partitions = [('train', train_set), ('validation', valid_set), ('test', test_set)] # 特徴量が引数 convert_data(13)
2. モデルでの学習
LinearLearnerのコンテナを取得します。
import sagemaker from sagemaker.amazon.amazon_estimator import get_image_uri container = get_image_uri(boto3.Session().region_name, 'linear-learner')
前ステップでアップロードしたS3から訓練データと検証データをダウンロードし、トレーニングの出力を保存する場所を設定します。
#Load the dataset from S3 train_data = get_data('train') validation_data = get_data('validation') s3_output_location = 's3://{}/{}/{}'.format(bucket, prefix, 'linear-learner_model_sdk')
モデルのハイパーパラメータを設定します。
特徴量は13、回帰なので regressor
です。
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=13, mini_batch_size=100, predictor_type='regressor')
モデルが利用するデータチャネルを train
と validation
で作ります。
train_channel = set_channel(train_data, 'text/csv') valid_channel = set_channel(validation_data, 'text/csv') data_channels = {'train': train_channel, 'validation': valid_channel}
訓練を開始します。
linear_model.fit(inputs=data_channels, logs=True)
回帰の損失関数は デフォルトで squared_loss
のようです。
3. モデルのデプロイ
訓練されたモデルをエンドポイントにデプロイすることもできますが、特にエンドポイントは必要ないのでバッチ変換します。
13つの特徴量のみを与えられているテストデータを元に、住宅価格を推定します。
batch_input = 's3://{}/{}/test/data'.format(bucket, prefix) 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()
4. モデルの検証
モデルから予測された予測を答え合わせします。
テストデータから予測したファイルをダウンロードします。
boto3.resource('s3').Bucket(bucket).download_file(prefix + '/batch-inference/data.out', 'test_results')
ちょっとファイルを整形します。
(うまい方法を探そう...)
with open('test_results', "r") as f: s = f.read() s = s.replace("{\"score\":", "") s = s.replace("}", "") with open('test_results_fixed', "w") as f: f.write(s) arr = [] f=open('test_results_fixed','r') for line in f: l = line.split(',')[0] arr.append(float(l)) f.close results = np.array(arr)
結果です。
np.sqrt(np.square(np.subtract(results, test_set[:,0])).mean())
5.09
(*$1000)なので、まぁ特別良いモデルではないですね。
特徴量をいじったり、ハイパーパラメータを調整することでもっと改善するかと思います。
(ちなみに、多重共線性の疑いがある RAD
を削除して同じように訓練してみましたが、5.06
で、大して変化しませんでした。)
特徴量を正規化しなくても特に支障ないんですね。
下図は、45度線が完全一致となる、予測と答え合わせのプロットです。
import matplotlib.pyplot as plt %matplotlib inline plt.scatter(test_set[:,0],results) plt.xlabel('Actual Prices') plt.ylabel('Predicted Prices') plt.title('Comparison of Actual Prices and Predicted Prices') plt.show()
5. 後片付け
不用なインスタンスは削除しましょう。
S3バケットも忘れずサヨナラしましょう。
以上です。
引き続き学習を進めていきます。
どなたかのお役に立てば幸いです。
参考:
脚注
- https://forums.aws.amazon.com/thread.jspa?threadID=317158 ↩