أصبح Tensorflow منصة قياسية للتعلم الآلي (ML) ، شعبية في كل من الصناعة والبحث. تم إنشاء العديد من المكتبات والأدوات والأطر المجانية لتدريب وصيانة نماذج ML. يساعد مشروع Tensorflow Serving في الحفاظ على نماذج ML في بيئة الإنتاج الموزعة.
تستخدم خدمة Mux الخاصة بنا خدمة Tensorflow في عدة أجزاء من البنية التحتية ، وقد ناقشنا بالفعل استخدام خدمة Tensorflow في ترميز عناوين الفيديو. سنركز اليوم على الأساليب التي تعمل على تحسين زمن الوصول من خلال تحسين كل من خادم التنبؤ والعميل. عادة ما تكون التنبؤات النموذجية عبارة عن عمليات "عبر الإنترنت" (على المسار الحرج لطلب تقديم طلب) ، وبالتالي ، فإن الأهداف الرئيسية للتحسين هي معالجة كميات كبيرة من الطلبات بأقل تأخير ممكن.
ما هي خدمة Tensorflow؟
يوفر Tensorflow Serving بنية خادم مرنة لنشر وصيانة نماذج ML. بمجرد تدريب النموذج وجاهزة للاستخدام للتنبؤ ، يتطلب Tensorflow Serving تصديره إلى تنسيق متوافق (قابل للتنفيذ).
Servable عبارة عن تجريد مركزي يلف كائنات Tensorflow. على سبيل المثال ، يمكن تمثيل نموذج ككائن واحد أو أكثر من الكائنات القابلة للخدمة. وبالتالي ، Servable هي الكائنات الأساسية التي يستخدمها العميل لتنفيذ العمليات الحسابية. أهمية الحجم القابل للتطبيق: تشغل الموديلات الأصغر مساحة أقل وتستخدم ذاكرة أقل وتحمّل بشكل أسرع. لتنزيل وصيانة واجهة برمجة تطبيقات Predict ، يجب أن تكون الطرز في تنسيق SavedModel.

يجمع Tensorflow Serving بين المكونات الأساسية لإنشاء خادم gRPC / HTTP الذي يخدم العديد من طرز ML (أو عدة إصدارات) ، ويوفر مكونات المراقبة وهيكل مخصص.
Tensorflow خدمة مع عامل الميناء
دعنا نلقي نظرة على المقاييس الأساسية للكمون في التنبؤ بالأداء من خلال إعدادات خدمة Tensorflow القياسية (بدون تحسين وحدة المعالجة المركزية).
أولاً ، قم بتنزيل أحدث صورة من مركز TensorFlow Docker:
docker pull tensorflow/serving:latest
في هذه المقالة ، تعمل جميع الحاويات على مضيف به أربعة نوى ، 15 جيجابايت ، أوبونتو 16.04.
تصدير نموذج Tensorflow إلى SavedModel
عند تدريب نموذج باستخدام Tensorflow ، يمكن حفظ الإخراج كنقاط تحكم متغيرة (ملفات على القرص). يتم تنفيذ الإخراج مباشرة من خلال استعادة نقاط التحكم في النموذج أو في تنسيق الرسم البياني المجمدة المجمدة (ملف ثنائي).
بالنسبة إلى Tensorflow Serving ، يجب تصدير هذا الرسم البياني المجمد إلى تنسيق SavedModel. تحتوي
وثائق Tensorflow على أمثلة لتصدير النماذج المدربة إلى تنسيق SavedModel.
يوفر Tensorflow أيضًا العديد من النماذج
الرسمية والبحثية كنقطة انطلاق للتجريب أو البحث أو الإنتاج.
على سبيل المثال ، سوف نستخدم
نموذج الشبكة العصبية العميقة (ResNet) لتصنيف مجموعة بيانات ImageNet من 1000 فئة. قم بتنزيل
ResNet-50 v2
، وتحديداً خيار Channels_last (NHWC) في
SavedModel : كقاعدة عامة ، يعمل بشكل أفضل على وحدة المعالجة المركزية.
انسخ دليل طراز RestNet إلى الهيكل التالي:
models/ 1/ saved_model.pb variables/ variables.data-00000-of-00001 variables.index
تتوقع خدمة Tensorflow بنية دليل مرتبة عدديًا للإصدار. في حالتنا ، يتوافق الدليل
1/
مع طراز الإصدار 1 ، الذي يحتوي على بنية طراز
saved_model.pb
مع لقطة من أوزان النموذج (المتغيرات).
تحميل ومعالجة SavedModel
يبدأ الأمر التالي في تشغيل خادم طراز Tensorflow Serving في حاوية Docker. لتحميل SavedModel ، يجب عليك تحميل دليل الطراز في دليل الحاوية المتوقع.
docker run -d -p 9000:8500 \ -v $(pwd)/models:/models/resnet -e MODEL_NAME=resnet \ -t tensorflow/serving:latest
يوضح التحقق من سجلات الحاوية أن ModelServer قيد التشغيل للتعامل مع طلبات الإخراج لطراز
resnet
في نقطتي gRPC و HTTP:
... I tensorflow_serving/core/loader_harness.cc:86] Successfully loaded servable version {name: resnet version: 1} I tensorflow_serving/model_servers/server.cc:286] Running gRPC ModelServer at 0.0.0.0:8500 ... I tensorflow_serving/model_servers/server.cc:302] Exporting HTTP/REST API at:localhost:8501 ...
التنبؤ العميل
يحدد Tensorflow Serving مخطط API في تنسيق
مخازن بروتوكول (protobufs). يتم حزم تطبيقات عميل GRPC لواجهة برمجة التطبيقات للتنبؤ كحزمة Python
tensorflow_serving.apis
. سنحتاج إلى حزمة
tensorflow
حزمة Python
tensorflow
المرافق.
تثبيت التبعيات لإنشاء عميل بسيط:
virtualenv .env && source .env/bin/activate && \ pip install numpy grpcio opencv-python tensorflow tensorflow-serving-api
يتوقع نموذج
ResNet-50 v2
مدخلات
ResNet-50 v2
العائمة في بنية بيانات التنسيق (NHWC) المنسقة. لذلك ، تتم قراءة صورة الإدخال باستخدام opencv-python وتحميلها في صفيف numpy (ارتفاع × عرض × قنوات) كنوع بيانات float32. يقوم البرنامج النصي أدناه بإنشاء كعب عميل تنبؤ وتحميل بيانات JPEG في صفيف numpy ، ويقوم بتحويلها إلى tensor_proto لتقديم طلب تنبؤ لـ gRPC:
بعد تلقي إدخال JPEG ، سينتج عن عميل عامل النتيجة التالية:
python tf_serving_client.py --image=images/pupper.jpg total time: 2.56152906418s
يحتوي الموتر الناتج على تنبؤ في شكل قيمة عددية واحتمال وجود علامات.
outputs { key: "classes" value { dtype: DT_INT64 tensor_shape { dim { size: 1 } } int64_val: 238 } } outputs { key: "probabilities" ...
لطلب واحد ، مثل هذا التأخير غير مقبول. لكن ليس من المستغرب أن يتم تصميم Tensorflow Serving binary افتراضيًا لأوسع مجموعة من المعدات لمعظم حالات الاستخدام. ربما لاحظت الأسطر التالية في سجلات حاوية Tensorflow Serving القياسية:
I external/org_tensorflow/tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
يشير هذا إلى وجود TensorFlow Serving ثنائي يعمل على منصة وحدة المعالجة المركزية والتي لم يتم تحسينها.
بناء ثنائي الأمثل
وفقًا
لوثائق Tensorflow ، يوصى بترجمة Tensorflow من المصدر مع جميع التحسينات المتاحة لوحدة المعالجة المركزية على المضيف حيث سيعمل الثنائي. عند التجميع ، تتيح العلامات الخاصة تنشيط مجموعات تعليمات وحدة المعالجة المركزية لمنصة معينة:
استنساخ Tensorflow تقديم إصدار محدد. في حالتنا ، هذا هو 1.13 (الأخير في وقت نشر هذه المقالة):
USER=$1 TAG=$2 TF_SERVING_VERSION_GIT_BRANCH="r1.13" git clone --branch="$TF_SERVING_VERSION_GIT_BRANCH" https://github.com/tensorflow/serving
تستخدم صورة dev Tensorflow Serving أداة بازل للبناء. نحن تكوينه لمجموعات محددة من تعليمات وحدة المعالجة المركزية:
TF_SERVING_BUILD_OPTIONS="--copt=-mavx --copt=-mavx2 --copt=-mfma --copt=-msse4.1 --copt=-msse4.2"
إذا لم تكن هناك ذاكرة كافية ،
--local_resources=2048,.5,1.0
استهلاك الذاكرة أثناء عملية
--local_resources=2048,.5,1.0
باستخدام العلامة
--local_resources=2048,.5,1.0
. للحصول على معلومات حول العلامات ، راجع
مساعدة Tensorflow Serving and Docker ، وكذلك
وثائق Bazel .
قم بإنشاء صورة عمل بناءً على الصورة الحالية:
تم تكوين
ModelServer باستخدام
إشارات TensorFlow لدعم التزامن. الخيارات التالية تكوين تجمعات مؤشر ترابط اثنين لعملية متوازية:
intra_op_parallelism_threads
- يتحكم في الحد الأقصى لعدد مؤشرات الترابط للتنفيذ المتوازي لعملية واحدة ؛
- تستخدم لموازنة العمليات التي لها عمليات فرعية مستقلة في الطبيعة.
inter_op_parallelism_threads
- يتحكم في الحد الأقصى لعدد مؤشرات الترابط للتنفيذ المتوازي للعمليات المستقلة ؛
- عمليات Tensorflow Graph ، والتي تكون مستقلة عن بعضها البعض ، وبالتالي ، يمكن تنفيذها في خيوط مختلفة.
افتراضيًا ، يتم تعيين المعلمتين على
0
. هذا يعني أن النظام نفسه يختار الرقم المناسب ، والذي يعني غالبًا خيط واحد لكل نواة. ومع ذلك ، يمكن تغيير المعلمة يدوياً من أجل التزامن متعدد النواة.
ثم قم بتشغيل الحاوية Serving بنفس طريقة الحاوية السابقة ، هذه المرة مع صورة Docker تم تجميعها من المصادر ومع إشارات Tensorflow المحسنة لمعالج محدد:
docker run -d -p 9000:8500 \ -v $(pwd)/models:/models/resnet -e MODEL_NAME=resnet \ -t $USER/tensorflow-serving:$TAG \ --tensorflow_intra_op_parallelism=4 \ --tensorflow_inter_op_parallelism=4
سجلات الحاويات يجب أن لا تظهر التحذيرات حول وحدة المعالجة المركزية غير محددة. بدون تغيير الكود بناءً على طلب التوقع نفسه ، يتم تقليل التأخير بحوالي 35.8٪:
python tf_serving_client.py --image=images/pupper.jpg total time: 1.64234706879s
زيادة السرعة في التنبؤ العميل
هل لا يزال من الممكن تسريع؟ لقد قمنا بتحسين جانب الخادم لوحدة المعالجة المركزية الخاصة بنا ، ولكن لا يزال تأخير أكثر من ثانية كبيرًا جدًا.
حدث أن تحميل مكتبات
tensorflow
و
tensorflow
يُسهم بشكل كبير في التأخير. يضيف كل مكالمة غير ضرورية إلى
tf.contrib.util.make_tensor_proto
أيضًا تقسيم ثانية.
قد تسأل: "ألا نحتاج إلى حزم TensorFlow Python لتقديم طلبات التنبؤ بالفعل إلى خادم Tensorflow؟" في الواقع ، ليست هناك
حاجة حقيقية
tensorflow_serving
و
tensorflow
.
كما ذكر سابقًا ، يتم تعريف واجهات برمجة التطبيقات للتنبؤ Tensorflow على أنها مخازن مؤقتة. لذلك ، يمكن استبدال اثنين من التبعيات الخارجية
tensorflow
و
tensorflow
المقابلة - ومن ثم لن تحتاج إلى سحب مكتبة Tensorflow (الثقيلة) بالكامل على العميل.
أولاً ، تخلص من
tensorflow
و
tensorflow_serving
وأضف حزمة
grpcio-tools
.
pip uninstall tensorflow tensorflow-serving-api && \ pip install grpcio-tools==1.0.0
قم
tensorflow/tensorflow
و
tensorflow/serving
المستودعات ونسخ ملفات protobuf التالية إلى مشروع العميل:
tensorflow/serving/ tensorflow_serving/apis/model.proto tensorflow_serving/apis/predict.proto tensorflow_serving/apis/prediction_service.proto tensorflow/tensorflow/ tensorflow/core/framework/resource_handle.proto tensorflow/core/framework/tensor_shape.proto tensorflow/core/framework/tensor.proto tensorflow/core/framework/types.proto
انسخ ملفات protobuf هذه إلى
protos/
directory مع الحفاظ على المسارات الأصلية:
protos/ tensorflow_serving/ apis/ *.proto tensorflow/ core/ framework/ *.proto
من أجل البساطة ، يمكن تبسيط
prediction_service.proto لتنفيذ Predict RPC فقط حتى لا يتم تنزيل التبعيات المتداخلة لـ RPC الأخرى المحددة في الخدمة.
فيما يلي مثال على
prediction_service.
المبسطة.
إنشاء تطبيقات PyRon gRPC باستخدام
grpcio.tools.protoc
:
PROTOC_OUT=protos/ PROTOS=$(find . | grep "\.proto$") for p in $PROTOS; do python -m grpc.tools.protoc -I . --python_out=$PROTOC_OUT --grpc_python_out=$PROTOC_OUT $p done
الآن يمكن إزالة وحدة
tensorflow_serving
بأكملها:
from tensorflow_serving.apis import predict_pb2 from tensorflow_serving.apis import prediction_service_pb2
... واستبدالها باستخدام protobuffers التي تم إنشاؤها من
protos/tensorflow_serving/apis
:
from protos.tensorflow_serving.apis import predict_pb2 from protos.tensorflow_serving.apis import prediction_service_pb2
يتم استيراد مكتبة Tensorflow لاستخدام وظيفة المساعد
make_tensor_proto
، وهي
ضرورية للالتفاف على كائن python / numpy ككائن TensorProto.
وبالتالي ، يمكننا استبدال جزء التبعية والرمز التالي:
import tensorflow as tf ... tensor = tf.contrib.util.make_tensor_proto(features) request.inputs['inputs'].CopyFrom(tensor)
استيراد protobuffers وبناء كائن TensorProto:
from protos.tensorflow.core.framework import tensor_pb2 from protos.tensorflow.core.framework import tensor_shape_pb2 from protos.tensorflow.core.framework import types_pb2 ...
النص الكامل لبيثون
هنا . قم بتشغيل عميل بدء تشغيل محدث يقدم طلب تنبؤ لخدمة Tensorflow المحسنة:
python tf_inception_grpc_client.py --image=images/pupper.jpg total time: 0.58314920859s
يوضح المخطط التالي وقت التنفيذ المتوقع في الإصدار الأمثل من Tensorflow Serving مقارنةً بالمعيار الذي يزيد عن 10 مرات:

انخفض متوسط التأخير بنحو 3.38 مرة.
عرض النطاق الترددي الأمثل
يمكن تكوين خدمة Tensorflow للتعامل مع كميات كبيرة من البيانات. عادةً ما يتم إجراء تحسين عرض النطاق الترددي لمعالجة الدُفعات "المستقلة" ، حيث لا تكون حدود زمن الوصول الضيق متطلبًا صارمًا.
خادم الجانب تجهيز الدفعات
كما هو مذكور في
الوثائق ، يتم دعم معالجة الدُفعات من جانب الخادم أصلاً في خدمة Tensorflow.
يتم تحديد المفاضلة بين الكمون والإنتاجية بواسطة معلمات معالجة الدُفعات. إنها تسمح لك بتحقيق الحد الأقصى من الإنتاجية التي تكون مسرعات الأجهزة قادرة عليها.
لتمكين التعبئة ، اضبط -
--batching_parameters_file
و -
--batching_parameters_file
. يتم تعيين المعلمات وفقًا
SessionBundleConfig . بالنسبة للأنظمة على وحدة المعالجة المركزية ، قم بتعيين
num_batch_threads
على عدد النوى المتاحة. ل GPU ، راجع المعلمات المناسبة
هنا .
بعد ملء الحزمة بأكملها على جانب الخادم ، يتم دمج طلبات الإصدار في طلب واحد كبير (tensor) ، وإرسالها إلى جلسة Tensorflow مع طلب مشترك. في هذه الحالة ، تشارك التوازي وحدة المعالجة المركزية / GPU حقا.
بعض الاستخدامات الشائعة لمعالجة الدفعات Tensorflow:
- استخدام طلبات العميل غير المتزامنة لتعبئة الحزم من جانب الخادم
- معالجة دفعية أسرع عن طريق نقل مكونات الرسم البياني النموذجي إلى وحدة المعالجة المركزية / وحدة معالجة الرسومات
- خدمة الطلبات من نماذج متعددة من خادم واحد
- يوصى بشدة بمعالجة الدُفعات للمعالجة "غير المتصلة" لعدد كبير من الطلبات
العميل دفعة تجهيز تجهيز
مجموعات المعالجة الدفعية من جانب العميل بتجميع العديد من الطلبات الواردة في واحد.
نظرًا لأن نموذج ResNet ينتظر الإدخال بتنسيق NHWC (البعد الأول هو عدد المدخلات) ، يمكننا دمج عدة صور إدخال في طلب RPC واحد:
... batch = [] for jpeg in os.listdir(FLAGS.images_path): path = os.path.join(FLAGS.images_path, jpeg) img = cv2.imread(path).astype(np.float32) batch.append(img) ... batch_np = np.array(batch).astype(np.float32) dims = [tensor_shape_pb2.TensorShapeProto.Dim(size=dim) for dim in batch_np.shape] t_shape = tensor_shape_pb2.TensorShapeProto(dim=dims) tensor = tensor_pb2.TensorProto( dtype=types_pb2.DT_FLOAT, tensor_shape=t_shape, float_val=list(batched_np.reshape(-1))) request.inputs['inputs'].CopyFrom(tensor)
بالنسبة لحزمة من الصور N ، سيحتوي موتر الإخراج في الاستجابة على نتائج التنبؤ لنفس عدد المدخلات. في حالتنا ، N = 2:
outputs { key: "classes" value { dtype: DT_INT64 tensor_shape { dim { size: 2 } } int64_val: 238 int64_val: 121 } } ...
تسريع الأجهزة
بضع كلمات عن GPUs.
تستخدم عملية التعلم بشكل طبيعي التوازي على وحدة معالجة الرسومات ، حيث أن بناء الشبكات العصبية العميقة يتطلب حسابات ضخمة لتحقيق الحل الأمثل.
لكن بالنسبة لنتائج النتائج ، فإن التوازي ليس واضحًا للغاية. غالبًا ما يمكنك تسريع إخراج الشبكة العصبية إلى وحدة معالجة الرسومات ، ولكن عليك اختيار المعدات واختبارها بعناية ، وإجراء تحليل تقني واقتصادي متعمق. تعد موازنة الأجهزة أكثر قيمة في معالجة الدُفعة من الاستنتاجات "المستقلة" (وحدات التخزين الضخمة).
قبل الانتقال إلى وحدة معالجة الرسومات ، فكر في متطلبات العمل من خلال تحليل دقيق للتكاليف (النقدية والتشغيلية والفنية) لتحقيق أكبر فائدة (تقليل زمن الوصول ، وارتفاع الإنتاجية).