텐서플로우 알아 보기
텐서플로우(TensorFlow)는 구글에서 만든 오픈소스 딥러닝 프레임워크입니다. 딥러닝 프로그램을 쉽게 구현할 수 있도록 다양한 기능을 제공해주고 있습니다. 텐서플로우 자체는 기본적으로 C++로 구현 되어 있으며, Python, Java, Go 등 다양한 언어를 지원합니다.
TFJob을 이용해서 텐서플로우로 모델을 학습 할 수 있습니다. TFJob은 쿠버네티스에서 분산 또는 비 분산 텐서플로우 작업을 쉽게 실행할 수 있는 쿠버네티스 사용자 리소스(Custom Resource) 입니다.
TFJob
TFJob은 쿠버네티스에서 텐서플로우를 이용한 학습 작업을 할 수 있게 해주는 쿠버네티스 사용자 리소스 입니다. TFJob의 구현은 tf-operator에 있습니다. tf-operator는 TFJob을 관리합니다. 쿠버네티스에 TFJob이 등록되면, 필요한 포드들을 생성하여 작업을 실행할 수 있도록 도와줍니다.
TFJob은 다음과 같이 YAML 형식으로 표현할 수 있는 쿠버네티스 사용자 리소스입니다.
apiVersion: kubeflow.org/v1 kind: TFJob metadata: name: mnist-tfjob-dist namespace: admin spec: cleanPodPolicy: Running tfReplicaSpecs: Chief: replicas: 1 template: metadata: annotations: sidecar.istio.io/inject: "false" name: tensorflow spec: containers: - command: - python - mnist-dist.py image: kangwoo/tfjob-dist:0.0.1 name: tensorflow volumeMounts: - mountPath: /app/data name: tfjob-data-volume workingDir: /app restartPolicy: Never volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume Worker: replicas: 2 template: metadata: annotations: sidecar.istio.io/inject: "false" name: tensorflow spec: containers: - command: - python - mnist-dist.py image: kangwoo/tfjob-dist:0.0.1 name: tensorflow volumeMounts: - mountPath: /app/data name: tfjob-data-volume workingDir: /app restartPolicy: Never volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume
분산 처리 기술을 사용한 모델 학습
쿠버네티스에서 TFJob을 관리하고 있는 tf-operator 는 텐서플로우를 사용한 모델 학습시 분산 처리가 가능하도록 도와주고 있습니다.
텐서플로우에는 분산 처리 기술을 사용하기 위해서 다음과 같은 역할을 가진 프로세스가 존재합니다.
- Chief: Chief는 학습을 조정하는 역할을 합니다. 그리고 모델의 체크포인트 같은 작업을 수행할 책임이 있습니다.
- Ps (Paramter Server) : Ps는 파라미터 서버로서, 모델 파라미터에 대한 분산 데이터 저장소를 제공하는 역할을 합니다.
- Worker : Worker는 실제 모델 학습 작업을 수행하는 역할을 합니다. 경우에 따라서 0번 Worker가 Chief 역할을 수행 할 수 있습니다.
- Evaluator : Evaluator는 평가 지표를 계산하는 역할을 합니다. 모델을 학습 할 때 Evaluator를 사용하여 평가 지표를 계산할 수 있습니다.
TFJob은 TF_CONFIG라는 환경 변수를 사용하여 프로세스의 역할 정의합니다.
모델 학습을 시작하려면 TF_CONFIG라는 환경 변수에 다음과 같은 변수가 json 형태로 설정되어 있어야 합니다.
{ "cluster": { "worker": ["host1:port", "host2:port", "host3:port"], "ps": ["host4:port", "host5:port"] }, "task": {"type": "worker", "index": 0} }
- cluster : 클러스터 내에 있는 서버들의 정보를 지정합니다.
- task : 클러스터 내에서 현재 작업이 담당한 역할을 지정합니다. 클러스터 내에서 가질 수 있는 역할은 “chief”, “worker”, “ps”, “evaluator” 중 하나입니다. 단, “ps” 역할은 tf.distribute.experimental.ParameterServerStrategy 전략을 사용할 때만 쓸 수 있습니다
TFJob 이라는 쿠터네티스트 사용자 리소스를 생성하여 모델 학습을 진행 경우에는, tf-operator 가 프로세스의 역할에 맞게 자동으로 환경 변수를 설정해 줍니다. 그래서 분산 작업을 위한 별도의 환경 변수 설정 작업을 하지 않아도 됩니다.
다음은 TFJob에서 생성한 포드의 환경 변수를 일부 출력해 본 것입니다.
spec: containers: - env: - name: TF_CONFIG value: '{"cluster":{"chief":["mnist-tfjob-dist-chief-0.admin.svc:2222"],"worker":["mnist-tfjob-dist-worker-0.admin.svc:2222","mnist-tfjob-dist-worker-1.admin.svc:2222"]},"task":{"type":"chief","index":0},"environment":"cloud"}'
텐서플로우 학습 작업 실행하기
TFJob을 정의한 후 학습 작업을 생성해 보겠습니다.
모델 코드 작성하기
텐서플로우로 학습할 모델을 작성해 보겠습니다.
다음은 텐서플로우 케라스를 사용하여 mnist 숫자 이미지를 분류하는 파이썬 코드인 mnist-dist.py입니다. Gtf.distribute.experimental.MultiWorkerMirroredStrategy 를 사용해서 분산 학습을 지원하고 있습니다.
from __future__ import absolute_import, division, print_function, unicode_literals import os import json import tensorflow as tf import tensorflow_datasets as tfds def build_and_compile_model(): model = tf.keras.models.Sequential([ tf.keras.layers.Flatten(input_shape=(28, 28, 1)), tf.keras.layers.Dense(128, activation='relu'), tf.keras.layers.Dropout(0.2), tf.keras.layers.Dense(10) ]) model.compile(loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True), optimizer=tf.keras.optimizers.Adam(), metrics=['accuracy']) return model @tfds.decode.make_decoder(output_dtype=tf.float32) def decode_image(example, feature): return tf.cast(feature.decode_example(example), dtype=tf.float32) / 255 def train(): print("TensorFlow version: ", tf.__version__) tf_config = os.environ.get('TF_CONFIG', '{}') print("TF_CONFIG %s", tf_config) tf_config_json = json.loads(tf_config) cluster = tf_config_json.get('cluster') job_name = tf_config_json.get('task', {}).get('type') task_index = tf_config_json.get('task', {}).get('index') print("cluster={} job_name={} task_index={}}", cluster, job_name, task_index) BATCH_SIZE = 64 tb_dir = '/app/data/logs' model_dir = '/app/data/export' version = 1 export_dir = os.path.join(model_dir, str(version)) strategy = tf.distribute.experimental.MultiWorkerMirroredStrategy() mnist = tfds.builder('mnist', data_dir='/app/mnist') mnist.download_and_prepare() mnist_train, mnist_test = mnist.as_dataset( split=['train', 'test'], decoders={'image': decode_image()}, as_supervised=True) train_input_dataset = mnist_train.cache().repeat().shuffle( buffer_size=50000).batch(BATCH_SIZE) # eval_input_dataset = mnist_test.cache().repeat().batch(BATCH_SIZE) options = tf.data.Options() options.experimental_distribute.auto_shard_policy = tf.data.experimental.AutoShardPolicy.OFF train_input_dataset = train_input_dataset.with_options(options) print("Training...") with strategy.scope(): multi_worker_model = build_and_compile_model() num_train_examples = mnist.info.splits['train'].num_examples train_steps = num_train_examples // BATCH_SIZE train_epochs = 10 callbacks = [ tf.keras.callbacks.TensorBoard(log_dir=tb_dir), ] history = multi_worker_model.fit(train_input_dataset, epochs=train_epochs, steps_per_epoch=train_steps, callbacks=callbacks) print("\\ntraining_history:", history.history) multi_worker_model.save(export_dir) if __name__ == '__main__': train()
모델 컨테이너 이미지 만들기
모델 학습용 컨테이너 이미지를 만들기 위해서 Dockerfile을 생성하겠습니다. 텐서플로우 데이터셋을 사용하기 때문에 pip를 이용해서 추가해주었습니다.
다음은 텐서플로우2.1을 기반 이미지로 해서, 모델 파일을 추가하는 Dockerfile 입니다.
Dockerfile
FROM tensorflow/tensorflow:2.1.0-py3 RUN pip install tensorflow-datasets==2.0.0 RUN mkdir -p /app ADD mnist-dist.py /app/
다음 명령어를 실행하면 kkangwoo/tfjob-dist:0.0.1 라는 이름의 컨테이너 이미지를 빌드 할 수 있습니다.
docker build -t kangwoo/tfjob-dist:0.0.1 .
빌드한 컨테이너 이미지를 컨테이너 레지스트리로 업로드 하겠습니다.
docker push kangwoo/tfjob-dist:0.0.1
PVC 생성하기
텐서플로우 학습 작업 중 생성되는 매트릭과, 학습이 완료된 모델을 저장하기 위해서 별도의 볼륨을 만들어서 사용하겠습니다.
다음은 100메가의 저장 용량을 가진 볼륨 생성을 요청하는 PVC 매니페스트입니다.
tfjob-pvc.yaml
kind: PersistentVolumeClaim apiVersion: v1 metadata: name: tfjob-data-volume spec: accessModes: - ReadWriteOnce resources: requests: storage: 100Mi
kubectl을 사용해서 admin 네임스페이스에 pvc를 생성합니다.
kubectl -n admin apply -f tfjob-pvc.yaml
TFJob 생성하기
tf-operator를 사용해서 텐서플로우로 작성한 모델을 학습 하려면 TFJob을 정의해야합니다.
- tfReplicaSpecs를 설정합니다. “tfReplicaSpecs”은 텐서플로우의 분산 학습시 사용하는 프로세스들을 정의하는데 사용합니다. Chief를 1개로 설정하고, Worker를 2개로 설정하였습니다.. Kubeflow 클러스터에 istio가 설치되어 있기 때문에, 자동으로 istio-proxy가 포드에 주입됩니다. 이것을 방지하기 위해서 어노테이션에 sidecar.istio.io/inject: “false” 을 추가해 주었습니다. 그리고 앞에 생성한 볼륨을 마운트 하였습니다.
tfReplicaSpecs: Chief: replicas: 1 template: metadata: annotations: sidecar.istio.io/inject: "false" name: tensorflow spec: containers: - command: - python - mnist-dist.py image: kangwoo/tfjob-dist:0.0.1 name: tensorflow volumeMounts: - mountPath: /app/data name: tfjob-data-volume workingDir: /app restartPolicy: Never volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume Worker: replicas: 2 template: metadata: annotations: sidecar.istio.io/inject: "false" name: tensorflow spec: containers: - command: - python - mnist-dist.py image: kangwoo/tfjob-dist:0.0.1 name: tensorflow volumeMounts: - mountPath: /app/data name: tfjob-data-volume workingDir: /app restartPolicy: Never volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume
다음은 TFJob 을 생성한 위한 메니페스트입니다.
tfjob-dist.yaml
apiVersion: kubeflow.org/v1 kind: TFJob metadata: name: mnist-tfjob-dist spec: tfReplicaSpecs: Chief: replicas: 1 template: metadata: annotations: sidecar.istio.io/inject: "false" name: tensorflow spec: containers: - command: - python - mnist-dist.py image: kangwoo/tfjob-dist:0.0.1 name: tensorflow volumeMounts: - mountPath: /app/data name: tfjob-data-volume workingDir: /app restartPolicy: Never volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume Worker: replicas: 2 template: metadata: annotations: sidecar.istio.io/inject: "false" name: tensorflow spec: containers: - command: - python - mnist-dist.py image: kangwoo/tfjob-dist:0.0.1 name: tensorflow volumeMounts: - mountPath: /app/data name: tfjob-data-volume workingDir: /app restartPolicy: Never volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume
다음 명령어를 실행하면 admin 네임스페이스에 tfjob-dist 이라는 이름의 TFJob을 생성할 수 있습니다.
kubectl -n admin apply -f tfjob-dist.yaml
텐서플로우 학습 작업 확인하기
생성한 TFJob은 다음 명령어를 실행해서 확인 해 볼 수 있습니다.
kubectl -n admin get tfjob
생성된 TFJob이 있다면, 다음과 같은 응답 결과를 얻을 수 있습니다.
NAME STATE AGE mnist-tfjob-dist Succeeded 19m
TFJob이 생성되면, tf-operator 에 의해서 포드들이 생성됩니다. TFJob 매니페스트에 정의한 개수대로 chief, worker 포드가 생성되게 됩니다.
생성된 포드들은 다음 명령어를 실행해서 확인 해 볼 수 있습니다.
kubectl -n admin get pod -l tf-job-name=mnist-tfjob-dist
생성된 포드들이 남아 있다면, 다음과 같은 응답 결과를 얻을 수 있습니다.
NAME READY STATUS RESTARTS AGE mnist-tfjob-dist-chief-0 0/1 Completed 0 20m mnist-tfjob-dist-worker-0 0/1 Completed 0 20m mnist-tfjob-dist-worker-1 0/1 Completed 0 20m
TFJob은 작업이 끝난 후, 관련 포드들을 삭제해버립니다. 그래서 작업이 완료되면 포드가 조회되지 않을 수 있습니다. 작업이 완료되어도 포드들을 남겨 두고 싶다면, TFJob 매니페스트의 spec 부분에 “cleanPodPolicy: None” 를 추가하시면 됩니다.
TFJob spec의 CleanPodPolicy는 작업이 종료 될 때 포드 삭제를 제어할 때 사용합니다. 다음 값들 중 하나를 사용할 수 있습니다.
- Running : 작업이 완료되었을 때, 실행이 끝난(Completed) 포드들은 삭제하지 않고, 실행중인(Running) 포드들만 삭제합니다.
- All : 작업이 완료되었을 때, 실행이 끝난 포드들을 즉시 삭제합니다.
- None : 작업이 완료되어도 포드들을 삭제하지 않습니다.
다음은 cleanPodPolicy를 추가한 메니페스트 예제입니다.
apiVersion: kubeflow.org/v1 kind: TFJob metadata: name: mnist-tfjob-dist spec: spec: cleanPodPolicy: None ...
TFJob의 작업 상태를 알고 싶으면 describe 명령어를 사용할 수 있습니다.
다음 명령어를 실행하면 admin 네임스페이스에 mnist-tfjob-dist 이라는 이름의 TFJob의 상태를 조회할 수 있습니다.
kubectl -n admin describe tfjob mnist-tfjob-dist
다음은 예제 작업에 대한 샘플 출력입니다.
Name: mnist-tfjob-dist Namespace: admin Labels: <none> Annotations: ... API Version: kubeflow.org/v1 Kind: TFJob Metadata: ... Spec: ... Status: Completion Time: 2020-03-07T02:17:17Z Conditions: Last Transition Time: 2020-03-07T02:16:10Z Last Update Time: 2020-03-07T02:16:10Z Message: TFJob mnist-tfjob-dist is created. Reason: TFJobCreated Status: True Type: Created Last Transition Time: 2020-03-07T02:16:12Z Last Update Time: 2020-03-07T02:16:12Z Message: TFJob mnist-tfjob-dist is running. Reason: TFJobRunning Status: False Type: Running Last Transition Time: 2020-03-07T02:17:17Z Last Update Time: 2020-03-07T02:17:17Z Message: TFJob mnist-tfjob-dist successfully completed. Reason: TFJobSucceeded Status: True Type: Succeeded Replica Statuses: Chief: Succeeded: 1 Worker: Succeeded: 2 Start Time: 2020-03-07T02:16:11Z Events: <none>
텐서플로우 학습 작업 삭제하기
작업이 완료되어도 TFJob은 삭제되지 않습니다.
다음 명령어를 실행하면 admin 네임스페이스에 mnist-tfjob-dist이라는 이름의 TFJob을 삭제할 수 있습니다.
kubectl -n admin delete tfjob mnist-tfjob-dist
텐서보드
TensorBoard는 텐서플로우의 실행 및 그래프를 검사하고, 이해하기 위한 웹 응용 도구로서, 텐서플로우에서 기록한 로그를 그래프로 시각화하여 보여줍니다. 텐서보드는 텐서플로우에 포함되어 있습니다.
텐서보드를 실행하기 위해서 쿠버네티스 디플로이먼트 리소스를 정의해 보겠습니다.
“tensorflow/tensorflow:2.1.0-py3” 라는 컨테이너 이미지를 사용하여 텐서보드를 실행하겠습니다. 텐서보드는는 실행 될 때, 주어진 파리미터 값에 의해 로그를 읽어옵니다.
다음은 텐서보드에서 사용하는 몇 가지 파라미터입니다.
- port : 텐서보드에 접근하기 위한 포트입니다.
- logdir : 로그가 저장된 디렉토리 경로입니다.
- path_prefix : 접속 URL에서 사용하는 경로 접두사입니다. 예를 들어
/foo/bar
의 경로 접두사를 사용하면,http://localhost/
이 아닌http://localhost:6006/foo/bar/
를 통해서 TensorBoard에 접속할 수 있습니다.
- command: - /usr/local/bin/tensorboard - --logdir=/app/data/logs - --path_prefix=/namespace/admin/tensorboard/mnist-dist/ - --port=80
앞서 실행한 TFJob에서 텐서플로우 로그를 쿠버네티스의 퍼시스턴스 볼륨에 저장하였습니다. tfjob-data-volume 라는 퍼시스턴스 볼륨 클레임 이름을 가지고 있습니다. 해당 볼륨을 텐서보드에 마운트 해줍니다.
volumeMounts: - mountPath: /app/data name: tfjob-data-volume volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume
다음은 디플로이먼트 매니페스트입니다.
mnist-dist-tensorboard-deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: labels: app: mnist-dist-tensorboard name: mnist-dist-tensorboard namespace: admin spec: selector: matchLabels: app: mnist-dist-tensorboard template: metadata: labels: app: mnist-dist-tensorboard spec: serviceAccount: default-editor containers: - command: - /usr/local/bin/tensorboard - --logdir=/app/data/logs - --path_prefix=/namespace/admin/tensorboard/mnist-dist/ - --port=80 image: tensorflow/tensorflow:2.1.0-py3 name: tensorboard ports: - containerPort: 80 volumeMounts: - mountPath: /app/data name: tfjob-data-volume volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume
kubectl을 사용해서 admin 네임스페이스에 디플로이먼트 리소스를 생성합니다.
kubectl -n admin apply -f mnist-dist-tensorboard-deployment.yaml
텐서보드에 접속하기 위해서 쿠버네티스 서비스를 리소스를 정의합니다.
mnist-dist-tensorboard-service.yaml
apiVersion: v1 kind: Service metadata: labels: app: mnist-dist-tensorboard name: mnist-dist-tensorboard namespace: admin spec: ports: - name: http port: 80 targetPort: 80 selector: app: mnist-dist-tensorboard type: ClusterIP
kubectl을 사용해서 admin 네임스페이스에 서비스 리소스를 생성합니다.
kubectl -n admin apply -f mnist-dist-tensorboard-service.yaml
Kubeflow 클러스터에는 istio가 설치되어 있었습니다. 텐서보드를 istio-ingressgateway를 통해서 접근할 수 있도록 하기위해서 VirtualService 라는 사용자 리소스를 정의하겠습니다.
mnist-dist-tensorboard-virtualservice.yaml
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: mnist-dist-tensorboard namespace: admin spec: gateways: - kubeflow/kubeflow-gateway hosts: - '*' http: - match: - uri: prefix: /namespace/admin/tensorboard/mnist-dist/ route: - destination: host: mnist-dist-tensorboard.admin.svc.cluster.local timeout: 300s
kubectl을 사용해서 admin 네임스페이스에 서비스 리소스를 생성합니다.
kubectl -n admin apply -f mnist-dist-tensorboard-virtualservice.yaml
다음 주소로 텐서보드에 접근할 수 있습니다.
http://kubeflow-address/namespace/admin/tensorboard/mnist-dist/
텐서플로우 서빙
Serving은 학습이 완료된 모델을 실제 서비스에 적용하는 것입니다. 텐서플로우 서빙을 이용하면 텐서플로우에서 생성한 모델을 서비스 환경에 쉽고 빠르게 적용할 수 있습니다.
텐서플로우 서빙을 이용해서 모델 서버 실행하기
TensorFlow 모델 서버를 실행하기 위해서 쿠버네티스 디플로이먼트 리소스를 정의해 보겠습니다.
“tensorflow/serving:2.1.0” 라는 컨테이너 이미지를 사용하여 모델 서버를 실행하겠습니다. 모델 서버는 실행 될 때, 주어진 파리미터 값에 의해 모델을 로드하게 됩니다. 모델 서버에 모델이 로드 되면, REST나 GRPC를 사용하여 추론 요청을 시작할 수 있습니다.
다음은 모델 서버에서 사용하는 몇 가지 파라미터입니다.
- port : GRPC 요청에 사용할 포트입니다.
- rest_api_port : REST 요청에 사용할 포트입니다.
- modeL_name : REST 요청의 URL에서 이를 사용합니다.
- mode_base_path : 모델을 저장 한 디렉토리 경로입니다. containers: – args: – –port=9000 – –rest_api_port=8500 – –model_name=mnist – –model_base_path=/app/data/export command: – /usr/bin/tensorflow_model_server image: tensorflow/serving:2.1.0
앞서 TFJob을 사용하여, 학습된 모델을 쿠버네티스의 퍼시스턴스 볼륨에 저장하였습니다. tfjob-data-volume 라는 퍼시스턴스 볼륨 클레임 이름을 가지고 있습니다. 해당 볼륨을 모델 서버에 마운트 해줍니다.
volumeMounts: - mountPath: /app/data name: tfjob-data-volume volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume
다음은 디플로이먼트 매니페스트입니다.
mnist-model-server-deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: labels: app: mnist-model-server name: mnist-model-server spec: selector: matchLabels: app: mnist-model-server template: metadata: annotations: sidecar.istio.io/inject: "false" labels: app: mnist-model-server version: v1 spec: serviceAccount: default-editor containers: - args: - --port=9000 - --rest_api_port=8500 - --model_name=mnist - --model_base_path=/app/data/export command: - /usr/bin/tensorflow_model_server image: tensorflow/serving:2.1.0 imagePullPolicy: IfNotPresent livenessProbe: initialDelaySeconds: 30 periodSeconds: 30 tcpSocket: port: 9000 name: mnist ports: - containerPort: 9000 - containerPort: 8500 volumeMounts: - mountPath: /app/data name: tfjob-data-volume volumes: - name: tfjob-data-volume persistentVolumeClaim: claimName: tfjob-data-volume
kubectl을 사용해서 admin 네임스페이스에 디플로이먼트 리소스를 생성합니다.
kubectl -n admin apply -f mnist-model-server-deployment.yaml
모델 서버에서 정상적으로 모델을 로드 했는지 여부를 확인하려면, 포드의 로그를 조회하면 됩니다.
kubectl -n admin logs -l app=mnist-model-server
정상적으로 모델이 로드 되었다면, 다음과 같은 응답 결과를 학인 수 있습니다.
2020-03-07 00:58:07.813234: I tensorflow_serving/model_servers/server.cc:86] Building single TensorFlow model file config: model_name: mnist model_base_path: /app/data/export 2020-03-07 00:58:07.814324: I tensorflow_serving/model_servers/server_core.cc:462] Adding/updating models. 2020-03-07 00:58:07.814334: I tensorflow_serving/model_servers/server_core.cc:573] (Re-)adding model: mnist 2020-03-07 00:58:07.918328: I tensorflow_serving/core/basic_manager.cc:739] Successfully reserved resources to load servable {name: mnist version: 1} 2020-03-07 00:58:07.918351: I tensorflow_serving/core/loader_harness.cc:66] Approving load for servable version {name: mnist version: 1} 2020-03-07 00:58:07.918357: I tensorflow_serving/core/loader_harness.cc:74] Loading servable version {name: mnist version: 1} 2020-03-07 00:58:07.918373: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:31] Reading SavedModel from: /app/data/export/1 2020-03-07 00:58:07.920422: I external/org_tensorflow/tensorflow/cc/saved_model/reader.cc:54] Reading meta graph with tags { serve } 2020-03-07 00:58:07.920439: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:264] Reading SavedModel debug info (if present) from: /app/data/export/1 2020-03-07 00:58:07.945844: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:203] Restoring SavedModel bundle. 2020-03-07 00:58:07.977914: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:152] Running initialization op on SavedModel bundle at path: /app/data/export/1 2020-03-07 00:58:07.983624: I external/org_tensorflow/tensorflow/cc/saved_model/loader.cc:333] SavedModel load for tags { serve }; Status: success: OK. Took 65247 microseconds. 2020-03-07 00:58:07.984126: I tensorflow_serving/servables/tensorflow/saved_model_warmup.cc:105] No warmup data file found at /app/data/export/1/assets.extra/tf_serving_warmup_requests 2020-03-07 00:58:07.984397: I tensorflow_serving/core/loader_harness.cc:87] Successfully loaded servable version {name: mnist version: 1} 2020-03-07 00:58:07.995156: I tensorflow_serving/model_servers/server.cc:358] Running gRPC ModelServer at 0.0.0.0:9000 ... 2020-03-07 00:58:07.997145: I tensorflow_serving/model_servers/server.cc:378] Exporting HTTP/REST API at:localhost:8500 ...
모델 서버에 접속하기 위해서 쿠버네티스 서비스를 리소스를 정의합니다.
mnist-model-server-service.yaml
apiVersion: v1 kind: Service metadata: labels: app: mnist-model-server name: mnist-model-server spec: ports: - name: grpc-tf-serving port: 9000 targetPort: 9000 - name: http-tf-serving port: 8500 targetPort: 8500 selector: app: mnist-model-server type: ClusterIP
kubectl을 사용해서 admin 네임스페이스에 서비스 리소스를 생성합니다.
kubectl -n admin apply -f mnist-model-server-service.yaml
텐서플로우 서빙에서 로드된 모델에게 요청 하기
아래 예제 코드를 실행하기 위해서는 request와 tensorflow-datasets라는 파이썬 패키지가 필요합니다. 설치가 되어 있지 않다면 다음 명령어로 설치할 수 있습니다.
pip install request pip install tensorflow-datasets==2.0.0
주피터 노트북 사용자 인터페이스의 메뉴에서 File > New > Notebook 을 클릭하여 노트북 환경에서 새 노트북을 생성하겠습니다.
먼저 요청에 사용할 테스트 데이터를 생성하는 코드를 작성합니다.
import os import json import numpy as np import matplotlib.pyplot as plt import subprocess import tensorflow as tf import tensorflow_datasets as tfds dataset = tfds.load("mnist", shuffle_files=True, as_supervised=False) train_dataset, test_dataset = dataset["train"], dataset["test"] test_data = [] iterator = iter(test_dataset.batch(5)) test_data.append(next(iterator))
이미지와 내용을 화면에 출력하기 위해서 show() 함수를 작성합니다.
def show(idx, title): plt.figure() plt.imshow(test_images[idx].reshape(28,28)) plt.axis('off') plt.title('\\n\\n{}'.format(title), fontdict={'size': 16})
test_data의 0번째에 있는 이미지와 라벨을 테스트 삼아 출력해 보겠습니다.
show(0, 'An Example Image: {}'.format(test_labels[0]))

모델 서버에 요청할 데이터를 json을 사용해서 작성합니다.
import json data = json.dumps({"signature_name": "serving_default", "instances": test_images.tolist()}) print('Data: {} ... {}'.format(data[:50], data[len(data)-52:]))
모델 서버에 POST 형식으로 요청 데이터를 전송하고, 예측 결과를 받습니다.
import requests headers = {"content-type": "application/json"} json_response = requests.post('<http://mnist-model-server:8500/v1/models/mnist:predict>', data=data, headers=headers) predictions = json.loads(json_response.text)['predictions']
예측 결과를 show() 함수를 사용하여 화면이 출력합니다.
for i in range(0, len(predictions)): show(i, 'The model thought this was number {} , and it was actually number {}'.format( np.argmax(predictions[i]), test_labels[i]))
정상적으로 처리 되었다면, 다음과 비슷한 응답 결과를 얻을 수 있습니다.
