6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Clipperを使った機械学習モデルのデプロイ管理

Last updated at Posted at 2018-03-21

機械学習モデルもdockerで管理するのが好きな人もいると思うので、
nipsで紹介されてたのを動かして確認してみました。
今回はモデルを登録して、api機能をデプロイするところをやってみました。

http://clipper.ai/tutorials/basic_concepts/
https://github.com/ucbrise/clipper

Clipperクラスタ

Clipperは3つPython,R,PySparkに対応しています。
Clipperクラスタのコアは、クエリフロントエンド、管理フロントエンド、および構成データベースの3つのコンポーネントで構成されています。
スクリーンショット 2018-03-20 15.06.06.png

  • クエリフロントエンド: 受信した予測リクエストとスケジュールをリッスンし、それらをデプロイされたモデルにルーティングします。ClipperのREST予測インターフェースを照会すると、フロントエンドに要求が送信されます。

  • 管理フロントエンド: Clipperの内部構成状態を管理および更新します。新しいアプリケーションの登録や新しいモデルの展開など、Clipperクラスタの設定を変更するときは、管理フロントエンドのadmin RESTインターフェイスに対してコマンドを発行しています。

  • 構成データベース: Clipperの内部構成状態を永続的に保存するために使用されるRedisインスタンス。Clipperの内部構成の変更は、管理フロントエンドからRedisに伝えられ、永続的に保存されます。
    クエリフロントエンドは、構成データベースへの変更を監視し、変更が検出されたときにその状態を適切に更新します。

自分で試した環境

cat /etc/os-release
>>>NAME="Ubuntu"
>>>VERSION="16.04.1 LTS (Xenial Xerus)"
>>>ID=ubuntu
>>>ID_LIKE=debian
>>>PRETTY_NAME="Ubuntu 16.04.1 LTS"
>>>VERSION_ID="16.04"
>>>...
pyenv versions
>>>* system (set by /home/user1/.pyenv/version)
>>>  anaconda2-4.4.0
>>>  anaconda2-4.4.0/envs/py27
python -V
>>>Python 2.7.13 :: Continuum Analytics, Inc.
docker -v
>>>Docker version 18.02.0-ce, build fc4de44

Python2.7の環境設定:Ubuntu

python2.7系しかclipperではサポートされていない。

git clone https://github.com/yyuu/pyenv.git ~/.pyenv

bachrcに設定。

# pyenv                                                                                                                                                        
export PYENV_ROOT=$HOME/.pyenv
export PATH=$PYENV_ROOT/bin:$PATH
eval "$(pyenv init -)"
pyenv install anaconda2-4.4.0

bachrcに設定。

export PATH="$PYENV_ROOT/versions/anaconda2-4.4.0/bin:$PATH"

conda環境を作る。

conda create -n py27 python=2.7

環境に入る。

source activate py27

参照
https://qiita.com/miyamotok0105/items/5f26e4ae41f0e35ded16

clipperの環境設定

# clipper
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates
pip install git+https://github.com/ucbrise/clipper.git@develop#subdirectory=clipper_admin

clipperの基礎を動かしてみる。

git clone https://github.com/ucbrise/clipper.git
cd examples/basic_query
python example_client.py
example_client.py
from __future__ import print_function
from clipper_admin import ClipperConnection, DockerContainerManager
from clipper_admin.deployers import python as python_deployer
import json
import requests
from datetime import datetime
import time
import numpy as np
import signal
import sys


def predict(addr, x, batch=False):
    url = "http://%s/simple-example/predict" % addr

    if batch:
        req_json = json.dumps({'input_batch': x})
    else:
        req_json = json.dumps({'input': list(x)})

    headers = {'Content-type': 'application/json'}
    start = datetime.now()
    r = requests.post(url, headers=headers, data=req_json)
    end = datetime.now()
    latency = (end - start).total_seconds() * 1000.0
    print("'%s', %f ms" % (r.text, latency))


def feature_sum(xs):
    return [str(sum(x)) for x in xs]


# Stop Clipper on Ctrl-C
def signal_handler(signal, frame):
    print("Stopping Clipper...")
    clipper_conn = ClipperConnection(DockerContainerManager())
    clipper_conn.stop_all()
    sys.exit(0)


if __name__ == '__main__':
    signal.signal(signal.SIGINT, signal_handler)
    clipper_conn = ClipperConnection(DockerContainerManager())
    clipper_conn.start_clipper()
    python_deployer.create_endpoint(clipper_conn, "simple-example", "doubles",
                                    feature_sum)
    time.sleep(2)

    # For batch inputs set this number > 1
    batch_size = 1

    try:
        while True:
            if batch_size > 1:
                predict(
                    clipper_conn.get_query_addr(),
                    [list(np.random.random(200)) for i in range(batch_size)],
                    batch=True)
            else:
                predict(clipper_conn.get_query_addr(), np.random.random(200))
            time.sleep(0.2)
    except Exception as e:
        clipper_conn.stop_all()

動いた。

python example_client.py 
18-03-21:15:40:06 INFO     [docker_container_manager.py:106] Starting managed Redis instance in Docker
18-03-21:15:40:09 INFO     [clipper_admin.py:114] Clipper is running
18-03-21:15:40:09 INFO     [clipper_admin.py:189] Application simple-example was successfully registered
18-03-21:15:40:09 INFO     [deployer_utils.py:49] Saving function to /tmp/clipper/tmpajrwFc
18-03-21:15:40:13 INFO     [deployer_utils.py:58] Anaconda environment found. Verifying packages.
18-03-21:15:40:24 INFO     [deployer_utils.py:158] Fetching package metadata .........
Solving package specifications: .

18-03-21:15:40:24 INFO     [deployer_utils.py:159] Using Anaconda API: https://api.anaconda.org

18-03-21:15:40:24 INFO     [deployer_utils.py:67] Supplied environment details
18-03-21:15:40:25 INFO     [deployer_utils.py:79] Supplied local modules
18-03-21:15:40:25 INFO     [deployer_utils.py:85] Serialized and supplied predict function
18-03-21:15:40:25 INFO     [python.py:184] Python closure saved
18-03-21:15:40:25 INFO     [clipper_admin.py:391] Building model Docker image with model data from /tmp/clipper/tmpajrwFc
18-03-21:15:40:26 INFO     [clipper_admin.py:395] Pushing model Docker image to simple-example:1
18-03-21:15:40:28 INFO     [docker_container_manager.py:243] Found 0 replicas for simple-example:1. Adding 1
18-03-21:15:40:29 INFO     [clipper_admin.py:569] Successfully registered model simple-example:1
18-03-21:15:40:29 INFO     [clipper_admin.py:487] Done deploying model simple-example:1.
18-03-21:15:40:29 INFO     [clipper_admin.py:232] Model simple-example is now linked to application simple-example
'{"query_id":0,"output":94.6718686566,"default":false}', 17.186000 ms
'{"query_id":1,"output":97.4406443212,"default":false}', 7.134000 ms
'{"query_id":2,"output":95.5675770539,"default":false}', 6.241000 ms
'{"query_id":3,"output":101.933600191,"default":false}', 6.785000 ms
...

dockerでバアアアっと立ち上がってる。

docker ps -a
CONTAINER ID        IMAGE                                 COMMAND                  CREATED              STATUS                        PORTS                    NAMES
81c72516f3aa        simple-example:1                      "/container/python_c…"   55 seconds ago       Exited (137) 26 seconds ago                            simple-example_1-46743
2e652c31cec7        prom/prometheus:v2.1.0                "/bin/prometheus --c…"   About a minute ago   Exited (0) 26 seconds ago                              metric_frontend-35815
a6d0987a6f74        clipper/frontend-exporter:develop     "python ./front_end_…"   About a minute ago   Exited (137) 15 seconds ago                            query_frontend_exporter-88393
5c1523a4b5ea        clipper/query_frontend:develop        "/clipper/release/sr…"   About a minute ago   Exited (137) 5 seconds ago                             query_frontend-88393
687d4f2e650a        clipper/management_frontend:develop   "/clipper/release/sr…"   About a minute ago   Up About a minute             0.0.0.0:1338->1338/tcp   mgmt_frontend-10174
81d8ec025f20        redis:alpine                          "docker-entrypoint.s…"   About a minute ago   Up About a minute             0.0.0.0:6379->6379/tcp   redis-75736

いらないのであれば消しておく。

docker system prune
docker rm -f `docker ps -a -q`

アプリケーション

  • clipperを動かす。

まだ動いてないなら
clipper_conn.start_clipper()
もう動いてるなら接続する。
clipper_conn.connect()

register_applicationでアプリを登録して、deploy_python_closureでデプロイする。
link_model_to_appでモデル同士を接続できる。

  • アプリケーションの登録は ClipperConnection.register_application

Clipperにアプリケーションを登録すると、そのアプリケーションのRESTエンドポイントが作成されます。

URL: /<app_name>/predict
Method: POST
Data Params: {"input": <input>}

register_app.png

  • モデルをアプリケーションにリンクするには ClipperConnection.link_model_to_app

モデルを登録したらリンクしてルーティングする。

link_model.png

001.py
from clipper_admin import ClipperConnection, DockerContainerManager
clipper_conn = ClipperConnection(DockerContainerManager())

# Start Clipper
clipper_conn.start_clipper()

# Register an application
# a prediction REST endpoint at http://localhost:1337/hello_world/predict
clipper_conn.register_application(name="hello-world", input_type="doubles", default_output="-1.0", slo_micros=100000)

# Inspect Clipper to see the registered apps
print(clipper_conn.get_all_apps())

def feature_sum(xs):
    return [str(sum(x)) for x in xs]


from clipper_admin.deployers import python as python_deployer

python_deployer.deploy_python_closure(clipper_conn, name="sum-model", version=1, input_type="doubles", func=feature_sum)

clipper_conn.link_model_to_app(app_name="hello-world", model_name="sum-model")

ローカルから呼び出す。

curl -X POST --header "Content-Type:application/json" -d '{"input": [1.1, 2.2, 3.3]}' 127.0.0.1:1337/hello-world/predict
>>>{"query_id":0,"output":6.6,"default":false}

外部から呼び出す時はipを変えるだけ。

curl -X POST --header "Content-Type:application/json" -d '{"input": [1.1, 2.2, 3.3]}' 150.95.145.xxx:1337/hello-world/predict
{"query_id":1,"output":6.6,"default":false}

pythonで呼び出す。

post.py
import requests, json, numpy as np
headers = {"Content-type": "application/json"}
requests.post("http://localhost:1337/hello-world/predict", headers=headers, data=json.dumps({"input": list(np.random.random(10))})).json()

なんとなく動かせたので、
今度は違うサンプルか他のものを動かしたいと思います。

エラー集

AttributeError: 'bool' object has no attribute 'rfind'
→このエラーで何度かハマった。Python3系を使ったり、2系入れてもanacondaのバージョンの問題なのかエラーが出てた。自分のubuntu16環境ではanaconda2-4.4.0を使ったら治った。あとはdockerバージョンも新しくしておいた気がする。
https://github.com/ucbrise/clipper/issues/382

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?