ECS + EI で最強のインファレンス環境を構築してみた

# はじめに
去る2018年，11月28日の AWS re:Invent 2018 Keynote にて [EI (Amazon Elastic Inference) のリリースがアナウンスされました．](https://aws.amazon.com/about-aws/whats-new/2018/11/introducing-amazon-elastic-inference)EI とは EC2 にアタッチすることでインファレンス時の速度をコスパよく向上するサービスです．インファレンス時の速度が向上するということは，サービス原価が 時間×コスト で決まる昨今においては非常に嬉しいことです．
今回はこれを ECS と組み合わせて使うことで，エラスティックでコスパもよい最強のインファレンス環境が構築できるのではと考え，その実現性と性能を検証してみました．


# 実装
EC2 + EI で ECS Cluster を構築し，mxnet + Flask のアプリを ECR からデプロイし ALB 配下に置くことで ECS + EI を実現することができました．以下のように `curl` で画像を投げることでインファレンス結果を得ることできます．

```bash
$ curl -sS -X POST -F images=@test.jpg http://xxx.us-west-2.elb.amazonaws.com/classify | jq .
[
  {
    "772": 0.3407192826271057,
    "288": 0.18289047479629517,
    "117": 0.06933033466339111,
    "683": 0.0603695772588253,
    "714": 0.052266042679548264
  }
]
```

インフラ面でもアプリ面でも何点かハマりポイントがあったので，それらについて触れていこうと思います．

## インフラ
まずは，ECS + EI の環境を構築します．EC2 で EI を使う場合には，[こちらのマニュアル](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-ei.html)に従えば問題なく使えるのですが，ECS で使う際にはいくつかハマりポイントがあったので，それを中心に触れたいと思います．
2018/12/31時点では，ECS と EI のインテグがまだ十分に実装されているわけではなく，ECS の画面からガバッと構築することは出来ません．そこで，起動テンプレートと Auto Scaling を組み合わせて環境を構築する必要があります．なお，一度 EC2 では EI を使ったことがある前提で記載しています．

### IAM Role
ECS Instance Role に用いる IAM Role を作成します．通常 ECS Service では Task Role を使って各種 AWS サービスとの認証をすると思います．一方で，EI では Task Role とのインテグが未実装なのか何らかの設定がいるのかは不明ですが，Task Role では上手く認証が通りませんでした．そこで，以下のように EI に対する権限を IAM Role に追加しました．
![Screenshot_2018-12-31 IAM Management Console_5.png](https://qiita-image-store.s3.amazonaws.com/0/130762/4a3156ce-6d5a-c356-70fd-e74b539001ef.png)

### 起動テンプレート
ECS Cluster に加える EC2 の起動テンプレートを実装します．ポイントとしては，「高度な設定」から以下を設定することです．

- 先ほど構築した IAM Role を指定する．
- 「Elastic Inference」で好きな Accelerator を選択する．
- 「ユーザーデータ」で ECS Cluster に加わるように設定する．

![Screenshot_2018-12-31 EC2 Management Console_1.png](https://qiita-image-store.s3.amazonaws.com/0/130762/983b0807-079c-8440-7822-290a9cb725ca.png)
![Screenshot_2018-12-31 EC2 Management Console_6.png](https://qiita-image-store.s3.amazonaws.com/0/130762/223afe73-de3a-9ec4-13a3-9ea0a9d05539.png)
![Screenshot_2018-12-31 EC2 Management Console_2.png](https://qiita-image-store.s3.amazonaws.com/0/130762/02c45ee4-e4cf-1f0b-a553-dbbf9338f164.png)

### Auto Scaling
ECS Cluster に自動でインスタンスを供給するため，Auto Scaling を作成します．ポイントとしては，[Configuring AWS PrivateLink Endpoint Services](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-ei.html#eia-privatelink)で EI の設定をした VPC を指定することです．
![Screenshot_2018-12-31 EC2 Management Console_3.png](https://qiita-image-store.s3.amazonaws.com/0/130762/87f516b8-0536-dac9-14c3-d45f5e4d10e5.png)
![Screenshot_2018-12-31 EC2 Management Console_4.png](https://qiita-image-store.s3.amazonaws.com/0/130762/8ece01ae-2577-92f3-1a89-64028bf635a1.png)

### Security Group
ECS Cluster から EI へ通信できるよう[Configuring Your Security Groups for Amazon EI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-ei.html#ei-security)で構築した SG に 設定を追加します．ECS Cluster では IP が固定されないため，ECS Cluster の SG からの HTTPS 通信を許可する必要があります．
![Screenshot_2018-12-31 EC2 Management Console_7.png](https://qiita-image-store.s3.amazonaws.com/0/130762/2b7ff072-1c1b-863c-f3e0-b89210768463.png)

## アプリ
ネットワークアーキテクチャが変わった時の影響も見たかったため，[mxnet 公式の model zoo](https://mxnet.incubator.apache.org/model_zoo/index.html)のモデルを使っていきます．実装も[チュートリアル](https://mxnet.incubator.apache.org/tutorials/python/predict_image.html)をベースに Flask で Web API 化しています．なお，全コードは[こちら](https://github.com/ynaka81/ecs_ei_sample)にあります．
1点ハマったポイントとしては，上記でも触れましたが，EI と Task Role の相性が悪いらしく以下を加えて無理やり Task Role が使われないようにした点です．ここは，よりよいやり方であったり公式サポートが得られることを期待しています．

```python:main.py
 10 # I don't know the reason but it works by removing AWS_CONTAINER_CREDENTIALS_RELATIVE_URI and using instance role.
 11 if 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' in os.environ:
 12     del os.environ['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI']
 13     del os.environ['ECS_CONTAINER_METADATA_URI']
```


# 性能検証
上記より実現性は検証できたので，今度は性能について検証したいと思います．ネットワークアーキテクチャやバッチサイズにより GPU 計算効率は変わるので，それが EI にどのように影響を与えるかも検証していきます．HTTP や Flask のオーバーヘッドがあるため純粋な EI と CPU の性能比較にはなっていませんが，実用を考えるとこのようなオーバーヘッドは発生するため計測に含めています．なお，画像は[mxnet のチュートリアルに用いられている猫の画像](https://raw.githubusercontent.com/dmlc/web-data/master/mxnet/doc/tutorials/python/predict_image/cat.jpg)を使います．また，速度計測は以下のスクリプトを用いて10回リクエストを送った時の平均レスポンスタイムを用います．

```python
import os
import requests
import sys
from io import BytesIO

from benchmarker import Benchmarker


base_url = os.environ['BASE_URL']
N = int(sys.argv[1])
filename = 'cat.jpg'
with open(filename, 'rb') as f:
    image = f.read()
    with Benchmarker(cycle=10, width=25) as bench:
        @bench('test')
        def _(bm):
            for _ in bm:
                files = [('images', (filename, BytesIO(image), 'image/jpeg')) for _ in range(N)]
                response = requests.post(
                    f'{base_url}/classify',
                    files=files
                )
                response.raise_for_status()
```


ネットワークアーキテクチャやバッチサイズの変更，EI の有無で計測した実行速度とコストパフォーマンス (千枚/ドル) を以下にプロットしました．コストは2018年12月31日時点でのスポットインスタンス@オレゴンの価格を使っています．なお，`ResNet, bs=1, eia1.medium`はネットワークアーキテクチャが ResNet，バッチサイズが1，EI の medium を使っていることを表しています．

![cost performance vs. process time_overall.png](https://qiita-image-store.s3.amazonaws.com/0/130762/86ad829c-2a31-107d-3339-ce48f6ab8cd1.png)
見事に3つのクラスタが見えます．CaffeNet や SqueezeNet の軽いネットワーク+CPUは処理時間は短くコストパフォーマンスは高いところに集中しています．また，ResNet や ResNext の深いネットワーク+CPUは処理時間は長くコストパフォーマンスは低いところに集中しています．最後に，ネットワークアーキテクチャによらず EI を用いたものは処理時間は短くコストパフォーマンスも低いところに集中しています．

CaffeNet と SqueezeNet だけに着目してプロットしてみると，
![cost performance vs. process time_small.png](https://qiita-image-store.s3.amazonaws.com/0/130762/68bbe4a6-a0be-66ed-f4f1-9e6ba2772f3b.png)
`CaffeNet, bs=1`だけやや特殊ですが，他のものは EI と CPU で処理時間は同程度だがコストパフォーマンスでは CPU の方が優っているため，EI を使う理由はなさそうです．

次に ResNet と ResNext だけに着目してプロットしてみると，
![cost performance vs. process time_large.png](https://qiita-image-store.s3.amazonaws.com/0/130762/9b3398b1-b07b-b9b5-2f74-e98fcaf04436.png)
処理速度では EI は CPU よりも3〜5倍早く，コストパフォーマンスは0.5〜0.7倍と劣っています．やはり銀の弾丸ということはないですが，処理速度が向上することによってシステムは安定する傾向があるので，特に深いネットワークの場合は多少のコスパを犠牲にしてでも使う機会は大いにありそうです．


# まとめ
AWS re:Invent 2018 Keynote にて発表された Elastic Inference を ECS と組み合わせて利用することが出来ました．これにより，エラスティックでコスパのよいインフェレンス環境が構築出来そうです．コスパの面で見れば，最強であるかといえばやはり銀の弾丸ではなかったです．一方で，CPU 負荷の高い深いネットワークにおいては，ややコスパが劣化するレベルで処理速度3〜5倍を達成できるので，利用する機会は多いにありそうです．
以下を2019年 Elastic Inference に期待しつつ終わりたいと思います．

- ECS とのインテグを公式にキレイな形で実現できることと．
- コストが低下してコスパの面でも最強となれること．