استخدم خدمة TensorFlow مع Kubernetes

يوضح هذا البرنامج التعليمي كيفية استخدام مكونات TensorFlow Serving التي تعمل في حاويات Docker لخدمة نموذج TensorFlow ResNet وكيفية نشر مجموعة الخدمة مع Kubernetes.

لمعرفة المزيد حول TensorFlow Serving، نوصي بالبرنامج التعليمي الأساسي لـ TensorFlow Serving والبرنامج التعليمي المتقدم لـ TensorFlow Serving .

لمعرفة المزيد حول نموذج TensorFlow ResNet، نوصي بقراءة ResNet في TensorFlow .

الجزء 1: الإعداد

قبل البدء، قم أولاً بتثبيت Docker .

قم بتنزيل ResNet SavedModel

دعونا نمسح دليل النماذج المحلية الخاص بنا في حالة وجود واحد بالفعل:

rm -rf /tmp/resnet

قدمت الشبكات المتبقية العميقة، أو ResNets باختصار، فكرة رائعة لرسم خرائط الهوية من أجل تمكين تدريب الشبكات العصبية التلافيفية العميقة جدًا. على سبيل المثال، سنقوم بتنزيل TensorFlow SavedModel من ResNet لمجموعة بيانات ImageNet.

# Download Resnet model from TF Hub
wget https://tfhub.dev/tensorflow/resnet_50/classification/1?tf-hub-format=compressed -o resnet.tar.gz

# Extract SavedModel into a versioned subfolder ‘123’
mkdir -p /tmp/resnet/123
tar xvfz resnet.tar.gz -C /tmp/resnet/123/

يمكننا التحقق من أن لدينا SavedModel:

$ ls /tmp/resnet/*
saved_model.pb  variables

الجزء 2: التشغيل في دوكر

الالتزام بالصورة للنشر

نريد الآن التقاط صورة عرض وتنفيذ جميع التغييرات على صورة جديدة $USER/resnet_serving لنشر Kubernetes.

أولاً نقوم بتشغيل صورة التقديم كبرنامج خفي:

docker run -d --name serving_base tensorflow/serving

بعد ذلك، نقوم بنسخ بيانات نموذج ResNet إلى مجلد نموذج الحاوية:

docker cp /tmp/resnet serving_base:/models/resnet

أخيرًا نلزم الحاوية بخدمة نموذج ResNet:

docker commit --change "ENV MODEL_NAME resnet" serving_base \
  $USER/resnet_serving

الآن دعونا نوقف حاوية قاعدة التقديم

docker kill serving_base
docker rm serving_base

ابدأ الخادم

لنبدأ الآن الحاوية بنموذج ResNet حتى تصبح جاهزة للعرض، ونكشف عن منفذ gRPC 8500:

docker run -p 8500:8500 -t $USER/resnet_serving &

الاستعلام عن الخادم

بالنسبة للعميل، سنحتاج إلى استنساخ TensorFlow Serving GitHub repo:

git clone https://github.com/tensorflow/serving
cd serving

استعلم عن الخادم باستخدام resnet_client_grpc.py . يقوم العميل بتنزيل الصورة وإرسالها عبر gRPC لتصنيفها إلى فئات ImageNet .

tools/run_in_docker.sh python tensorflow_serving/example/resnet_client_grpc.py

يجب أن يؤدي هذا إلى إخراج مثل:

outputs {
  key: "classes"
  value {
    dtype: DT_INT64
    tensor_shape {
      dim {
        size: 1
      }
    }
    int64_val: 286
  }
}
outputs {
  key: "probabilities"
  value {
    dtype: DT_FLOAT
    tensor_shape {
      dim {
        size: 1
      }
      dim {
        size: 1001
      }
    }
    float_val: 2.41628322328e-06
    float_val: 1.90121829746e-06
    float_val: 2.72477100225e-05
    float_val: 4.42638565801e-07
    float_val: 8.98362372936e-07
    float_val: 6.84421956976e-06
    float_val: 1.66555237229e-05
...
    float_val: 1.59407863976e-06
    float_val: 1.2315689446e-06
    float_val: 1.17812135159e-06
    float_val: 1.46365800902e-05
    float_val: 5.81210713335e-07
    float_val: 6.59980651108e-05
    float_val: 0.00129527016543
  }
}
model_spec {
  name: "resnet"
  version {
    value: 123
  }
  signature_name: "serving_default"
}

إنها تعمل! نجح الخادم في تصنيف صورة قطة!

الجزء 3: النشر في Kubernetes

نستخدم في هذا القسم صورة الحاوية المضمنة في الجزء 0 لنشر مجموعة الخدمة مع Kubernetes في Google Cloud Platform .

تسجيل الدخول لمشروع GCloud

نفترض هنا أنك قمت بإنشاء وتسجيل الدخول إلى مشروع gcloud باسم tensorflow-serving .

gcloud auth login --project tensorflow-serving

إنشاء مجموعة حاوية

نقوم أولاً بإنشاء مجموعة Google Kubernetes Engine لنشر الخدمة.

$ gcloud container clusters create resnet-serving-cluster --num-nodes 5

والتي يجب أن تنتج شيئًا مثل:

Creating cluster resnet-serving-cluster...done.
Created [https://container.googleapis.com/v1/projects/tensorflow-serving/zones/us-central1-f/clusters/resnet-serving-cluster].
kubeconfig entry generated for resnet-serving-cluster.
NAME                       ZONE           MASTER_VERSION  MASTER_IP        MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
resnet-serving-cluster  us-central1-f  1.1.8           104.197.163.119  n1-standard-1  1.1.8         5          RUNNING

قم بتعيين المجموعة الافتراضية لأمر حاوية gcloud وقم بتمرير بيانات اعتماد المجموعة إلى kubectl .

gcloud config set container/cluster resnet-serving-cluster
gcloud container clusters get-credentials resnet-serving-cluster

والتي ينبغي أن تؤدي إلى:

Fetching cluster endpoint and auth data.
kubeconfig entry generated for resnet-serving-cluster.

قم بتحميل صورة عامل الميناء

لننقل الآن صورتنا إلى Google Container Registry حتى نتمكن من تشغيلها على Google Cloud Platform.

أولاً نقوم بوضع علامة على الصورة $USER/resnet_serving باستخدام تنسيق Container Registry واسم مشروعنا،

docker tag $USER/resnet_serving gcr.io/tensorflow-serving/resnet

بعد ذلك، نقوم بتكوين Docker لاستخدام gcloud كمساعد بيانات الاعتماد:

gcloud auth configure-docker

بعد ذلك نقوم بدفع الصورة إلى التسجيل،

docker push gcr.io/tensorflow-serving/resnet

إنشاء نشر وخدمة Kubernetes

يتكون النشر من 3 نسخ متماثلة لخادم resnet_inference يتم التحكم فيها بواسطة نشر Kubernetes . يتم عرض النسخ المتماثلة خارجيًا بواسطة خدمة Kubernetes بالإضافة إلى موازن التحميل الخارجي .

نقوم بإنشائها باستخدام المثال Kubernetes config resnet_k8s.yaml .

kubectl create -f tensorflow_serving/example/resnet_k8s.yaml

مع الإخراج:

deployment "resnet-deployment" created
service "resnet-service" created

لعرض حالة النشر والبودات:

$ kubectl get deployments
NAME                    DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
resnet-deployment    3         3         3            3           5s
$ kubectl get pods
NAME                         READY     STATUS    RESTARTS   AGE
resnet-deployment-bbcbc   1/1       Running   0          10s
resnet-deployment-cj6l2   1/1       Running   0          10s
resnet-deployment-t1uep   1/1       Running   0          10s

لعرض حالة الخدمة:

$ kubectl get services
NAME                    CLUSTER-IP       EXTERNAL-IP       PORT(S)     AGE
resnet-service       10.239.240.227   104.155.184.157   8500/TCP    1m

قد يستغرق الأمر بعض الوقت حتى يصبح كل شيء جاهزًا للعمل.

$ kubectl describe service resnet-service
Name:           resnet-service
Namespace:      default
Labels:         run=resnet-service
Selector:       run=resnet-service
Type:           LoadBalancer
IP:         10.239.240.227
LoadBalancer Ingress:   104.155.184.157
Port:           <unset> 8500/TCP
NodePort:       <unset> 30334/TCP
Endpoints:      <none>
Session Affinity:   None
Events:
  FirstSeen LastSeen    Count   From            SubobjectPath   Type        Reason      Message
  --------- --------    -----   ----            -------------   --------    ------      -------
  1m        1m      1   {service-controller }           Normal      CreatingLoadBalancer    Creating load balancer
  1m        1m      1   {service-controller }           Normal      CreatedLoadBalancer Created load balancer

يتم إدراج عنوان IP الخارجي للخدمة بجوار LoadBalancer Ingress.

الاستعلام عن النموذج

يمكننا الآن الاستعلام عن الخدمة على عنوانها الخارجي من مضيفنا المحلي.

$ tools/run_in_docker.sh python \
  tensorflow_serving/example/resnet_client_grpc.py \
  --server=104.155.184.157:8500
outputs {
  key: "classes"
  value {
    dtype: DT_INT64
    tensor_shape {
      dim {
        size: 1
      }
    }
    int64_val: 286
  }
}
outputs {
  key: "probabilities"
  value {
    dtype: DT_FLOAT
    tensor_shape {
      dim {
        size: 1
      }
      dim {
        size: 1001
      }
    }
    float_val: 2.41628322328e-06
    float_val: 1.90121829746e-06
    float_val: 2.72477100225e-05
    float_val: 4.42638565801e-07
    float_val: 8.98362372936e-07
    float_val: 6.84421956976e-06
    float_val: 1.66555237229e-05
...
    float_val: 1.59407863976e-06
    float_val: 1.2315689446e-06
    float_val: 1.17812135159e-06
    float_val: 1.46365800902e-05
    float_val: 5.81210713335e-07
    float_val: 6.59980651108e-05
    float_val: 0.00129527016543
  }
}
model_spec {
  name: "resnet"
  version {
    value: 1538687457
  }
  signature_name: "serving_default"
}

لقد نجحت في نشر نموذج ResNet الذي يعمل كخدمة في Kubernetes!