AWS DeepRacer検出モデルを作ってみた（モデル作成編）

# はじめに
オブジェクト検出（object detection）アルゴリズムを利用してAWS DeepRacerを検出するモデルを作ってみました。
作成したモデルは、ラップタイム計測や走行軌跡の視覚化にも活用できそうです。
AWS DeepRacerは機械学習の中では「強化学習」という手法ですが、今回は「教師あり学習」について学んだことを公開します。

本記事では、AWS DeepRacer検出モデルのモデル作成について説明しています。
その他の内容については、以下の記事をご確認ください。

- [AWS DeepRacer検出モデルを作ってみた（データ準備編）](https://qiita.com/dnp-ayako/items/31cb5ff63346296aecfa)
- [AWS DeepRacer検出モデルを作ってみた（モデル作成編）](https://qiita.com/dnp-ayako/items/56bf9aa2e5d146b9ff12)※本記事
- [AWS DeepRacer検出モデルを使ってみた（実践編）](https://qiita.com/dnp-ayako/items/fb6de68391a6246cb199)

#### こんな人におすすめ
- AWS DeepRacerをきっかけに初めて機械学習に触れた。（筆者のレベル）
- 教師あり学習、オブジェクト検出モデル作成に興味がある。
- Amazon Rekognition Videoや公開されているデータセットでオブジェクト検出を試してみたが、検出対象が一般化されておらず期待している精度がでなかった。
- シミュレーションと現実世界の比較をしたい、視覚化をしたい。
 - [AWS DeepRacerの走行軌跡を手で描画して比較してみた記事はこちら](https://qiita.com/dnp-ayako/items/a1c902daf882ef2f86c7)

#### 注意
- 以下の内容は 2019 年 9 月 3 日時点のサンプルノートブックを対象にしています。その他のバージョンのノートブックでは利用できない可能性があります。
- サンプルノートブックの中には複数のインスタンスを利用する手順がありますのでAWS利用費にご注意ください。
- 本記事ではアジアパシフィック（東京）リージョンを使用しています。
- 本手順はAWS開発者ガイドに沿って行っています。
 - [Amazon SageMaker開発者ガイド　AmazonSageMaker組み込みアルゴリズムを使用する](https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/algos.html)

# 準備
AWS DeepRacer検出モデルの作成にはAmazon SageMakerで提供されているサンプルノートブックを使用します。そのための準備を行います。

#### IAMロールの作成
まず、Jupyter Notebookインスタンス用のIAMロールを作成します。

- AWSサービス：SageMaker
- アクセス許可ポリシー（Permissions policies）
 - AmazonSageMakerFullAccess　をアタッチする
 - 以下の内容のポリシーをアタッチする
 
```ruby:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket",
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Effect": "Allow",
            "Resource": [
                "arn:aws:s3:::*"
            ]
        }
    ]
}
```

作成したIAMロールのARNをコピーしてメモしておきます。

#### ノートブック環境の作成
Amazon SageMakerでJupyter Notebookを動かすインスタンスを作成します。

１. Management ConsoleでAmazon SageMakerを開き、アジアパシフィック（東京）に切り替えます。

２. ノートブックインスタンスの画面から「Create notebook instance」に進み、以下の内容でノートブックインスタンスを作成します。

- ノートブックインスタンス設定
 - ノートブックインスタンス名：作成者がわかるように命名
 - ノートブックインスタンスのタイプ：ml.t2.medium
 - Elastic Inference：なし
 - IAMロール：カスタムIAMロールのARN
     - カスタムIAMロールのARN：作成したIAMロールのARN
 - VPC：非VPC
 - ライフサイクル設定：設定なし
 - 暗号化キー：カスタム暗号化なし
 - ボリュームサイズ：5
- Gitリポジトリ
 - リポジトリ：なし

３. ノートブックインスタンスのステータスが「InService」になるまで待ち、「Open Jupyter」アクションからJupyter Notebookを開きます。
**※注意**　このインスタンスの起動中はAWS利用費が発生しますのでご注意ください。
![1-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/74dddab7-bdb7-2798-8d61-5246851676f9.png)

#### サンプルノートブックの利用
Amazon SageMakerで提供されている、オブジェクト検出モデルを作成するサンプルノートブックをコピーします。
対象のサンプルノートブックは
**Jupyter Notebookメニュー＞SageMaker Exsamples
Introduction to Amazon Algorithms　＞　object_detection_image_json_format.ipynb**
です。

![1-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/9df42db7-34c1-6e32-f977-b597fa4bd7d8.png)

object_detection_image_json_format.ipynbの「Use」をクリックし、インスタンスのホームディレクトリにコピーして開きます。

![1-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/1fcc264a-4c4c-cf2a-ae67-6c4859bd6aad.png)

ノートブックが開いたら、準備完了です。

# オブジェクト検出モデルの作成
コピーしたサンプルノートブックに沿って実行すると、Data PreparationにおいてMS COCOの教師あり学習向けに公開されているデータセットを使いトレーニングを行いますが、
今回は
[AWS DeepRacer検出モデルを作ってみた（データ準備編）](https://qiita.com/dnp-ayako/items/31cb5ff63346296aecfa)
で作成したデータセットを利用してトレーニングを行います。

セル単位に実行ボタンを押すか、Shift+Enterで実行します。

#### Setup
準備の手順で作成したIAMロールが紐づいていることを確認します。

```python:
%%time
import sagemaker
from sagemaker import get_execution_role

role = get_execution_role()
print(role)
sess = sagemaker.Session()
```

トレーニングデータのS3バケットの設定をします。

```python:
bucket = 'データ準備編で格納したS3バケット名' 
prefix = 'データ準備編で格納したprefixフォルダ名' 
```

トレーニング用イメージのURLを取得します。

```python:
from sagemaker.amazon.amazon_estimator import get_image_uri
training_image = get_image_uri(sess.boto_region_name, 'object-detection', repo_version="latest")
print (training_image)
```

次のData Preparationの以下のセルは実行しません。

- Data Preparation
 - Download data
 - Prepare dataset
 - Upload to S3

Jupyter notebookから空の実行セルを追加します。（メニュー Insert > Insert Cell Bellow）、
S3バケット名のフォーマット、出力先の設定を行うスクリプトを実行します。

```python:
train_channel = prefix + '/train'
validation_channel = prefix + '/validation'
train_annotation_channel = prefix + '/train_annotation'
validation_annotation_channel = prefix + '/validation_annotation'

s3_train_data = 's3://{}/{}'.format(bucket, train_channel)
s3_validation_data = 's3://{}/{}'.format(bucket, validation_channel)
s3_train_annotation = 's3://{}/{}'.format(bucket, train_annotation_channel)
s3_validation_annotation = 's3://{}/{}'.format(bucket, validation_annotation_channel)
print(s3_train_data)
print(s3_validation_data)
print(s3_train_annotation)
print(s3_validation_annotation)

s3_output_location = 's3://{}/{}/output'.format(bucket, prefix)
print(s3_output_location)
```

トレーニングデータと出力先の確認ができたら、Trainingに進みます。

#### Training

トレーニングを行うオブジェクトを作成します。
**※注意**　ここではサンプルノートブック通りのインスタンスタイプ（ml.p3.2xlarge）を指定しています。トレーニング実行中のAWS利用費にご注意ください。

```python:
od_model = sagemaker.estimator.Estimator(training_image,
                                         role, 
                                         train_instance_count=1, 
                                         train_instance_type='ml.p3.2xlarge',
                                         train_volume_size = 50,
                                         train_max_run = 360000,
                                         input_mode = 'File',
                                         output_path=s3_output_location,
                                         sagemaker_session=sess)
```

[AWS開発者ガイド　オブジェクト検出ハイパーパラメータ](https://docs.aws.amazon.com/ja_jp/sagemaker/latest/dg/object-detection-api-config.html)を参考にハイパーパラメータの設定を行います。

```python:
od_model.set_hyperparameters(base_network='resnet-50',
                             use_pretrained_model=1,
                             num_classes=1,
                             mini_batch_size=16,
                             epochs=30,
                             learning_rate=0.001,
                             lr_scheduler_step='10',
                             lr_scheduler_factor=0.1,
                             optimizer='sgd',
                             momentum=0.9,
                             weight_decay=0.0005,
                             overlap_threshold=0.5,
                             nms_threshold=0.45,
                             image_shape=512,
                             label_width=60,
                             num_training_samples=2661)
```

S3に格納したデータチャネルの設定をします。

```python:
train_data = sagemaker.session.s3_input(s3_train_data, distribution='FullyReplicated', 
                        content_type='image/jpeg', s3_data_type='S3Prefix')
validation_data = sagemaker.session.s3_input(s3_validation_data, distribution='FullyReplicated', 
                             content_type='image/jpeg', s3_data_type='S3Prefix')
train_annotation = sagemaker.session.s3_input(s3_train_annotation, distribution='FullyReplicated', 
                             content_type='image/jpeg', s3_data_type='S3Prefix')
validation_annotation = sagemaker.session.s3_input(s3_validation_annotation, distribution='FullyReplicated', 
                             content_type='image/jpeg', s3_data_type='S3Prefix')

data_channels = {'train': train_data, 'validation': validation_data, 
                 'train_annotation': train_annotation, 'validation_annotation':validation_annotation}
```

トレーニングの実行をします。（時間がかかります）

```python:
od_model.fit(inputs=data_channels, logs=True)
```

トレーニングジョブが作成され、ステータスや設定を確認することができます。
![2-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/8b8b288b-410c-1136-0d38-883af77d02bd.png)

Jupyter notebookに以下のログが出力されたらトレーニング完了です。

```python:
2019-09-03 08:58:43 Uploading - Uploading generated training model
2019-09-03 08:59:00 Completed - Training job completed
Billable seconds: 4065
```
※4065秒かかったので8USDくらいの利用費でした（2019年9月3 日時点の価格）

# モデルのデプロイ
作成したモデルをリアルタイム推論用エンドポイント（インスタンス）にデプロイします。

**※注意**　作成したエンドポイントはリクエストの有無にかかわらず時間課金されますのでご注意ください

#### Hosting

作成したモデルを推論用エンドポイントにデプロイします。（時間がかかります）

```python:
object_detector = od_model.deploy(initial_instance_count = 1,
                                 instance_type = 'ml.m4.xlarge')
```

進行状況に応じて増えていく「-」の最後に「！」が出力されたら完了です。

```python:
---------------------------（省略）-------------------------------!
```

Amazon SageMakerのエンドポイントメニューでも作成されたことを確認できます。
![2-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/ab493376-6422-23ef-cbd7-67b892d9fad2.png)

# モデルの評価
作成されたエンドポイントを使って、画像からAWS DeepRacerを検出できるか確認をします。

#### 評価用画像
データセットとして使われていない画像（test.jpg）を利用します。
（こちらはAWS Loftで開催されたAWS DeepRacerワークショップで撮影した写真です）
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/3b971262-4531-5685-a6fc-4cc2a39eb0f6.png)

#### 画像のアップロード
Jupyter notebookメニュー「File」からサンプルノートブックフォルダまで移動し、
右上の「Upload」ボタンでtest.jpgをノートブックインスタンスにアップロードします。
![3-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/722cfdfe-e00b-a366-0d26-6d80a2beb725.png)

#### Inference
サンプルノートブックに戻り、アップロードしたtest.jpgを読み込みます。

```python:
file_name = 'test.jpg'
with open(file_name, 'rb') as image:
    f = image.read()
    b = bytearray(f)
    ne = open('n.txt','wb')
    ne.write(b) 
```

推論エンドポイントの実行

```python:
import json
object_detector.content_type = 'image/jpeg'
results = object_detector.predict(b)
detections = json.loads(results)
print (detections)
```

json形式で結果が出力されます

```python:
{'prediction': [[0.0, 0.9997686743736267, 0.560955822467804, 0.523655354976654, 0.7612257599830627, 0.7705497145652771], [0.0, 0.011178643442690372, 0.6748533844947815, 0.28442221879959106, 0.7353938221931458, 0.379863440990448], [0.0, 0.010698996484279633, 0.643470823764801, 0.28412649035453796, 0.7043541073799133, 0.38124319911003113], [0.0, 0.010420815087854862, 0.7065418362617493, 0.2855299115180969, 0.7661884427070618, 0.3784949779510498]]}
```

jsonで得られた結果を可視化するための関数を定義します。

```python:
def visualize_detection(img_file, dets, classes=[], thresh=0.6):
        import random
        import matplotlib.pyplot as plt
        import matplotlib.image as mpimg

        img=mpimg.imread(img_file)
        plt.imshow(img)
        height = img.shape[0]
        width = img.shape[1]
        colors = dict()
        for det in dets:
            (klass, score, x0, y0, x1, y1) = det
            if score < thresh:
                continue
            cls_id = int(klass)
            if cls_id not in colors:
                colors[cls_id] = (random.random(), random.random(), random.random())
            xmin = int(x0 * width)
            ymin = int(y0 * height)
            xmax = int(x1 * width)
            ymax = int(y1 * height)
            rect = plt.Rectangle((xmin, ymin), xmax - xmin,
                                 ymax - ymin, fill=False,
                                 edgecolor=colors[cls_id],
                                 linewidth=3.5)
            plt.gca().add_patch(rect)
            class_name = str(cls_id)
            if classes and len(classes) > cls_id:
                class_name = classes[cls_id]
            plt.gca().text(xmin, ymin - 2,
                            '{:s} {:.3f}'.format(class_name, score),
                            bbox=dict(facecolor=colors[cls_id], alpha=0.5),
                                    fontsize=12, color='white')
        plt.show()
```

可視化の実行をします。信頼度が低い検出を除外するため0.20ポイント以上のみ可視化します。

```python:
object_categories = ['deepracer']
threshold = 0.20
visualize_detection(file_name, detections['prediction'], object_categories, threshold)
```

![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/995eda23-d69f-9bdf-a41e-769d269534a7.png)
うまく検出されました！


ちなみに、100個のデータセットでトレーニングしたモデルでは0.2ポイントの信頼度で且つ
AWS DeepRacer機の正面カメラ付近の黒い部分しか検出できませんでした。
![前回.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/0895e5ad-fc33-f206-2f34-46cd97acf4be.png)

#### 他の画像でも試してみた
多少ブレていても認識されました。
（こちらは当社で製作したコース＋AWSさんからお借りしたAWS DeepRacer機　で社外エンジニアと交流イベントを開催したときの動画です）
![3-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/8376ce3a-de33-c38a-13ce-56002fadc29b.png)

AWS DeepRacerの正面（カメラ部分）が写っている画像でも信頼度は低いですが認識しました。人の目で見ても一見、黒いかたまりにしか見えない物体ですが検出してくれました。
![3-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426326/71dae240-e7db-2cce-be41-83296e2eaf3c.png)

# エンドポイントの削除
評価が終わったら、推論エンドポイントを忘れずに削除します。

#### Delete the Endpoint
推論エンドポイントの削除

```python:
sagemaker.Session().delete_endpoint(object_detector.endpoint) 
```

以上でAWS DeepRacer検出モデルの作成は終わりです。
続いて、作ったモデルの利用例を紹介します。
[AWS DeepRacer検出モデルを使ってみた（実践編）](https://qiita.com/dnp-ayako/items/fb6de68391a6246cb199)