<jupyter_start><jupyter_text>SageMaker PyTorch Container を使用したハイパーパラメーターチューニング  目次1. [背景](背景)1. [セットアップ](セットアップ)1. [データ](データ)1. [学習](学習)1. [ホスティング](ホスティング)1. [HPO探索を行わない通常の学習](HPO探索を行わない通常の学習)1. [リソースの削除](リソースの削除)--- 背景MNISTは、手書き文字の分類に広く使用されているデータセットです。 70,000個のラベル付きの28x28ピクセルの手書き数字のグレースケール画像で構成されています。 データセットは、60,000個のトレーニング画像と10,000個のテスト画像に分割されます。 手書きの数字 0から9の合計10のクラスがあります。 このチュートリアルでは、PyTorch を使用して SageMaker で MNIST モデルをトレーニングおよびテストする方法を示します。 また、SageMaker の自動モデルチューニングを使用して適切なハイパーパラメーターを選択し、最適なモデルを取得する方法をご説明します。SageMaker の PyTorch の詳細については、[sagemaker-pytorch-containers](https://github.com/aws/sagemaker-pytorch-containers) と [sagemaker-python-sdk](https://github.com/aws/sagemaker-python-sdk) のレポジトリをご参照ください。--- セットアップSageMaker セッションを作成し、設定を開始します。- 学習およびモデルデータに使用する S3 バケットとプレフィックスは、ノートブックインスタンス、トレーニング、およびホスティングと同じリージョン内にある必要があります。- データへの学習およびホスティングアクセスを提供するために使用される IAM ロール arn を用います。 ノートブックインスタンス、学習インスタンス、および/またはホスティングインスタンスに複数のロールが必要な場合は、 `sagemaker.get_execution_role（）` を、適切な IAM ロール arn 文字列に置き換えてください。<jupyter_code>import sagemaker
from sagemaker.tuner import IntegerParameter, CategoricalParameter, ContinuousParameter, HyperparameterTuner

sagemaker_session = sagemaker.Session()

bucket = sagemaker_session.default_bucket()
prefix = 'sagemaker/DEMO-pytorch-mnist'

role = sagemaker.get_execution_role()<jupyter_output><empty_output><jupyter_text>データ データの取得<jupyter_code>from torchvision import datasets, transforms

datasets.MNIST('data', download=True, transform=transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.1307,), (0.3081,))
]))<jupyter_output><empty_output><jupyter_text>データをS3にアップロードするデータセットを S3 にアップロードするには、 `sagemaker.Session.upload_data` 関数を使用します。 戻り値として入力した S3 のロケーションは、後で学習ジョブを実行するときに使用します。<jupyter_code>inputs = sagemaker_session.upload_data(path='data', bucket=bucket, key_prefix=prefix)
print('input spec (in this case, just an S3 path): {}'.format(inputs))<jupyter_output><empty_output><jupyter_text>学習 学習スクリプト`mnist.py` スクリプトは、SageMaker モデルの学習とホスティングに必要なすべてのコードを提供します（`model_fn` はモデルをロードするための関数です）。学習スクリプトは、SageMaker の外部で実行するトレーニングスクリプトに非常に似ていますが、次のようなさまざまな環境変数を通じてトレーニング環境に関する有用なプロパティにアクセスできます。* `SM_MODEL_DIR`： モデルアーティファクトを書き込むディレクトリへのパスを表す文字列。モデルは、推論用ホスティングのためにS3にアップロードされます。* `SM_NUM_GPUS`： 現在のコンテナで利用可能な GPU の数。* `SM_CURRENT_HOST`： コンテナネットワーク上の現在のコンテナの名前。* `SM_HOSTS`： すべてのホストを含む JSON エンコードリスト。`fit（）`メソッドの呼び出しで1つの入力チャンネル `training` が使用されたとすると、 `SM_CHANNEL_ [channel_name]`の形式に従って以下が設定されます：* `SM_CHANNEL_TRAINING`：`training` チャンネルのデータを含むディレクトリへのパスを表す文字列。学習に関する環境変数の詳細については、[SageMaker Containers](https://github.com/aws/sagemaker-containers) をご覧ください。典型的なトレーニングスクリプトの書き方は下記の通りです。入力チャンネルからデータをロードし、ハイパーパラメーターで学習の設定、モデルを学習、モデルを `model_dir` に保存し、そこから学習済みモデルのホスティングを行います。 ハイパーパラメーターは引数としてスクリプトに渡され、 `argparse.ArgumentParser` インスタンスとして取得できます。下記は、このノートブックで使われるスクリプト ` mnist.py` です。SageMaker PyTorch Container で定義されるデフォルトの実装方法に従い [sagemaker-pytorch-containers](https://github.com/aws/sagemaker-pytorch-containers) 、 `input_fn` 、` predict_fn` 、 `output_fn` および ` transform_fn` の実装を使用します。メインガード  (``if __name__=='__main__':``)  の中のスクリプトは、学習時にのみ実行され、モデルのホスティング時には実行されません。上記のように、必要な `mnist.py` スクリプトに ` model_fn` を実装しています。<jupyter_code>!pygmentize mnist.py<jupyter_output><empty_output><jupyter_text>ハイパーパラメーター調整ジョブをセットアップする*以下のデフォルト設定では、ハイパーパラメーター調整ジョブが完了するまでに10分程度かかります。*データセットとスクリプトを準備したので、モデルをトレーニングする準備ができました。 ここで、トレーニングされたモデルのパフォーマンスに大きく影響を与える可能性のあるハイパーパラメーターについてご説明します。 たとえば、学習率、バッチサイズ、エポック数など、どのハイパーパラメーター設定が最良の結果をもたらすかはデータセットにも依存するため、探索なしに最適なハイパーパラメーター設定を選択することは困難です。SageMaker自動モデル調整を使用して、ハイパーパラメーター調整ジョブを作成し、自動化された効果的な方法で最適なハイパーパラメーター設定を探索できます。この例では、 SageMaker Python SDK を使用して、ハイパーパラメーター調整ジョブをセットアップ、管理しています。具体的には、探索したいハイパーパラメーターごとに、連続値として探索する場合はその範囲、またはリストの中から探索したい場合は探索可能な値のリストを指定します。ハイパーパラメーター調整ジョブは、異なるハイパーパラメーター設定で複数のトレーニングジョブを並列に起動し、定義済みの `objective metric` （目的関数） に基づいてそれらのトレーニングジョブの結果を評価します。以前の結果に基づいて次のハイパーパラメーター探索の設定を選択します。ハイパーパラメーター調整ジョブごとに、一度に並列で実行する学習インスタンス数、最大の学習ジョブ数、をそれぞれ割り当て、最大の学習ジョブ数が実行されると探索を終え、`objective metric` に対して最適なパフォーマンスを達成したハイパーパラメータの組み合わせを返します。次に、以下の手順に従って、SageMaker Python SDK を使用してハイパーパラメーター調整ジョブをセットアップします。* `estimator` を作成して PyTorch 学習ジョブをセットアップします。* 調整するハイパーパラメーターの範囲を定義します。この例では、学習率とバッチサイズのハイパーパラメータ探索範囲を設定します。* 最適化するチューニングジョブの `objective metric` を定義します。* 上記の設定で `HyperparameterTuner` オブジェクトを設定します。 SageMaker で単一の PyTorch ジョブを学習するのと同様に、PyTorchスクリプト、IAMロール、および（ジョブごとの）ハードウェア構成を渡す PyTorch Estimator を定義します。単一のジョブの実行方法については、最後の [HPO探索を行わない通常の学習](HPO探索を行わない通常の学習) で説明します。<jupyter_code>from sagemaker.pytorch import PyTorch

hpo_estimator = PyTorch(entry_point="mnist.py",
                    role=role,
                    framework_version='1.1.0',
                    train_instance_count=1,
                    train_instance_type='ml.m4.xlarge',
                    hyperparameters={
                        'epochs': 3,
                        'backend': 'gloo'
                    })<jupyter_output><empty_output><jupyter_text>`Estimator` を定義したら、調整するハイパーパラメーターとその探索範囲を指定します。ハイパーパラメーターの探索範囲の指定は3種類の方法があります。- カテゴリーパラメーターは、探索したい値のリストを `CategoricalParameter（list）` で定義します。このリストの中から最適な値を探索します。 - 連続パラメーターは、`ContinuousParameter（min、max）` で定義される最小値と最大値の間の連続空間で、任意の実数値から探索を行います。- 整数パラメーターは、`IntegerParameter（min、max）` で定義された最小値と最大値の間の任意の整数値で、探索を行います。* 可能であれば、値を最も制限の少ないタイプとして指定することをお勧めします。 たとえば、0.01〜0.2の連続値として学習率を調整すると、0.01、0.1、0.15、または0.2の値を持つカテゴリパラメーターとして調整するよりも良い結果が得られる可能性があります。 バッチサイズは一般的に2のべき乗であることが推奨されているため、ここではカテゴリパラメータとしてバッチサイズを指定しています。<jupyter_code>hyperparameter_ranges = {'lr': ContinuousParameter(0.001, 0.1),'batch-size': CategoricalParameter([32,64,128,256,512])}<jupyter_output><empty_output><jupyter_text>次に objective metric とその定義を指定します。これには、トレーニングジョブの CloudWatch ログからそのメトリックを抽出するために必要な正規表現（Regex）が含まれます。 この特定のケースでは、スクリプトは平均損失値を出力し、それを objective metric として使用します。また、`objective_type` を `minimize` に設定します。これにより、ハイパーパラメーターチューニングは、最適なハイパーパラメーター設定を検索するときに客観的なメトリックを最小化しようとします。 デフォルトでは、 `objective_type` は `maximize` に設定されています。<jupyter_code>objective_metric_name = 'average test loss'
objective_type = 'Minimize'
metric_definitions = [{'Name': 'average test loss',
                       'Regex': 'Test set: Average loss: ([0-9\\.]+)'}]<jupyter_output><empty_output><jupyter_text>次に、 `HyperparameterTuner` オブジェクトを作成します。- 上記で作成した PyTorch 推定器- ハイパーパラメーターの範囲- Objective metric 名と定義- 合計で実行するトレーニングジョブの数や並行して実行できるトレーニングジョブの数などのリソース構成の調整。<jupyter_code>tuner = HyperparameterTuner(hpo_estimator,
                            objective_metric_name,
                            hyperparameter_ranges,
                            metric_definitions,
                            max_jobs=4,
                            max_parallel_jobs=2,
                            objective_type=objective_type)<jupyter_output><empty_output><jupyter_text>ハイパーパラメーター調整ジョブを起動する`.fit（）`を呼び出し、学習およびテストデータセットへの S3 パスを渡すことで、ハイパーパラメーターチューニングジョブを開始できます。ハイパーパラメーターチューニングジョブが作成されたら、次のステップでチューニングジョブを記述してその進行状況を確認できます。SageMaker コンソールからジョブに移動して、ハイパーパラメーターチューニングジョブの進行状況を確認できます。<jupyter_code>tuner.fit({'training': inputs})
tuner.wait()<jupyter_output><empty_output><jupyter_text>ホスティング エンドポイントを作成するトレーニング完了後、Tuner オブジェクトを使用して、`PyTorchPredictor` をビルドおよびデプロイします。前の手順では、Tuner が複数のトレーニングジョブを起動し、最適な obejctive metric を持つ結果のモデルが最適なモデルとして定義ました。これにより、SageMaker エンドポイントが作成されます。これにより、チューナーが探索した最適なモデルに基づいて推論を実行するためのホスティングを行います。deploy 関数の引数により、エンドポイントに使用されるインスタンスの数とタイプを設定できます。これらは、トレーニングジョブで使用した値と同じである必要はありません。ここでは、モデルを単一の ```ml.m4.xlarge``` インスタンスにデプロイします。<jupyter_code>predictor = tuner.deploy(initial_instance_count=1, instance_type='ml.m4.xlarge')<jupyter_output><empty_output><jupyter_text>評価Estimator を使用して、手書きの数字を分類できるようになりました。下のセルを実行すると、空の画像ボックスが表示されます。 次に、その中に数字を描画すると、ピクセルデータがこのノートブックの `data` 変数にロードされ、`predictor` に渡すことができます。<jupyter_code>from IPython.display import HTML
HTML(open("input.html").read())
import numpy as np

image = np.array([data], dtype=np.float32)
response = predictor.predict(image)
prediction = response.argmax(axis=1)[0]
print(prediction)<jupyter_output><empty_output><jupyter_text>HPO探索を行わない通常の学習`mnist.py` で定義されているハイパーパラメータのデフォルト値（）を用いて、学習を行う場合は、 `estimator` に対して `.fit()`を実行します。<jupyter_code>from sagemaker.pytorch import PyTorch

estimator = PyTorch(entry_point="mnist.py",
                    role=role,
                    framework_version='1.1.0',
                    train_instance_count=2,
                    train_instance_type='ml.m4.xlarge',
                    hyperparameters={
                        'epochs': 4,
                        'backend': 'gloo'
                    })
estimator.fit({'training': inputs})<jupyter_output><empty_output><jupyter_text>評価上記のHPOの場合と同様に、手書き文字認識精度を評価します。<jupyter_code>predictor_estimator = estimator.deploy(initial_instance_count=1, instance_type='ml.m4.xlarge')
import numpy as np

image = np.array([data], dtype=np.float32)
response_estimator = predictor_estimator.predict(image)
prediction = response_estimator.argmax(axis=1)[0]
print(prediction)<jupyter_output><empty_output><jupyter_text>リソースの削除上記で作成したホスティングエンドポイントは、明示的に削除しないと立ち上がったままになり、課金が継続されます。不要な課金を防ぐために、このノートブックの実行が終了したらエンドポイントを削除しましょう。立ち上がっている同一リージョン内のエンドポイントは、SageMaker マネージメントコンソールの「推論」->「エンドポイント」から一覧を確認できます。<jupyter_code># tuner で作ったエンドポイントの削除
tuner.delete_endpoint()
# estimator で作ったエンドポイントの削除
estimator.delete_endpoint()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09894259818731117, 'fraction_numerical': 0.012408286577470867, 'mean_word_length': 7.836034318398474, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 58, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '410572', 'license': 'Apache-2.0', 'n_tokens_mistral': 6121, 'n_tokens_neox': 5859, 'n_words': 345, 'path': 'hpo_pytorch_mnist/hpo_pytorch_mnist.ipynb', 'repo_name': 'tyohei/amazon-sagemaker-examples-jp', 'size': '9268'}