Seldon Core – Tensorflow Serving

학습이 완료된 Tensorflow 모델을 저장 한 경우, Seldon의 사전 패키지 된 TensorFlow 서버를 사용하여 간단히 배포 할 수 있습니다.

전제 조건

  • REST의 경우 다음에 대한 파라미터를지정해야합니다.
    • signature_name
    • model_name
apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
  name: tfserving
spec:
  name: mnist
  predictors:
  - graph:
      children: []
      implementation: TENSORFLOW_SERVER
      modelUri: pvc://seldon-models-pvc/tensorflow/mnist/model
      name: mnist-model
      parameters:
        - name: signature_name
          type: STRING
          value: serving_default
        - name: model_name
          type: STRING
          value: mnist-model
    name: default
    replicas: 1

  • GRPC의 경우 다음에 대한 파라미터를 지정해야합니다.
    • signature_name
    • model_name
    • model_input
    • model_output
kind: SeldonDeployment
metadata:
  name: tfserving
spec:
  name: mnist
  predictors:
  - graph:
      children: []
      implementation: TENSORFLOW_SERVER
      modelUri: pvc://seldon-models-pvc/tensorflow/mnist/modell
      name: mnist-model
      endpoint:
        type: GRPC
      parameters:
        - name: signature_name
          type: STRING
          value: serving_default
        - name: model_name
          type: STRING
          value: mnist-model
        - name: model_input
          type: STRING
          value: images
        - name: model_output
          type: STRING
          value: scores
    name: default
    replicas: 1

모델 생성

Tensorflow 서버를 테스트 하려면 먼저 파이썬을 사용하여 간단한 Tensorflow 모델을 생성해야 합니다.

텐서플로우 모델을 만들고 훈련하기 위한 고수준 API인 tf.keras를 사용합니다. 모델의 save() 메소드를 이용하여, 전체 모델을 지정한 위치에 저장합니다. 여기에는 가중치, 모델 구성 등이 포함됩니다. 모델을 저장할 때 주의해야할 점은 모델 저장 위치의 마지막 디렉토리에 모델의 버전이 포함되어야 합니다. 모델 버전은 숫자를 사용해야합니다.

케라스의 데이터셋 중의 하나인 mnist 데이터를 분류하는 모델을 작성해 보겠습니다.

from __future__ import absolute_import, division, print_function, unicode_literals

import argparse
import os

import tensorflow as tf

def train():
    print("TensorFlow version: ", tf.__version__)

    parser = argparse.ArgumentParser()
    parser.add_argument('--model_path', default='/mnt/pv/tensorflow/mnist/model', type=str)
    args = parser.parse_args()

    version = 1
    export_path = os.path.join(args.model_path, str(version))

    (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
    x_train, x_test = x_train / 255.0, x_test / 255.0

    model = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(10, activation='softmax')
    ])

    model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.01),
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])

    print("Training...")
    training_history = model.fit(x_train, y_train, batch_size=64, epochs=10,
                                 validation_split=0.2)

    print('\\nEvaluate on test data')
    results = model.evaluate(x_test, y_test, batch_size=128)
    print('test loss, test acc:', results)

    model.save(export_path)
    print('"Saved model to {}'.format(export_path))

if __name__ == '__main__':
    train()

생성 된 모델을 사용하여 Tensorflow 서버를 실행하고 예측을 수행 할 수 있습니다. 모델은 PV, S3 호환 가능 개체 저장소, Azure Blob 저장소 또는 Google Cloud Storage에 있을 수 있습니다.

모델 저장하기

쿠버네티스의 퍼시스턴스 볼륨에 모델을 저장해 보겠습니다. PVC 는 앞서 생성한 seldon-models-pvc 을 사용하겠습니다. 모델을 학습시키기 위해서 쿠버네티스 잡(Job)을 사용하겠습니다. Job을 생성할 때 모델을 저장하기 위한 PVC를 마운트 해줍니다.

모델 코드 작성하기

mnist 이미지를 분류하는 모델입니다. 모델을 저장할 위치를 --model_path 파라미터로 입력받게 하였습니다.

tensorflow_mnist.py

from __future__ import absolute_import, division, print_function, unicode_literals

import argparse
import os

import tensorflow as tf

def train():
    print("TensorFlow version: ", tf.__version__)

    parser = argparse.ArgumentParser()
    parser.add_argument('--model_path', default='/mnt/pv/tensorflow/mnist/model', type=str)
    args = parser.parse_args()

    version = 1
    export_path = os.path.join(args.model_path, str(version))

    (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()
    x_train, x_test = x_train / 255.0, x_test / 255.0

    model = tf.keras.models.Sequential([
        tf.keras.layers.Flatten(input_shape=(28, 28)),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dropout(0.2),
        tf.keras.layers.Dense(10, activation='softmax')
    ])

    model.compile(optimizer=tf.keras.optimizers.SGD(learning_rate=0.01),
                  loss='sparse_categorical_crossentropy',
                  metrics=['accuracy'])

    print("Training...")
    training_history = model.fit(x_train, y_train, batch_size=64, epochs=10,
                                 validation_split=0.2)

    print('\\nEvaluate on test data')
    results = model.evaluate(x_test, y_test, batch_size=128)
    print('test loss, test acc:', results)

    model.save(export_path)
    print('"Saved model to {}'.format(export_path))

if __name__ == '__main__':
    train()

컨테이너 이미지를 만들기

컨테이너 이미지를 만들기 위한 Dockerfile 입니다. 텐서플로우를 기본 이미지로 사용합니다.

Dockerfile

FROM tensorflow/tensorflow:2.1.0-py3

RUN mkdir -p /app
ADD tensorflow_mnist.py /app/

쿠버네티스 잡 실행하기

컨테이너 이미지를 빌드하고, 컨테이너 이미지 레지스트리에 푸시 한 다음, 쿠버네티스 잡(Job)을 생성하겠습니다.

Job을 생성할 때는 모델을 저장하기 위해서 PVC를 마운트 해줍니다. 이 일련의 작업들은 직접 실행 할 수 있습니다. 하지만 좀 더 편하게 하기 위해서 앞서 배운 Kubeflow Fairing을 사용하겠습니다.

다음은 로컬 개발 환경에서 Fairing을 사용하여 컨테이너 이미지를 만들고, 쿠버네티스 잡을 실행하는 예제입니다.

fairing-local-docker.py

import uuid

from kubeflow import fairing
from kubeflow.fairing.kubernetes import utils as k8s_utils

CONTAINER_REGISTRY = 'kangwoo'

namespace = 'admin'
job_name = f'tensorflow-mnist-job-{uuid.uuid4().hex[:4]}'

command = ["python", "tensorflow_mnist.py", "--model_path", "/mnt/pv/tensorflow/mnist/models"]
output_map = {
    "Dockerfile": "Dockerfile",
    "tensorflow_mnist.py": "tensorflow_mnist.py"
}

fairing.config.set_preprocessor('python', command=command, path_prefix="/app", output_map=output_map)

fairing.config.set_builder('docker', registry=CONTAINER_REGISTRY, image_name="tensorflow-mnist",
                           dockerfile_path="Dockerfile")

fairing.config.set_deployer('job', namespace=namespace, job_name=job_name,
                            pod_spec_mutators=[
                                k8s_utils.mounting_pvc(pvc_name='seldon-models-pvc', pvc_mount_path='/mnt/pv')],
                            cleanup=True, stream_log=True)

fairing.config.run()

fairing을 실행하면 쿠버네티스 잡이 생성되고, 학습이 완료된 모델이 지정한 경로에 저장됩니다.

Tensorflow을 사용하는 SeldonDeployment 로 배포 하기

SeldonDeployment 생성

SeldonDeployment 매니페스트를 작성합니다. predictor의 구현체를 SKLEARN_SERVER 로 사용합니다. modelUri 필드로 모델 저장 위치를 지정해 줍니다. pvc 의 이름이 kfserving-models-pvc 이고 저장 위치가 models/sklearn/iris 이므로, pvc://kfserving-models-pvc/models/sklearn/iris 라고 지정해 줍니다.

기본적으로 모델 서버는 로드한 모델의 predict_proba 메소드를 호출합니다. 만약 다른 메소드를 사용하고 싶다면 파라미터로 변경할 수 있습니다. 예를 들어 predict 메소드를 호출하게 하라면, parameters 섹션에 method 란 이름으로 값을 지정해 주면 됩니다. 다음 예제는 predict 메소드를 호출하게 설정하였습니다.

tensorflow-mnist.yaml

apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
  name: tensorflow-mnist
spec:
  name: mnist
  predictors:
  - graph:
      children: []
      implementation: TENSORFLOW_SERVER
      modelUri: pvc://seldon-models-pvc/tensorflow/mnist/model
      name: mnist-model
      parameters:
        - name: signature_name
          type: STRING
          value: serving_default
        - name: model_name
          type: STRING
          value: mnist-model
    name: default
    replicas: 1

SeldonDeployment 를 생성합니다.

다음은 admin 네임스페이스 SeldonDeployment 를 생성하는 예제입니다.

kubectl -n admin apply -f tensorflow-mnist.yaml

생성한 SeldonDeployment를 조회해 보겠습니다.

kubectl -n admin get seldondeployment tensorflow-mnist -o yaml

SeldonDeployment 가 정상적으로 생성되면 다음과 같은 응답 결과를 확인할 수 있습니다.

apiVersion: machinelearning.seldon.io/v1
kind: SeldonDeployment
metadata:
  ...
spec:
  ...
status:
  deploymentStatus:
    mnist-default-725903e:
      availableReplicas: 1
      replicas: 1
  serviceStatus:
    mnist-default-mnist-model-seldonio-tfserving-proxy-rest-0-7:
      httpEndpoint: mnist-default-mnist-model-seldonio-tfserving-proxy-rest-0-7.admin:9000
      svcName: mnist-default-mnist-model-seldonio-tfserving-proxy-rest-0-7
    tensorflow-mnist-mnist-default:
      grpcEndpoint: tensorflow-mnist-mnist-default.admin:5001
      httpEndpoint: tensorflow-mnist-mnist-default.admin:8000
      svcName: tensorflow-mnist-mnist-default
  state: Available

SeldonDeploymentstateAvailable 이면 예측을 요청 할 수 있습니다.

예측 실행하기

예측을 요청하기 위해서는 모델 서버에 접근해야 합니다. 모델 서버는 ingressgateway 를 통해서 접근할 수 있습니다. ingressgateway 는 모델 서버들을 구분하기 위해서 호스트 이름을 사용합니다. ingressgateway에 접근하 기 위한 주소는 앞서 정의한 CLUSTER_IP 를 사용하겠습니다.

예측을 요청할 데이터를 json 파일로 작성합니다.

데이터의 크기가 크기 때문에 git 에 있는 파일을 다운받아서 사용해주세요.

mnist-input.json

{
  "data": {
    "ndarray": [
      [...],
       ...
      [...]
    ]
  }
}

다음은 admin 네임스페이스의 tensorflow-mnist SeldonDeployment 에 예측을 요청하는 예제입니다.

MODEL_NAME=tensorflow-mnist
NAMESPACE=admin

INPUT_PATH=@./mnist-input.json
curl -v -H "Content-Type: application/json" http://$CLUSTER_IP/seldon/${NAMESPACE}/${MODEL_NAME}/api/v1.0/predictions -d $INPUT_PATH

정상적으로 실행되면 다음과 같은 응답 결과를 확인 할 수 있습니다.

*   Trying 192.168.21.38...
* TCP_NODELAY set
* Connected to 192.168.21.38 (192.168.21.38) port 32380 (#0)
> POST /seldon/admin/tensorflow-mnist/api/v1.0/predictions HTTP/1.1
> Host: 192.168.21.38:32380
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 5725
> Expect: 100-continue
> 
< HTTP/1.1 100 Continue
* We are completely uploaded and fine
< HTTP/1.1 200 OK
< x-content-type-options: nosniff
< vary: Origin,Access-Control-Request-Method,Access-Control-Request-Headers
< content-type: application/json;charset=utf-8
< content-length: 470
< date: Thu, 09 Apr 2020 15:51:27 GMT
< x-envoy-upstream-service-time: 142
< server: istio-envoy
< 
{
  "meta": {
    "puid": "ufdopha1s5gnemt86h06d4jg5e",
    "tags": {
    },
    "routing": {
    },
    "requestPath": {
      "mnist-model": "seldonio/tfserving-proxy_rest:0.7"
    },
    "metrics": []
  },
  "data": {
    "names": ["t:0", "t:1", "t:2", "t:3", "t:4", "t:5", "t:6", "t:7", "t:8", "t:9"],
    "ndarray": [[3.01518681E-4, 1.00308341E-6, 4.13124333E-4, 0.00133548444, 4.15516388E-6, 7.8677E-5, 5.88266346E-7, 0.996478, 3.98369411E-5, 0.00134761049]]
  }
}

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다