2019年のAmazon SageMakerハンズオンでのハマりポイント

##はじめに


2019年5月にAWS主催で開催された[Amazon SageMaker 機械学習エンジニア向け体験ハンズオン](https://pages.awscloud.com/event_JAPAN_hands-on-ml_ondemand.html)が、一般に公開されています。

SageMakerを含むAI/MLサービスに興味がわいてきたので、専門知識-データアナリティクスと、機械学習の試験勉強も兼ねてハンズオンを実施したのですが、SageMakerのバージョンが1.xx時代のもので、今は2.xxにバージョンが上がっているため、このまま実施するとあちこちでエラーとなり、どはまりします。

このSageMakerハンズオンを実施する皆様が、私のようにはまらないように、本記事を投稿させていただきます。

## ビルトインアルゴリズムを利用したハンズオン
####1. 学習の実行でAttributeErroreエラーが発生
学習の実行で、AttributeError: 'Series' object has no attribute 'as_matrix' が発生します。

```
train_instance_count has been renamed in sagemaker>=2.
See: https://sagemaker.readthedocs.io/en/stable/v2.html for details.
train_instance_type has been renamed in sagemaker>=2.
See: https://sagemaker.readthedocs.io/en/stable/v2.html for details.

AttributeError                            Traceback (most recent call last)
<ipython-input-4-906e26861145> in <module>
     17 
     18 # automatically upload the training data to S3 and run the training job
---> 19 rcf.fit(rcf.record_set(taxi_data.value.as_matrix().reshape(-1,1)))

~/anaconda3/envs/python3/lib/python3.6/site-packages/pandas/core/generic.py in __getattr__(self, name)
   5139             if self._info_axis._can_hold_identifiers_and_holds_name(name):
   5140                 return self[name]
-> 5141             return object.__getattribute__(self, name)
   5142 
   5143     def __setattr__(self, name: str, value) -> None:

AttributeError: 'Series' object has no attribute 'as_matrix'
```

以下に対策が記載されていたので、参考にします。

　https://zenn.dev/jinwatanabe/articles/5eb172b0ea2587

>Python3.7では、pandasでas_matrix()メソッドが非推奨になっています。故に使えない。
>以下のようにas_matrix()をvaluesに変更することで動くようになります。

具体的には、以下のas_matrix()をvaluesに変更します。

```python
rcf.fit(rcf.record_set(taxi_data.value.as_matrix().reshape(-1,1)))
           ↓
rcf.fit(rcf.record_set(taxi_data.value.values.reshape(-1,1)))
```

上記以外にも推論実行のところにもas_matrix() があるので、valuesに差し替えてください。

修正後に再実行すると、最初に以下のメッセージは出力されますが、SageMakerのバージョン違いのようなので、（結果的には）無視してOKのようです。このメッセージは今後もずっと出力されます。

```
train_instance_count has been renamed in sagemaker>=2.
See: https://sagemaker.readthedocs.io/en/stable/v2.html for details.
train_instance_type has been renamed in sagemaker>=2.
See: https://sagemaker.readthedocs.io/en/stable/v2.html for details.
Defaulting to the only supported framework/algorithm version: 1. Ignoring framework/algorithm version: 1.
Defaulting to the only supported framework/algorithm version: 1. Ignoring framework/algorithm version: 1.
```

#### 2.異常を検知するデータの用意でAttributeErrorが発生

異常を検知するデータの用意で、以下のAttributeError: can't set attribute エラーが発生しました。text/csvがcontent_typeとして指定ができないエラーです。これはSageMakerのバージョンが違うことより、構文が異なるため発生しています。

```
AttributeError                            Traceback (most recent call last)
<ipython-input-10-6baf8918ddf5> in <module>
      1 from sagemaker.predictor import csv_serializer, json_deserializer
      2 
----> 3 rcf_inference.content_type = 'text/csv'
      4 rcf_inference.serializer = csv_serializer
      5 rcf_inference.accept = 'application/json'

AttributeError: can't set attribute
```

このため、以下の構文を

```python
from sagemaker.predictor import csv_serializer, json_deserializer

rcf_inference.content_type = 'text/csv'
rcf_inference.serializer = csv_serializer
rcf_inference.accept = 'application/json'
rcf_inference.deserializer = json_deserializer
```

SageMaker 2.xxに対応した以下の構文へ差し替えて、再度実行してください。

```python
from sagemaker.serializers import CSVSerializer
from sagemaker.deserializers import JSONDeserializer

rcf_inference.serializer=CSVSerializer()
rcf_inference.deserializer=JSONDeserializer()
```

この後の「RCFの応用ならびに改善（オプション）」を実行する場合も、上記の修正を実施してください。

具体的には以下の箇所を

```python
from sagemaker.predictor import csv_serializer, json_deserializer

rcf_inference = rcf.deploy(
    initial_instance_count=1,
    instance_type='ml.m4.xlarge',
)

rcf_inference.content_type = 'text/csv'
rcf_inference.serializer = csv_serializer
rcf_inference.accept = 'appliation/json'
rcf_inference.deserializer = json_deserializer
```

以下と差し替えてください。

```python
from sagemaker.deserializers import JSONDeserializer
from sagemaker.serializers import CSVSerializer

rcf_inference = rcf.deploy(
    initial_instance_count=1,
    instance_type='ml.m4.xlarge',
    serializer=CSVSerializer(),
    deserializer=JSONDeserializer()
)
```


## Deep Learningフレームワークを利用したハンズオン
こちらのハンズオンでもエラーが発生しました。

#### 1.モデルの学習でValueErrorが発生
モデルの学習で、ValueError: framework_version or py_version was None, yet image_uri was also None のエラーが発生しました。

```
ValueError                                Traceback (most recent call last)
<ipython-input-3-7583fa72b5e5> in <module>
      7 chainer_estimator = Chainer(entry_point='chainer_mnist.py', role=role,
      8                             train_instance_count=1, train_instance_type=instance_type,
----> 9                             hyperparameters={'epochs': 3, 'batch_size': 128})
     10 
     11 chainer_estimator.fit({'train': train_input, 'test': test_input})

~/anaconda3/envs/chainer_p36/lib/python3.6/site-packages/sagemaker/chainer/estimator.py in __init__(self, entry_point, use_mpi, num_processes, process_slots_per_host, additional_mpi_options, source_dir, hyperparameters, framework_version, py_version, image_uri, **kwargs)
    129             :class:`~sagemaker.estimator.EstimatorBase`.
    130         """
--> 131         validate_version_or_image_args(framework_version, py_version, image_uri)
    132         if py_version == "py2":
    133             logger.warning(

~/anaconda3/envs/chainer_p36/lib/python3.6/site-packages/sagemaker/fw_utils.py in validate_version_or_image_args(framework_version, py_version, image_uri)
    590     if (framework_version is None or py_version is None) and image_uri is None:
    591         raise ValueError(
--> 592             "framework_version or py_version was None, yet image_uri was also None. "
    593             "Either specify both framework_version and py_version, or specify image_uri."
    594         )

ValueError: framework_version or py_version was None, yet image_uri was also None. Either specify both framework_version and py_version, or specify image_uri.
```

framework_versionとpy_versionが指定されていないのが原因のようなので、
Chainerにframework_versionと、py_versionを明示的に指定すればOKです。

具体的には、以下の箇所を

```python
import subprocess

from sagemaker.chainer.estimator import Chainer

instance_type = 'ml.m4.xlarge'

chainer_estimator = Chainer(entry_point='chainer_mnist.py', role=role,
                            train_instance_count=1, train_instance_type=instance_type,
                            hyperparameters={'epochs': 3, 'batch_size': 128})

chainer_estimator.fit({'train': train_input, 'test': test_input})

# Keep the job name for checking training loss later 
training_job = chainer_estimator.latest_training_job.name
```

以下に変更します。train_instance_type=instance_typeに、framework_versionと、py_versionを追加しています。

```python
import subprocess

from sagemaker.chainer.estimator import Chainer

instance_type = 'ml.m4.xlarge'

chainer_estimator = Chainer(entry_point='chainer_mnist.py', role=role,
                            train_instance_count=1, train_instance_type=instance_type,
                            framework_version='5.0.0',
                            py_version='py3',
                            hyperparameters={'epochs': 3, 'batch_size': 128})

chainer_estimator.fit({'train': train_input, 'test': test_input})
py_version="py3"
# Keep the job name for checking training loss later 
training_job = chainer_estimator.latest_training_job.name
```

##最後に
SageMaker 2.xxに対応したハンズオン資料もアップして欲しいですね。今度AWSに開催してもらうよう、相談をしてみようかな。

AWSもAzureも、操作画面やサービスがどんどん変更されるため、手順を作成してもすぐに使えなくなるのは困りものですね…。オンプレのときのように、最低３年間は後方互換を保持するようにしてくれるとありがたいのですが、考え方がオッサン過ぎますでしょうか。
画面が変わるだけならまだしも、今回のようにスクリプトが動きもしないのはどうにかして欲しいですよね。SageMakerのバージョンを1.xxにダウングレードしようとしましたが、結果的にできませんでした。

続いてTranScribeやComprehendも触ってみる予定です。SageMakerの深い勉強はもっと先になりそうです。

それでわ。