AWS Amazon SageMaker つかってみた3 〜サンプルの実行〜

# はじめに
Amazon SageMaker を利用する上で自分が気になったところ、詰まったところを中心にまとめていきます。
この記事では予めノートブックインスタンスに用意されていたサンプルの実行した時のことを書きます。
前回の記事は[こちら](https://qiita.com/okazy/items/beac79862303b287b8f2)
次回の記事は[こちら](https://qiita.com/okazy/items/1131ad73b92a7c9de03b)

# 注意
この記事ではパラメータの設定で失敗しており、最後まで実行ができていません。
完全版は[こちら](https://qiita.com/okazy/items/1131ad73b92a7c9de03b)の記事を御覧ください。
この記事は戦いの歴史として残しておきます。

# サンプルの場所
Jupyterの導入については[前回の記事](https://qiita.com/okazy/items/beac79862303b287b8f2)を参照

1. Jupyterのホーム画面を開きます。
2. `sample-notebooks` ディレクトリを開きます。
3. `sagemaker-python-sdk` ディレクトリを開きます。
4. `mxnet_gluon_cifar10` ディレクトリを開きます。
5. `cifar10.ipynb` ファイルを開きます。

# サンプルの実行
Jupyter NotebookにはPythonのコードと説明が含まれます。
右に `In [ ]:` と書かれているのがコードで、それ以外は説明です。

## サンプルについて

- 用いるデータセット: CIFAR-10という6万枚の画像データのデータセット
- 学習内容: 34層の深層学習により画像を10種類にクラスタリングする。

### セットアップ
`scikit-image` のライブラリが必要なのでcondaのページから入れてください。

1. `conda tab` のリンクをクリックするか、Jupyterのホームから `Conda` のタブをクリックします。
2. `Available packages` のところのSearchで `scikit-image` を検索します。
3. `scikit-image` を選択します。
4. 右矢印を押下してインストールします。

はじめのpythonコードを実行してみましょう。
以下のコードを選択して、画面上部の `Cell` から `Run Cells` を選択します。

```python3
import os
import boto3
import sagemaker
from sagemaker.mxnet import MXNet
from mxnet import gluon
from sagemaker import get_execution_role

sagemaker_session = sagemaker.Session()

role = get_execution_role()
```

右の表示が `In [*]:` に変わります。
`*` は実行中であることを表します。
実行が終わると `In [1]:` のような表示になります。
数字は実行された順番を表します。

初めて実行すると以下のWarningが表示されます。
こちらの原因と解消方法を知っている人がいれば教えてください。

```
/home/ec2-user/anaconda3/envs/mxnet_p27/lib/python2.7/site-packages/urllib3/contrib/pyopenssl.py:46: DeprecationWarning: OpenSSL.rand is deprecated - you should use os.urandom instead
  import OpenSSL.SSL
```

もう一度実行するとWarningが消えます。

## データのダウンロード
CIFAR10をダウンロードします。

```python3
from cifar10_utils import download_training_data
download_training_data()
```

少し時間がかかります。

## データのアップロード
Amazon SageMakerで学習させるにはまずS3に学習データをアップロードする必要があります。
S3へデータをアップロードするには `sagemaker.Session.upload_data` の関数を使います。
戻り値の `inputs` にはアップロード先のIDが入っており、後ほど学習の際に利用します。

```python3
inputs = sagemaker_session.upload_data(path='data', key_prefix='data/gluon-cifar10')
print('input spec (in this case, just an S3 path): {}'.format(inputs))
```

## `train` 関数の確認

SageMakerで利用可能な学習スクリプトを用意する必要があります。
学習スクリプトは `train` 関数として実装されている必要があります。
学習環境の情報が `train` 関数の引数として渡されます。

```python3
!cat 'cifar10.py'
```

## 学習スクリプトの実行

`MXNet` クラスを用いると分散環境での学習が可能です。
以下の設定をします。

- 学習スクリプト
- IAMロール
- インスタンス数
- インスタンスタイプ

デフォルトでは２個の `ml.p2.xlarge` インスタンスで学習ができるように設定されています。
`ml.p2.xlarge` インスタンスはGPUのインスタンスで、無料枠ではないので注意してください。
また、私のアカウントでは利用制限がかかっており、制限解除にはサポートへの問い合わせが必要なようです。

今回は無料枠の `ml.m4.xlarge` インスタンスを４個利用して学習をさせることにしました。

```python3
m = MXNet("cifar10.py", 
          role=role, 
          train_instance_count=4, 
          train_instance_type="ml.m4.xlarge",
          hyperparameters={'batch_size': 128, 
                           'epochs': 50, 
                           'learning_rate': 0.1, 
                           'momentum': 0.9})
```

`MXNet` クラスができましたので、アップロードしたデータを使って学習を開始します。
SageMakerではローカルディスクからデータを読み出すようにデータを扱えます。

```python3
m.fit(inputs)
```

学習がスタートしました。
現在実行中の学習はAmazon SageMakerのジョブの画面で確認できます。


# 学習の結果と考察
結論から言いますと、タイムアウトとなって学習が最後まで終わりませんでした。
ジョブの時間はデフォルトで24時間に設定されておりましたが、24時間以内に学習ができませんでした。
24時間時点で正解率は約80%程度でした。

## GPU恐るべし
今回ケチってCPUのインスタンス `ml.m4.xlarge` を４個使ったんですが、まさか24時間で学習が終わらないなんて。。。
GPUのインスタンス `ml.p2.xlarge` を１個で学習をすると45分ほどで学習できたとの報告もありますので、GPU恐るべしというところです。

料金で見ても

- `ml.p2.xlarge` を1時間で$1.26
- `ml.m4.xlarge` を96時間で$26.88

とGPUインスタンスを使ったほうが圧倒的に安くなります。
今後の学習ではGPUを積極的に使っていきたいところです。

# 分散環境での学習がうまくできていない？
４個のインスタンスの途中経過を見てみたところ、同じタイミングで同じ正解率しか返していませんでした。
以下に途中経過の一部を載せます。

```
2017-12-23 21:24:43,320 INFO - root - Epoch [17] Batch [144]#011Speed: 22.750666 samples/sec#011accuracy=0.805550
2017-12-23 21:24:43,319 INFO - root - Epoch [17] Batch [144]#011Speed: 22.242182 samples/sec#011accuracy=0.805550
2017-12-23 21:24:43,321 INFO - root - Epoch [17] Batch [144]#011Speed: 22.460798 samples/sec#011accuracy=0.805550
2017-12-23 21:24:43,309 INFO - root - Epoch [17] Batch [144]#011Speed: 22.502813 samples/sec#011accuracy=0.805550
2017-12-23 21:24:49,176 INFO - root - Epoch [17] Batch [145]#011Speed: 21.862221 samples/sec#011accuracy=0.805544
2017-12-23 21:24:49,123 INFO - root - Epoch [17] Batch [145]#011Speed: 22.058621 samples/sec#011accuracy=0.805544
2017-12-23 21:24:49,051 INFO - root - Epoch [17] Batch [145]#011Speed: 22.333050 samples/sec#011accuracy=0.805544
2017-12-23 21:24:49,128 INFO - root - Epoch [17] Batch [145]#011Speed: 21.997985 samples/sec#011accuracy=0.805544
2017-12-23 21:24:55,092 INFO - root - Epoch [17] Batch [146]#011Speed: 21.445688 samples/sec#011accuracy=0.805538
2017-12-23 21:24:55,111 INFO - root - Epoch [17] Batch [146]#011Speed: 21.570254 samples/sec#011accuracy=0.805538
2017-12-23 21:24:55,115 INFO - root - Epoch [17] Batch [146]#011Speed: 21.379692 samples/sec#011accuracy=0.805538
2017-12-23 21:24:55,088 INFO - root - Epoch [17] Batch [146]#011Speed: 21.204017 samples/sec#011accuracy=0.805538
```

４個のインスタンスで学習しているので４行ずつ学習結果が出力されています。
分散学習をさせているのに全インスタンスで同じ学習率なのは違和感があります。
これは分散学習がうまくできていなかったのでしょうか？

MXNetに問題があるのか、パラメータの設定に問題があるのか、学習スクリプトに問題があるのかはわかっていませんが、こちらの原因調査は今後の課題です。
分かる人がいたら教えてください。