こんにちは。
最近AWSの機械学習サービスであるSageMakerを使っています。
SageMakerは公式サンプルが充実していて、色々な機械学習を簡単に動かすことができます。
ただ、一通りサンプルを動かして、いざ自前の機械学習プログラムを動かそうと思うと、調べても資料があまりない!ということに気づきます。
試行錯誤しつつ自分で動かして、分からず結構苦労したので、そのノウハウをここにまとめました。
方法
SageMaker Python SDKを使って少しコードを変えることで自前のコードをSageMaker上で動かすことができます。
ChainerやTensorflowなど各フレームワークごとにやり方が決まっています。
https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/frameworks.html
今回はChainerのコードをSageMaker上で動かしてみました。
大まかな流れ
下記の3つを準備して実行します。
1)学習させるデータ
2)学習を行うPythonのソースコード
3)学習用のインスタンスを立ち上げ学習を起動するJupyter notebook
Chainerのexampleにあるimagenetを使った画像分類のソースコード(詳細は後述)を例に今回は説明します。
データセットはimagenetでは重いので、Caltech101を入力の型をあわせて使用しました。
1)学習させるデータ
まずはCaltech101のデータセットの準備です。ここはSageMaker関係ない一般的な部分ですね。
下記リンクの「画像の用意、教師データとテストデータ」を参考にデータのダウンロード、リサイズ、meanファイルの作成を行います。
https://qiita.com/rerere0101/items/fde1661df4a26f1d0626
下記のようなディレクトリ構成になるはずです。
(親ディレクトリ)
├train.txt
├test.txt
├lobel.txt
└caltech101
├accordion
image_0001.jpg
…
├airplanes
image_0001.jpg
…
…
~~~
(下記は本データセットをSageMaker上で使用するときの特有の手順)
train.txt, test.txtは画像パスを下記のように変更します。
train.txtの中身
/opt/ml/input/data/train/caltech101/accordion/image_0001.jpg 0
/opt/ml/input/data/train/caltech101/accordion/image_0002.jpg 0
test.txtの中身
/opt/ml/input/data/test/caltech101/accordion/image_0043.jpg 0
/opt/ml/input/data/test/caltech101/accordion/image_0044.jpg 0
★注意点
<画像のパス> <正解ラベル>
という並びになりますが、データの呼び出しはS3からSageMakerインスタンスに呼び出された後に行われるため、パスはSageMakerインスタンスに置かれたときのパスを指定します。
S3に置いた画像データセットは/opt/ml/input/data/以下にコピーされます。
学習用の画像は/opt/ml/input/data/train, 評価用の画像は/opt/ml/input/data/test以下に置かれます。
SageMakerインスタンスにどのようにコピーされるかの構成はこちらを参照ください。
http://acro-engineer.hatenablog.com/entry/2018/09/05/120000
~~~
このセット(train.txt, test.txt, caltech101ディレクトリ)をS3に配置してください。
2)学習を行うPythonのソースコード
Chainerのexampleにある下記のソースコードを使用してSageMaker対応を行いました。
https://github.com/chainer/chainer/blob/master/examples/imagenet/train_imagenet.py
名前はtrain_imagenet.pyですが、入力の型を合わせることによって、imagenet以外のデータセットでも使用することができます。
ただし、二箇所変更が必要です。
★変更箇所① S3のパスを学習を起動するJupyter Notebook側で指定するので受け口を作ります。
下記をargparseで指定しているところに追記します。
parser.add_argument('--output_data_dir', type=str, default=os.environ['SM_OUTPUT_DATA_DIR'])
parser.add_argument('--model-dir', type=str, default=os.environ['SM_MODEL_DIR'])
parser.add_argument('--train', type=str, default=os.environ['SM_CHANNEL_TRAIN'])
parser.add_argument('--test', type=str, default=os.environ['SM_CHANNEL_TEST'])
次の引数を指定できるようにします。
・output_data_dir グラフ等生成物の出力ディレクトリ
・model-dir モデルの出力ディレクトリ
・train train画像のディレクトリ
・test test画像のディレクトリ
これに伴い、ソースコード内で同様の働きをしているもの(元々ソースコード内にあるtrainやval等)は書き換えます。
★変更箇所② データセットを指定する部分を変更します。
最初にargs.trainを呼び出す前(165行目より前)、に下記を入れます。
# data setting
train = args.train + "/train.txt"
test = args.test + "/test.txt"
学習を起動するJupyter NotebookではS3のバケットまでしか指定できないので、それ以降のファイル指定・操作は ソースコード側で行う必要があります。
ソースコード内でargs.trainやargs.test(元々はargs.val)で渡しているものはそれぞれtrain, testに書き換えましょう。
3)学習用のインスタンスを立ち上げ学習を起動するJupyter notebook
学習用のノートブックインスタンスには、以下のように配置します。
呼び出される側のプログラムはすべてsrcフォルダの中に入れておいてください。
学習起動Jupyter notebook
src/
└学習を行うPythonのソースコード
学習起動Jupyter notebookには下記のように書いていき、順に実行していきます。
sagemaker.session()の作成と、ロールの取得を行います。
import sagemaker
from sagemaker import get_execution_role
sagemaker_session = sagemaker.Session()
# Get a SageMaker-compatible role used by this Notebook Instance.
role = get_execution_role()
バケットとその後のパスを指定します。
imagenet_sagemaker以下に学習用データを置きました。
bucket = '<your-bucket-name>'
prefix = 'sagemaker/imagenet-chainer'
S3_INPUT_PATH = 's3://{0}/{1}/'.format(bucket, prefix)
print(S3_INPUT_PATH)
estimaterの作成を行います。
呼び出すプログラムをentry_pointで、プログラムを入れたフォルダをsource_dirで指定しました。
source_dirで指定したフォルダがそのままSageMakerインスタンスにコピーされます。
(なので、ソースコード以外にもSageMakerインスタンスにコピーしたいものがあればsource_dirに入れておけば、一緒にコピーすることができます。)
train_imagenet.py内で元々指定するようになっている引数はhyperparametersで指定します。
import subprocess
from sagemaker.chainer.estimator import Chainer
instance_type = 'ml.c4.xlarge'
chainer_estimator = Chainer(entry_point='train_imagenet.py', role=role,
source_dir="src",
train_instance_count=1,
train_instance_type=instance_type,
framework_version='5.0.0',
hyperparameters={'arch': "mobilenet"})
学習を実行します。
train_input = "s3://20190225-sagemaker/sagemaker/imagenet-chainer/"
test_input = "s3://20190225-sagemaker/sagemaker/imagenet-chainer/"
chainer_estimator.fit({'train': train_input, 'test': test_input})
これで自前の機械学習コードをSageMaker上で動かすことができました。
ポイント
①データはすべてS3にあげる。パスを指定する場合はSageMakerインスタンスにコピーされた後の構成を意識する
②学習を行うソースコードはSageMakerの仕様に合わせる。
③ノートブックインスタンスからSageMakerインスタンスに送る学習用ソースコードは別ディレクトリにまとめておく。
まとめ
自前のソースコードをSageMaker上でPython SDKを用いて動かしてみました。
SageMaker特有の仕様もあり、最初は分からない部分もありましたが、分かってしまえば簡単に実行できるものだな、と思います。
この記事ではChainerを例に説明しましたが、ポイントを押さえて書くことで、他のフレームワークでも応用できると思います。